/* Copyright (c) 1990,1991,1992 Chris and John Downey */
#ifndef lint
static char *sccsid = "@(#)unix.c	2.7 (Chris & John Downey) 11/25/92";
#endif

/***

* program name:
    xvi
* function:
    PD version of UNIX "vi" editor, with extensions.
* module name:
    unix.c
* module function:
    System interface routines for all versions of UNIX.
* history:
    STEVIE - ST Editor for VI Enthusiasts, Version 3.10
    Originally by Tim Thompson (twitch!tjt)
    Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
    Heavily modified by Chris & John Downey

***/

#include "xvi.h"

#ifndef SIGINT
#   include <signal.h>		/* get signals for call_shell() */
#endif

#ifdef	BSD
#   include <sys/wait.h>	/* get wait stuff for call_shell() */
    typedef	union wait Wait_t;
#else
    typedef	int Wait_t;
#endif

/*
 * CTRL is defined by sgtty.h (or by a file it includes)
 * so we undefine it here to avoid conflicts with the
 * version defined in "xvi.h".
 */
#undef	CTRL

#ifdef	sun
#   ifndef TERMIOS
#	define	TERMIOS
#   endif
#endif

#ifdef TERMIOS
#   ifndef TERMIO
#	define	TERMIO
#   endif
#endif

#ifdef	TERMIO
#   ifdef	TERMIOS
#	include <termios.h>
   typedef struct termios	Termstate;
#	define getstate(p)	((void) tcgetattr(0, (p)))
#	define setstate(p)	((void) tcsetattr(0, TCSANOW, (p)))
#	define w_setstate(p)	((void) tcsetattr(0, TCSADRAIN, (p)))
#   else	/* no TERMIOS */
#	include <termio.h>
   typedef struct termio	Termstate;
#	define getstate(p)	((void) ioctl(0,TCGETA,(char *)(p)))
#	define setstate(p)	((void) ioctl(0,TCSETA,(char *)(p)))
#	define w_setstate(p)	((void) ioctl(0,TCSETAW,(char *)(p)))
#   endif	/* no TERMIOS */

    /*
     * Table of line speeds ... exactly 16 long, and the CBAUD mask
     * is 017 (i.e. 15) so we will never access outside the array.
     */
    short	speeds[] = {
	/* B0 */	0,
	/* B50 */	50,
	/* B75 */	75,
	/* B110 */	110,
	/* B134 */	134,
	/* B150 */	150,
	/* B200 */	200,
	/* B300 */	300,
	/* B600 */	600,
	/* B1200 */	1200,
	/* B1800 */	1800,
	/* B2400 */	2400,
	/* B4800 */	4800,
	/* B9600 */	9600,
	/* EXTA */	19200,		/* not defined at V.2 */
	/* EXTB */	38400,		/* not defined at V.2 */
    };

#else	/* not TERMIO */

#   include <sgtty.h>
    typedef struct sgttyb	Termstate;

    static	struct	tchars	ckd_tchars, raw_tchars;
    static	struct	ltchars	ckd_ltchars, raw_ltchars;

#   ifdef FD_SET
#	define	fd_set_type	fd_set
#   else		/* FD_SET not defined */
	/*
	 * BSD 4.2 doesn't have these macros.
	 */
	typedef int fd_set_type;
#	define	FD_ZERO(p)	(*(p) = 0)
#	define	FD_SET(f,p)	(*(p) |= (1 << (f)))
#   endif		/* FD_SET not defined */
#endif	/* not TERMIO */

static	Termstate	cooked_state, raw_state;

#undef	CTRL

#ifdef	SETVBUF_AVAIL
    /*
     * Output buffer to save function calls.
     */
    static	char	outbuffer[128];
#endif

#ifdef MEMTEST
#   include <sys/resource.h>
#endif		/* MEMTEST */

/*
 * Expected for termcap's benefit.
 */
short		ospeed;			/* tty's baud rate */

/*
 * We sometimes use a lot of system calls while trying to read from
 * the keyboard; these are needed to make our automatic buffer
 * preservation and input timeouts work properly. Nevertheless, it
 * is possible that, with this much overhead, a reasonably fast typist
 * could get ahead of us, so we do a small amount of input buffering
 * to reduce the number of system calls.
 *
 * This variable gives the number of characters in the buffer.
 */
static int	kb_nchars;

/*
 * Get a single byte from the keyboard.
 *
 * If the keyboard input buffer is empty, & read() fails or times out,
 * return EOF.
 */
static int
kbgetc()
{
    static unsigned char	kbuf[48];
    static unsigned char	*kbp;

    if (kb_nchars <= 0) {
	int nread;

	if ((nread = read(0, (char *) kbuf, sizeof kbuf)) <= 0) {
	    return EOF;
	} else {
	    kb_nchars = nread;
	    kbp = kbuf;
	}
    }
    --kb_nchars;
    return(*kbp++);
}

#ifdef TERMIO

/*
 * Set a timeout on standard input. 0 means no timeout.
 *
 * This depends on raw_state having been properly initialized, which
 * should have been done by sys_startv().
 */
static void
input_timeout(msec)
long	msec;
{
    int		vtime;
    static int	lastvtime;

    /*
     * If the device state hasn't been changed since last time, we
     * don't need to do anything.
     */
    if ((vtime = (msec + 99) / 100) != lastvtime) {
	lastvtime = vtime;
	raw_state.c_cc[VMIN] = (vtime == 0 ? 1 : 0);
	raw_state.c_cc[VTIME] = vtime;
	setstate(&raw_state);
    }
}

#endif	/* TERMIO */

/*
 * Get a character from the keyboard.
 *
 * Make sure screen is updated first.
 */
int
inch(timeout)
long	timeout;
{
    int		c;

    /*
     * If we had characters left over from last time, return one.
     *
     * Note that if this happens, we don't call flush_output().
     */
    if (kb_nchars > 0) {
	return(kbgetc());
    }

    /*
     * Need to get a character. First, flush output to the screen.
     */
    flush_output();

#ifdef TERMIO
    if (timeout != 0) {
	input_timeout(timeout);
	c = kbgetc();
	input_timeout(0L);
	return(c);
    }
#else	/* no TERMIO */
    if (timeout != 0) {
	struct timeval	tv;
	fd_set_type	readfds;

	tv.tv_sec = (long) (timeout / 1000);
	tv.tv_usec = ((long) timeout * 1000) % (long) 1000000;

	FD_ZERO(&readfds);
	FD_SET(0, &readfds);

	/*
	 * If select does not return 0, some input is available
	 * (ignoring the possibility of errors). Otherwise, we
	 * timed out, so return EOF.
	 */
	if (select(1, &readfds, (fd_set_type *) NULL,
		    (fd_set_type *) NULL, &tv) == 0) {
	    return(EOF);
	}
    }
#endif	/* no TERMIO */

    /*
     * Keep trying until we get at least one character.
     */
    while ((c = kbgetc()) == EOF)
	;

    return(c);
}

#ifdef	NEED_STRERROR
const char *
strerror(err)
int	err;
{
    extern char	*sys_errlist[];
    extern int	sys_nerr;

    return(
	err == 0 ?
	    "No error"
	    :
	    (err > 0 && err < sys_nerr) ?
		sys_errlist[err]
		:
		"Unknown error"
    );
}
#endif /* NEED_STRERROR */

static int
runvp(args)
char	**args;
{
    int		pid;
    Wait_t	status;

    pid = fork();
    switch (pid) {
    case -1:		/* fork() failed */
	return(-1);

    case 0:			/* this is the child */
	(void) signal(SIGINT, SIG_DFL);
	(void) signal(SIGQUIT, SIG_DFL);
	(void) execvp(args[0], args);

	/*
	 * Couldn't do it ... use standard output functions here
	 * because none of xvi's higher-level functions are usable
	 * from this module.
	 */
	(void) fputs("\007Can't execute ", stdout);
	(void) fputs(args[0], stdout);
	(void) fputs("\n(", stdout);
	(void) fputs(strerror(errno), stdout);
	(void) fputs(")\nHit return to continue", stdout);
	(void) fflush(stdout);
	(void) getc(stdin);
	exit(1);

    default:		/* this is the parent */
	while (wait(&status) != pid)
	    ;
	return(0);
    }
}

int
call_shell(sh)
char	*sh;
{
    static char	*args[] = { NULL, NULL };

    args[0] = sh;
    return(runvp(args));
}

int
call_system(command)
char	*command;
{
    static char	*args[] = { NULL, "-c", NULL, NULL };

    if (Ps(P_shell) == NULL) {
	(void) puts("\007Can't execute command without SHELL parameter");
	return(-1);
    }
    args[0] = Ps(P_shell);
    args[2] = command;
    return(runvp(args));
}

#ifdef ITIMER_REAL
    static int
    nothing()
    {
	return(0);
    }
#endif

/*
 * Delay for a short time - preferably less than 1 second.
 * This is for use by showmatch, which wants to hold the
 * cursor over the matching bracket for just long enough
 * that it will be seen.
 */
void
delay()
{
#ifdef ITIMER_REAL
    struct itimerval	timer;
    
    (void) signal(SIGALRM, nothing);

    /*
     * We want to pause for 200 msec (1/5th of a second) here,
     * as this seems like a reasonable figure. Note that we can
     * assume that the implementation will have defined tv_usec
     * of a type large enough to hold up to 999999, since that's
     * the largest number of microseconds we can possibly need.
     */
    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = 0;
    timer.it_value.tv_sec = 0;
    timer.it_value.tv_usec = 200000;
    if (setitimer(ITIMER_REAL, &timer, (struct itimerval *) NULL) == -1)
	return;
    (void) pause();

    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = 0;
    (void) setitimer(ITIMER_REAL, &timer, (struct itimerval *) NULL);
#else /* not ITIMER_REAL */
    sleep(1);
#endif /* not ITIMER_REAL */
}

/*
 * Initialise the terminal so that we can do single-character
 * input and output, with no strange mapping, and no echo of
 * input characters.
 *
 * This must be done before any screen-based i/o is performed.
 */
void
sys_init()
{
#ifdef	TIOCGWINSZ
    struct winsize	winsz;	/* for getting window wize */
#endif

    /*
     * What the device driver thinks the window's dimensions are.
     */
    unsigned int	rows = 0;
    unsigned int	columns = 0;

#ifdef MEMTEST
    {
	static struct rlimit dlimit = { 400 * 1024, 400 * 1024 };

	(void) setrlimit(RLIMIT_DATA, &dlimit);
    }
#endif		/* MEMTEST */

    /*
     * Set up tty flags in raw and cooked structures.
     * Do this before any termcap-initialisation stuff
     * so that start sequences can be sent.
     */
#ifdef	TERMIO

    getstate(&cooked_state);
    raw_state = cooked_state;

    raw_state.c_oflag &= ~(0
#ifdef   OPOST
			| OPOST
#else	/* no OPOST */
			/*
			 * Not a POSIX system. If it's a Sun, this might work:
			 */
			| OLCUC | OCRNL | ONLRET
#endif
#ifdef	ONLCR
			| ONLCR
#endif
#ifdef	OXTABS
			| OXTABS
#endif
#ifdef	XTABS
			| XTABS
#endif
#ifdef	TAB3
			| TAB3
#endif
    );

    raw_state.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL
#ifdef	XCASE
			| XCASE
#endif
#ifdef	ECHOCTL
			| ECHOCTL
#endif
#ifdef	ECHOPRT
			| ECHOPRT
#endif
#ifdef	ECHOKE
			| ECHOKE
#endif
    );

    raw_state.c_iflag &= ~(ICRNL | IGNCR | INLCR);
    raw_state.c_cc[VMIN] = 1;
    raw_state.c_cc[VTIME] = 0;
#   ifdef	TERMIOS
#	ifdef	    VDISCARD
	    raw_state.c_cc[VDISCARD] = 0;
#	endif
	raw_state.c_cc[VSUSP] = 0;
#   endif	/* TERMIOS */

#else	/* not TERMIO */

    /*
     * Probably a BSD system; we must
     *	turn off echo,
     *	set cbreak mode (not raw because we lose
     *		typeahead when switching modes),
     *	turn off tab expansion on output (in case termcap
     *		puts out any tabs in cursor motions etc),
     *	turn off CRMOD so we get \r and \n as they are typed,
     * and
     *	turn off nasty interrupt characters like ^Y so that
     *		we get the control characters we want.
     *
     * All this has to be put back as it was when we go into
     * system mode; a pain, but we have to get it right.
     */
    (void) ioctl(0, TIOCGETP, (char *) &cooked_state);
    raw_state = cooked_state;
    raw_state.sg_flags |= CBREAK;
    raw_state.sg_flags &= ~ (ECHO | XTABS | CRMOD);

    (void) ioctl(0, TIOCGETC, (char *) &ckd_tchars);
    raw_tchars = ckd_tchars;
    raw_tchars.t_quitc = -1;

    (void) ioctl(0, TIOCGLTC, (char *) &ckd_ltchars);
    raw_ltchars = ckd_ltchars;
    raw_ltchars.t_flushc = -1;
    raw_ltchars.t_lnextc = -1;
    raw_ltchars.t_suspc = -1;
    raw_ltchars.t_dsuspc = -1;

#endif	/* not TERMIO */

#ifdef	SETVBUF_AVAIL
    /*
     * Set output buffering to avoid calling _flsbuf()
     * for every character sent to the screen.
     */
    setvbuf(stdout, outbuffer, _IOFBF, sizeof(outbuffer));
#endif

    /*
     * This is for termcap's benefit.
     */
#ifdef	TERMIO
#   ifdef	POSIX
	ospeed = cfgetospeed(&cooked_state);
#   else	/* not POSIX */
	ospeed = speeds[cooked_state.c_cflag & CBAUD];
#   endif
#else
    ospeed = cooked_state.sg_ospeed;
#endif

#ifdef	TIOCGWINSZ
    /*
     * Find out how big the kernel thinks the window is.
     * These values (if they are non-zero) will override
     * any settings obtained by the terminal interface code.
     */
    (void) ioctl(0, TIOCGWINSZ, (char *) &winsz);
    rows = winsz.ws_row;
    columns = winsz.ws_col;
#else
    rows = columns = 0;
#endif

    /*
     * Now set up the terminal interface.
     */
    tty_open(&rows, &columns);

    /*
     * Go into raw/cbreak mode, and do any initialisation stuff.
     */
    sys_startv();
}

static enum { m_SYS = 0, m_VI = 1 } curmode;

/*
 * Set terminal into the mode it was in when we started.
 *
 * sys_endv() can be called when we're already in system mode, so we
 * have to check.
 */
void
sys_endv()
{
    if (curmode == m_SYS)
	return;
    tty_goto((int) Rows - 1, 0);
    set_colour(Pn(P_systemcolour));
    erase_line();
    tty_endv();

    (void) fflush(stdout);

    /*
     * Restore terminal modes.
     */
#ifdef	TERMIO
    w_setstate(&cooked_state);
#else
    (void) ioctl(0, TIOCSETN, (char *) &cooked_state);
    (void) ioctl(0, TIOCSETC, (char *) &ckd_tchars);
    (void) ioctl(0, TIOCSLTC, (char *) &ckd_ltchars);
#endif
    curmode = m_SYS;
}

/*
 * Set terminal to raw/cbreak mode.
 */
void
sys_startv()
{
    if (curmode == m_VI)
	return;
#ifdef	TERMIO
    w_setstate(&raw_state);
#else
    (void) ioctl(0, TIOCSETN, (char *) &raw_state);
    (void) ioctl(0, TIOCSETC, (char *) &raw_tchars);
    (void) ioctl(0, TIOCSLTC, (char *) &raw_ltchars);
#endif

    tty_startv();

    set_colour(Pn(P_colour));

    curmode = m_VI;
}

/*
 * "Safe" form of exit - doesn't leave the tty in yillruction mode.
 */
void
sys_exit(code)
int	code;
{
    sys_endv();
    tty_close();
    (void) fclose(stdin);
    (void) fclose(stdout);
    (void) fclose(stderr);

    /*
     * This is desperation really.
     * On some BSD systems, calling exit() here produces a core dump.
     */
    _exit(code);
}

/*
 * This is a routine that can be passed to tputs() (in the termcap
 * library): it does the same thing as putchar().
 */
void
foutch(c)
int	c;
{
    putchar(c);
}

/*
 * Construct unique name for temporary file, to be used as a backup
 * file for the named file.
 */
char *
tempfname(srcname)
char	*srcname;
{
    char	tailname[MAXNAMLEN + 1];
    char	*srctail;
    char	*endp;
    char	*retp;
    unsigned	headlen;
    unsigned	rootlen;
    unsigned	indexnum = 0;

    if ((srctail = strrchr(srcname, '/')) == NULL) {
	srctail = srcname;
    } else {
	srctail++;
    }
    headlen = srctail - srcname;

    /*
     * Make copy of filename's tail & change it from "wombat" to
     * "#wombat.tmp".
     */
    tailname [0] = '#';
    (void) strncpy(& tailname [1], srctail, sizeof tailname - 1);
    tailname [sizeof tailname - 1] = '\0';
    endp = tailname + strlen(tailname);

    /*
     * Don't let name get too long.
     */
    if (endp > & tailname [sizeof tailname - 5]) {
	endp = & tailname [sizeof tailname - 5];
    }
    rootlen = endp - tailname;

    /*
     * Now allocate space for new pathname.
     */
    retp = alloc(headlen + rootlen + 5);
    if (retp == NULL) {
	return(NULL);
    }

    /*
     * Copy name to new storage, leaving space for ".tmp"
     * (or ".xxx") suffix ...
     */
    if (headlen > 0) {
	(void) memcpy(retp, srcname, (int) headlen);
    }
    (void) memcpy(&retp[headlen], tailname, (int) rootlen);

    /*
     * ... & make endp point to the space we're leaving for the suffix.
     */
    endp = &retp[headlen + rootlen];
    strcpy(endp++, ".tmp");
    while (access(retp, 0) == 0) {
	/*
	 * Keep trying this until we get a unique file name.
	 */
	Flexbuf suffix;

	flexnew(&suffix);
	(void) lformat(&suffix, "%03u", ++indexnum);
	(void) strncpy(endp, flexgetstr(&suffix), 3);
	flexdelete(&suffix);
    }
    return retp;
}

/*
 * Fork off children thus:
 *
 * [CHILD 1] --- pipe1 ---> [CHILD 2] --- pipe2 ---> [PARENT]
 *    |                        |                        |
 *    V                        V                        V
 * writefunc                exec(cmd)                readfunc
 *
 * connecting the pipes to stdin/stdout as appropriate.
 *
 * We assume that on entry to this function, file descriptors 0, 1 and 2
 * are already open, so we do not have to deal with the possibility of
 * them being allocated as pipe descriptors.
 */
bool_t
sys_pipe(cmd, writefunc, readfunc)
char	*cmd;
int	(*writefunc) P((FILE *));
long	(*readfunc) P((FILE *));
{
    int		pd1[2], pd2[2];		/* descriptors for pipes 1 and 2 */
    int		pid1, pid2;		/* process ids for children 1 and 2 */
    int		died;
    int		retval;
    FILE	*fp;
    Wait_t	status;
    static char	*args[] = { NULL, "-c", NULL, NULL };

    if (Ps(P_shell) == NULL) {
	return(FALSE);
    }
    args[0] = Ps(P_shell);
    args[2] = cmd;

    /*
     * Initialise these values so we can work out what has happened
     * so far if we have to goto fail for any reason.
     */
    pd1[0] = pd1[1] = pd2[0] = pd2[1] = -1;
    pid1 = pid2 = -1;

    if (pipe(pd1) == -1) {
	goto fail;
    }

    switch (pid1 = fork()) {
    case -1:					/* error */
	goto fail;

    case 0:					/* child 1 */
	/*
	 * Fdopen pipe1 to get a stream.
	 */
	(void) close(pd1[0]);
	fp = fdopen(pd1[1], "w");
	if (fp == NULL) {
	    exit(1);
	}

	/*
	 * Call writefunc.
	 */
	(void) (*writefunc)(fp);
	(void) fclose(fp);

	/*
	 * Our work is done.
	 */
	exit(0);

    default:					/* parent */
	(void) close(pd1[1]);
	break;
    }

    if (pipe(pd2) == -1) {
	goto fail;
    }
    switch (pid2 = fork()) {
    case -1:					/* error */
	goto fail;

    case 0:						/* child 2 */

	/*
	 * Rearrange file descriptors onto stdin/stdout/stderr.
	 */
	(void) close(pd1[1]);
	(void) close(pd2[0]);

	(void) close(0);
	(void) dup(pd1[0]);		/* dup2(pd1[0], 0) */
	(void) close(pd1[0]);

	(void) close(1);
	(void) dup(pd2[1]);		/* dup2(pd2[1], 1) */
	(void) close(pd2[1]);

	(void) close(2);
	(void) dup(1);			/* dup2(1, 2) */
	
	/*
	 * Exec the command.
	 */
	(void) execvp(args[0], args);
	exit(1);

    default:					/* parent */
	(void) close(pd1[0]);
	(void) close(pd2[1]);
	pd1[0] = pd2[1] = -1;
	fp = fdopen(pd2[0], "r");
	if (fp == NULL) {
	    goto fail;
	}
	(void) (*readfunc)(fp);
	(void) fclose(fp);
	break;
    }

    /*
     * Finally, clean up the children.
     */
    retval = TRUE;
    goto cleanup;

fail:
    /*
     * Come here if anything fails (if we are the original process).
     * Close any open pipes and goto cleanup to reap the child processes.
     */
    if (pd1[0] >= 0) {
	(void) close(pd1[0]);
	(void) close(pd1[1]);
    }
    if (pd2[0] >= 0) {
	(void) close(pd2[0]);
	(void) close(pd2[1]);
    }
    retval = FALSE;

cleanup:
    /*
     * Come here whether or not we failed, to clean up the children ...
     */
#ifdef	WIFEXITED
#   define	FAILED(s)	(!WIFEXITED(s) || (s).w_retcode != 0)
#else
#   define	FAILED(s)	((s) != 0)
#endif
#ifndef WTERMSIG
#   ifdef   WIFSIGNALED
#	define	WTERMSIG(s)	(WIFSIGNALED(s) ? (s).w_termsig : 0)
#   else
#	define	WTERMSIG(s)	((s) & 0177)
#   endif
#endif
    while ((died = wait(&status)) != -1) {
	if (died == pid1 || died == pid2) {
	    /*
	     * If child 1 was killed with SIGPIPE -
	     * because child 2 exited before reading all
	     * of its input - it isn't necessarily an
	     * error.
	     */
	    if (
		FAILED(status)
		&&
		(
		    died == pid2
		    ||
		    WTERMSIG(status) != SIGPIPE
		)
	    ) {
		retval = FALSE;
	    }
	}
    }

    return(retval);
}

char *
fexpand(name)
char	*name;
{
    static char	meta[] = "*?[]~${}`";
    char	*cp;
    int		pd[2];
    int		has_meta;

    has_meta = FALSE;
    for (cp = meta; *cp != '\0'; cp++) {
	if (strchr(name, *cp) != NULL) {
	    has_meta = TRUE;
	    break;
	}
    }
    if (!has_meta) {
	return(name);
    }

    if (Ps(P_shell) == NULL) {
	return(name);
    }
    if (pipe(pd) == -1) {
	return(name);
    }
    fflush(stdout);
    switch (fork()) {
    case -1:				/* error */
	return(name);

    case 0:				/* child */
    {
	static char	*args[] = {
	    NULL,			/* path of shell */
	    "-c",
	    NULL,			/* echo %s */
	    NULL,
	};
	Flexbuf		cmd;
	int		errout;

	args[0] = Ps(P_shell);
	flexnew(&cmd);
	(void) lformat(&cmd, "echo %s", name);
	args[2] = flexgetstr(&cmd);
	(void) fclose(stdout);
	(void) fclose(stderr);
	(void) close(pd[0]);
	while (dup(pd[1]) < 2)		/* redirect both stdout & stderr */
	    ;
	(void) close(pd[1]);
	(void) close(0);
	(void) execvp(args[0], args);
	puts(name);
	exit(0);
    }
    default:				/* parent */
    {
	Wait_t		status;
	FILE		*pfp;
	static Flexbuf	newname;
	register int	c;

	flexclear(&newname);
	(void) close(pd[1]);
	pfp = fdopen(pd[0], "r");
	if (pfp != NULL) {
	    while ((c = getc(pfp)) != EOF && c != '\n') {
		if (!flexaddch(&newname, c)) {
		    break;
		}
	    }
	}
	(void) fclose(pfp);
	(void) wait(&status);
	if (flexempty(&newname)) {
	    return(name);
	}
	return(flexgetstr(&newname));
    }
    }
}
