/*
 *	system.c
 *
 *	command line analysis
 */

#ifdef	FD
#include "fd.h"
#include "term.h"
#include "types.h"
#include "kconv.h"
#else
#define	K_EXTERN
#include "headers.h"
#include "depend.h"
#include "printf.h"
#include "kctype.h"
#include "string.h"
#include "malloc.h"
#endif

#include "dirent.h"
#include "sysemu.h"

#if	!defined (FD) && !defined (MINIMUMSHELL)
#include "time.h"
#endif
#ifdef	DEP_ORIGSHELL
#include "system.h"
#endif
#if	defined (DEP_ORIGSHELL) && !defined (MINIMUMSHELL)
#include "posixsh.h"
#endif
#ifdef	DEP_PTY
#include "termemu.h"
#endif
#ifdef	DEP_SOCKET
#include "socket.h"
#include "url.h"
#endif
#ifdef	DEP_URLPATH
#include "urldisk.h"
#endif

#if	MSDOS
#include <process.h>
# ifdef	USERESOURCEH
# include <sys/time.h>
# include <sys/resource.h>
# endif
#else	/* !MSDOS */
# ifdef	USERESOURCEH
# include <sys/resource.h>
# endif
# ifdef	USETIMES
# include <sys/times.h>
# endif
# ifdef	USEULIMITH
# include <ulimit.h>
# endif
#endif	/* !MSDOS */

#ifndef	FD
typedef struct _lockbuf_t {
	int fd;
} lockbuf_t;

# ifdef	PATHNOCASE
# define	TMPPREFIX	"TM"
# else
# define	TMPPREFIX	"tm"
# endif
#endif	/* !FD */

#ifdef	GETPGRPVOID
#define	getpgroup		getpgrp
#else
#define	getpgroup()		getpgrp(0)
#endif
#ifdef	USESETPGID
#define	setpgroup		setpgid
#else
#define	setpgroup		setpgrp
#endif

#if	MSDOS
#define	havetty()		(1)
#else
# ifdef	NOJOB
# define	havetty()	(mypid == shellpid)
# else
# define	havetty()	(ttypgrp >= (p_id_t)0 && mypid == ttypgrp)
# endif
#endif

#define	getwaitsig(x)		((WIFSTOPPED(x)) ? WSTOPSIG(x) : \
				((WIFSIGNALED(x)) ? (128 + WTERMSIG(x)) : -1))

#ifndef	RLIM_INFINITY
#define	RLIM_INFINITY		0x7fffffff
#endif
#ifndef	RLIMIT_FSIZE
#define	RLIMIT_FSIZE		255
#endif
#ifndef	UL_GETFSIZE
#define	UL_GETFSIZE		1
#endif
#ifndef	UL_SETFSIZE
#define	UL_SETFSIZE		2
#endif

#if	!defined (RLIMIT_NOFILE) && defined (RLIMIT_OFILE)
#define	RLIMIT_NOFILE		RLIMIT_OFILE
#endif
#if	!defined (RLIMIT_VMEM) && defined (RLIMIT_AS)
#define	RLIMIT_VMEM		RLIMIT_AS
#endif

#if	defined (USESYSCONF) && defined (_SC_CLK_TCK)
#define	CLKPERSEC		sysconf(_SC_CLK_TCK)
#else
# ifdef	CLK_TCK
# define	CLKPERSEC	CLK_TCK
# else
#  ifdef	HZ
#  define	CLKPERSEC	HZ
#  else
#  define	CLKPERSEC	60
#  endif
# endif
#endif

#if	MSDOS
#define	LSH_DEFRC		"\\etc\\profile"
#define	LSH_RCFILE		"~\\profile.rc"
#define	SH_RCFILE		"~\\fdsh.rc"
#else	/* !MSDOS */
#define	LSH_DEFRC		"/etc/profile"
#define	LSH_RCFILE		"~/.profile"
#define	SH_RCFILE		"~/.fdshrc"
#endif	/* !MSDOS */

#define	PS1STR			"$ "
#define	PS1ROOT			"# "
#define	PS2STR			"> "
#define	PS4STR			"+ "
#define	UNLIMITED		"unlimited"
#define	MAXTMPNAMLEN		8
#define	getconstvar(s)		(getshellvar(s, strsize(s)))
#define	constequal(s, c, l)	((l) == strsize(c) && !strnenvcmp(s, c, l))

#ifdef	WITHSOCKREDIR
#define	TYPE_CONNECT		3
#define	TYPE_ACCEPT		4
#define	TYPE_BIND		5
#endif

#ifdef	PSIGNALSTYLE
#define	MESHUP			"Hangup"
#define	MESINT			"Interrupt"
#define	MESQUIT			"Quit"
#define	MESILL			"Illegal instruction"
#define	MESTRAP			"Trace/BPT trap"
#define	MESIOT			"IOT trap"
#define	MESABRT			"Aborted"
#define	MESEMT			"EMT trap"
#define	MESFPE			"Floating point exception"
#define	MESKILL			"Killed"
#define	MESBUS			"Bus error"
#define	MESSEGV			"Segmentation fault"
#define	MESSYS			"Bad system call"
#define	MESPIPE			"Broken pipe"
#define	MESALRM			"Alarm clock"
#define	MESTERM			"Terminated"
#define	MESSTKFLT		"Stack fault"
#define	MESURG			"Urgent I/O condition"
#define	MESSTOP			"Stopped (signal)"
#define	MESTSTP			"Stopped"
#define	MESCONT			"Continued"
#define	MESCHLD			"Child exited"
#define	MESTTIN			"Stopped (tty input)"
#define	MESTTOU			"Stopped (tty output)"
#define	MESIO			"I/O possible"
#define	MESPOLL			"Profiling alarm clock"
#define	MESXCPU			"Cputime limit exceeded"
#define	MESXFSZ			"Filesize limit exceeded"
#define	MESVTALRM		"Virtual timer expired"
#define	MESPROF			"Profiling timer expired"
#define	MESWINCH		"Window changed"
#define	MESLOST			"Resource lost"
#define	MESINFO			"Information request"
#define	MESPWR			"Power failure"
#define	MESUSR1			"User defined signal 1"
#define	MESUSR2			"User defined signal 2"
#else	/* !PSIGNALSTYLE */
#define	MESHUP			"Hangup"
#define	MESINT			""
#define	MESQUIT			"Quit"
#define	MESILL			"Illegal instruction"
#define	MESTRAP			"Trace/BPT trap"
#define	MESIOT			"IOT trap"
#define	MESABRT			"Aborted"
#define	MESEMT			"EMT trap"
#define	MESFPE			"Floating exception"
#define	MESKILL			"Killed"
#define	MESBUS			"Bus error"
#define	MESSEGV			"Memory fault"
#define	MESSYS			"Bad system call"
#define	MESPIPE			""
#define	MESALRM			"Alarm call"
#define	MESTERM			"Terminated"
#define	MESSTKFLT		"Stack fault"
#define	MESURG			"Urgent condition"
#define	MESSTOP			"Stopped"
#define	MESTSTP			"Stopped from terminal"
#define	MESCONT			"Continued"
#define	MESCHLD			"Child terminated"
#define	MESTTIN			"Stopped on terminal input"
#define	MESTTOU			"Stopped on terminal output"
#define	MESIO			"Asynchronous I/O"
#define	MESPOLL			"Profiling alarm clock"
#define	MESXCPU			"Exceeded cpu time limit"
#define	MESXFSZ			"Exceeded file size limit"
#define	MESVTALRM		"Virtual time alarm"
#define	MESPROF			"Profiling time alarm"
#define	MESWINCH		"Window changed"
#define	MESLOST			"Resource lost"
#define	MESINFO			"Information request"
#define	MESPWR			"Power failure"
#define	MESUSR1			"User defined signal 1"
#define	MESUSR2			"User defined signal 2"
#endif	/* !PSIGNALSTYLE */

#ifdef	DEBUG
#define	PSIGNALSTYLE
#define	SHOWSTREE
#endif

#ifdef	DEP_ORIGSHELL

/*
 *	Notes: extension from original Bourne shell
 *
 *	1. pathname globing: wildcard `**' matches any directories recursively.
 *	2. `export' & `readonly' allows arguments with `=value'.
 *	3. redirector `<>' and `><' mean redirection for read and write.
 *	4. command hash is always valid. Instead,
 *	   -h option means to check command-path ahead for pipe line,
 *	   and to check command-path when a function is defined (not executed).
 *	5. job control: `jobs', `fg', `bg', ^Z/^Y(suspend) are valid.
 *	   -m option means to validate the job control.
 *	   this option is valid by default for the interactive shell.
 *	6. builtin command: `echo', `test', `kill' is builtin.
 *	7. job name: `%' introduces job name in `fg', `bg', `wait', `kill'.
 *	   `%%', `%', '%+' mean the current job.
 *	   `%-' means the previous job.
 *	   `%n' means the job which has job number n.
 *	   `%string' means the job whose command begins with `string'.
 *	8. builtin commands of COMMAND.COM for MS-DOS:
 *	   `rem', `dir', `mkdir(md)', `rmdir(rd)', `erase(del)',
 *	   `rename(ren)', `copy' is builtin on MS-DOS.
 *	9. aliases: `alias', `unalias' is builtin.
 *	10. optional POSIX utilities are added.
 */

#ifdef	FD
extern VOID calcwin __P_((VOID_A));
extern VOID main_fd __P_((char *CONST *, int));
extern VOID checkscreen __P_((int, int));
# ifdef	SIGWINCH
extern VOID pollscreen __P_((int));
# endif
extern int sigvecset __P_((int));
# ifndef	_NOCUSTOMIZE
extern VOID saveorigenviron __P_((VOID_A));
# endif
extern VOID initfd __P_((char *CONST *));
extern VOID prepareexitfd __P_((int));
extern int savestdio __P_((int));
extern int checkbuiltin __P_((CONST char *));
extern int checkinternal __P_((CONST char *));
extern int execbuiltin __P_((int, int, char *CONST []));
extern int execinternal __P_((int, int, char *CONST []));
# ifndef	_NOCOMPLETE
extern int completebuiltin __P_((CONST char *, int, int, char ***));
extern int completeinternal __P_((CONST char *, int, int, char ***));
# endif
extern VOID initenv __P_((VOID_A));
extern VOID evalenv __P_((CONST char *, int));
extern int underhome __P_((char *));
extern int replaceargs __P_((int *, char ***, char *CONST *, int));
extern int replacearg __P_((char **));
extern VOID demacroarg __P_((char **));
extern char *inputshellstr __P_((CONST char *, int, CONST char *));
extern int evalprompt __P_((char **, CONST char *));
#endif	/* !FD */

#ifdef	DOSCOMMAND
extern int doscomdir __P_((int, char *CONST []));
extern int doscommkdir __P_((int, char *CONST []));
extern int doscomrmdir __P_((int, char *CONST []));
extern int doscomerase __P_((int, char *CONST []));
extern int doscomrename __P_((int, char *CONST []));
extern int doscomcopy __P_((int, char *CONST []));
extern int doscomcls __P_((int, char *CONST []));
extern int doscomtype __P_((int, char *CONST []));
#endif	/* DOSCOMMAND */

#ifdef	DEBUG
extern VOID mtrace __P_ ((VOID_A));
extern VOID muntrace __P_ ((VOID_A));
extern char *_mtrace_file;
#endif

#if	MSDOS
extern int setcurdrv __P_((int, int));
#endif

#ifdef	FD
# ifndef	_NOEDITMODE
extern char *editmode;
# endif
extern int internal_status;
extern char fullpath[];
extern char *origpath;
extern int inruncom;
extern int fd_restricted;
extern int fdmode;
extern int physical_path;
extern char *deftmpdir;
#else	/* !FD */
# ifdef	__TURBOC__
extern u_int _stklen = 0x8000;
# endif
# if	MSDOS
# define	deftmpdir	_SS_
# else
# define	deftmpdir	"/tmp"
# endif
int ttyio = -1;
XFILE *ttyout = NULL;
#endif	/* !FD */

#ifndef	MINIMUMSHELL
# ifndef	NOJOB
extern jobtable *joblist;
extern int maxjobs;
# endif
# if	!MSDOS
extern int mailcheck;
# endif
# ifndef	NOALIAS
extern shaliastable *shellalias;
# endif
# ifndef	NOPOSIXUTIL
extern int posixoptind;
# endif
#endif	/* !MINIMUMSHELL */

#ifdef	FD
extern lockbuf_t *lockopen __P_((CONST char *, int, int));
extern VOID lockclose __P_((lockbuf_t *));
extern int mktmpfile __P_((char *));
extern int rmtmpfile __P_((CONST char *));
extern int chdir2 __P_((CONST char *));
extern int chdir3 __P_((CONST char *, int));
extern int chdir4 __P_((CONST char *, int, CONST char *));
extern int setenv2 __P_((CONST char *, CONST char *, int));
extern char *getenv2 __P_((CONST char *));
# ifdef	USESIGACTION
extern sigcst_t signal2 __P_((int, sigcst_t));
# else
# define	signal2		signal
# endif
# if	MSDOS && !defined (BSPATHDELIM)
extern char *adjustpname __P_((char *));
# endif
#else	/* !FD */
static lockbuf_t *NEAR lockopen __P_((CONST char *, int, int));
static VOID NEAR lockclose __P_((lockbuf_t *));
static int NEAR genrand __P_((int));
static char *NEAR genrandname __P_((char *, int));
static int NEAR mktmpfile __P_((char *));
static int NEAR rmtmpfile __P_((CONST char *));
# define	chdir2		Xchdir
int chdir3 __P_((CONST char *, int));
# define	chdir4(p, r, a)	chdir3(p, r)
int setenv2 __P_((CONST char *, CONST char *, int));
# ifdef	USESIGACTION
static sigcst_t NEAR signal2 __P_((int, sigcst_t));
# else
# define	signal2		signal
# endif
# if	MSDOS && !defined (BSPATHDELIM)
static char *NEAR adjustpname __P_((char *));
# endif
# if	!MSDOS && !defined (NOJOB)
static VOID NEAR stdiomode __P_((VOID_A));
# endif
#endif	/* !FD */

#ifndef	NOJOB
static VOID NEAR notifyjob __P_((VOID_A));
#endif
static VOID NEAR setsignal __P_((VOID_A));
static VOID NEAR resetsignal __P_((int));
static VOID NEAR exectrapcomm __P_((VOID_A));
static VOID NEAR killmyself __P_((int));
static int NEAR trap_common __P_((int));
#ifdef	SIGHUP
static int trap_hup __P_((VOID_A));
#endif
#ifdef	SIGINT
static int trap_int __P_((VOID_A));
#endif
#ifdef	SIGQUIT
static int trap_quit __P_((VOID_A));
#endif
#ifdef	SIGILL
static int trap_ill __P_((VOID_A));
#endif
#ifdef	SIGTRAP
static int trap_trap __P_((VOID_A));
#endif
#ifdef	SIGIOT
static int trap_iot __P_((VOID_A));
#else
# ifdef	SIGABRT
static int trap_abrt __P_((VOID_A));
# endif
#endif
#ifdef	SIGEMT
static int trap_emt __P_((VOID_A));
#endif
#ifdef	SIGFPE
static int trap_fpe __P_((VOID_A));
#endif
#ifdef	SIGBUS
static int trap_bus __P_((VOID_A));
#endif
#ifdef	SIGSEGV
static int trap_segv __P_((VOID_A));
#endif
#ifdef	SIGSYS
static int trap_sys __P_((VOID_A));
#endif
#ifdef	SIGPIPE
static int trap_pipe __P_((VOID_A));
#endif
#ifdef	SIGALRM
static int trap_alrm __P_((VOID_A));
#endif
#ifdef	SIGTERM
static int trap_term __P_((VOID_A));
#endif
#ifdef	SIGSTKFLT
static int trap_stkflt __P_((VOID_A));
#endif
#ifdef	SIGURG
static int trap_urg __P_((VOID_A));
#endif
#ifdef	SIGSTOP
static int trap_stop __P_((VOID_A));
#endif
#ifdef	SIGTSTP
static int trap_tstp __P_((VOID_A));
#endif
#ifdef	SIGCONT
static int trap_cont __P_((VOID_A));
#endif
#ifdef	SIGCHLD
static int trap_chld __P_((VOID_A));
#endif
#ifdef	SIGTTIN
static int trap_ttin __P_((VOID_A));
#endif
#ifdef	SIGTTOU
static int trap_ttou __P_((VOID_A));
#endif
#ifdef	SIGIO
static int trap_io __P_((VOID_A));
#else
# ifdef	SIGPOLL
static int trap_poll __P_((VOID_A));
# endif
#endif
#ifdef	SIGXCPU
static int trap_xcpu __P_((VOID_A));
#endif
#ifdef	SIGXFSZ
static int trap_xfsz __P_((VOID_A));
#endif
#ifdef	SIGVTALRM
static int trap_vtalrm __P_((VOID_A));
#endif
#ifdef	SIGPROF
static int trap_prof __P_((VOID_A));
#endif
#ifdef	SIGWINCH
static int trap_winch __P_((VOID_A));
#endif
#ifdef	SIGLOST
static int trap_lost __P_((VOID_A));
#endif
#ifdef	SIGINFO
static int trap_info __P_((VOID_A));
#endif
#ifdef	SIGPWR
static int trap_pwr __P_((VOID_A));
#endif
#ifdef	SIGUSR1
static int trap_usr1 __P_((VOID_A));
#endif
#ifdef	SIGUSR2
static int trap_usr2 __P_((VOID_A));
#endif
#ifdef	FD
static VOID NEAR argfputs __P_((CONST char *, XFILE *));
#else
#define	argfputs		Xfputs
#endif
#ifdef	DEP_FILECONV
static int NEAR Kopen __P_((CONST char *, int, int));
#else
#define	Kopen			Xopen
#endif
static VOID NEAR syntaxerror __P_((CONST char *));
#if	!MSDOS && !defined (NOJOB) && defined (DEP_PTY) && defined (CYGWIN)
static VOID NEAR addmychild __P_((p_id_t));
#endif
#if	!MSDOS
static VOID NEAR setstopsig __P_((int));
static p_id_t NEAR makechild __P_((int, p_id_t));
#endif
static VOID NEAR safermtmpfile __P_((CONST char *));
static int NEAR getoption __P_((int, char *CONST *, int));
static ALLOC_T NEAR c_allocsize __P_((int));
static int NEAR readchar __P_((int));
static char *NEAR readline __P_((int, int));
static char *NEAR readfile __P_((int, ALLOC_T *));
static heredoc_t *NEAR newheredoc __P_((char *, char *, int, int));
static redirectlist *NEAR newrlist __P_((int, char *, int, redirectlist *));
static command_t *NEAR newcomm __P_((VOID_A));
static syntaxtree *NEAR eldeststree __P_((syntaxtree *));
static syntaxtree *NEAR childstree __P_((syntaxtree *, int));
static syntaxtree *NEAR skipfuncbody __P_((syntaxtree *));
static syntaxtree *NEAR insertstree __P_((syntaxtree *, syntaxtree *, int));
static syntaxtree *NEAR linkstree __P_((syntaxtree *, int));
#if	!MSDOS || defined (DEP_PTY) || !defined (USEFAKEPIPE)
static VOID NEAR nownstree __P_((syntaxtree *));
#endif
static int NEAR evalfiledesc __P_((CONST char *));
static int NEAR redmode __P_((int));
static int NEAR cancelredirect __P_((redirectlist *));
static VOID NEAR closeredirect __P_((redirectlist *));
static heredoc_t *NEAR searchheredoc __P_((syntaxtree *, int));
static int NEAR saveheredoc __P_((CONST char *, syntaxtree *));
static int NEAR openheredoc __P_((heredoc_t *, int));
#ifdef	DEP_DOSDRIVE
static int NEAR fdcopy __P_((int, int));
static int NEAR openpseudofd __P_((redirectlist *));
static int NEAR closepseudofd __P_((redirectlist *));
#endif
#ifdef	WITHSOCKREDIR
static int NEAR parsesocket __P_((CONST char *, char **, int *));
#endif
static redirectlist *NEAR doredirect __P_((redirectlist *));
static heredoc_t *NEAR heredoc __P_((char *, int));
static int NEAR redirect __P_((syntaxtree *, int, char *, int));
#if	!MSDOS && defined (MINIMUMSHELL)
static VOID NEAR checkmail __P_((int));
#endif
static int NEAR searchvar __P_((char *CONST *, CONST char *, int, int));
static char *NEAR searchvar2 __P_((int, CONST char **,
		CONST char *, int));
static char **NEAR expandvar __P_((char **, CONST char *, int));
static char *NEAR getvar __P_((char *CONST *, CONST char *, int));
static char **NEAR putvar __P_((char **, char *, int));
static int NEAR checkprimal __P_((CONST char *, int));
static int NEAR checkrestrict __P_((CONST char *, int));
static int NEAR checkronly __P_((CONST char *, int));
static int NEAR _putshellvar __P_((char *, int));
#ifndef	MINIMUMSHELL
static VOID NEAR setshlineno __P_((long));
#endif
static heredoc_t *NEAR duplheredoc __P_((heredoc_t *));
static redirectlist *NEAR duplrlist __P_((redirectlist *));
static shfunctable *NEAR duplfunc __P_((shfunctable *));
static VOID NEAR freefunc __P_((shfunctable *));
static int cmpfunc __P_((CONST VOID_P, CONST VOID_P));
static char *NEAR getifs __P_((VOID_A));
static int getretval __P_((VOID_A));
static p_id_t getorgpid __P_((VOID_A));
static p_id_t getlastpid __P_((VOID_A));
static char *getflagstr __P_((VOID_A));
static int checkundefvar __P_((CONST char *, CONST char *, int));
static VOID safeexit __P_((VOID_A));
static int NEAR getparenttype __P_((syntaxtree *));
static int NEAR parsestatement __P_((syntaxtree **, int, int, int));
static syntaxtree *NEAR _addarg __P_((syntaxtree *, CONST char *));
static int NEAR addarg __P_((syntaxtree **, redirectlist *,
		char *, int *, int));
static int NEAR evalredprefix __P_((syntaxtree **, redirectlist *, int *));
static syntaxtree *NEAR rparen __P_((syntaxtree *));
static syntaxtree *NEAR semicolon __P_((syntaxtree *, redirectlist *,
		CONST char *, int *));
static syntaxtree *NEAR ampersand __P_((syntaxtree *, redirectlist *,
		CONST char *, int *));
static syntaxtree *NEAR vertline __P_((syntaxtree *, CONST char *, int *));
static syntaxtree *NEAR lessthan __P_((syntaxtree *, redirectlist *,
		CONST char *, int *));
static syntaxtree *NEAR morethan __P_((syntaxtree *, redirectlist *,
		CONST char *, int *));
#if	defined (BASHSTYLE) || !defined (MINIMUMSHELL)
static syntaxtree *NEAR endvar __P_((syntaxtree *, redirectlist *,
		CONST char *, int *, int *, ALLOC_T *, int));
static syntaxtree *NEAR addvar __P_((syntaxtree *,
		CONST char *, int *, CONST char *, int *, int));
#endif
static syntaxtree *NEAR normaltoken __P_((syntaxtree *, redirectlist *,
		CONST char *, int *, int *, ALLOC_T *));
static syntaxtree *NEAR casetoken __P_((syntaxtree *, redirectlist *,
		CONST char *, int *, int *));
#if	!defined (BASHBUG) && !defined (MINIMUMSHELL)
static int NEAR cmpstatement __P_((CONST char *, int));
static syntaxtree *NEAR comsubtoken __P_((syntaxtree *, redirectlist *,
		CONST char *, int *, int *, ALLOC_T *));
#endif
static syntaxtree *NEAR analyzeloop __P_((syntaxtree *, redirectlist *,
		CONST char *, int));
static syntaxtree *NEAR analyzeeof __P_((syntaxtree *));
static syntaxtree *NEAR statementcheck __P_((syntaxtree *, int));
static int NEAR check_statement __P_((syntaxtree *));
static int NEAR check_command __P_((syntaxtree *));
static int NEAR check_stree __P_((syntaxtree *));
static syntaxtree *NEAR analyzeline __P_((CONST char *));
#ifdef	DEBUG
static VOID NEAR Xexecve __P_((CONST char *, char *[], char *[], int));
#define	Xexecve2		Xexecve
#else
static VOID NEAR Xexecve __P_((CONST char *, char *[], char *[]));
#define	Xexecve2(p, a, e, b)	Xexecve(p, a, e)
#endif
#if	MSDOS
static char *NEAR addext __P_((char *, int));
static char **NEAR replacebat __P_((char **, char **));
#endif
#ifdef	USEFAKEPIPE
static int NEAR openpipe __P_((p_id_t *, int, int));
#define	openpipe2(p, f, n, t, pp) \
				openpipe(p, f, n)
#else
static int NEAR openpipe __P_((p_id_t *, int, int, int, p_id_t));
#define	openpipe2		openpipe
#endif
static pipelist **NEAR searchpipe __P_((int));
static int NEAR reopenpipe __P_((int, int));
static XFILE *NEAR fdopenpipe __P_((int));
#ifndef	_NOUSEHASH
static VOID NEAR disphash __P_((VOID_A));
#endif
#ifdef	BASHSTYLE
static char *NEAR quotemeta __P_((char *));
#endif
static int NEAR substvar __P_((char **, int));
static int NEAR evalargv __P_((command_t *, int *, int *));
static char *NEAR evalexternal __P_((command_t *));
static VOID NEAR printindent __P_((int, XFILE *));
static VOID NEAR printnewline __P_((int, XFILE *));
static int NEAR printredirect __P_((redirectlist *, XFILE *));
#ifndef	MINIMUMSHELL
static VOID NEAR printheredoc __P_((redirectlist *, XFILE *));
static redirectlist **NEAR _printstree __P_((syntaxtree *,
		redirectlist **, int, XFILE *));
#endif
static VOID NEAR printshfunc __P_((shfunctable *, XFILE *));
static int NEAR dochild __P_((syntaxtree *));
static int NEAR doif __P_((syntaxtree *));
static int NEAR dowhile __P_((syntaxtree *));
static int NEAR dountil __P_((syntaxtree *));
static int NEAR dofor __P_((syntaxtree *));
static int NEAR docase __P_((syntaxtree *));
static int NEAR dolist __P_((syntaxtree *));
static int NEAR donull __P_((syntaxtree *));
static int NEAR dobreak __P_((syntaxtree *));
static int NEAR docontinue __P_((syntaxtree *));
static int NEAR doreturn __P_((syntaxtree *));
static int NEAR execpath __P_((command_t *, int));
static int NEAR doexec __P_((syntaxtree *));
#ifndef	MINIMUMSHELL
static int NEAR dologin __P_((syntaxtree *));
static int NEAR dologout __P_((syntaxtree *));
#endif
static int NEAR doeval __P_((syntaxtree *));
static int NEAR doexit __P_((syntaxtree *));
static int NEAR doread __P_((syntaxtree *));
static int NEAR doshift __P_((syntaxtree *));
static int NEAR doset __P_((syntaxtree *));
static int NEAR dounset __P_((syntaxtree *));
#ifndef	_NOUSEHASH
static int NEAR dohash __P_((syntaxtree *));
#endif
static int NEAR dochdir __P_((syntaxtree *));
static int NEAR dopwd __P_((syntaxtree *));
static int NEAR dosource __P_((syntaxtree *));
static int NEAR expandlist __P_((char ***, CONST char *));
static int NEAR doexport __P_((syntaxtree *));
static int NEAR doreadonly __P_((syntaxtree *));
static int NEAR dotimes __P_((syntaxtree *));
static int NEAR dowait __P_((syntaxtree *));
static int NEAR doumask __P_((syntaxtree *));
static int NEAR doulimit __P_((syntaxtree *));
static int NEAR dotrap __P_((syntaxtree *));
#ifndef	NOJOB
static int NEAR dojobs __P_((syntaxtree *));
static int NEAR dofg __P_((syntaxtree *));
static int NEAR dobg __P_((syntaxtree *));
static int NEAR dodisown __P_((syntaxtree *));
#endif
static int NEAR dotype __P_((syntaxtree *));
#ifdef	DOSCOMMAND
static int NEAR dodir __P_((syntaxtree *));
static int NEAR domkdir __P_((syntaxtree *));
static int NEAR dormdir __P_((syntaxtree *));
static int NEAR doerase __P_((syntaxtree *));
static int NEAR dorename __P_((syntaxtree *));
static int NEAR docopy __P_((syntaxtree *));
static int NEAR docls __P_((syntaxtree *));
static int NEAR dodtype __P_((syntaxtree *));
#endif
#ifndef	NOALIAS
static int NEAR doalias __P_((syntaxtree *));
static int NEAR dounalias __P_((syntaxtree *));
#endif
static int NEAR doecho __P_((syntaxtree *));
#ifndef	MINIMUMSHELL
static int NEAR dokill __P_((syntaxtree *));
static int NEAR dotest __P_((syntaxtree *));
#endif
#ifndef	NOPOSIXUTIL
static int NEAR dofalse __P_((syntaxtree *));
static int NEAR docommand __P_((syntaxtree *));
static int NEAR dogetopts __P_((syntaxtree *));
static int NEAR donewgrp __P_((syntaxtree *));
# if	0				/* exists in FD original builtin */
static int NEAR dofc __P_((syntaxtree *));
# endif
#endif	/* !NOPOSIXUTIL */
#ifndef	MINIMUMSHELL
static int NEAR getworkdir __P_((char *));
static int NEAR dopushd __P_((syntaxtree *));
static int NEAR dopopd __P_((syntaxtree *));
static int NEAR dodirs __P_((syntaxtree *));
static int NEAR doenable __P_((syntaxtree *));
static int NEAR dobuiltin __P_((syntaxtree *));
static int NEAR doaddcr __P_((syntaxtree *));
#endif
#ifdef	WITHSOCKET
static int NEAR doaccept __P_((syntaxtree *));
static int NEAR dosocketinfo __P_((syntaxtree *));
#endif
#ifdef	FD
static int NEAR dofd __P_((syntaxtree *));
#endif
static int NEAR doshfunc __P_((syntaxtree *, int));
#ifdef	SHOWSTREE
static VOID NEAR show_stree __P_((syntaxtree *, int));
#endif
static int NEAR dosetshfunc __P_((CONST char *, syntaxtree *));
static int NEAR exec_statement __P_((syntaxtree *));
static char **NEAR checkshellbuiltinargv __P_((int, char **));
static int NEAR checkshellbuiltin __P_((syntaxtree *));
#if	MSDOS
static int NEAR exec_command __P_((syntaxtree *, int *));
#define	exec_command2(tr, c, b)	exec_command(tr, c)
#else
static int NEAR exec_command __P_((syntaxtree *, int *, int));
#define	exec_command2		exec_command
#endif
#ifdef	USEFAKEPIPE
static int NEAR exec_process __P_((syntaxtree *));
#define	exec_process2(tr, p)	exec_process(tr)
#else
static int NEAR exec_process __P_((syntaxtree *, p_id_t));
#define	exec_process2		exec_process
#endif
static int NEAR exec_stree __P_((syntaxtree *, int));
static syntaxtree *NEAR execline __P_((CONST char *,
		syntaxtree *, syntaxtree *, int));
static int NEAR exec_line __P_((CONST char *));
static int NEAR _dosystem __P_((CONST char *));
static XFILE *NEAR _dopopen __P_((CONST char *));
#ifndef	FDSH
int dosystem __P_((CONST char *));
XFILE *dopopen __P_((CONST char *));
int dopclose __P_((XFILE *));
#endif
static int NEAR sourcefile __P_((int, CONST char *, int));
#if	MSDOS && !defined (BSPATHDELIM)
static VOID NEAR adjustdelim __P_((char *CONST *));
#endif
static VOID NEAR initrc __P_((int));
#ifdef	FDSH
int main __P_((int, char *CONST [], char *CONST []));
#endif

int shellmode = 0;
p_id_t mypid = (p_id_t)-1;
p_id_t orgpid = (p_id_t)-1;
p_id_t shellpid = (p_id_t)-1;
int ret_status = RET_SUCCESS;
int interactive = 0;
int errorexit = 0;
#ifdef	DEP_PTY
int shptymode = 0;
#define	isshptymode()		(!fdmode && shptymode)
#endif
#ifndef	NOJOB
int lastjob = -1;
int prevjob = -1;
int stopped = 0;
p_id_t orgpgrp = (p_id_t)-1;
p_id_t childpgrp = (p_id_t)-1;
p_id_t ttypgrp = (p_id_t)-1;
#endif
int interrupted = 0;
int nottyout = 0;
int syntaxerrno = 0;
#if	!MSDOS
int sigconted = 0;
#endif
CONST statementtable statementlist[] = {
	{doif, "if", STT_NEEDLIST, {0, 0, 0, 0}},
	{NULL, "then", STT_NEEDLIST, {SM_IF, SM_ELIF, 0, 0}},
	{NULL, "elif", STT_NEEDLIST, {SM_THEN, 0, 0, 0}},
	{NULL, "else", STT_NEEDLIST, {SM_THEN, 0, 0, 0}},
	{NULL, "fi", STT_NEEDNONE, {SM_THEN, SM_ELSE, 0, 0}},
	{dowhile, "while", STT_NEEDLIST, {0, 0, 0, 0}},
	{dountil, "until", STT_NEEDLIST, {0, 0, 0, 0}},
	{NULL, "do", STT_NEEDLIST, {SM_WHILE, SM_UNTIL, SM_FOR, SM_IN}},
	{NULL, "done", STT_NEEDNONE, {SM_DO, 0, 0, 0}},
	{dofor, "for", STT_NEEDIDENT | STT_FOR, {0, 0, 0, 0}},
	{NULL, "in", STT_NEEDIDENT | STT_IN, {SM_FOR, SM_ANOTHER, 0, 0}},
	{docase, "case", STT_NEEDIDENT | STT_CASE, {0, 0, 0, 0}},
	{NULL, "in", STT_NEEDIDENT | STT_INCASE, {SM_CASE, 0, 0, 0}},
	{NULL, ")", STT_NEEDLIST, {SM_INCASE, SM_CASEEND, SM_ANOTHER, 0}},
	{NULL, ";;", STT_CASEEND, {SM_RPAREN, 0, 0, 0}},
	{NULL, "esac", STT_NEEDNONE, {SM_CASEEND, SM_RPAREN, 0, 0}},
	{NULL, "(", STT_LPAREN, {0, 0, 0, 0}},
	{NULL, ")", STT_FUNC, {SM_LPAREN, 0, 0, 0}},
	{dolist, "{", STT_LIST | STT_NEEDLIST, {0, 0, 0, 0}},
	{NULL, "}", STT_NEEDNONE, {SM_LIST, 0, 0, 0}},
};
#define	STATEMENTSIZ		arraysize(statementlist)
CONST signaltable signallist[] = {
#ifdef	SIGHUP
	{SIGHUP, trap_hup, "HUP", MESHUP, TR_TERM | TR_BLOCK},
#endif
#ifdef	SIGINT
	{SIGINT, trap_int, "INT", MESINT, TR_TERM | TR_BLOCK | TR_READBL},
#endif
#ifdef	SIGQUIT
	{SIGQUIT, trap_quit, "QUIT", MESQUIT, TR_TERM | TR_BLOCK},
#endif
#ifdef	SIGILL
	{SIGILL, trap_ill, "ILL", MESILL, TR_TERM},
#endif
#ifdef	SIGTRAP
	{SIGTRAP, trap_trap, "TRAP", MESTRAP, TR_TERM},
#endif
#ifdef	SIGIOT
	{SIGIOT, trap_iot, "IOT", MESIOT, TR_TERM},
#else
# ifdef	SIGABRT
	{SIGABRT, trap_abrt, "ABRT", MESABRT, TR_TERM},
# endif
#endif
#ifdef	SIGEMT
	{SIGEMT, trap_emt, "EMT", MESEMT, TR_TERM},
#endif
#ifdef	SIGFPE
	{SIGFPE, trap_fpe, "FPE", MESFPE, TR_TERM},
#endif
#ifdef	SIGKILL
	{SIGKILL, NULL, "KILL", MESKILL, 0},
#endif
#ifdef	SIGBUS
	{SIGBUS, trap_bus, "BUS", MESBUS, TR_TERM},
#endif
#ifdef	SIGSEGV
# ifdef	BASHSTYLE
	{SIGSEGV, trap_segv, "SEGV", MESSEGV, TR_TERM},
# else
	{SIGSEGV, trap_segv, "SEGV", MESSEGV, TR_TERM | TR_BLOCK | TR_NOTRAP},
# endif
#endif
#ifdef	SIGSYS
	{SIGSYS, trap_sys, "SYS", MESSYS, TR_TERM},
#endif
#ifdef	SIGPIPE
	{SIGPIPE, trap_pipe, "PIPE", MESPIPE, TR_TERM},
#endif
#ifdef	SIGALRM
	{SIGALRM, trap_alrm, "ALRM", MESALRM, TR_TERM | TR_READBL},
#endif
#ifdef	SIGTERM
	{SIGTERM, trap_term, "TERM", MESTERM, TR_TERM | TR_BLOCK | TR_READBL},
#endif
#ifdef	SIGSTKFLT
	{SIGSTKFLT, trap_stkflt, "STKFLT", MESSTKFLT, TR_TERM},
#endif
#ifdef	SIGURG
	{SIGURG, trap_urg, "URG", MESURG, TR_IGN},
#endif
#ifdef	SIGSTOP
	{SIGSTOP, trap_stop, "STOP", MESSTOP, TR_STOP},
#endif
#ifdef	SIGTSTP
	{SIGTSTP, trap_tstp, "TSTP", MESTSTP, TR_STOP | TR_BLOCK},
#endif
#ifdef	SIGCONT
# if	MSDOS
	{SIGCONT, trap_cont, "CONT", MESCONT, TR_IGN},
# else
	{SIGCONT, trap_cont, "CONT", MESCONT, TR_IGN | TR_BLOCK},
# endif
#endif
#ifdef	SIGCHLD
# ifdef	NOJOB
	{SIGCHLD, trap_chld, "CHLD", MESCHLD, TR_IGN},
# else
	{SIGCHLD, trap_chld, "CHLD", MESCHLD, TR_IGN | TR_BLOCK},
# endif
#endif
#ifdef	SIGTTIN
	{SIGTTIN, trap_ttin, "TTIN", MESTTIN, TR_STOP | TR_BLOCK},
#endif
#ifdef	SIGTTOU
	{SIGTTOU, trap_ttou, "TTOU", MESTTOU, TR_STOP | TR_BLOCK},
#endif
#ifdef	SIGIO
	{SIGIO, trap_io, "IO", MESIO, TR_IGN},
#else
# ifdef	SIGPOLL
	{SIGPOLL, trap_poll, "POLL", MESPOLL, TR_TERM},
# endif
#endif
#ifdef	SIGXCPU
	{SIGXCPU, trap_xcpu, "XCPU", MESXCPU, TR_TERM},
#endif
#ifdef	SIGXFSZ
	{SIGXFSZ, trap_xfsz, "XFSZ", MESXFSZ, TR_TERM},
#endif
#ifdef	SIGVTALRM
	{SIGVTALRM, trap_vtalrm, "VTALRM", MESVTALRM, TR_TERM},
#endif
#ifdef	SIGPROF
	{SIGPROF, trap_prof, "PROF", MESPROF, TR_TERM},
#endif
#ifdef	SIGWINCH
# ifdef	FD
	{SIGWINCH, trap_winch, "WINCH", MESWINCH, TR_IGN | TR_BLOCK},
# else
	{SIGWINCH, trap_winch, "WINCH", MESWINCH, TR_IGN},
# endif
#endif
#ifdef	SIGLOST
	{SIGLOST, trap_lost, "LOST", MESLOST, TR_TERM},
#endif
#ifdef	SIGINFO
	{SIGINFO, trap_info, "INFO", MESINFO, TR_IGN},
#endif
#ifdef	SIGPWR
	{SIGPWR, trap_pwr, "PWR", MESPWR, TR_TERM},
#endif
#ifdef	SIGUSR1
# ifdef	BASHSTYLE
	{SIGUSR1, trap_usr1, "USR1", MESUSR1, TR_TERM},
# else
	{SIGUSR1, trap_usr1, "USR1", MESUSR1, TR_TERM | TR_BLOCK},
# endif
#endif
#ifdef	SIGUSR2
# ifdef	BASHSTYLE
	{SIGUSR2, trap_usr2, "USR2", MESUSR2, TR_TERM},
# else
	{SIGUSR2, trap_usr2, "USR2", MESUSR2, TR_TERM | TR_BLOCK},
# endif
#endif
	{-1, NULL, NULL, NULL, 0}
};
int verboseexec = 0;
int notexec = 0;
int verboseinput = 0;
int terminated = 0;
int forcedstdin = 0;
int interactive_io = 0;
int tmperrorexit = 0;
int restricted = 0;
int freeenviron = 0;
int undeferror = 0;
int hashahead = 0;
#if	MSDOS
int noglob = 1;
#else
int noglob = 0;
#endif
int autoexport = 0;
#ifndef	MINIMUMSHELL
int noclobber = 0;
int ignoreeof = 0;
#endif
#ifndef	NOJOB
int bgnotify = 0;
int jobok = -1;
#endif
#ifdef	FD
# ifndef	_NOEDITMODE
int emacsmode = 0;
int vimode = 0;
# endif
# ifdef	DEP_PTY
int tmpshptymode = 0;
# endif
# if	!MSDOS
int autosavetty = 0;
# endif
#endif	/* FD */
int loginshell = 0;
int noruncom = 0;

static char **shellvar = NULL;
static char **exportvar = NULL;
static u_long exportsize = (u_long)0;
static char **exportlist = NULL;
static char **ronlylist = NULL;
static char *shellname = NULL;
static int definput = -1;
static int exit_status = RET_SUCCESS;
#if	!MSDOS
static sigmask_t oldsigmask;
#endif
static p_id_t lastpid = (p_id_t)-1;
#ifndef	NOJOB
static p_id_t oldttypgrp = (p_id_t)-1;
#endif
static int setsigflag = 0;
static int trapok = 0;
static shfunctable *shellfunc = NULL;
static pipelist *pipetop = NULL;
static int childdepth = 0;
static int loopdepth = 0;
static int breaklevel = 0;
static int continuelevel = 0;
static int shfunclevel = 0;
static int returnlevel = 0;
#ifndef	MINIMUMSHELL
static char **dirstack = NULL;
static long shlineno = 0L;
static int lastsig = -1;
#endif
static int isshellbuiltin = 0;
static int execerrno = 0;
#if	!MSDOS && defined (DEP_PTY) && defined (CYGWIN)
static p_id_t *mychildren = (p_id_t *)NULL;
#endif
static CONST char *CONST syntaxerrstr[] = {
	NULL,
#define	ER_UNEXPTOK		1
	"unexpected token",
#define	ER_UNEXPNL		2
	"unexpected newline or `;'",
#define	ER_UNEXPEOF		3
	"unexpected end of file",
};
#define	SYNTAXERRSIZ		arraysize(syntaxerrstr)
static CONST char *CONST execerrstr[] = {
	NULL,
#define	ER_COMNOFOUND		1
	"command not found",
#define	ER_NOTFOUND		2
	"not found",
#define	ER_CANNOTEXE		3
	"cannot execute",
#define	ER_NOTIDENT		4
	"is not an identifier",
#define	ER_BADSUBST		5
	"bad substitution",
#define	ER_BADNUMBER		6
	"bad number",
#define	ER_BADDIR		7
	"bad directory",
#define	ER_CANNOTRET		8
	"cannot return when not in function",
#define	ER_CANNOTSTAT		9
	"cannot stat .",
#define	ER_CANNOTUNSET		10
	"cannot unset",
#define	ER_ISREADONLY		11
	"is read only",
#define	ER_CANNOTSHIFT		12
	"cannot shift",
#define	ER_BADOPTIONS		13
	"bad option(s)",
#define	ER_PARAMNOTSET		14
	"parameter not set",
#define	ER_RESTRICTED		15
	"restricted",
#define	ER_BADULIMIT		16
	"bad ulimit",
#define	ER_BADTRAP		17
	"bad trap",
#define	ER_NUMOUTRANGE		18
	"number out of range",
#define	ER_NOHOMEDIR		19
	"no home directory",
#ifdef	NOALIAS
	NULL,
#else
#define	ER_NOTALIAS		20
	"is not an alias",
#endif
#ifdef	NOPOSIXUTIL
	NULL,
#else
#define	ER_MISSARG		21
	"missing argument",
#endif
#ifdef	NOJOB
	NULL, NULL,
#else
#define	ER_NOSUCHJOB		22
	"no such job",
#define	ER_TERMINATED		23
	"job has terminated",
#endif
#ifdef	MINIMUMSHELL
	NULL, NULL, NULL,
#else
#define	ER_NOTLOGINSH		24
	"not login shell",
#define	ER_DIREMPTY		25
	"directory stack empty",
#define	ER_UNKNOWNSIG		26
	"unknown signal; kill -l lists signals",
#endif
#if	!MSDOS
	NULL,
#else
#define	ER_INVALDRIVE		27
	"invalid drive specification",
#endif
#ifdef	WITHSOCKET
#define	ER_NOTSOCKET		28
	"not socket",
#define	ER_INVALSOCKET		29
	"invalid socket",
#else
	NULL, NULL,
#endif
#ifdef	FD
#define	ER_RECURSIVEFD		30
	"recursive call for FDclone",
#define	ER_INVALTERMFD		31
	"invalid terminal for FDclone",
#endif
};
#define	EXECERRSIZ		arraysize(execerrstr)
static CONST opetable opelist[] = {
	{OP_FG, 4, ";"},
	{OP_BG, 4, "&"},
#ifndef	MINIMUMSHELL
	{OP_NOWN, 4, "&|"},
#endif
	{OP_AND, 3, "&&"},
	{OP_OR, 3, "||"},
	{OP_PIPE, 2, "|"},
#ifndef	MINIMUMSHELL
	{OP_NOT, 2, "!"},
#endif
};
#define	OPELISTSIZ		arraysize(opelist)
#if	!defined (BASHBUG) && !defined (MINIMUMSHELL)
static CONST opetable delimlist[] = {
	{OP_NONE, 1, "{"},
	{OP_NONE, 1, ";;"},
	{OP_FG, 1, ";"},
	{OP_NONE, 0, "&>>"},
	{OP_NONE, 0, "&>|"},
	{OP_NONE, 0, "&>"},
	{OP_AND, 1, "&&"},
	{OP_NOWN, 1, "&|"},
	{OP_BG, 1, "&"},
	{OP_OR, 1, "||"},
	{OP_PIPE, 1, "|&"},
	{OP_PIPE, 1, "|"},
	{OP_NOT, 1, "!"},
	{OP_NONE, 0, "<<-"},
	{OP_NONE, 0, "<<"},
	{OP_NONE, 0, "<>"},
	{OP_NONE, 0, "<&-"},
	{OP_NONE, 0, "<&"},
	{OP_NONE, 0, "<-"},
	{OP_NONE, 0, "<"},
	{OP_NONE, 0, "><"},
	{OP_NONE, 0, ">>"},
	{OP_NONE, 0, ">&-"},
	{OP_NONE, 0, ">&"},
	{OP_NONE, 0, ">-"},
	{OP_NONE, 0, ">|"},
	{OP_NONE, 0, ">"},
};
#define	DELIMLISTSIZ		arraysize(delimlist)
#endif	/* !BASHBUG && !MINIMUMSHELL */
static shbuiltintable shbuiltinlist[] = {
	{donull, ":", BT_POSIXSPECIAL},
	{dobreak, "break", BT_POSIXSPECIAL},
	{docontinue, "continue", BT_POSIXSPECIAL},
	{doreturn, "return", BT_POSIXSPECIAL},
	{doexec, "exec", BT_POSIXSPECIAL | BT_NOKANJIFGET},
#ifndef	MINIMUMSHELL
	{dologin, "login", 0},
	{dologout, "logout", 0},
#endif
	{doeval, "eval", BT_POSIXSPECIAL | BT_NOKANJIFGET},
	{doexit, "exit", BT_POSIXSPECIAL},
	{doread, "read", 0},
	{doshift, "shift", BT_POSIXSPECIAL},
	{doset, "set", BT_POSIXSPECIAL},
	{dounset, "unset", BT_POSIXSPECIAL},
#ifndef	_NOUSEHASH
	{dohash, "hash", 0},
#endif
	{dochdir, "cd", BT_RESTRICT | BT_FILENAME},
	{dopwd, "pwd", 0},
	{dosource, ".", BT_POSIXSPECIAL | BT_FILENAME},
#ifndef	MINIMUMSHELL
	{dosource, "source", BT_FILENAME},
#endif
	{doexport, "export", BT_POSIXSPECIAL},
	{doreadonly, "readonly", BT_POSIXSPECIAL},
	{dotimes, "times", BT_POSIXSPECIAL},
	{dowait, "wait", 0},
	{doumask, "umask", 0},
	{doulimit, "ulimit", 0},
	{dotrap, "trap", BT_POSIXSPECIAL},
#ifndef	NOJOB
	{dojobs, "jobs", 0},
	{dofg, "fg", 0},
	{dobg, "bg", 0},
	{dodisown, "disown", 0},
#endif
	{dotype, "type", 0},
#ifdef	DOSCOMMAND
	{donull, "rem", 0},
	{dodir, "dir", BT_NOGLOB | BT_FILENAME},
	{dochdir, "chdir", BT_RESTRICT | BT_FILENAME},
# if	MSDOS
	{domkdir, "mkdir", BT_RESTRICT | BT_FILENAME},
	{dormdir, "rmdir", BT_RESTRICT | BT_FILENAME},
# endif
	{domkdir, "md", BT_RESTRICT | BT_FILENAME},
	{dormdir, "rd", BT_RESTRICT | BT_FILENAME},
	{doerase, "erase", BT_NOGLOB | BT_RESTRICT | BT_FILENAME},
	{doerase, "del", BT_NOGLOB | BT_RESTRICT | BT_FILENAME},
	{dorename, "rename", BT_NOGLOB | BT_RESTRICT | BT_FILENAME},
	{dorename, "ren", BT_NOGLOB | BT_RESTRICT | BT_FILENAME},
	{docopy, "copy", BT_NOGLOB | BT_RESTRICT | BT_FILENAME},
	{docls, "cls", 0},
	{dodtype, "dtype", BT_FILENAME},
#endif	/* DOSCOMMAND */
#ifndef	NOALIAS
	{doalias, "alias", 0},
	{dounalias, "unalias", BT_NOGLOB},
#endif
	{doecho, "echo", 0},
#ifndef	MINIMUMSHELL
	{dokill, "kill", 0},
	{dotest, "test", 0},
	{dotest, "[", 0},
#endif
#ifndef	NOPOSIXUTIL
	{donull, "true", 0},
	{dofalse, "false", 0},
	{docommand, "command", BT_NOKANJIFGET},
	{dogetopts, "getopts", 0},
	{donewgrp, "newgrp", BT_RESTRICT},
# if	0				/* exists in FD original builtin */
	{dofc, "fc", 0},
# endif
#endif
#ifndef	MINIMUMSHELL
	{dopushd, "pushd", BT_RESTRICT | BT_FILENAME},
	{dopopd, "popd", 0},
	{dodirs, "dirs", 0},
	{doenable, "enable", 0},
	{dobuiltin, "builtin", 0},
	{doaddcr, "addcr", 0},
#endif
#ifdef	WITHSOCKET
	{doaccept, "accept", BT_RESTRICT},
	{dosocketinfo, "socketinfo", BT_RESTRICT},
#endif
#ifdef	FD
	{dofd, "fd", BT_FILENAME},
#endif
};
#define	SHBUILTINSIZ		arraysize(shbuiltinlist)
static CONST char *primalvar[] = {
	ENVPATH, ENVPS1, ENVPS2, ENVIFS,
#if	!MSDOS && !defined (MINIMUMSHELL)
	ENVMAILCHECK, ENVPPID,
#endif
};
#define	PRIMALVARSIZ		arraysize(primalvar)
static CONST char *restrictvar[] = {
	ENVPATH,
#ifdef	FD
	"FD_SHELL",
#else
	ENVSHELL,
#endif
#ifndef	MINIMUMSHELL
	ENVENV,
#endif
};
#define	RESTRICTVARSIZ		arraysize(restrictvar)
#if	MSDOS && !defined (BSPATHDELIM)
static CONST char *adjustvar[] = {
	ENVPATH, ENVHOME,
# ifndef	MINIMUMSHELL
	ENVENV,
# endif
# ifdef	FD
	ENVPWD,
	"FD_HISTFILE",
#  ifdef	FD >= 2
	"FD_DIRHISTFILE",
#  endif
#  if	FD >= 3
	"FD_FREQFILE",
#  endif
	"FD_TMPDIR",
#  ifndef	_NOROCKRIDGE
	"FD_RRPATH",
#  endif
#  ifndef	_NOPRECEDE
	"FD_PRECEDEPATH",
#  endif
#  ifdef	DEP_LOGGING
	"FD_LOGFILE",
#  endif
#  ifdef	DEP_FTPPATH
	"FD_FTPLOGFILE",
#  endif
#  ifdef	DEP_HTTPPATH
	"FD_HTTPLOGFILE", "FD_HTMLLOGFILE",
#  endif
#  ifdef	DEP_FILECONV
	"FD_SJISPATH", "FD_EUCPATH",
	"FD_JISPATH", "FD_JIS8PATH", "FD_JUNETPATH",
	"FD_OJISPATH", "FD_OJIS8PATH", "FD_OJUNETPATH",
	"FD_HEXPATH", "FD_CAPPATH",
	"FD_UTF8PATH", "FD_UTF8MACPATH", "FD_UTF8ICONVPATH",
	"FD_NOCONVPATH",
#  endif
	"FD_PAGER", "FD_EDITOR", "FD_SHELL",
#  ifndef	NOPOSIXUTIL
	"FD_FCEDIT",
#  endif
#  if	MSDOS
	"FD_COMSPEC",
#  endif
# else	/* !FD */
	ENVSHELL,
#  if	MSDOS
	ENVCOMSPEC,
#  endif
# endif	/* !FD */
};
#define	ADJUSTVARSIZ		arraysize(adjustvar)
#endif	/* MSDOS && !BSPATHDELIM */
static CONST shflagtable shflaglist[] = {
	{"xtrace", &verboseexec, 'x'},
	{"noexec", &notexec, 'n'},
	{"verbose", &verboseinput, 'v'},
	{"onecmd", &terminated, 't'},
	{NULL, &forcedstdin, 's'},
	{NULL, &interactive_io, 'i'},
	{"errexit", &tmperrorexit, 'e'},
	{NULL, &restricted, 'r'},
	{"keyword", &freeenviron, 'k'},
	{"nounset", &undeferror, 'u'},
	{"hashahead", &hashahead, 'h'},
	{"noglob", &noglob, 'f'},
	{"allexport", &autoexport, 'a'},
#ifndef	MINIMUMSHELL
	{"noclobber", &noclobber, 'C'},
	{"ignoreeof", &ignoreeof, '\0'},
#endif
#ifndef	NOJOB
	{"notify", &bgnotify, 'b'},
	{"monitor", &jobok, 'm'},
#endif
#ifdef	FD
	{"physical", &physical_path, 'P'},
# ifndef	_NOEDITMODE
	{"emacs", &emacsmode, '\0'},
	{"vi", &vimode, '\0'},
# endif
# ifdef	DEP_PTY
	{"ptyshell", &tmpshptymode, 'T'},
# endif
# if	!MSDOS
	{"autosavetty", &autosavetty, 'S'},
# endif
#endif	/* FD */
	{NULL, &loginshell, 'l'},
	{NULL, &noruncom, 'N'},
};
#define	FLAGSSIZ		arraysize(shflaglist)
#ifdef	USERESOURCEH
static CONST ulimittable ulimitlist[] = {
#ifdef	RLIMIT_CPU
	{'t', RLIMIT_CPU, 1, "time(seconds)"},
#endif
#ifdef	RLIMIT_FSIZE
	{'f', RLIMIT_FSIZE, DEV_BSIZE, "file(blocks)"},
#endif
#ifdef	RLIMIT_DATA
	{'d', RLIMIT_DATA, 1024, "data(kbytes)"},
#endif
#ifdef	RLIMIT_STACK
	{'s', RLIMIT_STACK, 1024, "stack(kbytes)"},
#endif
#ifdef	RLIMIT_CORE
	{'c', RLIMIT_CORE, DEV_BSIZE, "coredump(blocks)"},
#endif
#ifdef	RLIMIT_RSS
	{'m', RLIMIT_RSS, 1024, "memory(kbytes)"},
#endif
#ifdef	RLIMIT_MEMLOCK
	{'l', RLIMIT_MEMLOCK, 1024, "locked memory(kbytes)"},
#endif
#ifdef	RLIMIT_NPROC
	{'u', RLIMIT_NPROC, 1, "processes(units)"},
#endif
#ifdef	RLIMIT_NOFILE
	{'n', RLIMIT_NOFILE, 1, "nofiles(descriptors)"},
#endif
#ifdef	RLIMIT_VMEM
	{'v', RLIMIT_VMEM, 1024, "virtual memory(kbytes)"},
#endif
};
#define	ULIMITSIZ		arraysize(ulimitlist)
#endif	/* USERESOURCEH */
static int trapmode[NSIG];
static char *trapcomm[NSIG];
static sigarg_t (*oldsigfunc[NSIG])__P_((sigfnc_t));
#ifdef	WITHSOCKREDIR
static scheme_t schemelist[] = {
#define	DEFSCHEME(i, t)		{i, strsize(i), -1, t}
	DEFSCHEME("connect", TYPE_CONNECT),
	DEFSCHEME("accept", TYPE_ACCEPT),
	DEFSCHEME("bind", TYPE_BIND),
	{NULL, 0, 0},
};
#endif


#ifndef	FD
static lockbuf_t *NEAR lockopen(path, flags, mode)
CONST char *path;
int flags, mode;
{
	lockbuf_t *lck;
	int fd;

	if ((fd = newdup(Xopen(path, flags, mode))) >= 0) /*EMPTY*/;
	else if (errno != ENOENT) return(NULL);
	lck = (lockbuf_t *)Xmalloc(sizeof(lockbuf_t));
	lck -> fd = fd;

	return(lck);
}

static VOID NEAR lockclose(lck)
lockbuf_t *lck;
{
	if (lck) {
		if (lck -> fd >= 0) VOID_C Xclose(lck -> fd);
		Xfree(lck);
	}
}

static int NEAR genrand(max)
int max;
{
# ifdef	MINIMUMSHELL
	static u_int last = 0;
	static int init = 0;

	if (!init) {
		init++;
		last = getpid();
	}
	last = last * 12345 + 101;

	return(last % max);
# else	/* !MINIMUMSHELL */
	static long last = -1L;
	time_t now;

	if (last < 0L) {
		now = Xtime(NULL);
		last = ((now & 0xff) << 16) + (now & ~0xff) + getpid();
	}

	do {
		last = last * (u_long)1103515245 + 12345;
	} while (last < 0L);

	return((last / 65537L) % max);
# endif	/* !MINIMUMSHELL */
}

static char *NEAR genrandname(buf, len)
char *buf;
int len;
{
	static char seq[] = {
# ifdef	PATHNOCASE
		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
		'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
		'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
		'U', 'V', 'W', 'X', 'Y', 'Z', '_'
# else
		'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
		'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
		'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
		'u', 'v', 'w', 'x', 'y', 'z', '_'
# endif
	};
	int i, j, c;

	if (!buf) {
		for (i = 0; i < arraysize(seq); i++) {
			j = genrand(arraysize(seq));
			c = seq[i];
			seq[i] = seq[j];
			seq[j] = c;
		}
	}
	else {
		for (i = 0; i < len; i++) {
			j = genrand(arraysize(seq));
			buf[i] = seq[j];
		}
		buf[i] = '\0';
	}

	return(buf);
}

static int NEAR mktmpfile(file)
char *file;
{
	char *cp, path[MAXPATHLEN];
	int fd, n, len;

	Xstrcpy(path, deftmpdir);
	cp = strcatdelim(path);
	n = strsize(TMPPREFIX);
	Xstrncpy(cp, TMPPREFIX, n);
	len = strsize(path) - (cp - path);
	if (len > MAXTMPNAMLEN) len = MAXTMPNAMLEN;
	len -= n;
	genrandname(NULL, 0);

	for (;;) {
		genrandname(cp + n, len);
		fd = Xopen(path, O_BINARY | O_WRONLY | O_CREAT | O_EXCL, 0644);
		if (fd >= 0) {
			Xstrcpy(file, path);
			return(fd);
		}
		if (errno != EEXIST) break;
	}

	return(-1);
}

static int NEAR rmtmpfile(file)
CONST char *file;
{
	if (Xunlink(file) < 0 && errno != ENOENT) return(-1);

	return(0);
}

/*ARGSUSED*/
int chdir3(dir, raw)
CONST char *dir;
int raw;
{
	if (chdir2(dir) < 0) return(-1);
# ifndef	_NOUSEHASH
	VOID_C searchhash(NULL, nullstr, nullstr);
# endif

	return(0);
}

int setenv2(name, value, export)
CONST char *name, *value;
int export;
{
	char *cp;
	int len;

	len = strlen(name);
	if (!value) return(unset(name, len));
	else {
		cp = Xmalloc(len + strlen(value) + 2);
		memcpy(cp, name, len);
		cp[len] = '=';
		Xstrcpy(&(cp[len + 1]), value);
	}
	if (((export) ? putexportvar(cp, len) : putshellvar(cp, len)) < 0) {
		Xfree(cp);
		return(-1);
	}

	return(0);
}

# ifdef	USESIGACTION
static sigcst_t NEAR signal2(sig, func)
int sig;
sigcst_t func;
{
	struct sigaction act, oact;

	act.sa_handler = func;
#ifdef	SA_INTERRUPT
	act.sa_flags = SA_INTERRUPT;
#else
	act.sa_flags = 0;
#endif
	sigemptyset(&(act.sa_mask));
	sigemptyset(&(oact.sa_mask));
	if (sigaction(sig, &act, &oact) < 0) return(SIG_ERR);

	return(oact.sa_handler);
}
# endif	/* USESIGACTION */

# if	MSDOS && !defined (BSPATHDELIM)
static char *NEAR adjustpname(path)
char *path;
{
	int i;

	for (i = 0; path[i]; i++) {
		if (path[i] == '\\') path[i] = _SC_;
		else if (iskanji1(path, i)) i++;
	}

	return(path);
}
# endif	/* MSDOS && !BSPATHDELIM */

# if	!MSDOS && !defined (NOJOB)
static VOID NEAR stdiomode(VOID_A)
{
#  ifdef	USESGTTY
	int lflag;
#  endif
	termioctl_t tty;

	if (tioctl(ttyio, REQGETP, &tty) >= 0) {
#  ifdef	USESGTTY
		tty.sg_flags |= ECHO | CRMOD;
		tty.sg_flags &= ~(RAW | CBREAK | XTABS);
#  else
		tty.c_lflag |= (TIO_LCOOKED | TIO_LECHO);
		tty.c_lflag &= ~(PENDIN | ECHONL);
		tty.c_iflag |= (TIO_ICOOKED | ICRNL);
		tty.c_iflag &= TIO_INOCOOKED;
		tty.c_oflag |= TIO_ONL;
		tty.c_oflag &= (TIO_ONONL & ~TAB3);
#  endif
		VOID_C tioctl(ttyio, REQSETP, &tty);
	}
#  ifdef	USESGTTY
	if (Xioctl(ttyio, TIOCLGET, &lflag) >= 0) {
		lflag |= LPASS8 | LCRTBS | LCRTERA | LCRTKIL | LCTLECH;
		lflag &= ~(LLITOUT | LPENDIN);
		VOID_C Xioctl(ttyio, TIOCLSET, &lflag);
	}
#  endif
}
# endif	/* !MSDOS && !NOJOB */
#endif	/* !FD */

#ifndef	NOJOB
static VOID NEAR notifyjob(VOID_A)
{
	XFILE *fp;
	int fd;

	if ((fd = Xdup(STDERR_FILENO)) < 0) fp = Xstderr;
	else if (!(fp = Xfdopen(fd, "a"))) {
		VOID_C Xclose(fd);
		fp = Xstderr;
	}
	else Xsetlinebuf(fp);
	checkjob(fp);
	safefclose(fp);
}
#endif	/* !NOJOB */

static VOID NEAR setsignal(VOID_A)
{
	int i, sig;

	if (setsigflag++) return;
#if	defined (FD) && defined (SIGALRM)
	noalrm++;
#endif
	for (i = 0; signallist[i].sig >= 0; i++) {
		sig = signallist[i].sig;
		if (signallist[i].flags & TR_BLOCK) /*EMPTY*/;
		else if (signallist[i].flags & TR_READBL) /*EMPTY*/;
		else if ((trapmode[sig] & TR_STAT) != TR_TRAP) continue;
		else if ((signallist[i].flags & TR_STAT) == TR_STOP) continue;

		signal2(sig, (sigcst_t)(signallist[i].func));
	}
}

static VOID NEAR resetsignal(forced)
int forced;
{
#if	!MSDOS && defined (NOJOB)
	p_id_t tmp;
#endif
#if	defined (SIGCHLD) && !defined (NOJOB)
	int n;
#endif
	int i, duperrno;

	duperrno = errno;
#if	!MSDOS
# ifdef	NOJOB
	do {
		tmp = Xwait3(NULL, WNOHANG | WUNTRACED, NULL);
	} while (tmp > (p_id_t)0 || (tmp < (p_id_t)0 && errno == EINTR));
# else
	notifyjob();
	stopped = 0;
# endif
#endif	/* !MSDOS */
	if (havetty() && interrupted && interactive && !nottyout) {
		Xfflush(Xstdout);
		VOID_C fputnl(Xstderr);
	}
	interrupted = 0;
	if (setsigflag) {
		if (forced) setsigflag = 1;
		if (!(--setsigflag)) {
#if	defined (FD) && defined (SIGALRM)
			noalrm--;
#endif
			for (i = 0; i < NSIG; i++) {
#if	defined (SIGCHLD) && !defined (NOJOB)
				if (i == SIGCHLD && bgnotify) {
					for (n = 0; n < maxjobs; n++)
						if (joblist[n].pids) break;
					if (n < maxjobs) continue;
				}
#endif
				if (oldsigfunc[i] != SIG_ERR)
					signal2(i, oldsigfunc[i]);
			}
#if	!MSDOS
			Xsigsetmask(oldsigmask);
#endif
		}
	}
	errno = duperrno;
}

static VOID NEAR exectrapcomm(VOID_A)
{
#if	MSDOS
	sigcst_t ofunc;
#else
	sigmask_t mask, omask;
#endif
	int i, dupinterrupted;

#if	!MSDOS
	if (!havetty()) return;
#endif
	for (i = 0; i < NSIG; i++) {
		if (!(trapmode[i] & TR_CATCH)) continue;
		trapmode[i] &= ~TR_CATCH;
		if (!trapcomm[i] || !*(trapcomm[i])) continue;
#if	MSDOS
		ofunc = signal2(i, SIG_IGN);
#else
		Xsigemptyset(mask);
		Xsigaddset(mask, i);
		Xsigblock(omask, mask);
#endif
		dupinterrupted = interrupted;
		interrupted = 0;
		_dosystem(trapcomm[i]);
		interrupted = dupinterrupted;
#if	MSDOS
		signal2(i, ofunc);
#else
		Xsigsetmask(omask);
#endif
	}
}

static VOID NEAR killmyself(sig)
int sig;
{
	printf_urgent++;
	prepareexit(0);
	printf_urgent--;

	signal2(sig, SIG_DFL);
	VOID_C kill(mypid, sig);

	exit(sig + 128);
}

static int NEAR trap_common(sig)
int sig;
{
#if	!MSDOS && defined (DEP_PTY) && defined (CYGWIN)
	int n;
#endif
#if	!MSDOS
	sigmask_t mask, omask;
#endif
	int i, trapped, flags, duperrno, duptrapok;

	duperrno = errno;
	duptrapok = trapok;
	trapok = -1;
#ifndef	MINIMUMSHELL
	lastsig = -1;
#endif
#if	!MSDOS
	Xsigemptyset(mask);
	Xsigaddset(mask, sig);
	Xsigblock(omask, mask);		/* for obsolete signal() */
#endif

	for (i = 0; signallist[i].sig >= 0; i++)
		if (sig == signallist[i].sig) break;
	if (signallist[i].sig < 0) {
		signal2(sig, SIG_DFL);
#if	!MSDOS
		Xsigsetmask(omask);
#endif
		errno = duperrno;
		trapok = duptrapok;
		VOID_C kill(mypid, sig);
		return(0);
	}

	trapped = 0;
	flags = signallist[i].flags;

	if (mypid != shellpid) {
		if ((flags & TR_BLOCK) && (flags & TR_STAT) == TR_TERM)
			trapped = -1;
	}
	else if ((trapmode[sig] & TR_STAT) == TR_TRAP) trapped = 1;
	else if (flags & TR_BLOCK) /*EMPTY*/;
	else if ((flags & TR_STAT) == TR_TERM) trapped = -1;

	if (trapped > 0) {
		trapmode[sig] |= TR_CATCH;
		if (duptrapok > 0) exectrapcomm();
	}
	else if (trapped < 0 && signallist[i].mes && *(signallist[i].mes)) {
		Xfputs(signallist[i].mes, Xstderr);
		VOID_C fputnl(Xstderr);
	}

#if	!MSDOS && defined (DEP_PTY) && defined (CYGWIN)
	if (mychildren && (flags & TR_STAT) == TR_TERM) {
		for (n = 0; mychildren[n] >= (p_id_t)0; n++) {
			VOID_C kill(mychildren[n], sig);
# ifdef	SIGCONT
			VOID_C kill(mychildren[n], SIGCONT);
# endif
		}
		Xfree(mychildren);
		mychildren = NULL;
	}
#endif	/* !MSDOS && DEP_PTY && CYGWIN */

	if (trapped < 0) {
#if	!MSDOS
		Xsigsetmask(omask);
#endif
		killmyself(sig);
	}

#ifndef	MINIMUMSHELL
	lastsig = i;
#endif
	switch (sig) {
#ifdef	SIGHUP
		case SIGHUP:
			if (trapped) break;
# ifndef	NOJOB
			if (havetty()) killjob();
# endif
			if (oldsigfunc[sig] && oldsigfunc[sig] != SIG_ERR
			&& oldsigfunc[sig] != SIG_DFL
			&& oldsigfunc[sig] != SIG_IGN) {
# ifdef	SIGFNCINT
				(*oldsigfunc[sig])(sig);
# else
				(*oldsigfunc[sig])();
# endif
			}
#if	!MSDOS
			Xsigsetmask(omask);
#endif
			killmyself(sig);
			break;
#endif	/* SIGHUP */
#if	!MSDOS && defined (SIGCONT)
		case SIGCONT:
# ifdef	FD
			suspended = 1;
# endif
			sigconted = 1;
			break;
#endif	/* !MSDOS && SIGCONT */
#ifdef	SIGINT
		case SIGINT:
			interrupted = 1;
			break;
#endif
#if	defined (SIGCHLD) && (!defined (NOJOB) || defined (SYSV))
		case SIGCHLD:
# ifndef	NOJOB
			if (bgnotify) {
				notifyjob();
				break;
			}
# endif
# ifdef	SYSV
	/* TIPS for obsolete SystemV signal() */
			while (waitjob(-1, NULL, WNOHANG | WUNTRACED) > 0)
				/*EMPTY*/;
# endif
			break;
#endif	/* SIGCHLD && (!NOJOB || SYSV) */
#if	defined (FD) && defined (SIGWINCH)
		case SIGWINCH:
			pollscreen(1);
			break;
#endif	/* FD && SIGWINCH */
		default:
			break;
	}

	if (signallist[i].func) signal2(sig, (sigcst_t)(signallist[i].func));
#if	!MSDOS
	Xsigsetmask(omask);
#endif
	errno = duperrno;
	trapok = duptrapok;

	return(trapped);
}

#ifdef	SIGHUP
static int trap_hup(VOID_A)
{
	return(trap_common(SIGHUP));
}
#endif

#ifdef	SIGINT
static int trap_int(VOID_A)
{
	return(trap_common(SIGINT));
}
#endif

#ifdef	SIGQUIT
static int trap_quit(VOID_A)
{
	return(trap_common(SIGQUIT));
}
#endif

#ifdef	SIGILL
static int trap_ill(VOID_A)
{
	return(trap_common(SIGILL));
}
#endif

#ifdef	SIGTRAP
static int trap_trap(VOID_A)
{
	return(trap_common(SIGTRAP));
}
#endif

#ifdef	SIGIOT
static int trap_iot(VOID_A)
{
	return(trap_common(SIGIOT));
}
#else
# ifdef	SIGABRT
static int trap_abrt(VOID_A)
{
	return(trap_common(SIGABRT));
}
# endif
#endif	/* SIGIOT */

#ifdef	SIGEMT
static int trap_emt(VOID_A)
{
	return(trap_common(SIGEMT));
}
#endif

#ifdef	SIGFPE
static int trap_fpe(VOID_A)
{
	return(trap_common(SIGFPE));
}
#endif

#ifdef	SIGBUS
static int trap_bus(VOID_A)
{
	return(trap_common(SIGBUS));
}
#endif

#ifdef	SIGSEGV
static int trap_segv(VOID_A)
{
	return(trap_common(SIGSEGV));
}
#endif

#ifdef	SIGSYS
static int trap_sys(VOID_A)
{
	return(trap_common(SIGSYS));
}
#endif

#ifdef	SIGPIPE
static int trap_pipe(VOID_A)
{
	return(trap_common(SIGPIPE));
}
#endif

#ifdef	SIGALRM
static int trap_alrm(VOID_A)
{
	return(trap_common(SIGALRM));
}
#endif

#ifdef	SIGTERM
static int trap_term(VOID_A)
{
	return(trap_common(SIGTERM));
}
#endif

#ifdef	SIGSTKFLT
static int trap_stkflt(VOID_A)
{
	return(trap_common(SIGSTKFLT));
}
#endif

#ifdef	SIGURG
static int trap_urg(VOID_A)
{
	return(trap_common(SIGURG));
}
#endif

#ifdef	SIGSTOP
static int trap_stop(VOID_A)
{
	return(trap_common(SIGSTOP));
}
#endif

#ifdef	SIGTSTP
static int trap_tstp(VOID_A)
{
	return(trap_common(SIGTSTP));
}
#endif

#ifdef	SIGCONT
static int trap_cont(VOID_A)
{
	return(trap_common(SIGCONT));
}
#endif

#ifdef	SIGCHLD
static int trap_chld(VOID_A)
{
	return(trap_common(SIGCHLD));
}
#endif

#ifdef	SIGTTIN
static int trap_ttin(VOID_A)
{
	return(trap_common(SIGTTIN));
}
#endif

#ifdef	SIGTTOU
static int trap_ttou(VOID_A)
{
	return(trap_common(SIGTTOU));
}
#endif

#ifdef	SIGIO
static int trap_io(VOID_A)
{
	return(trap_common(SIGIO));
}
#else
# ifdef	SIGPOLL
static int trap_poll(VOID_A)
{
	return(trap_common(SIGPOLL));
}
# endif
#endif	/* SIGIO */

#ifdef	SIGXCPU
static int trap_xcpu(VOID_A)
{
	return(trap_common(SIGXCPU));
}
#endif

#ifdef	SIGXFSZ
static int trap_xfsz(VOID_A)
{
	return(trap_common(SIGXFSZ));
}
#endif

#ifdef	SIGVTALRM
static int trap_vtalrm(VOID_A)
{
	return(trap_common(SIGVTALRM));
}
#endif

#ifdef	SIGPROF
static int trap_prof(VOID_A)
{
	return(trap_common(SIGPROF));
}
#endif

#ifdef	SIGWINCH
static int trap_winch(VOID_A)
{
	return(trap_common(SIGWINCH));
}
#endif

#ifdef	SIGLOST
static int trap_lost(VOID_A)
{
	return(trap_common(SIGLOST));
}
#endif

#ifdef	SIGINFO
static int trap_info(VOID_A)
{
	return(trap_common(SIGINFO));
}
#endif

#ifdef	SIGPWR
static int trap_pwr(VOID_A)
{
	return(trap_common(SIGPWR));
}
#endif

#ifdef	SIGUSR1
static int trap_usr1(VOID_A)
{
	return(trap_common(SIGUSR1));
}
#endif

#ifdef	SIGUSR2
static int trap_usr2(VOID_A)
{
	return(trap_common(SIGUSR2));
}
#endif

#ifndef	MINIMUMSHELL
int catchsignal(verbose)
int verbose;
{
	int n, duperrno;

	if (verbose < 0) {
		lastsig = -1;
		return(0);
	}

	if (lastsig < 0) return(0);

	switch (signallist[lastsig].sig) {
# ifdef	SIGTTIN
		case SIGTTIN:
			n = -1;
			break;
# endif
# ifdef	SIGTTOU
		case SIGTTOU:
			n = -1;
			break;
# endif
		default:
			n = 0;
			break;
	}

	if (n >= 0 || !verbose) /*EMPTY*/;
	else if (signallist[lastsig].mes && *(signallist[lastsig].mes)) {
		duperrno = errno;
		Xfputs(signallist[lastsig].mes, Xstderr);
		VOID_C fputnl(Xstderr);
		errno = duperrno;
	}

	return(n);
}
#endif	/* !MINIMUMSHELL */

VOID prepareexit(noexit)
int noexit;
{
#ifdef	DEBUG
	int i;
#endif
	int duperrno;

	if (noexit > 0) {
		resetsignal(1);
		return;
	}
	duperrno = errno;
	exectrapcomm();
	if (mypid == orgpid && !noexit && (trapmode[0] & TR_STAT) == TR_TRAP) {
		trapmode[0] = 0;
		if (trapcomm[0] && *(trapcomm[0])) _dosystem(trapcomm[0]);
	}
#ifndef	NOJOB
	if (ttyio >= 0) VOID_C gettermio(oldttypgrp, 1);
#endif
#ifdef	DEP_URLPATH
	VOID_C urlallclose();
#endif

	resetsignal(1);
	freefunc(shellfunc);
	shellfunc = NULL;
#ifdef	DEBUG
	if (definput >= 0) {
		if (definput != ttyio) safeclose(definput);
		definput = -1;
	}
	freevar(shellvar);
	shellvar = NULL;
	freevar(exportvar);
	exportvar = NULL;
	exportsize = (u_long)0;
	freevar(exportlist);
	exportlist = NULL;
	freevar(ronlylist);
	ronlylist = NULL;
	freevar(argvar);
	argvar = NULL;
# ifndef	MINIMUMSHELL
	freevar(dirstack);
	dirstack = NULL;
# endif	/* !MINIMUMSHELL */
# ifndef	NOALIAS
	freealias(shellalias);
	shellalias = NULL;
# endif
# ifndef	_NOUSEHASH
	VOID_C searchhash(NULL, NULL, NULL);
# endif
# if	!MSDOS && !defined (MINIMUMSHELL)
	checkmail(1);
# endif
	for (i = 0; i < NSIG; i++) {
		signal2(i, SIG_IGN);
		Xfree(trapcomm[i]);
		trapcomm[i] = NULL;
		signal2(i, oldsigfunc[i]);
	}
# ifndef	NOJOB
	if (joblist) {
		for (i = 0; i < maxjobs; i++) freejob(i);
		maxjobs = 0;
		Xfree(joblist);
		joblist = NULL;
	}
# endif	/* !NOJOB */
# ifdef	CYGWIN
	freecygdrive();
# endif
# if	defined (DEP_KANJIPATH) || defined (DEP_ROCKRIDGE) \
|| defined (DEP_PSEUDOPATH)
	freeopenlist();
# endif
#endif	/* DEBUG */
	errno = duperrno;
}

VOID Xexit2(n)
int n;
{
	if (mypid == orgpid) {
#ifndef	NOJOB
		if (loginshell && interactive_io) killjob();
#endif
#ifdef	FD
		if (havetty() && interactive && !nottyout) {
			if (!dumbterm) putterm(T_NORMAL);
			endterm();
			inittty(1);
			keyflush();
		}
#endif
		exec_line(NULL);
		_dosystem(NULL);
	}
	prepareexit(0);
#ifdef	FD
	if (mypid == orgpid) prepareexitfd(n);
#endif

#ifdef	DEBUG
	if (havetty() && mypid == orgpid) {
# if	!MSDOS && defined (FD)
		freeterment();
# endif
		closetty(&ttyio, &ttyout);
		muntrace();
	}
#endif	/* DEBUG */

	exit(n);
}

#ifdef	FD
static VOID NEAR argfputs(s, fp)
CONST char *s;
XFILE *fp;
{
# ifdef	DEP_FILECONV
	char *cp;
# endif

# ifdef	DEP_FILECONV
	cp = newkanjiconv(s, defaultkcode, DEFCODE, L_FNAME);
	VOID_C Xfprintf(fp, "%a", cp);
	if (cp != s) Xfree(cp);
# else
	VOID_C Xfprintf(fp, "%a", s);
# endif
}
#endif	/* FD */

#ifdef	DEP_FILECONV
static int NEAR Kopen(path, flags, mode)
CONST char *path;
int flags, mode;
{
	char buf[MAXPATHLEN];

	path = kanjiconv2(buf, path,
		MAXPATHLEN - 1, defaultkcode, DEFCODE, L_FNAME);

	return(Xopen(path, flags, mode));
}
#endif	/* DEP_FILECONV */

static VOID NEAR syntaxerror(s)
CONST char *s;
{
	if (syntaxerrno <= 0 || syntaxerrno >= SYNTAXERRSIZ) return;
#ifndef	BASHSTYLE
	/* bash shows its name, even in interective shell */
	if (interactive) /*EMPTY*/;
	else
#endif
	if (argvar && argvar[0]) VOID_C Xfprintf(Xstderr, "%k: ", argvar[0]);
#ifndef	MINIMUMSHELL
	if (!interactive && shlineno > 0L)
		VOID_C Xfprintf(Xstderr, "%ld: ", shlineno);
#endif
	if (s) VOID_C Xfprintf(Xstderr,
		"%a: ",
		(*s && syntaxerrno != ER_UNEXPNL) ? s : "syntax error");
	if (syntaxerrstr[syntaxerrno])
		Xfputs(syntaxerrstr[syntaxerrno], Xstderr);
	VOID_C fputnl(Xstderr);
	ret_status = RET_SYNTAXERR;
	if (errorexit) Xexit2(RET_SYNTAXERR);
	safeexit();
}

/*ARGSUSED*/
VOID execerror(argv, s, n, noexit)
char *CONST *argv;
CONST char *s;
int n, noexit;
{
	if (n == ER_BADSUBST
	&& (execerrno == ER_ISREADONLY || execerrno == ER_PARAMNOTSET))
		return;

	if (!n || n >= EXECERRSIZ) return;
#ifndef	BASHSTYLE
	/* bash shows its name, even in interective shell */
	if (interactive) /*EMPTY*/;
	else
#endif
	if (argvar && argvar[0]) VOID_C Xfprintf(Xstderr, "%k: ", argvar[0]);
#ifndef	MINIMUMSHELL
	if (!interactive && shlineno > 0L)
		VOID_C Xfprintf(Xstderr, "%ld: ", shlineno);
#endif
	if (argv && argv[0]) VOID_C Xfprintf(Xstderr, "%k: ", argv[0]);
	if (s) VOID_C Xfprintf(Xstderr, "%a: ", s);
	if (execerrstr[n]) Xfputs(execerrstr[n], Xstderr);
	VOID_C fputnl(Xstderr);
	execerrno = n;
#ifndef	BASHSTYLE
	/* bash does not break any statement on error */
	if (noexit < 2) breaklevel = loopdepth;
#endif
#if	MSDOS
	/* child process fake */
	if (childdepth) return;
#endif

	if (noexit < 0) safeexit();
	else if (noexit) /*EMPTY*/;
#if	!defined (BASHSTYLE) || defined (STRICTPOSIX)
	/* bash does not exit on error, in non interactive shell */
	else if (isshellbuiltin) safeexit();
#endif
}

VOID doperror(command, s)
CONST char *command, *s;
{
	int duperrno;

	duperrno = errno;
	if (errno < 0) return;
	if (!command) command = (argvar && argvar[0]) ? argvar[0] : shellname;
	VOID_C Xfprintf(Xstderr, "%k: ", command);
	if (s) VOID_C Xfprintf(Xstderr, "%a: ", s);
	Xfputs(Xstrerror(duperrno), Xstderr);
	VOID_C fputnl(Xstderr);
	errno = 0;

#ifndef	BASHSTYLE
	/* bash does not break any statement on error */
	breaklevel = loopdepth;
#endif
#if	MSDOS
	/* child process fake */
	if (childdepth) return;
#endif

#if	!defined (BASHSTYLE) || defined (STRICTPOSIX)
	/* bash does not exit on error, in non interactive shell */
	if (isshellbuiltin) safeexit();
#endif
}

int isnumeric(s)
CONST char *s;
{
	int i, n;

	i = 0;
	if ((n = getnum(s, &i)) < 0) return(-1);
#ifndef	BASHSTYLE
	/* bash always treats non numeric character as error */
	if (s[i]) return(-1);
#endif

	return(n);
}

#if	!MSDOS
VOID dispsignal(sig, width, fp)
int sig, width;
XFILE *fp;
{
	int i;

	for (i = 0; signallist[i].sig >= 0; i++)
		if (sig == signallist[i].sig) break;
	if (signallist[i].sig < 0) {
		if ((width -= strsize("Signal ")) < 0) width = 0;
		VOID_C Xfprintf(fp, "Signal %-*d", width, sig);
	}
	else if (!signallist[i].mes || !*(signallist[i].mes)) /*EMPTY*/;
	else if (!width) Xfputs(signallist[i].mes, fp);
	else VOID_C Xfprintf(fp, "%-*.*s", width, width, signallist[i].mes);
}

int waitjob(pid, wp, opt)
p_id_t pid;
wait_pid_t *wp;
int opt;
{
# if	!defined (NOJOB) || defined (SYSV)
	static wait_pid_t *waitlist = NULL;
	static p_id_t *pidlist = NULL;
	static int maxtrap = 0;
# endif
# ifndef	NOJOB
	int i, n, sig;
# endif
	sigmask_t mask, omask;
	wait_pid_t w;
	p_id_t tmp;
	int ret;

	Xsigemptyset(mask);
# ifdef	SIGCHLD
	Xsigaddset(mask, SIGCHLD);
# endif
	Xsigblock(omask, mask);
	for (;;) {
# ifndef	JOBTEST
		tmp = (pid < (p_id_t)0)
			? Xwait3(&w, opt, NULL) : Xwait4(pid, &w, opt, NULL);
# else	/* JOBTEST */
		tmp = Xwait3(&w, opt, NULL);
		if (pid >= (p_id_t)0 && tmp >= (p_id_t)0 && pid != tmp) {
			if ((n = searchjob(tmp, &i)) < 0) continue;
			sig = joblist[n].stats[i];
			joblist[n].stats[i] = getwaitsig(w);
#  if	defined (SIGCONT) && (defined (SIGTTIN) || defined (SIGTTOU))
			if (sig == joblist[n].stats[i]) sig = 0;
#   ifdef	SIGTTIN
			else if (joblist[n].stats[i] == SIGTTIN) /*EMPTY*/;
#   endif
#   ifdef	SIGTTOU
			else if (joblist[n].stats[i] == SIGTTOU) /*EMPTY*/;
#   endif
			else sig = 0;

			if (sig) {
				usleep(100000L);
				VOID_C kill(tmp, SIGCONT);
			}
#  endif	/* SIGCONT  && (SIGTTIN || SIGTTOU) */
			continue;
		}
# endif	/* JOBTEST */
		if (tmp >= (p_id_t)0 || errno != EINTR) break;
	}

# if	!defined (NOJOB) || defined (SYSV)
	if (pid >= (p_id_t)0) {
		if (tmp < (p_id_t)0 && errno == ECHILD) {
			while (maxtrap-- > 0) if (pid == pidlist[maxtrap]) {
				tmp = pidlist[maxtrap];
				w = waitlist[maxtrap];
				break;
			}
		}
		Xfree(waitlist);
		waitlist = NULL;
		Xfree(pidlist);
		pidlist = NULL;
		maxtrap = 0;
	}
	else if (tmp >= (p_id_t)0) {
		waitlist = (wait_pid_t *)Xrealloc(waitlist,
			(maxtrap + 1) * sizeof(wait_pid_t));
		pidlist = (p_id_t *)Xrealloc(pidlist,
			(maxtrap + 1) * sizeof(p_id_t));
		pidlist[maxtrap] = tmp;
		waitlist[maxtrap] = w;
		maxtrap++;
	}
# endif	/* !NOJOB || SYSV */
	Xsigsetmask(omask);

	if (!tmp) return(0);
	else if (tmp < (p_id_t)0) return(-1);
	else {
		ret = (pid < (p_id_t)0 || tmp == pid) ? 1 : 0;
# ifndef	NOJOB
		sig = getwaitsig(w);
# endif
	}

# ifndef	NOJOB
	if (ret < 0) /*EMPTY*/;
	else if ((n = searchjob(tmp, &i)) >= 0 && sig != joblist[n].stats[i]) {
		joblist[n].stats[i] = sig;
		if (pid == tmp || i < joblist[n].npipe || !WIFSTOPPED(w))
			/*EMPTY*/;
		else if (jobok && interactive && !nottyout)
			dispjob(n, Xstderr);
	}
# endif
	if (wp) *wp = w;

	return(ret);
}

# if	!defined (NOJOB) && defined (DEP_PTY) && defined (CYGWIN)
static VOID NEAR addmychild(pid)
p_id_t pid;
{
	int n;

	n = 0;
	if (mychildren) while (mychildren[n] >= (p_id_t)0) n++;
	mychildren = (p_id_t *)Xrealloc(mychildren, (n + 2) * sizeof(p_id_t));
	mychildren[n++] = pid;
	mychildren[n] = (p_id_t)-1;
}
# endif	/* !NOJOB && DEP_PTY && CYGWIN */

static VOID NEAR setstopsig(valid)
int valid;
{
	sigcst_t func;
	int i;

	for (i = 0; signallist[i].sig >= 0; i++) {
		func = (valid) ? SIG_DFL : (sigcst_t)(signallist[i].func);
		if ((signallist[i].flags & TR_STAT) == TR_STOP) {
			if (valid < 0) func = SIG_IGN;
		}
		else if (!(signallist[i].flags & TR_BLOCK)) continue;

		signal2(signallist[i].sig, func);
	}
}

/*ARGSUSED*/
static p_id_t NEAR makechild(tty, ppid)
int tty;
p_id_t ppid;
{
# ifdef	DEBUG
	extern VOID (*__free_hook) __P_((VOID_P));
	extern VOID_P (*__malloc_hook) __P_((ALLOC_T));
	extern VOID_P (*__realloc_hook) __P_((VOID_P, ALLOC_T));
# endif
	sigmask_t mask, omask;
	p_id_t pid;
	int i, stop;

	Xsigfillset(mask);
	Xsigblock(omask, mask);
	if (!(pid = Xfork())) {
# ifdef	DEBUG
		__free_hook = NULL;
		__malloc_hook = NULL;
		__realloc_hook = NULL;
# endif

		memcpy((char *)&omask, (char *)&oldsigmask, sizeof(sigmask_t));
# ifdef	SIGCHLD
		Xsigdelset(omask, SIGCHLD);
# endif
		mypid = getpid();
		stop = -1;
# ifdef	NOJOB
		if (loginshell) stop = 0;
# else
		if (jobok) stop = (tty && childpgrp != orgpgrp) ? 1 : 0;
# endif
		setstopsig(stop);
	}
	Xsigsetmask(omask);
	if (pid < (p_id_t)0) return(pid);

# ifndef	NOJOB
	if (childpgrp < (p_id_t)0)
		childpgrp = (ppid >= (p_id_t)0) ? ppid : (pid) ? pid : mypid;
	if (pid) {
		if (jobok) VOID_C setpgroup(pid, childpgrp);
		if (tty && ttypgrp >= (p_id_t)0) ttypgrp = childpgrp;
#  if	defined (DEP_PTY) && defined (CYGWIN)
		if (parentfd >= 0) addmychild(pid);
#  endif
	}
	else {
		if (jobok && setpgroup(mypid, childpgrp) < 0) {
			doperror(NULL, "fatal error");
			prepareexit(-1);
			Xexit(RET_FATALERR);
		}
		if (tty) VOID_C gettermio(childpgrp, jobok);
#  if	defined (DEP_PTY) && defined (CYGWIN)
		Xfree(mychildren);
		mychildren = NULL;
#  endif
	}
# endif	/* !NOJOB */

	if (!pid) for (i = 0; shellfunc[i].ident; i++)
		nownstree(shellfunc[i].func);

	return(pid);
}

int waitchild(pid, trp)
p_id_t pid;
syntaxtree *trp;
{
# ifndef	NOJOB
	int n;
# endif
	wait_pid_t w;
	int ret;

	if (trapok >= 0) trapok = 1;
	sigconted = 0;
	for (;;) {
		if (interrupted) {
			if (trapok >= 0) trapok = 0;
# ifdef	FD
			checkscreen(-1, -1);
# endif
			return(RET_INTR);
		}
		if ((ret = waitjob(pid, &w, WUNTRACED)) < 0) break;
		if (!ret) continue;
		if (!WIFSTOPPED(w)) break;

		ret = WSTOPSIG(w);
		if (sigconted) {
			sigconted = 0;
# ifdef	SIGSTOP
			if (ret == SIGSTOP) continue;
# endif
		}

# ifdef	NOJOB
#  ifdef	SIGCONT
		if (loginshell) VOID_C kill(pid, SIGCONT);
#  endif
# else	/* !NOJOB */
#  ifdef	BASHBUG
	/* bash cannot be suspended with +m mode */
		if (!jobok) continue;
#  endif
		if (mypid != orgpgrp) continue;

		if (trapok >= 0) trapok = 0;
		VOID_C gettermio(orgpgrp, jobok);
#  ifdef	FD
		checkscreen(-1, -1);
#  endif
		prevjob = lastjob;
		lastjob = stackjob(pid, ret, -1, trp);
		breaklevel = loopdepth;
		stopped = 1;
		if (!jobok) return(RET_SUCCESS);
		Xfree(joblist[lastjob].tty);
		savetermio(ttyio, &(joblist[lastjob].tty), NULL);

		if (interactive && !nottyout) {
			stdiomode();
			dispjob(lastjob, Xstderr);
		}

		return(RET_SUCCESS);
# endif	/* !NOJOB */
	}
	if (trapok >= 0) trapok = 0;

# ifndef	NOJOB
	if (childpgrp >= (p_id_t)0 && pid != childpgrp) /*EMPTY*/;
	else if (mypid == orgpgrp) {
		VOID_C gettermio(orgpgrp, jobok);
#  ifdef	FD
		checkscreen(-1, -1);
#  endif
		childpgrp = (p_id_t)-1;
	}
# endif	/* !NOJOB */

	if (ret < 0) {
		if (!trp && errno == ECHILD) ret = errno = 0;
	}
	else if (WIFSIGNALED(w)) {
		ret = WTERMSIG(w);

# ifndef	NOJOB
		if ((n = searchjob(pid, NULL)) >= 0)
			Xkillpg(joblist[n].pids[0], ret);
# endif
# ifdef	SIGINT
		if (ret == SIGINT) interrupted = 1;
		else
# endif
# if	defined (SIGPIPE) && !defined (PSIGNALSTYLE)
		if (ret == SIGPIPE) /*EMPTY*/;
		else
# endif
		if (!nottyout) {
			if (!interactive && argvar && argvar[0])
				VOID_C Xfprintf(Xstderr,
					"%k: %id ", argvar[0], pid);
			dispsignal(ret, 0, Xstderr);
			VOID_C fputnl(Xstderr);
		}
		ret += 128;
	}
	else ret = (WEXITSTATUS(w) & 0377);

	if (ret < 0) return(-1);
# ifndef	NOJOB
	if ((n = searchjob(pid, NULL)) >= 0 && statjob(n) < 0) freejob(n);
# endif

	return((int)ret);
}
#endif	/* !MSDOS */

static VOID NEAR safermtmpfile(file)
CONST char *file;
{
	int duperrno;

	duperrno = errno;
	rmtmpfile(file);
	errno = duperrno;
}

VOID setshflag(n, val)
int n, val;
{
#if	defined (FD) && !defined (_NOEDITMODE)
	CONST char *cp;
#endif

	*(shflaglist[n].var) = val;

#if	defined (FD) && !defined (_NOEDITMODE)
	if (shflaglist[n].var == &emacsmode) cp = "emacs";
	else if (shflaglist[n].var == &vimode) cp = "vi";
	else cp = NULL;

	if (cp) {
		emacsmode = vimode = 0;
		if (editmode && !strcmp(editmode, cp)) cp = NULL;

		/*
		 * The stupid HP cc says that
		 * ternary operator with NULL is const char *,
		 * not to be converted to char *.
		 */
		if (!val) cp = (cp) ? NULL : nullstr;

		if (cp) {
			setenv2("FD_EDITMODE", cp, 0);
			editmode = getconstvar("FD_EDITMODE");
		}
	}
#endif	/* FD && !_NOEDITMODE */
#ifdef	DEP_PTY
	sendparent(TE_SETSHFLAG, n, val);
#endif
}

static int NEAR getoption(argc, argv, isopt)
int argc;
char *CONST *argv;
int isopt;
{
	u_long flags;
	CONST char *arg;
	int i, j, com;

#if	defined (FD) && !defined (_NOEDITMODE)
	emacsmode = vimode = 0;
	if (editmode) {
		if (!strcmp(editmode, "emacs")) emacsmode = 1;
		else if (!strcmp(editmode, "vi")) vimode = 1;
	}
#endif

	if (argc <= 1) return(1);
	arg = argv[1];
	if (arg[0] != '-' && arg[0] != '+') return(1);
	else if (arg[1] == '-') return(2);

	com = 0;
	flags = (u_long)0;
	for (i = 1; arg[i]; i++) {
		if (isopt) {
#ifdef	FD
			if (fdmode) /*EMPTY*/;
			else
#endif
			if (arg[i] == 'c' && !com && argc > 2) {
				com = 1;
				continue;
			}
		}
		else {
			if (arg[i] == 'o' && !com) {
				com = (arg[0] == '-') ? 1 : 2;
				continue;
			}
		}
		for (j = 0; j < FLAGSSIZ; j++) {
			if (!isopt && !(shflaglist[j].ident)) continue;
			if (arg[i] == shflaglist[j].letter) break;
		}
		if (j < FLAGSSIZ) flags |= ((u_long)1 << j);
		else if (arg[0] == '-') {
			execerror(argv, arg, ER_BADOPTIONS, 0);
			return(-1);
		}
	}

	for (j = 0; j < FLAGSSIZ; j++) {
		if (flags & (u_long)1) setshflag(j, (arg[0] == '-') ? 1 : 0);
		flags >>= 1;
	}

	return(com + 2);
}

static ALLOC_T NEAR c_allocsize(n)
int n;
{
	ALLOC_T size;

	n++;
	for (size = BUFUNIT; size < n; size *= 2) /*EMPTY*/;

	return(size);
}

static int NEAR readchar(fd)
int fd;
{
	u_char ch;
	int n;

	if (trapok >= 0) trapok = 1;
#ifdef	USESIGACTION
	n = sureread(fd, &ch, sizeof(ch));
#else
	n = checkread(fd, &ch, sizeof(ch), 0);
#endif
	if (n > 0) n = (int)ch;
	else if (!n) n = READ_EOF;
	if (trapok >= 0) trapok = 0;

	return(n);
}

/*ARGSUSED*/
static char *NEAR readline(fd, opt)
int fd, opt;
{
	char *cp;
	ALLOC_T i, size;
	int c;

	cp = c_realloc(NULL, 0, &size);
	for (i = 0; (c = readchar(fd)) != '\n'; i++) {
		if (c < 0) {
			Xfree(cp);
			return(NULL);
		}
		if (c == READ_EOF) {
			if (i) break;
			Xfree(cp);
			return(vnullstr);
		}
		cp = c_realloc(cp, i, &size);
		cp[i] = c;
	}
#ifndef	USECRNL
	if (opt != 'N') /*EMPTY*/;
	else
#endif
	if (c == '\n' && i > 0 && cp[i - 1] == '\r') i--;
	cp[i++] = '\0';

	return(Xrealloc(cp, i));
}

static char *NEAR readfile(fd, lenp)
int fd;
ALLOC_T *lenp;
{
	char *cp;
	ALLOC_T i, size;
	int c;

	cp = c_realloc(NULL, 0, &size);
	for (i = 0; (c = readchar(fd)) != READ_EOF; i++) {
		if (c < 0) {
			Xfree(cp);
			return(NULL);
		}
#ifdef	USECRNL
		if (c == '\n' && i > 0 && cp[i - 1] == '\r') i--;
#endif
		cp = c_realloc(cp, i, &size);
		cp[i] = c;
	}
	if (lenp) *lenp = i;
	cp[i++] = '\0';

	return(Xrealloc(cp, i));
}

char *evalvararg(arg, flags, noexit)
char *arg;
int flags, noexit;
{
	char *tmp;

	if ((tmp = evalarg(arg, flags))) return(tmp);
#if	defined (BASHSTYLE) && defined (STRICTPOSIX)
	if (!noexit) noexit = -1;
#endif
	if (*arg) execerror(NULL, arg, ER_BADSUBST, noexit);

	return(NULL);
}

static heredoc_t *NEAR newheredoc(eof, path, fd, flags)
char *eof, *path;
int fd, flags;
{
	heredoc_t *new;

	new = (heredoc_t *)Xmalloc(sizeof(heredoc_t));
	new -> eof = eof;
	new -> filename = path;
	new -> buf = NULL;
	new -> fd = fd;
#ifndef	USEFAKEPIPE
	new -> pipein = (p_id_t)-1;
#endif
	new -> flags = flags;

	return(new);
}

VOID freeheredoc(hdp, nown)
heredoc_t *hdp;
int nown;
{
	if (!hdp) return;
	Xfree(hdp -> eof);
	safeclose(hdp -> fd);
	if (hdp -> filename) {
		if (!nown) safermtmpfile(hdp -> filename);
		Xfree(hdp -> filename);
	}
	Xfree(hdp -> buf);
	Xfree(hdp);
}

static redirectlist *NEAR newrlist(fd, filename, type, next)
int fd;
char *filename;
int type;
redirectlist *next;
{
	redirectlist *new;

	new = (redirectlist *)Xmalloc(sizeof(redirectlist));
	new -> fd = fd;
	new -> filename = filename;
	new -> type = (u_short)type;
	new -> new = new -> old = -1;
#ifdef	DEP_DOSDRIVE
	new -> fakepipe = NULL;
	new -> dosfd = -1;
#endif
	new -> next = next;

	return(new);
}

VOID freerlist(rp, nown)
redirectlist *rp;
int nown;
{
	if (!rp) return;
	freerlist(rp -> next, nown);

	if (rp -> type & MD_HEREDOC)
		freeheredoc((heredoc_t *)(rp -> filename), nown);
	else Xfree(rp -> filename);
	Xfree(rp);
}

static command_t *NEAR newcomm()
{
	command_t *new;

	new = (command_t *)Xmalloc(sizeof(command_t));
	new -> hash = NULL;
	new -> argc = -1;
	new -> argv = NULL;
	new -> redp = NULL;
	new -> type =
	new -> id = 0;

	return(new);
}

VOID freecomm(comm, nown)
command_t *comm;
int nown;
{
	int i;

	if (!comm) return;
	if (comm -> argv) {
		if (isstatement(comm))
			freestree((syntaxtree *)(comm -> argv));
		else for (i = 0; i <= comm -> argc; i++)
			Xfree(comm -> argv[i]);
		Xfree(comm -> argv);
	}
	if (comm -> redp) {
		closeredirect(comm -> redp);
		freerlist(comm -> redp, nown);
	}
	Xfree(comm);
}

syntaxtree *newstree(parent)
syntaxtree *parent;
{
	syntaxtree *new;

	new = (syntaxtree *)Xmalloc(sizeof(syntaxtree));
	new -> comm = NULL;
	new -> parent = parent;
	new -> next = NULL;
#ifndef	MINIMUMSHELL
	new -> lineno = shlineno;
#endif
	new -> type = OP_NONE;
	if (parent) {
		new -> cont = (parent -> cont & CN_INHR);
		new -> flags = 0;
	}
	else {
		new -> cont = 0;
		new -> flags = ST_TOP;
	}

	return(new);
}

VOID freestree(trp)
syntaxtree *trp;
{
#ifndef	MINIMUMSHELL
	redirectlist *rp;
#endif
	int duperrno;

	if (!trp) return;

	duperrno = errno;
	if (trp -> comm) {
		if (!(trp -> flags & ST_NODE))
			freecomm(trp -> comm, trp -> flags & ST_NOWN);
		else {
			freestree((syntaxtree *)(trp -> comm));
			Xfree(trp -> comm);
		}
		trp -> comm = NULL;
	}

	if (trp -> next) {
#ifndef	MINIMUMSHELL
		if (trp -> flags & ST_BUSY) {
			rp = (redirectlist *)(trp -> next);
			Xfree(rp -> filename);
			freestree((syntaxtree *)(rp -> next));
			Xfree(rp -> next);
		}
		else
#endif
		if (!(trp -> cont & (CN_QUOT | CN_ESCAPE)))
			freestree(trp -> next);
		else Xfree(((redirectlist *)(trp -> next)) -> filename);
		Xfree(trp -> next);
		trp -> next = NULL;
	}
	trp -> type = OP_NONE;
	trp -> cont = trp -> flags = 0;
	errno = duperrno;
}

static syntaxtree *NEAR eldeststree(trp)
syntaxtree *trp;
{
	syntaxtree *tmptr;

	while (trp) {
		tmptr = getparent(trp);
		if (!(trp -> flags & ST_NEXT)) return(tmptr);
		trp = tmptr;
	}

	return(NULL);
}

syntaxtree *parentstree(trp)
syntaxtree *trp;
{
	while ((trp = eldeststree(trp)))
		if (!(trp -> flags & ST_NODE) && isstatement(trp -> comm))
			return(trp);

	return(NULL);
}

static syntaxtree *NEAR childstree(trp, n)
syntaxtree *trp;
int n;
{
	syntaxtree *new;

	new = newstree(trp);
	if (!(trp -> comm)) trp -> comm = newcomm();
	(trp -> comm) -> argc = 0;
	(trp -> comm) -> argv = (char **)new;
	(trp -> comm) -> type = CT_STATEMENT;
	(trp -> comm) -> id = n;
	trp -> type = OP_NONE;
	trp -> flags &= ~ST_NODE;

	return(new);
}

static syntaxtree *NEAR skipfuncbody(trp)
syntaxtree *trp;
{
	syntaxtree *tmptr;

	if (trp -> flags & ST_BUSY) return(trp);
	tmptr = statementcheck(trp -> next, SM_STATEMENT);
	if (getstatid(tmptr) == SM_LPAREN - 1) trp = trp -> next;

	return(trp);
}

static syntaxtree *NEAR insertstree(trp, parent, type)
syntaxtree *trp, *parent;
int type;
{
	syntaxtree *new, *tmptr;
	int i, l1, l2;

	for (i = 0; i < OPELISTSIZ; i++) if (type == opelist[i].op) break;
	l1 = (i < OPELISTSIZ) ? opelist[i].level : 0;
	if (!parent) l2 = 0;
	else {
		for (i = 0; i < OPELISTSIZ; i++)
			if (parent -> type == opelist[i].op) break;
		l2 = (i < OPELISTSIZ) ? opelist[i].level : 0;
	}

	if (!l1) /*EMPTY*/;
#ifndef	BASHSTYLE
	/* bash does not allow the format like as "foo | ; bar" */
	else if (parent && isoppipe(parent) && l1 > l2) /*EMPTY*/;
#endif
#ifndef	MINIMUMSHELL
	else if (type == OP_NOT && (!l2 || l1 < l2)) /*EMPTY*/;
#endif
	else if (!hascomm(trp)) {
		if (type != OP_FG) {
			syntaxerrno = ER_UNEXPTOK;
			return(trp);
		}
#ifdef	BASHSTYLE
	/* bash does not allow a null before ";" */
		else {
			syntaxerrno = ER_UNEXPNL;
			return(trp);
		}
#endif
	}

	/* && or || */
	if (l1 == 3 && l2 == 3) l2--;

	if (!l1 || !l2) /*EMPTY*/;
	else if (l1 < l2) {
		new = newstree(trp);
		new -> comm = trp -> comm;
		new -> next = trp -> next;
#ifndef	MINIMUMSHELL
		new -> lineno = trp -> lineno;
#endif
		new -> type = trp -> type;
		new -> cont = trp -> cont;
		if (trp -> flags & ST_NODE) {
			new -> flags = trp -> flags;
			trp -> flags = 0;
		}
		trp -> comm = (command_t *)new;
		trp -> type = OP_NONE;
		trp -> flags |= ST_NODE;
		trp = new;
	}
	else if (l1 > l2) {
		if (!(tmptr = eldeststree(parent))) {
			while (hasparent(trp)) trp = trp -> parent;
			new = newstree(trp);
			new -> comm = trp -> comm;
			new -> next = trp -> next;
#ifndef	MINIMUMSHELL
			new -> lineno = trp -> lineno;
#endif
			new -> type = trp -> type;
			new -> cont = trp -> cont;
			new -> flags = trp -> flags;
			if (trp -> next) (trp -> next) -> parent = new;
			trp -> comm = (command_t *)new;
			trp -> type = type;
			trp -> cont = 0;
			trp -> flags = ST_NODE;
		}
		else if (tmptr -> flags & ST_NODE)
			trp = insertstree(tmptr, eldeststree(tmptr), type);
		else if (!isstatement(tmptr -> comm))
			trp = insertstree(trp, tmptr, type);
		else {
			new = newstree(tmptr);
			new -> comm = (command_t *)((tmptr -> comm) -> argv);
			new -> next = trp -> next;
			(tmptr -> comm) -> argv = (char **)new;
			trp = new;
			trp -> type = type;
			trp -> cont = 0;
			trp -> flags = ST_NODE;
		}
	}

	return(trp);
}

static syntaxtree *NEAR linkstree(trp, type)
syntaxtree *trp;
int type;
{
	syntaxtree *new, *tmptr;
	int cont;

	cont = ((trp -> cont) & CN_INHR);
#ifndef	MINIMUMSHELL
	trp -> lineno = shlineno;
#endif
	if (trp && isstatement(trp -> comm)
	&& (trp -> comm) -> id == SM_STATEMENT
	&& (tmptr = statementbody(trp))
	&& getstatid(tmptr) == SM_LPAREN - 1)
		trp = getparent(trp);
	tmptr = (trp -> flags & ST_NEXT) ? getparent(trp) : NULL;
	trp = insertstree(trp, tmptr, type);

	trp = skipfuncbody(trp);
	new = trp -> next = newstree(trp);
	new -> cont = cont;
	new -> flags = ST_NEXT;
	trp -> type = type;

	return(new);
}

#if	!MSDOS || defined (DEP_PTY) || !defined (USEFAKEPIPE)
static VOID NEAR nownstree(trp)
syntaxtree *trp;
{
	if (!trp) return;

	if (!(trp -> comm)) /*EMPTY*/;
	else if (trp -> flags & ST_NODE)
		nownstree((syntaxtree *)(trp -> comm));
	else if (isstatement(trp -> comm)) nownstree(statementbody(trp));

	trp -> flags |= ST_NOWN;
	nownstree(trp -> next);
}
#endif

static int NEAR evalfiledesc(s)
CONST char *s;
{
	int i, n;

	i = 0;
	if ((n = getnum(s, &i)) < 0) return(-1);

	i = Xgetdtablesize();
	if (n > i) n = i;

	return(n);
}

static int NEAR redmode(type)
int type;
{
	int mode;

	mode = O_TEXT;
	if (type & MD_READ) {
		if (type & MD_WRITE) {
			mode |= (O_RDWR | O_CREAT);
#ifndef	MINIMUMSHELL
			if (noclobber && !(type & MD_FORCED)) mode |= O_EXCL;
#endif
		}
		else mode |= O_RDONLY;
		if (type & MD_APPEND) mode |= O_APPEND;
	}
	else {
		mode |= (O_WRONLY | O_CREAT);
		if (type & MD_APPEND) mode |= O_APPEND;
		else {
			mode |= O_TRUNC;
#ifndef	MINIMUMSHELL
			if (noclobber && !(type & MD_FORCED)) mode |= O_EXCL;
#endif
		}
	}

	return(mode);
}

static int NEAR cancelredirect(rp)
redirectlist *rp;
{
	if (!rp) return(0);
#ifdef	DEP_DOSDRIVE
	if (rp -> fakepipe) {
		errno = EBADF;
		doperror(NULL, rp -> filename);
		return(-1);
	}
#endif
	if (cancelredirect(rp -> next) < 0) return(-1);

	if ((rp -> type & MD_WITHERR) && rp -> fd != STDOUT_FILENO) {
		safeclose(rp -> fd);
		rp -> fd = STDOUT_FILENO;
	}

	if (rp -> old >= 0) safeclose(rp -> old);
	if (rp -> new >= 0) {
		if (rp -> type & MD_HEREDOC)
			VOID_C closepipe2(rp -> new, rp -> fd);
		else if (!(rp -> type & MD_FILEDESC)) safeclose(rp -> new);
	}
#ifdef	DEP_DOSDRIVE
	VOID_C closepseudofd(rp);
#endif
	rp -> old = rp -> new = -1;

	return(0);
}

static VOID NEAR closeredirect(rp)
redirectlist *rp;
{
#ifndef	USEFAKEPIPE
	heredoc_t *hdp;
#endif

	if (!rp) return;
	closeredirect(rp -> next);
	if (rp -> type & MD_DUPL) return;

	if ((rp -> type & MD_WITHERR) && rp -> fd != STDOUT_FILENO) {
		if (rp -> fd != STDERR_FILENO) {
			Xdup2(rp -> fd, STDERR_FILENO);
			safeclose(rp -> fd);
		}
		rp -> fd = STDOUT_FILENO;
	}

	if (rp -> old >= 0) {
		Xdup2(rp -> old, rp -> fd);
		safeclose(rp -> old);
	}
	if (rp -> new >= 0) {
		if (rp -> type & MD_HEREDOC) {
#ifndef	USEFAKEPIPE
			hdp = (heredoc_t *)(rp -> filename);
			if (hdp -> pipein >= (p_id_t)0)
				VOID_C waitchild(hdp -> pipein, NULL);
#endif
			VOID_C closepipe2(rp -> new, -1);
		}
		else if (!(rp -> type & MD_FILEDESC)) safeclose(rp -> new);
	}
#ifdef	DEP_DOSDRIVE
	VOID_C closepseudofd(rp);
#endif
	rp -> old = rp -> new = -1;
}

static heredoc_t *NEAR searchheredoc(trp, rm)
syntaxtree *trp;
int rm;
{
	redirectlist *rp;
	heredoc_t *hdp, *hit;

	if (!trp) return(NULL);
	if (rm && (trp -> flags & ST_NOWN)) return(NULL);

	if (!(trp -> comm)) /*EMPTY*/;
	else if (trp -> flags & ST_NODE) {
		if ((hdp = searchheredoc((syntaxtree *)(trp -> comm), rm)))
			return(hdp);
	}
	else {
		if (isstatement(trp -> comm)) {
			if ((hdp = searchheredoc(statementbody(trp), rm)))
				return(hdp);
		}

		hit = NULL;
		if (!rm && !(trp -> cont & CN_HDOC)) /*EMPTY*/;
		else for (rp = (trp -> comm) -> redp; rp; rp = rp -> next) {
			if (!(rp -> type & MD_HEREDOC)) continue;
			if (!(hdp = (heredoc_t *)(rp -> filename))) continue;

			if (!rm) {
				if (hdp -> fd >= 0) hit = hdp;
			}
			else if (hdp -> filename) {
				safermtmpfile(hdp -> filename);
				Xfree(hdp -> filename);
				hdp -> filename = NULL;
			}
		}
		if (hit) return(hit);
	}

	if ((hdp = searchheredoc(trp -> next, rm))) return(hdp);
	if (!rm) trp -> cont &= ~CN_HDOC;

	return(NULL);
}

static int NEAR saveheredoc(s, trp)
CONST char *s;
syntaxtree *trp;
{
	heredoc_t *hdp;
	char *cp;
	int i, len, ret;

	while (hasparent(trp)) trp = trp -> parent;
	if (!(hdp = searchheredoc(trp, 0))) return(0);

	cp = hdp -> buf;
	hdp -> buf = NULL;
	if (!cp) cp = Xstrdup(s);
	else {
		len = strlen(cp);
		if (s) {
			cp = Xrealloc(cp, len + strlen(s) + 1);
			Xstrcpy(&(cp[len]), s);
		}
	}

	if (cp) {
		if (!(hdp -> flags & HD_QUOTED)) {
			for (i = 0; cp[i]; i++) {
				if (cp[i] == PESCAPE) {
					if (!cp[i + 1]) break;
					i++;
				}
				else if (iswchar(cp, i)) i++;
			}
			if (cp[i]) {
				cp[i] = '\0';
				if (s) {
					hdp -> buf = cp;
					return(1);
				}
			}
		}
		i = 0;
		if (hdp -> flags & HD_IGNORETAB) while (cp[i] == '\t') i++;
		if (!strcmp(&(cp[i]), hdp -> eof)) {
			Xfree(cp);
			s = cp = NULL;
		}
	}
#ifdef	FAKEUNINIT
	else i = 0;	/* fake for -Wuninitialized */
#endif

	ret = 1;
	if (cp) {
#ifdef	FD
		demacroarg(&cp);
#endif
		len = strlen(cp);
		cp[len++] = '\n';
		len -= i;
		if (surewrite(hdp -> fd, &(cp[i]), len) < 0) ret = -1;
		Xfree(cp);
	}
	if (!s) {
		safeclose(hdp -> fd);
		hdp -> fd = -1;
		searchheredoc(trp, 0);
	}

	return(ret);
}

static int NEAR openheredoc(hdp, old)
heredoc_t *hdp;
int old;
{
	char *cp, *buf;
	p_id_t pipein;
	int fd, fdin, ret;

	fdin = newdup(Xopen(hdp -> filename, O_BINARY | O_RDONLY, 0666));
	if (fdin < 0) return(-1);
	if ((fd = openpipe2(&pipein, old, 1, interactive_io, mypid)) < 0) {
		safeclose(fdin);
		return(-1);
	}
#ifdef	DJGPP
	setmode(STDOUT_FILENO, O_TEXT);
#endif

	if (pipein > (p_id_t)0) {
#ifndef	USEFAKEPIPE
		hdp -> pipein = pipein;
#endif
		safeclose(fdin);
		return(fd);
	}

	ret = RET_SUCCESS;
	while ((buf = readline(fdin, '\0')) != vnullstr) {
		if (!buf) {
			VOID_C closepipe2(fd, -1);
			safeclose(fdin);
			return(-1);
		}

		if (!(hdp -> flags & HD_QUOTED) && ret == RET_SUCCESS) {
			cp = evalvararg(buf,
				EA_STRIPESCAPE
				| EA_NOEVALQ | EA_NOEVALDQ | EA_BACKQ, 1);
			if (!cp) ret = RET_FAIL;
			Xfree(buf);
			buf = cp;
		}
		if (ret == RET_SUCCESS) {
			Xfputs(buf, Xstdout);
			VOID_C fputnl(Xstdout);
		}
		Xfree(buf);
	}
	safeclose(fdin);
	Xfflush(Xstdout);
	if ((fd = reopenpipe(fd, ret)) < 0) return(-1);
	if (ret != RET_SUCCESS) {
		VOID_C closepipe2(fd, -1);
		return(seterrno(-1));
	}

	return(fd);
}

#ifdef	DEP_DOSDRIVE
static int NEAR fdcopy(fdin, fdout)
int fdin, fdout;
{
	u_char ch;
	int n;

	for (;;) {
		if ((n = sureread(fdin, &ch, sizeof(ch))) <= 0) break;
		if (surewrite(fdout, &ch, sizeof(ch)) < 0) return(-1);
	}

	return(n);
}

static int NEAR openpseudofd(rp)
redirectlist *rp;
{
# ifndef	USEFAKEPIPE
	p_id_t pid;
	int fds[2];
# endif
	char pfile[MAXPATHLEN];
	int fd;

	if (!(rp -> type & MD_RDWR) || (rp -> type & MD_RDWR) == MD_RDWR)
		return(-1);

# ifndef	USEFAKEPIPE
	if (pipe(fds) < 0) /*EMPTY*/;
	else if ((pid = makechild(0, mypid)) < (p_id_t)0) {
		safeclose(fds[0]);
		safeclose(fds[1]);
		return(-1);
	}
	else if (pid) {
		safeclose(rp -> new);
		if (rp -> type & MD_READ) {
			safeclose(fds[1]);
			rp -> new = newdup(fds[0]);
		}
		else {
			safeclose(fds[0]);
			rp -> new = newdup(fds[1]);
		}
		return(0);
	}
	else {
		if (rp -> type & MD_READ) {
			safeclose(fds[0]);
			fds[1] = newdup(fds[1]);
			fdcopy(rp -> new, fds[1]);
			safeclose(rp -> new);
			safeclose(fds[1]);
		}
		else {
			safeclose(fds[1]);
			fds[0] = newdup(fds[0]);
			fdcopy(fds[0], rp -> new);
			safeclose(rp -> new);
			safeclose(fds[0]);
		}
		prepareexit(1);
		Xexit(RET_SUCCESS);
	}
# endif	/* !USEFAKEPIPE */

	if ((fd = newdup(mktmpfile(pfile))) < 0) return(-1);
	if (rp -> type & MD_WRITE) rp -> dosfd = rp -> new;
	else {
		fdcopy(rp -> new, fd);
		safeclose(rp -> new);
		safeclose(fd);
		fd = newdup(Xopen(pfile, O_BINARY | O_RDONLY, 0666));
		if (fd < 0) {
			safermtmpfile(pfile);
			rp -> fakepipe = NULL;
			return(-1);
		}
	}
	rp -> new = fd;
	rp -> fakepipe = Xstrdup(pfile);

	return(0);
}

static int NEAR closepseudofd(rp)
redirectlist *rp;
{
	int fd;

	if (!(rp -> fakepipe)) return(0);
	if (rp -> dosfd >= 0 && rp -> type & MD_WRITE) {
		fd = Xopen(rp -> fakepipe, O_BINARY | O_RDONLY, 0666);
		if (fd >= 0) {
			fdcopy(fd, rp -> dosfd);
			safeclose(fd);
		}
		safeclose(rp -> dosfd);
	}
	safermtmpfile(rp -> fakepipe);
	Xfree(rp -> fakepipe);
	rp -> fakepipe = NULL;
	rp -> dosfd = -1;

	return(0);
}
#endif	/* DEP_DOSDRIVE */

#ifdef	WITHSOCKREDIR
static int NEAR parsesocket(s, addrp, portp)
CONST char *s;
char **addrp;
int *portp;
{
	urlhost_t host;
	char *cp;
	int n, scheme;

	if ((n = urlparse(s, schemelist, &cp, &scheme, 0)) <= 0) return(0);
	if (s[n] == '/') n++;
	if (s[n]) {
		Xfree(cp);
		return(0);
	}
	n = urlgethost(cp, &host);
	Xfree(cp);
	if (n < 0) return(-1);

	if (host.user || host.pass) n = -1;
	switch (scheme) {
		case TYPE_CONNECT:
			if (!(host.host)) n = -1;
			if (host.port < 0) n = -1;
			break;
		case TYPE_ACCEPT:
		case TYPE_BIND:
			if (host.port < 0) host.port = 0;
			break;
		default:
			n = -1;
			break;
	}

	if (n < 0) {
		urlfreehost(&host);
		return(seterrno(EINVAL));
	}
	*addrp = host.host;
	*portp = host.port;

	return(scheme);
}
#endif	/* WITHSOCKREDIR */

static redirectlist *NEAR doredirect(rp)
redirectlist *rp;
{
#ifdef	WITHSOCKREDIR
	char *addr;
	int s, port, scheme, opt;
#endif
	redirectlist *errp;
	char *tmp;
	int n, type;

#ifdef	FD
	rp -> type &= ~MD_REST;
#endif
	if (rp -> next) {
		if ((errp = doredirect(rp -> next))) return(errp);
#ifdef	FD
		if (((rp -> next) -> type) & MD_REST) rp -> type |= MD_REST;
#endif
	}

	type = rp -> type;
	if (!(rp -> filename)) tmp = NULL;
	else if (type & MD_HEREDOC) tmp = rp -> filename;
	else {
		tmp = evalvararg(rp -> filename, EA_BACKQ | EA_STRIPQLATER, 0);
		if (!tmp) {
			errno = -1;
			return(rp);
		}
		else if ((type & MD_FILEDESC) && tmp[0] == '-' && !tmp[1]) {
			Xfree(tmp);
			tmp = NULL;
			type &= ~MD_FILEDESC;
		}
#ifdef	FD
		else if ((n = replacearg(&tmp)) < 0) {
			Xfree(tmp);
			return(rp);
		}
		else if (n) rp -> type |= MD_REST;
#endif
	}

	if (!tmp) /*EMPTY*/;
	else if (type & MD_HEREDOC) {
		if ((rp -> new = openheredoc((heredoc_t *)tmp, rp -> fd)) < 0)
			return(rp);
	}
	else if (type & MD_FILEDESC) {
		rp -> new = evalfiledesc(tmp);
		Xfree(tmp);
		if (rp -> new < 0) {
			errno = EBADF;
			return(rp);
		}
		if (isvalidfd(rp -> new) < 0) {
			rp -> new = -1;
#ifdef	BASHSTYLE
	/* bash treats ineffective descriptor as error */
			errno = EBADF;
			return(rp);
#else
			rp -> old = -1;
			rp -> type = 0;
			return(NULL);
#endif
		}
	}
	else if (restricted && (type & MD_WRITE)) {
		Xfree(tmp);
		errno = 0;
		return(rp);
	}
	else {
#ifdef	WITHSOCKREDIR
# ifdef	FAKEUNINIT
		addr = NULL;
		port = -1;
# endif
		if ((scheme = parsesocket(tmp, &addr, &port))) {
			if (scheme < 0) return(rp);
			opt = SCK_LOWDELAY;
			switch (scheme) {
				case TYPE_CONNECT:
					s = sockconnect(addr, port, 0, opt);
					break;
				case TYPE_ACCEPT:
					s = sockbind(addr, port,
						opt | SCK_REUSEADDR);
					if (s >= 0) s = sockaccept(s, opt);
					break;
				case TYPE_BIND:
					s = sockbind(addr, port, opt);
					break;
				default:
					s = seterrno(EINVAL);
					break;
			}
			rp -> new = s;
			Xfree(addr);
		}
		else
#endif	/* WITHSOCKREDIR */
		rp -> new = Kopen(tmp, redmode(type), 0666);
		rp -> new = newdup(rp -> new);
		Xfree(tmp);
		if (rp -> new < 0) return(rp);
#if	MSDOS && !defined (LSI_C)
# ifdef	DJGPP
		if (isatty(rp -> new)) /*EMPTY*/;
		else
# endif
		setmode(rp -> new, O_BINARY);
#endif	/* MSDOS && !LSI_C */
	}

#if	!MSDOS
	if (rp -> new != STDIN_FILENO
	&& rp -> new != STDOUT_FILENO
	&& rp -> new != STDERR_FILENO)
		closeonexec(rp -> new);
#endif

	if (isvalidfd(rp -> fd) < 0) rp -> old = -1;
	else if ((rp -> old = newdup(Xdup(rp -> fd))) < 0) return(rp);
#if	!MSDOS
	else if (rp -> old != STDIN_FILENO
	&& rp -> old != STDOUT_FILENO
	&& rp -> old != STDERR_FILENO)
		closeonexec(rp -> old);
#endif

	if (rp -> new != rp -> fd) {
		if (rp -> new < 0) VOID_C Xclose(rp -> fd);
		else {
#ifdef	DEP_DOSDRIVE
			if (chkopenfd(rp -> new) == DEV_DOS) openpseudofd(rp);
#endif
			if (Xdup2(rp -> new, rp -> fd) < 0) return(rp);
		}
	}

	if ((type & MD_WITHERR) && rp -> new != STDERR_FILENO
	&& rp -> fd == STDOUT_FILENO) {
		if ((n = newdup(Xdup(STDERR_FILENO))) < 0
		|| Xdup2(rp -> new, STDERR_FILENO) < 0) {
			safeclose(n);
			return(rp);
		}
		rp -> fd = n;
#if	!MSDOS
		if (rp -> fd != STDIN_FILENO
		&& rp -> fd != STDOUT_FILENO
		&& rp -> fd != STDERR_FILENO)
			closeonexec(rp -> fd);
#endif
	}

	return(NULL);
}

static heredoc_t *NEAR heredoc(eof, ignoretab)
char *eof;
int ignoretab;
{
	char *cp, path[MAXPATHLEN];
	int fd, flags;

	flags = (ignoretab) ? HD_IGNORETAB : 0;

	cp = Xstrdup(eof);
	if (stripquote(cp, EA_STRIPQ)) flags |= HD_QUOTED;
#ifndef	BASHSTYLE
	/* bash allows no variables as the EOF identifier */
	Xfree(cp);
	if (!(cp = evalvararg(eof, EA_STRIPQLATER, 0))) {
		errno = -1;
		return(NULL);
	}
#endif
#ifdef	FD
	demacroarg(&cp);
#endif

	if ((fd = newdup(mktmpfile(path))) < 0) {
		Xfree(cp);
		return(NULL);
	}

	return(newheredoc(cp, Xstrdup(path), fd, flags));
}

static int NEAR redirect(trp, from, to, type)
syntaxtree *trp;
int from;
char *to;
int type;
{
	redirectlist *rp;
	char *cp;

	if (to && !*to) {
		syntaxerrno = ER_UNEXPTOK;
		return(-1);
	}

	if (!(type & MD_HEREDOC)) cp = Xstrdup(to);
	else {
		if (!(cp = (char *)heredoc(to, type & MD_APPEND))) {
			doperror(NULL, deftmpdir);
			return(-1);
		}
		trp -> cont |= CN_HDOC;
	}
	if (from < 0) from = (type & MD_READ) ? STDIN_FILENO : STDOUT_FILENO;

	if (!(trp -> comm)) trp -> comm = newcomm();
	rp = newrlist(from, cp, type, (trp -> comm) -> redp);
	(trp -> comm) -> redp = rp;

	return(0);
}

#if	!MSDOS
/*ARGSUSED*/
VOID cmpmail(path, msg, mtimep)
CONST char *path, *msg;
time_t *mtimep;
{
	struct stat st;

	if (!path || Xstat(path, &st) < 0) return;
	if (st.st_size > 0 && *mtimep && st.st_mtime > *mtimep) {
# ifdef	MINIMUMSHELL
		Xfputs("you have mail", Xstderr);
# else
		kanjifputs((msg) ? msg : "you have mail", Xstderr);
# endif
		VOID_C fputnl(Xstderr);
	}
	*mtimep = st.st_mtime;
}

# ifdef	MINIMUMSHELL
static VOID NEAR checkmail(reset)
int reset;
{
	static time_t mtime = 0;

	if (reset) mtime = 0;
	else cmpmail(getconstvar(ENVMAIL), NULL, &mtime);
}
# endif	/* !MINIMUMSHELL */
#endif	/* !MSDOS */

int identcheck(ident, delim)
CONST char *ident;
int delim;
{
	int i;

	if (!ident || !*ident || !isidentchar(*ident)) return(0);
	for (i = 1; ident[i]; i++) if (!isidentchar2(ident[i])) break;

	return((ident[i] == delim) ? i : ((ident[i]) ? 0 : -i));
}

static int NEAR searchvar(var, ident, len, c)
char *CONST *var;
CONST char *ident;
int len, c;
{
	int i;

	if (!var) return(-1);
	for (i = 0; var[i]; i++)
		if (!strnenvcmp(ident, var[i], len) && var[i][len] == c)
			return(i);

	return(-1);
}

static char *NEAR searchvar2(max, var, ident, len)
int max;
CONST char **var, *ident;
int len;
{
	int i;

	if (!var) return(NULL);
	for (i = 0; i < max; i++) {
		if (!var[i]) continue;
		if (!strnenvcmp(ident, var[i], len) && !var[i][len])
			return((char *)var[i]);
#ifdef	FD
		if (!strnenvcmp(var[i], FDENV, FDESIZ)
		&& !strnenvcmp(ident, &(var[i][FDESIZ]), len)
		&& !var[i][FDESIZ + len])
			return((char *)&(var[i][FDESIZ]));
#endif
	}

	return(NULL);
}

static char **NEAR expandvar(var, ident, len)
char **var;
CONST char *ident;
int len;
{
	int i;

	if (searchvar(var, ident, len, '\0') >= 0) return(var);
	i = countvar(var);
	var = (char **)Xrealloc(var, (i + 2) * sizeof(char *));
	var[i] = Xstrndup(ident, len);
	var[++i] = NULL;

	return(var);
}

static char *NEAR getvar(var, ident, len)
char *CONST *var;
CONST char *ident;
int len;
{
	int i;

	if (len < 0) len = strlen(ident);
	if ((i = searchvar(var, ident, len, '=')) < 0) return(NULL);

	return(&(var[i][len + 1]));
}

char *getshellvar(ident, len)
CONST char *ident;
int len;
{
	return(getvar(shellvar, ident, len));
}

static char **NEAR putvar(var, s, len)
char **var, *s;
int len;
{
#if	MSDOS
	char *cp;
#endif
	u_long size;
	int i, export;

	export = (var == exportvar);
	i = searchvar(var, s, len, '=');

	if (i >= 0) {
		if (export) {
			size = (u_long)strlen(var[i]) + 1;
			if (size >= exportsize) exportsize = (u_long)0;
			else exportsize -= size;
		}
		Xfree(var[i]);
		if (!s[len]) {
			for (; var[i + 1]; i++) var[i] = var[i + 1];
			var[i] = NULL;
			return(var);
		}
	}
	else if (!s[len]) return(var);
	else {
		i = countvar(var);
		var = (char **)Xrealloc(var, (i + 2) * sizeof(char *));
		var[i + 1] = NULL;
	}

	var[i] = s;
#ifdef	ENVNOCASE
	Xstrntoupper(s, len);
#endif
	if (export) {
		exportsize += (u_long)strlen(s) + 1;
#if	MSDOS
		if (constequal(s, ENVPATH, len)) {
			cp = &(s[len + 1]);
			if (_dospath(cp)) cp += 2;
			for (; *cp; cp++) {
				if (*cp == PATHDELIM) {
					*cp = ';';
					if (_dospath(cp + 1)) cp += 2;
				}
# ifndef	BSPATHDELIM
				else if (*cp == _SC_) *cp = '\\';
# endif
			}
		}
#endif	/* MSDOS */
	}

	return(var);
}

static int NEAR checkprimal(s, len)
CONST char *s;
int len;
{
	char *cp;

	if ((cp = searchvar2(PRIMALVARSIZ, primalvar, s, len))) {
		execerror(NULL, cp, ER_CANNOTUNSET, 0);
		return(seterrno(EPERM));
	}

	return(0);
}

static int NEAR checkrestrict(s, len)
CONST char *s;
int len;
{
	char *cp;

	if (!restricted) return(0);

	if ((cp = searchvar2(RESTRICTVARSIZ, restrictvar, s, len))) {
		execerror(NULL, cp, ER_RESTRICTED, 0);
		return(seterrno(EPERM));
	}

	return(0);
}

static int NEAR checkronly(s, len)
CONST char *s;
int len;
{
	int i;

	if ((i = searchvar(ronlylist, s, len, '\0')) >= 0) {
		execerror(NULL, ronlylist[i], ER_ISREADONLY, 0);
		return(seterrno(EACCES));
	}

	return(0);
}

static int NEAR _putshellvar(s, len)
char *s;
int len;
{
#if	!MSDOS && defined (FD)
	keyseq_t *keymap;
	CONST char *term;
#endif
#if	(!MSDOS && (!defined (MINIMUMSHELL) || defined (FD))) \
|| !defined (NOPOSIXUTIL) \
|| !defined (FD) \
&& (defined (DEP_URLPATH) || defined (DEP_FTPPATH) || defined (DEP_HTTPPATH))
	CONST char *cp;
#endif

#if	(!MSDOS && (!defined (MINIMUMSHELL) || defined (FD))) \
|| !defined (NOPOSIXUTIL) \
|| !defined (FD) \
&& (defined (DEP_URLPATH) || defined (DEP_FTPPATH) || defined (DEP_HTTPPATH))
	cp = &(s[len + 1]);
#endif

	if (checkrestrict(s, len) < 0) return(-1);
#ifdef	BASHSTYLE
	/* bash distinguishes the same named function and variable */
	else if (checkronly(s, len) < 0) return(-1);
#else
	else if (unsetshfunc(s, len) < 0) return(-1);
#endif
#ifndef	_NOUSEHASH
	else if (constequal(s, ENVPATH, len))
		VOID_C searchhash(NULL, NULL, NULL);
#endif
#if	!MSDOS
# ifdef	MINIMUMSHELL
	else if (constequal(s, ENVMAIL, len)) checkmail(1);
# else	/* !MINIMUMSHELL */
	else if (!strnenvcmp(s, ENVMAIL, strsize(ENVMAIL))) {
		if (len == strsize(ENVMAIL)) {
			if (!getconstvar(ENVMAILPATH)) replacemailpath(cp, 0);
		}
		else if (constequal(s, ENVMAILPATH, len))
			replacemailpath(cp, 1);
		else if (constequal(s, ENVMAILCHECK, len)) {
			if ((mailcheck = isnumeric(cp)) < 0) mailcheck = 0;
		}
	}
# endif	/* !MINIMUMSHELL */
# ifdef	FD
	else if (constequal(s, ENVTERM, len)) {
		keymap = copykeyseq(NULL);
		regetterment(cp, interactive);
		if (dumbterm > 1 && (!shellmode || exit_status < 0)) {
			if (!(term = getconstvar(ENVTERM))) term = nullstr;
			regetterment(term, interactive);
			execerror(NULL, cp, ER_INVALTERMFD, 1);
			copykeyseq(keymap);
			freekeyseq(keymap);
			return(seterrno(EINVAL));
		}
		freekeyseq(keymap);
	}
# endif	/* FD */
#endif	/* !MSDOS */
#ifndef	NOPOSIXUTIL
	else if (constequal(s, ENVOPTIND, len)) {
		if ((posixoptind = isnumeric(cp)) <= 1) posixoptind = 0;
	}
#endif
#ifndef	MINIMUMSHELL
	else if (constequal(s, ENVLINENO, len)) shlineno = -1L;
#endif
#if	!defined (FD) && defined (DEP_URLPATH)
	else if (constequal(s, "URLTIMEOUT", len)) {
		if ((urltimeout = isnumeric(cp)) < 0) urltimeout = 0;
	}
	else if (constequal(s, "URLOPTIONS", len)) {
		if ((urloptions = isnumeric(cp)) < 0) urloptions = 0;
	}
#endif
#if	!defined (FD) && defined (DEP_FTPPATH)
	else if (constequal(s, "FTPADDRESS", len)) ftpaddress = (char *)cp;
	else if (constequal(s, "FTPPROXY", len)) ftpproxy = (char *)cp;
	else if (constequal(s, "FTPLOGFILE", len)) ftplogfile = (char *)cp;
#endif
#if	!defined (FD) && defined (DEP_HTTPPATH)
	else if (constequal(s, "HTTPPROXY", len)) httpproxy = (char *)cp;
	else if (constequal(s, "HTTPLOGFILE", len)) httplogfile = (char *)cp;
	else if (constequal(s, "HTMLLOGFILE", len)) htmllogfile = (char *)cp;
#endif

	shellvar = putvar(shellvar, s, len);
#ifdef	FD
	evalenv(s, len);
#endif

	return(0);
}

int putexportvar(s, len)
char *s;
int len;
{
	char *cp;

	if (len < 0) {
		if (!(cp = Xstrchr(s, '='))) return(0);
		len = cp - s;
	}

	if (_putshellvar(s, len) < 0) return(-1);
	exportlist = expandvar(exportlist, s, len);
	exportvar = putvar(exportvar, Xstrdup(s), len);
#ifdef	DEP_PTY
	sendparent(TE_PUTEXPORTVAR, s, len);
#endif

	return(0);
}

int putshellvar(s, len)
char *s;
int len;
{
	char *cp;

	if (autoexport) return(putexportvar(s, len));
	if (len < 0) {
		if (!(cp = Xstrchr(s, '='))) return(0);
		len = cp - s;
	}

	if (_putshellvar(s, len) < 0) return(-1);
	if (searchvar(exportlist, s, len, '\0') >= 0)
		exportvar = putvar(exportvar, Xstrdup(s), len);
#ifdef	DEP_PTY
	sendparent(TE_PUTSHELLVAR, s, len);
#endif

	return(0);
}

int unset(ident, len)
CONST char *ident;
int len;
{
#if	!MSDOS && !defined (MINIMUMSHELL)
	CONST char *cp;
#endif
	int i;

	if (checkprimal(ident, len) < 0 || checkrestrict(ident, len) < 0
	|| checkronly(ident, len) < 0)
		return(-1);
#if	!MSDOS
# ifndef	MINIMUMSHELL
	else if (!strnenvcmp(ident, ENVMAIL, strsize(ENVMAIL))) {
		if (len == strsize(ENVMAIL)) {
			if (!getconstvar(ENVMAILPATH)) checkmail(1);
		}
		else if (constequal(ident, ENVMAILPATH, len)) {
			cp = getconstvar(ENVMAIL);
			if (cp) replacemailpath(cp, 0);
			else checkmail(1);
		}
	}
# endif	/* !MINIMUMSHELL */
# ifdef	FD
	else if (constequal(ident, ENVTERM, len))
		regetterment(nullstr, interactive);
# endif
#endif	/* !MSDOS */
#ifndef	NOPOSIXUTIL
	else if (constequal(ident, ENVOPTIND, len)) posixoptind = 0;
#endif
#ifndef	MINIMUMSHELL
	else if (constequal(ident, ENVLINENO, len)) shlineno = -1L;
#endif
#if	!defined (FD) && defined (DEP_URLPATH)
	else if (constequal(ident, "URLTIMEOUT", len)) urltimeout = 0;
	else if (constequal(ident, "URLOPTIONS", len)) urloptions = 0;
#endif
#if	!defined (FD) && defined (DEP_FTPPATH)
	else if (constequal(ident, "FTPADDRESS", len)) ftpaddress = NULL;
	else if (constequal(ident, "FTPPROXY", len)) ftpproxy = NULL;
	else if (constequal(ident, "FTPLOGFILE", len)) ftplogfile = NULL;
#endif
#if	!defined (FD) && defined (DEP_HTTPPATH)
	else if (constequal(ident, "HTTPPROXY", len)) httpproxy = NULL;
	else if (constequal(ident, "HTTPLOGFILE", len)) httplogfile = NULL;
#endif

	shellvar = putvar(shellvar, (char *)ident, len);
	exportvar = putvar(exportvar, (char *)ident, len);

	if ((i = searchvar(exportlist, ident, len, '\0')) >= 0) {
		Xfree(exportlist[i]);
		for (; exportlist[i + 1]; i++)
			exportlist[i] = exportlist[i + 1];
		exportlist[i] = NULL;
	}
#ifdef	FD
	evalenv(ident, len);
#endif
#ifdef	DEP_PTY
	sendparent(TE_UNSET, ident, len);
#endif

	return(0);
}

#ifndef	MINIMUMSHELL
static VOID NEAR setshlineno(n)
long n;
{
	char tmp[strsize(ENVLINENO) + 1 + MAXLONGWIDTH + 1];

	if (shlineno < 0L) return;

	shlineno = n;
	VOID_C Xsnprintf(tmp, sizeof(tmp), "%s=%ld", ENVLINENO, n);
	shellvar = putvar(shellvar, Xstrdup(tmp), strsize(ENVLINENO));
}
#endif	/* !MINIMUMSHELL */

static heredoc_t *NEAR duplheredoc(hdp)
heredoc_t *hdp;
{
	heredoc_t *new;

	if (!hdp) return(NULL);
	new = newheredoc(Xstrdup(hdp -> eof), Xstrdup(hdp -> filename),
		hdp -> fd, hdp -> flags);
	new -> buf = Xstrdup(hdp -> buf);

	return(new);
}

static redirectlist *NEAR duplrlist(rp)
redirectlist *rp;
{
	redirectlist *new, *next;
	char *filename;

	if (!rp) return(NULL);
	next = duplrlist(rp -> next);
	if (rp -> type & MD_HEREDOC)
		filename = (char *)duplheredoc((heredoc_t *)(rp -> filename));
	else filename = Xstrdup(rp -> filename);
	new = newrlist(rp -> fd, filename, rp -> type | MD_DUPL, next);
	new -> new = rp -> new;
	new -> old = rp -> old;
#ifdef	DEP_DOSDRIVE
	new -> fakepipe = rp -> fakepipe;
	new -> dosfd = rp -> dosfd;
#endif

	return(new);
}

#ifdef	MINIMUMSHELL
syntaxtree *duplstree(trp, parent)
syntaxtree *trp, *parent;
#else
syntaxtree *duplstree(trp, parent, top)
syntaxtree *trp, *parent;
long top;
#endif
{
	syntaxtree *new;
	redirectlist *rp;
	command_t *comm;

	new = newstree(parent);
	new -> type = trp -> type;
	new -> cont = trp -> cont;
	new -> flags = (trp -> flags | ST_NOWN);
#ifndef	MINIMUMSHELL
	new -> lineno = trp -> lineno - top;
#endif
	if ((comm = trp -> comm)) {
		if (trp -> flags & ST_NODE)
			new -> comm = (command_t *)duplstree2(
				(syntaxtree *)comm, new, top);
		else {
			new -> comm = newcomm();
			(new -> comm) -> hash = comm -> hash;
			(new -> comm) -> argc = comm -> argc;
			(new -> comm) -> type = comm -> type;
			(new -> comm) -> id = comm -> id;
			if ((trp -> comm) -> argv) {
				if (!isstatement(new -> comm))
					(new -> comm) -> argv =
						duplvar(comm -> argv, 0);
				else (new -> comm) -> argv =
					(char **)duplstree2((syntaxtree *)
						(comm -> argv), new, top);
			}
			(new -> comm) -> redp = duplrlist(comm -> redp);
		}
	}
	if (trp -> next) {
		if (!(trp -> cont & (CN_QUOT | CN_ESCAPE)))
			new -> next = duplstree2(trp -> next, new, top);
		else {
			rp = (redirectlist *)Xmalloc(sizeof(redirectlist));
			memcpy((char *)rp, (char *)(trp -> next),
				sizeof(redirectlist));
			rp -> filename = Xstrdup(rp -> filename);
			new -> next = (syntaxtree *)rp;
		}
	}

	return(new);
}

static shfunctable *NEAR duplfunc(func)
shfunctable *func;
{
	shfunctable *dupl;
	int i, n;

	if (!func) n = 0;
	else for (n = 0; func[n].ident; n++) /*EMPTY*/;
	dupl = (shfunctable *)Xmalloc((n + 1) * sizeof(shfunctable));
	for (i = 0; i < n; i++) {
		dupl[i].ident = Xstrdup(func[i].ident);
		dupl[i].func = duplstree2(func[i].func, NULL, 0L);
	}
	dupl[i].ident = NULL;

	return(dupl);
}

static VOID NEAR freefunc(func)
shfunctable *func;
{
	int i;

	if (func) {
		for (i = 0; func[i].ident; i++) {
			Xfree(func[i].ident);
			freestree(func[i].func);
			Xfree(func[i].func);
		}
		Xfree(func);
	}
}

static int cmpfunc(vp1, vp2)
CONST VOID_P vp1;
CONST VOID_P vp2;
{
	shfunctable *fp1, *fp2;

	fp1 = (shfunctable *)vp1;
	fp2 = (shfunctable *)vp2;

	return(strpathcmp2(fp1 -> ident, fp2 -> ident));
}

static char *NEAR getifs(VOID_A)
{
	char *ifs;

	return((ifs = getconstvar(ENVIFS)) ? ifs : IFS_SET);
}

static int getretval(VOID_A)
{
	return(ret_status);
}

static p_id_t getorgpid(VOID_A)
{
	return(orgpid);
}

static p_id_t getlastpid(VOID_A)
{
	return(lastpid);
}

static char *getflagstr(VOID_A)
{
	char *cp;
	int i, j;

	cp = Xmalloc(FLAGSSIZ + 1);
	for (i = j = 0; i < FLAGSSIZ; i++)
		if (*(shflaglist[i].var) && shflaglist[i].letter)
			cp[j++] = shflaglist[i].letter;
	cp[j] = '\0';

	return(cp);
}

static int checkundefvar(cp, arg, len)
CONST char *cp, *arg;
int len;
{
	char *new;

	if (cp || !undeferror) return(0);
	new = Xstrndup(arg, len);
	execerror(NULL, new, ER_PARAMNOTSET, 0);
	Xfree(new);

	return(-1);
}

static VOID safeexit(VOID_A)
{
	if (interactive_io) return;

	Xexit2(RET_FAIL);
}

int getstatid(trp)
syntaxtree *trp;
{
	int id;

	if (!trp || (trp -> flags & ST_NODE) || !isstatement(trp -> comm)
	|| (id = (trp -> comm) -> id) <= 0 || id > STATEMENTSIZ)
		return(-1);

	return(id - 1);
}

static int NEAR getparenttype(trp)
syntaxtree *trp;
{
	int id;

	if ((id = getstatid(parentstree(trp))) < 0) return(0);

	return(statementlist[id].type & STT_TYPE);
}

static int NEAR parsestatement(trpp, n, prev, type)
syntaxtree **trpp;
int n, prev, type;
{
	syntaxtree *tmptr;
	int i;

	if (!(statementlist[n].prev[0])) {
		if ((*trpp) -> comm) return(-1);
		if (prev > 0) {
			if ((statementlist[prev - 1].type & STT_FUNC)) {
#ifdef	BASHSTYLE
	/* bash does not allow the function definition without "{ }" */
				if (n != SM_LIST - 1) return(-1);
#endif
			}
			else if (!(statementlist[prev - 1].type
			& STT_NEEDLIST))
				return(-1);
		}
		*trpp = childstree(*trpp, SM_STATEMENT);
	}
	else {
		if (prev <= 0 || !(tmptr = parentstree(*trpp))) return(-1);

		for (i = 0; i < SMPREV; i++) {
			if (!(statementlist[n].prev[i])) continue;
			if (statementlist[n].prev[i] == SM_ANOTHER) return(0);
			if (prev == statementlist[n].prev[i]) break;
		}
		if (i >= SMPREV) return(-1);

		if (!(statementlist[n].type & STT_CASEEND)
		&& (type & STT_NEEDLIST) && !isopnot(statementbody(tmptr))
		&& !(statementbody(tmptr) -> comm))
			return(-1);

		*trpp = tmptr;
	}

	if (statementlist[n].type & STT_NEEDNONE) {
		if (!(tmptr = parentstree(*trpp))) return(-1);
		*trpp = tmptr;
		if (getstatid(tmptr = getparent(tmptr)) == SM_FUNC - 1) {
			if (!(tmptr = parentstree(tmptr))) return(-1);
			*trpp = tmptr;
		}
	}
	else {
		if (statementlist[n].prev[0]) {
			tmptr = (*trpp) -> next = newstree(*trpp);
			tmptr -> flags = ST_NEXT;
			*trpp = tmptr;
		}
		*trpp = childstree(*trpp, n + 1);
	}

	return(1);
}

static syntaxtree *NEAR _addarg(trp, arg)
syntaxtree *trp;
CONST char *arg;
{
	syntaxtree *tmptr;
	command_t *comm;
	int id;

	if (trp -> flags & ST_NODE) return(trp);

	comm = trp -> comm;
	if (ischild(comm)) /*EMPTY*/;
	else if (comm) {
		if (isstatement(comm)) return(trp);
		comm -> argc++;
		if (arg) {
			comm -> argv = (char **)Xrealloc(comm -> argv,
					(comm -> argc + 2) * sizeof(char *));
			comm -> argv[comm -> argc] = Xstrdup(arg);
			comm -> argv[comm -> argc + 1] = NULL;
		}
	}
	else if (arg) {
		if ((trp -> flags & ST_NEXT)
		&& (id = getstatid(parentstree(trp))) >= 0
		&& !(statementlist[id].type & STT_NEEDLIST)) {
			syntaxerrno = ER_UNEXPTOK;
			return(trp);
		}

		comm = trp -> comm = newcomm();
		comm -> argc = 0;
		comm -> argv = (char **)Xmalloc(2 * sizeof(char *));
		comm -> argv[0] = Xstrdup(arg);
		comm -> argv[1] = NULL;
	}

	if ((id = getstatid(getparent(trp))) >= 0)
	switch (statementlist[id].type & STT_TYPE) {
		case STT_FOR:
		case STT_CASE:
			if (!comm || comm -> argc) {
				syntaxerrno = ER_UNEXPNL;
				return(trp);
			}
			comm -> argv = (char **)Xrealloc(comm -> argv,
					2 * sizeof(char *));
			comm -> argv[1] = NULL;
			comm -> argc = 1;
			trp -> next = newstree(trp);
			(trp -> next) -> flags = ST_NEXT;
			trp = trp -> next;
			break;
		case STT_IN:
			if (!comm) {
				syntaxerrno = ER_UNEXPNL;
				return(trp);
			}
			break;
		case STT_FUNC:
			if (!arg && comm) {
				if (!(tmptr = parentstree(trp))
				|| !(tmptr = parentstree(tmptr))) {
					syntaxerrno = ER_UNEXPNL;
					return(trp);
				}
				trp = tmptr;
			}
			break;
		default:
			break;
	}

	return(trp);
}

static int NEAR addarg(trpp, rp, tok, lenp, notok)
syntaxtree **trpp;
redirectlist *rp;
char *tok;
int *lenp, notok;
{
	syntaxtree *tmptr;
	int i, n, id, type;

	type = rp -> type;
	rp -> type = 0;
	if (!tok) tok = rp -> filename;

	if ((*trpp) -> flags & ST_NODE) {
		syntaxerrno = ER_UNEXPTOK;
		return(-1);
	}

	if (!*lenp) {
		if (type && notok) {
			syntaxerrno = ER_UNEXPTOK;
			return(-1);
		}
		rp -> type = type;
		return(0);
	}
	tok[*lenp] = '\0';
	*lenp = 0;

	if (!type) {
		id = getstatid(tmptr = parentstree(*trpp));
		type = (id >= 0) ? statementlist[id].type : 0;

		if ((type & STT_NEEDIDENT) && tmptr == getparent(*trpp))
			/*EMPTY*/;
		else if (!((*trpp) -> comm) || ischild((*trpp) -> comm)) {
			for (i = 0; i < STATEMENTSIZ; i++)
			if (!strcommcmp(tok, statementlist[i].ident)) {
				n = parsestatement(trpp, i, id + 1, type);
				if (!n) continue;
				else if (n > 0) return(0);

				syntaxerrno = ER_UNEXPTOK;
				return(-1);
			}
		}
		if (isstatement((*trpp) -> comm)) {
			syntaxerrno = ER_UNEXPTOK;
			return(-1);
		}

		*trpp = _addarg(*trpp, tok);
	}
	else if (redirect(*trpp, rp -> fd, tok, type) < 0) return(-1);

	return(0);
}

static int NEAR evalredprefix(trpp, rp, lenp)
syntaxtree **trpp;
redirectlist *rp;
int *lenp;
{
	int n;

	rp -> filename[*lenp] = '\0';
	if ((n = evalfiledesc(rp -> filename)) < 0)
		addarg(trpp, rp, NULL, lenp, 1);
	else if (rp -> type) {
		syntaxerrno = ER_UNEXPTOK;
		return(-2);
	}
	*lenp = 0;

	return(n);
}

static syntaxtree *NEAR rparen(trp)
syntaxtree *trp;
{
	syntaxtree *tmptr;

	if ((!(trp -> comm) && !(trp -> flags & ST_NEXT))
	|| !(tmptr = parentstree(trp)) || !ischild(tmptr -> comm))
		syntaxerrno = ER_UNEXPTOK;
	else {
		_addarg(trp, NULL);
		tmptr = _addarg(tmptr, NULL);
		tmptr -> cont |= (trp -> cont & CN_INHR);
		trp = tmptr;
	}

	return(trp);
}

static syntaxtree *NEAR semicolon(trp, rp, s, ptrp)
syntaxtree *trp;
redirectlist *rp;
CONST char *s;
int *ptrp;
{
	char tmptok[3];
	int i, id;

	id = getstatid(parentstree(trp));

	if (s[*ptrp + 1] != ';') {
		if (!(trp -> comm) && !(trp -> flags & ST_NEXT) && id >= 0) {
			syntaxerrno = ER_UNEXPNL;
			return(trp);
		}
		trp = _addarg(trp, NULL);
		trp = linkstree(trp, OP_FG);
	}
	else {
		if (id != SM_RPAREN - 1) {
			syntaxerrno = ER_UNEXPTOK;
			return(trp);
		}
		if (trp -> comm) {
			trp = _addarg(trp, NULL);
			trp = linkstree(trp, OP_FG);
		}
		(*ptrp)++;
		i = 0;
		tmptok[i++] = ';';
		tmptok[i++] = ';';
		addarg(&trp, rp, tmptok, &i, 0);
	}

	return(trp);
}

static syntaxtree *NEAR ampersand(trp, rp, s, ptrp)
syntaxtree *trp;
redirectlist *rp;
CONST char *s;
int *ptrp;
{
	switch (s[*ptrp + 1]) {
		case '&':
			trp = _addarg(trp, NULL);
			(*ptrp)++;
			trp = linkstree(trp, OP_AND);
			break;
#ifndef	MINIMUMSHELL
		case '>':
			(*ptrp)++;
			rp -> fd = STDOUT_FILENO;
			rp -> type = (MD_WRITE | MD_WITHERR);
			if (s[*ptrp + 1] == '>') {
				(*ptrp)++;
				rp -> type |= MD_APPEND;
			}
			else if (s[*ptrp + 1] == '|') {
				(*ptrp)++;
				rp -> type |= MD_FORCED;
			}
			break;
		case '|':
			trp = _addarg(trp, NULL);
			(*ptrp)++;
			trp = linkstree(trp, OP_NOWN);
			break;
#endif	/* !MINIMUMSHELL */
		default:
			trp = _addarg(trp, NULL);
			trp = linkstree(trp, OP_BG);
			break;
	}

	return(trp);
}

static syntaxtree *NEAR vertline(trp, s, ptrp)
syntaxtree *trp;
CONST char *s;
int *ptrp;
{
#ifndef	MINIMUMSHELL
	int n;
#endif

	trp = _addarg(trp, NULL);
	switch (s[*ptrp + 1]) {
		case '|':
			(*ptrp)++;
			trp = linkstree(trp, OP_OR);
			break;
#ifndef	MINIMUMSHELL
		case '&':
			(*ptrp)++;
			n = redirect(trp,
				STDERR_FILENO, "1", MD_WRITE | MD_FILEDESC);
			if (n >= 0) trp = linkstree(trp, OP_PIPE);
			break;
#endif
		default:
			trp = linkstree(trp, OP_PIPE);
			break;
	}

	return(trp);
}

static syntaxtree *NEAR lessthan(trp, rp, s, ptrp)
syntaxtree *trp;
redirectlist *rp;
CONST char *s;
int *ptrp;
{
	int n;

	rp -> type = MD_READ;
	n = 0;
	switch (s[*ptrp + 1]) {
		case '<':
			(*ptrp)++;
			rp -> type |= MD_HEREDOC;
			if (s[*ptrp + 1] == '-') {
				(*ptrp)++;
				rp -> type |= MD_APPEND;
			}
			break;
#ifndef	MINIMUMSHELL
		case '>':
			(*ptrp)++;
			rp -> type |= MD_WRITE;
			if (s[*ptrp + 1] == '-') n++;
			else if (s[*ptrp + 1] == '&') {
				(*ptrp)++;
				if (s[*ptrp + 1] == '-') n++;
				else rp -> type |= MD_FILEDESC;
			}
			break;
#endif	/* !MINIMUMSHELL */
		case '&':
			(*ptrp)++;
			if (s[*ptrp + 1] == '-') n++;
			else rp -> type |= MD_FILEDESC;
			break;
		case '-':
			n++;
			break;
		default:
			break;
	}
	if (n) {
		(*ptrp)++;
		if (redirect(trp, rp -> fd, NULL, rp -> type) >= 0)
			rp -> type = 0;
	}

	return(trp);
}

static syntaxtree *NEAR morethan(trp, rp, s, ptrp)
syntaxtree *trp;
redirectlist *rp;
CONST char *s;
int *ptrp;
{
	int n;

	rp -> type = MD_WRITE;
	n = 0;
	switch (s[*ptrp + 1]) {
		case '>':
			(*ptrp)++;
			rp -> type |= MD_APPEND;
			break;
#ifndef	MINIMUMSHELL
		case '<':
			if (rp -> fd < 0) rp -> fd = STDOUT_FILENO;
			(*ptrp)++;
			rp -> type |= MD_READ;
			if (s[*ptrp + 1] == '-') n++;
			else if (s[*ptrp + 1] == '&') {
				(*ptrp)++;
				if (s[*ptrp + 1] == '-') n++;
				else rp -> type |= MD_FILEDESC;
			}
			break;
#endif	/* !MINIMUMSHELL */
		case '&':
			(*ptrp)++;
			if (s[*ptrp + 1] == '-') n++;
			else rp -> type |= MD_FILEDESC;
			break;
		case '-':
			n++;
			break;
#ifndef	MINIMUMSHELL
		case '|':
			(*ptrp)++;
			rp -> type |= MD_FORCED;
			break;
#endif
		default:
			break;
	}
	if (n) {
		(*ptrp)++;
		if (redirect(trp, rp -> fd, NULL, rp -> type) >= 0)
			rp -> type = 0;
	}

	return(trp);
}

#if	defined (BASHSTYLE) || !defined (MINIMUMSHELL)
syntaxtree *startvar(trp, rp, s, ptrp, tptrp, n)
syntaxtree *trp;
redirectlist *rp;
CONST char *s;
int *ptrp, *tptrp, n;
{
	syntaxtree *new;
	redirectlist *newrp;
	char *cp;

	cp = Xmalloc(*tptrp + n + 1);
	Xstrncpy(cp, rp -> filename, *tptrp);
	Xstrncpy(&(cp[*tptrp]), &(s[*ptrp]), n);
	*tptrp = 0;
	*ptrp += n - 1;

	new = newstree(NULL);
	new -> parent = trp;
	newrp = (redirectlist *)Xmalloc(sizeof(redirectlist));
	memcpy((char *)newrp, (char *)rp, sizeof(redirectlist));
	newrp -> filename = cp;
	newrp -> next = (redirectlist *)new;
	trp -> next = (syntaxtree *)newrp;
	trp -> flags |= ST_BUSY;
	rp -> type = MD_NORMAL;
	rp -> fd = -1;
	rp -> new = rp -> old = '\0';

	return(new);
}

static syntaxtree *NEAR endvar(trp, rp, s, ptrp, tptrp, sp, n)
syntaxtree *trp;
redirectlist *rp;
CONST char *s;
int *ptrp, *tptrp;
ALLOC_T *sp;
int n;
{
	syntaxtree *tmptr;
	char *cp;
	ALLOC_T size;
	int len;

	if (!(tmptr = trp -> parent) || !(tmptr -> flags & ST_BUSY)
	|| !(tmptr -> next)) {
		syntaxerrno = ER_UNEXPTOK;
		return(trp);
	}
	trp = tmptr;
	tmptr = trp -> next;
	trp -> next = NULL;
	trp -> flags &= ~ST_BUSY;

	cp = rp -> filename;
	memcpy((char *)rp, (char *)tmptr, sizeof(redirectlist));
	len = strlen(rp -> filename);
	size = c_allocsize(len + *tptrp + n + 2);
	if (size > *sp) cp = Xrealloc(cp, *sp = size);
	memmove(&(cp[len]), cp, *tptrp);
	memcpy(cp, rp -> filename, len);
	*tptrp += len;
	Xstrncpy(&(cp[*tptrp]), &(s[*ptrp]), n);
	*tptrp += n;
	*ptrp += n - 1;
	Xfree(rp -> filename);
	rp -> filename = cp;

	Xfree(rp -> next);
	rp -> next = NULL;
	Xfree(tmptr);

	return(trp);
}

static syntaxtree *NEAR addvar(trp, s, ptrp, tok, tptrp, n)
syntaxtree *trp;
CONST char *s;
int *ptrp;
CONST char *tok;
int *tptrp, n;
{
	syntaxtree *tmptr;
	redirectlist *rp;
	int len;

	if (!(tmptr = trp -> parent) || !(tmptr -> flags & ST_BUSY)
	|| !(rp = (redirectlist *)(tmptr -> next))) {
		syntaxerrno = ER_UNEXPTOK;
		return(trp);
	}

	len = strlen(rp -> filename);
	rp -> filename = Xrealloc(rp -> filename, len + *tptrp + n + 1);
	Xstrncpy(&(rp -> filename[len]), tok, *tptrp);
	len += *tptrp;
	*tptrp = 0;
	if (n > 0) {
		Xstrncpy(&(rp -> filename[len]), &(s[*ptrp]), n);
		len += n;
		*ptrp += n - 1;
	}
	rp -> filename[len] = '\0';

	return(trp);
}
#endif	/* BASHSTYLE || !MINIMUMSHELL */

static syntaxtree *NEAR normaltoken(trp, rp, s, ptrp, tptrp, sp)
syntaxtree *trp;
redirectlist *rp;
CONST char *s;
int *ptrp, *tptrp;
ALLOC_T *sp;
{
	char tmptok[2];
	ALLOC_T size;
	int i;

	switch (s[*ptrp]) {
		case '{':
			if (!Xstrchr(IFS_SET, s[*ptrp + 1])) {
				rp -> filename[(*tptrp)++] = s[*ptrp];
				break;
			}
			if (addarg(&trp, rp, NULL, tptrp, 0) < 0) break;
			i = 0;
			tmptok[i++] = s[*ptrp];
			addarg(&trp, rp, tmptok, &i, 0);
			break;
		case '}':
			rp -> filename[(*tptrp)++] = s[*ptrp];
			if ((*tptrp) > 1 || getparenttype(trp) != STT_LIST)
				break;
			if (trp -> comm) {
#ifdef	BASHSTYLE
	/* bash allows the list which does not end with ";" */
				trp = _addarg(trp, NULL);
				trp = linkstree(trp, OP_NONE);
#else
				break;
#endif
			}
			addarg(&trp, rp, NULL, tptrp, 0);
			break;
		case '(':
			if (hascomm(trp)) {
				if (rp -> type
				|| *tptrp || (trp -> comm) -> argc)
					syntaxerrno = ER_UNEXPTOK;
				else {
					trp = _addarg(trp, NULL);
					trp = linkstree(trp, OP_NONE);
					i = 0;
					tmptok[i++] = s[*ptrp];
					addarg(&trp, rp, tmptok, &i, 0);
				}
			}
			else if (*tptrp) {
				if (!*tptrp
				|| addarg(&trp, rp, NULL, tptrp, 0) < 0)
					syntaxerrno = ER_UNEXPTOK;
				else {
					trp = _addarg(trp, NULL);
					trp = linkstree(trp, OP_NONE);
					i = 0;
					tmptok[i++] = s[*ptrp];
					addarg(&trp, rp, tmptok, &i, 0);
				}
			}
			else if (rp -> type) syntaxerrno = ER_UNEXPTOK;
#ifdef	BASHSTYLE
	/* bash does not allow the function definition without "{ }" */
			else if (getstatid(parentstree(trp)) == SM_FUNC - 1)
				syntaxerrno = ER_UNEXPTOK;
#endif
			else trp = childstree(trp, SM_CHILD);
			break;
		case ')':
			if (getparenttype(trp) == STT_LPAREN) {
				if (*tptrp || trp -> comm)
					syntaxerrno = ER_UNEXPTOK;
				else {
					i = 0;
					tmptok[i++] = s[*ptrp];
					addarg(&trp, rp, tmptok, &i, 0);
				}
				break;
			}
			if (addarg(&trp, rp, NULL, tptrp, 0) < 0) break;
			trp = rparen(trp);
			break;
		case ';':
			if (addarg(&trp, rp, NULL, tptrp, 1) < 0) break;
			trp = semicolon(trp, rp, s, ptrp);
			break;
		case '&':
			if (addarg(&trp, rp, NULL, tptrp, 1) < 0) break;
			trp = ampersand(trp, rp, s, ptrp);
			break;
		case '|':
			if (addarg(&trp, rp, NULL, tptrp, 1) < 0) break;
			trp = vertline(trp, s, ptrp);
			break;
#ifndef	MINIMUMSHELL
		case '!':
			if (*tptrp || trp -> comm)
				rp -> filename[(*tptrp)++] = s[*ptrp];
			else if (rp -> type || (trp -> flags & ST_NODE)) {
				syntaxerrno = ER_UNEXPTOK;
				return(trp);
			}
			else trp = linkstree(trp, OP_NOT);
			break;
#endif
		case '<':
			if ((rp -> fd = evalredprefix(&trp, rp, tptrp)) >= -1)
				trp = lessthan(trp, rp, s, ptrp);
			break;
		case '>':
			if ((rp -> fd = evalredprefix(&trp, rp, tptrp)) >= -1)
				trp = morethan(trp, rp, s, ptrp);
			break;
		case '\r':
#ifdef	BASHSTYLE
	/* bash treats '\r' as just a character */
			if (rp -> type) syntaxerrno = ER_UNEXPNL;
			else rp -> filename[(*tptrp)++] = s[*ptrp];
			break;
#else
/*FALLTHRU*/
#endif
		case '\n':
			if (addarg(&trp, rp, NULL, tptrp, 1) < 0) break;
			if (trp -> comm) {
				trp = _addarg(trp, NULL);
				trp = linkstree(trp, OP_FG);
			}
			else if (!(trp -> flags & ST_NEXT)
			&& (i = getstatid(parentstree(trp)) + 1) > 0) {
				if (i == SM_FOR || i == SM_CASE || i == SM_IN
				|| *tptrp)
					syntaxerrno = ER_UNEXPNL;
			}

			if (trp -> cont & CN_HDOC) {
				i = *ptrp + 1;
#ifdef	BASHSTYLE
	/* bash treats '\r' as just a character */
				while (s[*ptrp + 1] && s[*ptrp + 1] != '\n')
					(*ptrp)++;
#else
				while (s[*ptrp + 1] && s[*ptrp + 1] != '\r'
				&& s[*ptrp + 1] != '\n') (*ptrp)++;
#endif
				*tptrp = *ptrp - i + 1;
				size = c_allocsize(*tptrp + 2);
				if (size > *sp)
					rp -> filename =
						Xrealloc(rp -> filename,
							*sp = size);
				Xstrncpy(rp -> filename, &(s[i]), *tptrp);
				trp -> flags |= ST_HDOC;
			}
			break;
		case '#':
			if (*tptrp) rp -> filename[(*tptrp)++] = s[*ptrp];
			else {
#ifdef	BASHSTYLE
	/* bash treats '\r' as just a character */
				while (s[*ptrp + 1] && s[*ptrp + 1] != '\n')
					(*ptrp)++;
#else
				while (s[*ptrp + 1] && s[*ptrp + 1] != '\r'
				&& s[*ptrp + 1] != '\n') (*ptrp)++;
#endif
			}
			break;
		default:
			if (!Xstrchr(IFS_SET, s[*ptrp]))
				rp -> filename[(*tptrp)++] = s[*ptrp];
			else addarg(&trp, rp, NULL, tptrp, 0);
			break;
	}

	return(trp);
}

static syntaxtree *NEAR casetoken(trp, rp, s, ptrp, tptrp)
syntaxtree *trp;
redirectlist *rp;
CONST char *s;
int *ptrp, *tptrp;
{
	char tmptok[2];
	int i, stype;

	switch (s[*ptrp]) {
		case ')':
			if (!*tptrp) /*EMPTY*/;
			else if (addarg(&trp, rp, NULL, tptrp, 0) < 0) break;
			trp = _addarg(trp, NULL);
			trp = linkstree(trp, OP_NONE);
			i = 0;
			tmptok[i++] = s[*ptrp];
			addarg(&trp, rp, tmptok, &i, 0);
			break;
		case '|':
			if (!*tptrp) /*EMPTY*/;
			else addarg(&trp, rp, NULL, tptrp, 0);
			break;
		case ';':
			if (addarg(&trp, rp, NULL, tptrp, 1) < 0) break;
			/* for "esac;" */
			if ((stype = getparenttype(trp)) != STT_INCASE
			&& stype != STT_CASEEND)
				trp = semicolon(trp, rp, s, ptrp);
			else syntaxerrno = ER_UNEXPTOK;
			break;
		case '{':
		case '}':
		case '&':
		case '<':
		case '>':
			syntaxerrno = ER_UNEXPTOK;
			break;
		case '(':
#ifdef	STRICTPOSIX
			if (!*tptrp && !(trp -> comm)) break;
#endif
			syntaxerrno = ER_UNEXPTOK;
			break;
		case '\r':
#ifdef	BASHSTYLE
	/* bash treats '\r' as just a character */
			syntaxerrno = ER_UNEXPNL;
			break;
#endif
		case '\n':
			if (getparenttype(trp) == STT_INCASE && *tptrp) {
				syntaxerrno = ER_UNEXPNL;
				break;
			}
			if (addarg(&trp, rp, NULL, tptrp, 1) < 0) break;
			if (trp -> comm) {
				trp = _addarg(trp, NULL);
				trp = linkstree(trp, OP_FG);
			}
			else if (!(trp -> flags & ST_NEXT)
			&& (i = getstatid(parentstree(trp)) + 1) > 0) {
				if (*tptrp) syntaxerrno = ER_UNEXPNL;
			}
			break;
		case '#':
			if (*tptrp) rp -> filename[(*tptrp)++] = s[*ptrp];
			else {
#ifdef	BASHSTYLE
	/* bash treats '\r' as just a character */
				while (s[*ptrp + 1] && s[*ptrp + 1] != '\n')
					(*ptrp)++;
#else
				while (s[*ptrp + 1] && s[*ptrp + 1] != '\r'
				&& s[*ptrp + 1] != '\n') (*ptrp)++;
#endif
			}
			break;
		default:
			if (!Xstrchr(IFS_SET, s[*ptrp]))
				rp -> filename[(*tptrp)++] = s[*ptrp];
			else if (*tptrp > 0) {
				addarg(&trp, rp, NULL, tptrp, 0);
				do {
					(*ptrp)++;
				} while (Xisblank(s[*ptrp]));

				/* for "esac " */
				if ((stype = getparenttype(trp)) != STT_INCASE
				&& stype != STT_CASEEND)
					(*ptrp)--;
				else if (!s[*ptrp] || s[*ptrp] == '\n')
					syntaxerrno = ER_UNEXPNL;
				else if (s[*ptrp] == ')' || s[*ptrp] == '|')
					(*ptrp)--;
				else if (s[*ptrp] == PESCAPE
				&& (!s[*ptrp + 1] || s[*ptrp] == '\n'))
					(*ptrp)--;
				else syntaxerrno = ER_UNEXPTOK;
			}
			break;
	}

	return(trp);
}

#if	!defined (BASHBUG) && !defined (MINIMUMSHELL)
static int NEAR cmpstatement(s, id)
CONST char *s;
int id;
{
	int len;

	if (--id < 0) return(-1);
	len = strlen(statementlist[id].ident);
	if (strncommcmp(s, statementlist[id].ident, len)) return(-1);

	return(len);
}

static syntaxtree *NEAR comsubtoken(trp, rp, s, ptrp, tptrp, sp)
syntaxtree *trp;
redirectlist *rp;
CONST char *s;
int *ptrp, *tptrp;
ALLOC_T *sp;
{
	int i, len;

	if (!*tptrp) {
		if ((len = cmpstatement(&(s[*ptrp]), SM_CASE)) >= 0
		&& (!s[*ptrp + len] || Xstrchr(IFS_SET, s[*ptrp + len]))) {
			trp = startvar(trp, rp, s, ptrp, tptrp, len);
			trp -> cont = CN_CASE;
			return(trp);
		}
		if ((trp -> cont & CN_SBST) == CN_CASE
		&& (len = cmpstatement(&(s[*ptrp]), SM_ESAC)) >= 0
		&& (!s[*ptrp + len] || Xstrchr(IFS_SET, s[*ptrp + len])
		|| s[*ptrp + len] == ';' || s[*ptrp + len] == ')')) {
			trp = endvar(trp, rp, s, ptrp, tptrp, sp, len);
			return(trp);
		}
		if (Xstrchr(IFS_SET, s[*ptrp])) {
			for (len = 1; s[*ptrp + len]; len++)
				if (!Xstrchr(IFS_SET, s[*ptrp + len])) break;
			trp = addvar(trp, s, ptrp, rp -> filename, tptrp, len);
			return(trp);
		}
	}

	if (s[*ptrp] == '(') {
		trp = startvar(trp, rp, s, ptrp, tptrp, 1);
		trp -> cont = CN_COMM;
	}
	else if (s[*ptrp] == ')') {
		if ((trp -> cont & CN_SBST) == CN_CASE)
			rp -> filename[(*tptrp)++] = s[*ptrp];
		else trp = endvar(trp, rp, s, ptrp, tptrp, sp, 1);
	}
# ifdef	BASHSTYLE
	/* bash treats '\r' as just a character */
	else if (s[*ptrp] == '\n')
# else
	else if (s[*ptrp] == '\n' || s[*ptrp] == '\r')
# endif
		trp = addvar(trp, s, ptrp, rp -> filename, tptrp, 1);
	else {
		for (i = 0; i < DELIMLISTSIZ; i++) {
			len = strlen(delimlist[i].symbol);
			if (!strncmp(&(s[*ptrp]), delimlist[i].symbol, len))
				break;
		}
		if (i >= DELIMLISTSIZ) rp -> filename[(*tptrp)++] = s[*ptrp];
		else if (delimlist[i].level)
			trp = addvar(trp, s, ptrp, rp -> filename, tptrp, len);
		else {
			Xstrncpy(&(rp -> filename[*tptrp]), &(s[*ptrp]), len);
			*tptrp += len;
			*ptrp += len - 1;
		}
	}

	return(trp);
}
#endif	/* !BASHBUG && !MINIMUMSHELL */

static syntaxtree *NEAR analyzeloop(trp, rp, s, quiet)
syntaxtree *trp;
redirectlist *rp;
CONST char *s;
int quiet;
{
#if	!defined (BASHSTYLE) && !defined (MINIMUMSHELL)
	syntaxtree *tmptr;
#endif
	char *cp;
	ALLOC_T size;
	int i, j, n, pc, stype, hdoc;

	if (!rp -> filename) {
		j = 0;
		rp -> filename = c_realloc(NULL, 0, &size);
	}
	else {
		j = strlen(rp -> filename);
		size = c_allocsize(j);
	}

	hdoc = 0;
	for (i = 0; s && s[i]; i++) {
		syntaxerrno = 0;
		rp -> filename = c_realloc(rp -> filename, j + 2, &size);

#ifndef	MINIMUMSHELL
		if (s[i] == '\n') setshlineno(shlineno + 1L);
#endif
		if ((trp -> cont & CN_HDOC) && (trp -> flags & ST_HDOC)) {
			if (s[i] != '\n') {
				if (hdoc) {
					rp -> filename[j++] = s[i];
					continue;
				}
			}
			else {
				rp -> filename[j] = '\0';
				n = saveheredoc(rp -> filename, trp);
				if (n < 0) {
					if (!quiet) doperror(NULL, NULL);
					return(NULL);
				}
				hdoc = (trp -> cont & CN_HDOC);
				if (n > 0) {
					j = 0;
					continue;
				}
				hdoc = 0;
				trp -> cont &= ~CN_HDOC;
			}
		}

		pc = parsechar(&(s[i]), -1, '$', EA_BACKQ | EA_EOLESCAPE,
#ifdef	NESTINGQUOTE
			&(rp -> new), &(rp -> old));
#else
			&(rp -> new), NULL);
#endif

		if (pc == PC_OPQUOTE || pc == PC_CLQUOTE || pc == PC_SQUOTE)
			rp -> filename[j++] = s[i];
#ifdef	BASHSTYLE
		else if (pc == PC_BQUOTE) rp -> filename[j++] = s[i];
#endif
		else if (pc == PC_WCHAR) {
			rp -> filename[j++] = s[i++];
			rp -> filename[j++] = s[i];
		}
		else if (pc == PC_ESCAPE) {
			if (s[++i] != '\n' || rp -> new) {
				rp -> filename[j++] = PESCAPE;
				if (s[i]) rp -> filename[j++] = s[i];
				else {
					trp -> cont |= CN_ESCAPE;
					break;
				}
			}
		}
		else if (pc == '$') {
			if (s[i + 1] == '{') {
#ifdef	BASHSTYLE
	/* bash treats any meta character in ${} as just a character */
				trp = startvar(trp, rp, s, &i, &j, 2);
#else	/* !BASHSTYLE */
# ifndef	MINIMUMSHELL
				if (rp -> new == '"')
					trp = startvar(trp, rp, s, &i, &j, 2);
				else
# endif
				{
					rp -> filename[j++] = s[i++];
					rp -> filename[j++] = s[i];
				}
#endif	/* !BASHSTYLE */
				trp -> cont = CN_VAR;
			}
#ifndef	MINIMUMSHELL
			else if (s[i + 1] == '(') {
				if (s[i + 2] != '(') {
					trp = startvar(trp, rp, s, &i, &j, 2);
					trp -> cont = CN_COMM;
				}
				else {
					trp = startvar(trp, rp, s, &i, &j, 3);
					trp -> cont = CN_EXPR;
				}
			}
#endif	/* !MINIMUMSHELL */
			else rp -> filename[j++] = s[i];
		}
		else if (s[i] == '}' && (trp -> cont & CN_SBST) == CN_VAR) {
#ifdef	BASHSTYLE
	/* bash treats any meta character in ${} as just a character */
			trp = endvar(trp, rp, s, &i, &j, &size, 1);
#else	/* !BASHSTYLE */
# ifndef	MINIMUMSHELL
			tmptr = trp -> parent;
			if (tmptr) tmptr = tmptr -> next;
			if (tmptr && ((redirectlist *)tmptr) -> new == '"')
				trp = endvar(trp, rp, s, &i, &j, &size, 1);
			else
# endif
			{
				rp -> filename[j++] = s[i];
				trp -> cont &= ~CN_VAR;
			}
#endif	/* !BASHSTYLE */
		}
#ifndef	MINIMUMSHELL
		else if ((trp -> cont & CN_SBST) == CN_VAR)
			rp -> filename[j++] = s[i];
#endif
#ifndef	BASHSTYLE
		else if (pc == PC_BQUOTE) rp -> filename[j++] = s[i];
#endif
		else if (pc == PC_DQUOTE) rp -> filename[j++] = s[i];
#ifndef	MINIMUMSHELL
# ifdef	BASHBUG
	/* bash cannot include 'case' statement within $() */
		else if ((trp -> cont & CN_SBST) == CN_COMM) {
			if (s[i] == '(') {
				trp = startvar(trp, rp, s, &i, &j, 1);
				trp -> cont = CN_COMM;
			}
			else if (s[i] != ')') rp -> filename[j++] = s[i];
			else trp = endvar(trp, rp, s, &i, &j, &size, 1);
		}
# else
		else if ((trp -> cont & CN_SBST) == CN_COMM
		|| (trp -> cont & CN_SBST) == CN_CASE)
			trp = comsubtoken(trp, rp, s, &i, &j, &size);
# endif
		else if ((trp -> cont & CN_SBST) == CN_EXPR) {
			if (s[i] == '(') {
				trp = startvar(trp, rp, s, &i, &j, 2);
				trp -> cont = CN_COMM;
			}
			else if (s[i] != ')' || s[i + 1] != ')')
				rp -> filename[j++] = s[i];
			else trp = endvar(trp, rp, s, &i, &j, &size, 2);
		}
#endif	/* !MINIMUMSHELL */
		else if ((stype = getparenttype(trp)) == STT_INCASE
		|| stype == STT_CASEEND)
			trp = casetoken(trp, rp, s, &i, &j);
		else {
#ifndef	NOALIAS
			n = checkalias(trp, rp -> filename, j, s[i]);
			if (n >= 0) {
				trp = analyzeloop(trp, rp,
					shellalias[n].comm, quiet);
				shellalias[n].flags &= ~AL_USED;
				if (!trp) return(NULL);
				j = strlen(rp -> filename);
				i--;
			}
			else
#endif	/* !NOALIAS */
			trp = normaltoken(trp, rp, s, &i, &j, &size);
		}

		if (syntaxerrno) {
			if (quiet) return(NULL);
			if (Xstrchr(IFS_SET, s[i])) {
				if (j < 0) j = 0;
				rp -> filename[j] = '\0';
				syntaxerror(rp -> filename);
				return(NULL);
			}
			for (j = i + 1; s[j]; j++) {
				if (Xstrchr(IFS_SET, s[j])) break;
				else if (iswchar(s, j)) j++;
			}
			cp = Xstrndup(&(s[i]), j - i);
			syntaxerror(cp);
			Xfree(cp);
			return(NULL);
		}
	}

	if (j && (trp -> cont & CN_HDOC) && (trp -> flags & ST_HDOC)) {
		rp -> filename[j] = '\0';
		if ((n = saveheredoc(rp -> filename, trp)) < 0) {
			if (!quiet) doperror(NULL, NULL);
			return(NULL);
		}
		if (n > 0) j = 0;
		else trp -> cont &= ~CN_HDOC;
	}
	rp -> filename[j] = '\0';

#ifndef	NOALIAS
	if ((n = checkalias(trp, rp -> filename, j, '\0')) >= 0) {
		trp = analyzeloop(trp, rp, shellalias[n].comm, quiet);
		shellalias[n].flags &= ~AL_USED;
	}
#endif

	return(trp);
}

syntaxtree *analyze(s, trp, quiet)
CONST char *s;
syntaxtree *trp;
int quiet;
{
	syntaxtree *parent;
	redirectlist *rp, red;
	int i;

	syntaxerrno = 0;
	red.fd = -1;
	red.filename = NULL;
	red.type = MD_NORMAL;
	red.new = red.old = '\0';
#ifdef	DEP_DOSDRIVE
	red.fakepipe = NULL;
	red.dosfd = -1;
#endif
	red.next = NULL;

	if (trp -> cont & (CN_QUOT | CN_ESCAPE)) {
		memcpy((char *)&red, (char *)(trp -> next), sizeof(red));
		Xfree(trp -> next);

		i = strlen(red.filename);
		if (i > 0) {
			if ((trp -> cont & CN_ESCAPE) && red.new != '\'') i--;
			else if (s) red.filename[i++] = '\n';
		}
#ifndef	BASHSTYLE
	/* bash does not allow unclosed quote */
		if (!s) {
			if (trp -> cont & CN_QUOT) red.filename[i++] = red.new;
			red.new = '\0';
		}
#endif
		red.filename[i] = '\0';
	}
	else if ((trp -> cont & CN_HDOC)) {
		if ((i = saveheredoc(s, trp)) < 0) {
			if (!quiet) doperror(NULL, NULL);
			return(NULL);
		}
		else if (!i) {
			trp -> cont &= ~CN_HDOC;
			return(NULL);
		}

		return(trp);
	}
	else if (!s) return(NULL);

	trp -> next = NULL;
	trp -> cont &= CN_INHR;

	exectrapcomm();
	if (s) {
#ifndef	NOALIAS
		for (i = 0; shellalias[i].ident; i++)
			shellalias[i].flags &= ~AL_USED;
#endif
		if (!(trp = analyzeloop(trp, &red, s, quiet))) {
			Xfree(red.filename);
			return(NULL);
		}
	}

	i = (red.filename) ? strlen(red.filename) : 0;
	if (red.new) trp -> cont |= CN_QUOT;

	if (trp -> cont & (CN_QUOT | CN_ESCAPE)) {
		rp = (redirectlist *)Xmalloc(sizeof(redirectlist));
		memcpy((char *)rp, (char *)&red, sizeof(red));
		trp -> next = (syntaxtree *)rp;
		red.filename = NULL;
	}
#if	defined (BASHSTYLE) || !defined (MINIMUMSHELL)
	else if (trp -> cont & CN_SBST) {
		red.filename[i++] = '\n';
		red.filename[i] = '\0';
		trp = addvar(trp, NULL, NULL, red.filename, &i, 0);
	}
#endif
	else if (getparenttype(trp) == STT_INCASE && (trp -> comm || i))
		syntaxerrno = ER_UNEXPNL;
	else if (addarg(&trp, &red, NULL, &i, 1) < 0) /*EMPTY*/;
	else if (!(trp = _addarg(trp, NULL)) || syntaxerrno) /*EMPTY*/;
	else if (trp -> comm) trp = linkstree(trp, OP_FG);
	else if (hasparent(trp)) {
#ifndef	MINIMUMSHELL
		if (isopnot(trp -> parent)) syntaxerrno = ER_UNEXPNL;
#endif
		if (!isopfg(trp -> parent) && !isopbg(trp -> parent)
		&& !isopnown(trp -> parent))
			trp -> cont |= CN_STAT;
	}
	if (syntaxerrno) {
		if (!quiet) syntaxerror(red.filename);
		Xfree(red.filename);
		return(NULL);
	}
	Xfree(red.filename);
	if ((parent = parentstree(trp)) && isstatement(parent -> comm))
		trp -> cont |= CN_STAT;

	return(trp);
}

static syntaxtree *NEAR analyzeeof(trp)
syntaxtree *trp;
{
	while (trp && trp -> cont) {
#ifdef	BASHSTYLE
		if (trp -> cont & CN_HDOC) trp = analyze(NULL, trp, 0);
#else
	/* bash does not allow unclosed quote */
		if (trp -> cont & (CN_ESCAPE | CN_QUOT | CN_HDOC))
			trp = analyze(NULL, trp, 0);
	/* bash does not allow the format like as "foo |" */
		else if ((trp -> flags & ST_NEXT) && hasparent(trp)
		&& isoppipe(trp -> parent))
			break;
#endif
		else {
			syntaxerrno = ER_UNEXPEOF;
			syntaxerror(nullstr);
			return(NULL);
		}
	}

	return(trp);
}

static syntaxtree *NEAR statementcheck(trp, id)
syntaxtree *trp;
int id;
{
	if (!trp || !isstatement(trp -> comm)
	|| (id > 0 && (trp -> comm) -> id != id)
	|| !(trp = statementbody(trp))) {
		errno = EINVAL;
		return(NULL);
	}

	return(trp);
}

static int NEAR check_statement(trp)
syntaxtree *trp;
{
	syntaxtree *body;
	int i, id, prev;

	if ((trp -> comm) -> id == SM_CHILD)
		return(check_stree(statementbody(trp)));
	if ((id = getstatid(trp = statementcheck(trp, SM_STATEMENT))) < 0
	|| !statementlist[id].func || statementlist[id].prev[0]) {
		errno = EINVAL;
		doperror(NULL, NULL);
		return(-1);
	}

	for (;;) {
		if (statementlist[id].type & STT_NEEDLIST) {
			if (!(body = statementcheck(trp, 0))) {
				doperror(NULL, statementlist[id].ident);
				return(-1);
			}
			if (check_stree(body)) return(-1);
		}
		if (!(trp = trp -> next) || !(trp -> comm)) break;
		prev = id + 1;
		if ((id = getstatid(trp)) < 0) i = SMPREV;
		else for (i = 0; i < SMPREV; i++) {
			if (!(statementlist[id].prev[i])) continue;
			if (prev == statementlist[id].prev[i]) break;
		}
		if (i >= SMPREV) {
			errno = EINVAL;
			doperror(NULL, statementlist[id].ident);
			return(-1);
		}
	}

	return(0);
}

static int NEAR check_command(trp)
syntaxtree *trp;
{
	command_t *comm;
	char **argv, **subst;
	int id, type, argc, *len;

	comm = trp -> comm;
	argc = comm -> argc;
	argv = comm -> argv;
	if (argc <= 0) return(0);

	comm -> argv = duplvar(comm -> argv, 0);
	comm -> argc = getsubst(comm -> argc, comm -> argv, &subst, &len);
	if ((id = evalargv(comm, &type, NULL)) >= 0 && type == CT_COMMAND)
		id = searchhash(&(comm -> hash), comm -> argv[0], NULL);
	freevar(subst);
	Xfree(len);
	comm -> argc = argc;
	freevar(comm -> argv);
	comm -> argv = argv;

	return((id >= 0) ? 0 : -1);
}

static int NEAR check_stree(trp)
syntaxtree *trp;
{
	syntaxtree *tmptr;

	if (!trp) {
		errno = EINVAL;
		doperror(NULL, NULL);
		return(-1);
	}
	if (!(trp -> comm)) return(0);
	if (trp -> flags & ST_NODE) {
		if (check_stree((syntaxtree *)(trp -> comm)) < 0) return(-1);
	}
	else if (isstatement(trp -> comm)) {
		if (check_statement(trp) < 0) return(-1);
	}
	else if (getstatid(tmptr = statementcheck(trp -> next, SM_STATEMENT))
	== SM_LPAREN - 1) {
#ifndef	BASHSTYLE
	/* bash allows any character in the function identifier */
		if (identcheck((trp -> comm) -> argv[0], '\0') <= 0) {
			execerror(NULL,
				(trp -> comm) -> argv[0], ER_NOTIDENT, 0);
			return(-1);
		}
#endif
		if (!statementcheck(tmptr -> next, 0)) {
			doperror(NULL, NULL);
			return(-1);
		}
		trp = trp -> next;
	}
	else if (check_command(trp) < 0) return(-1);

	return((trp -> next) ? check_stree(trp -> next) : 0);
}

static syntaxtree *NEAR analyzeline(command)
CONST char *command;
{
#ifndef	MINIMUMSHELL
	long dupshlineno;
#endif
	syntaxtree *trp, *stree;

#ifndef	MINIMUMSHELL
	dupshlineno = shlineno;
	setshlineno(1L);
#endif
	stree = newstree(NULL);
	trp = analyze(command, stree, 0);
	if (!(trp = analyzeeof(trp))) /*EMPTY*/;
#ifndef	_NOUSEHASH
	else if (hashahead && check_stree(stree) < 0) trp = NULL;
#endif
	if (!trp) {
		freestree(stree);
		Xfree(stree);
		stree = NULL;
	}
#ifndef	MINIMUMSHELL
	setshlineno(dupshlineno);
#endif

	return(stree);
}

#ifdef	DEBUG
static VOID NEAR Xexecve(path, argv, envp, bg)
CONST char *path;
char *argv[], *envp[];
int bg;
#else
static VOID NEAR Xexecve(path, argv, envp)
CONST char *path;
char *argv[], *envp[];
#endif
{
#ifdef	DEP_PTY
	char *cp;
	int len;
#endif
	int fd, ret;

#ifdef	DEP_PTY
	if (parentfd >= 0 && ptyterm && *ptyterm) {
		len = strsize(ENVTERM);
		cp = Xmalloc(len + strlen(ptyterm) + 2);
		memcpy(cp, ENVTERM, len);
		cp[len] = '=';
		Xstrcpy(&(cp[len + 1]), ptyterm);
		envp = putvar(envp, cp, len);
	}
#endif	/* DEP_PTY */

	execve(path, argv, envp);
	if (errno != ENOEXEC) {
		if (errno == EACCES) {
			execerror(NULL, argv[0], ER_CANNOTEXE, 1);
			ret = RET_NOTEXEC;
		}
		else {
			doperror(NULL, argv[0]);
			ret = RET_FAIL;
		}
	}
	else if ((fd = newdup(Kopen(path, O_TEXT | O_RDONLY, 0666))) < 0) {
		doperror(NULL, argv[0]);
		ret = RET_NOTEXEC;
	}
	else {
		argvar = argv;
		sourcefile(fd, argv[0], 0);
		safeclose(fd);
		ret = ret_status;
	}
#ifdef	DEBUG
	if (!bg) Xexit2(ret);
#endif
	prepareexit(1);

	Xexit(ret);
}

#if	MSDOS
static char *NEAR addext(path, ext)
char *path;
int ext;
{
	int len;

	len = strlen(path);
	path = Xrealloc(path, len + 1 + 3 + 1);
	path[len++] = '.';
	if (ext & CM_BATCH) Xstrcpy(&(path[len]), EXTBAT);
	else if (ext & CM_EXE) Xstrcpy(&(path[len]), EXTEXE);
	else Xstrcpy(&(path[len]), EXTCOM);

	return(path);
}

static char **NEAR replacebat(pathp, argv)
char **pathp, **argv;
{
	char *com;
	int i;

# ifdef	FD
	if (!(com = getenv2("FD_COMSPEC")) && !(com = getenv2("FD_SHELL")))
# else
	if (!(com = getconstvar(ENVCOMSPEC)) && !(com = getconstvar(ENVSHELL)))
# endif
# ifdef	BSPATHDELIM
		com = "\\COMMAND.COM";
# else
		com = "/COMMAND.COM";
# endif

	i = countvar(argv);
	memmove((char *)(&(argv[i + 2])), (char *)(&(argv[i])),
		(i + 1) * sizeof(char *));
	Xfree(argv[2]);
	argv[2] = Xstrdup(*pathp);
	argv[1] = Xstrdup("/C");
	argv[0] = *pathp = Xstrdup(com);

	return(argv);
}
#endif	/* MSDOS */

#ifdef	USEFAKEPIPE
static int NEAR openpipe(pidp, fdin, new)
p_id_t *pidp;
int fdin, new;
#else
static int NEAR openpipe(pidp, fdin, new, tty, ppid)
p_id_t *pidp;
int fdin, new, tty;
p_id_t ppid;
#endif
{
#ifndef	USEFAKEPIPE
	p_id_t pid;
	int fds[2];
#endif
	pipelist *pl;
	char pfile[MAXPATHLEN];
	int fd, dupl;

	pl = (pipelist *)Xmalloc(sizeof(pipelist));
	pl -> file = NULL;
	pl -> fp = NULL;
#ifndef	USEFAKEPIPE
	pl -> trp = NULL;
#endif
	pl -> fd = fdin;
	pl -> old = -1;

#ifndef	USEFAKEPIPE
	if (pipe(fds) < 0) {
# ifdef	FAKEUNINIT
		fd = -1;	/* fake for -Wuninitialized */
# endif
		pid = (p_id_t)-1;
	}
	else if ((pid = makechild(tty, ppid)) < (p_id_t)0) {
# ifdef	FAKEUNINIT
		fd = -1;	/* fake for -Wuninitialized */
# endif
		safeclose(fds[0]);
		safeclose(fds[1]);
	}
	else if (!pid) {
		if ((fd = newdup(Xdup(STDOUT_FILENO))) < 0
		|| fds[1] == STDOUT_FILENO
		|| Xdup2(fds[1], STDOUT_FILENO) < 0) {
			prepareexit(1);
			Xexit(RET_NOTEXEC);
		}
		safeclose(fds[0]);
		safeclose(fds[1]);
		pl -> old = fd;
	}
	else {
		if (new) fd = newdup(fds[0]);
		else {
			if ((fd = newdup(Xdup(fdin))) < 0
			|| fds[0] == fdin || Xdup2(fds[0], fdin) < 0) {
				safeclose(fd);
# ifndef	NOJOB
				if (stoppedjob(pid)) /*EMPTY*/;
				else
# endif
				{
					VOID_C kill(pid, SIGPIPE);
					while (!waitjob(pid, NULL, WUNTRACED))
						if (interrupted) break;
				}
				pid = (p_id_t)-1;
			}
			else {
# ifndef	NOJOB
				stackjob(pid, 0, fd, NULL);
# endif
				pl -> old = fd;
			}
			safeclose(fds[0]);
		}
		safeclose(fds[1]);
	}
	if (pid >= (p_id_t)0) {
		pl -> new = fd;
		pl -> pid = *pidp = pid;
		pl -> next = pipetop;
		pipetop = pl;
		return(fd);
	}
#endif	/* !USEFAKEPIPE */

	if ((dupl = newdup(Xdup(STDOUT_FILENO))) < 0) {
		Xfree(pl);
		return(-1);
	}
	if ((fd = newdup(mktmpfile(pfile))) < 0
	|| fd == STDOUT_FILENO || Xdup2(fd, STDOUT_FILENO) < 0) {
		Xfree(pl);
		safeclose(fd);
		safeclose(dupl);
		return(-1);
	}
	safeclose(fd);

	if (new) pl -> fd = -1;
	pl -> file = Xstrdup(pfile);
	pl -> new = pl -> old = dupl;
	pl -> pid = *pidp = (p_id_t)0;
	pl -> next = pipetop;
	pipetop = pl;

	return(dupl);
}

static pipelist **NEAR searchpipe(fd)
int fd;
{
	pipelist **prevp;

	if (fd < 0) return(NULL);
	prevp = &pipetop;
	while (*prevp) {
		if (fd == (*prevp) -> new) break;
		prevp = &((*prevp) -> next);
	}
	if (!*prevp) return(NULL);

	return(prevp);
}

static int NEAR reopenpipe(fd, ret)
int fd, ret;
{
	pipelist *pl, **prevp;
	int dupl;

	if (!(prevp = searchpipe(fd))) return(-1);
	pl = *prevp;

	if (pl -> pid > (p_id_t)0) return(fd);
	if (pl -> old >= 0) {
		Xdup2(pl -> old, STDOUT_FILENO);
		safeclose(pl -> old);
		pl -> old = -1;
	}
	if (!(pl -> file)) {
		prepareexit(1);
		Xexit(ret);
	}

	pl -> ret = ret;
	if ((fd = newdup(Xopen(pl -> file, O_BINARY | O_RDONLY, 0666))) < 0) {
		safermtmpfile(pl -> file);
		Xfree(pl -> file);
		*prevp = pl -> next;
		Xfree(pl);
		return(-1);
	}
	if (pl -> fd >= 0) {
		if ((dupl = newdup(Xdup(pl -> fd))) < 0
		|| fd == pl -> fd || Xdup2(fd, pl -> fd) < 0) {
			safeclose(fd);
			safermtmpfile(pl -> file);
			Xfree(pl -> file);
			*prevp = pl -> next;
			Xfree(pl);
			return(-1);
		}
		safeclose(fd);
		fd = pl -> old = dupl;
	}
	pl -> new = fd;

	return(fd);
}

static XFILE *NEAR fdopenpipe(fd)
int fd;
{
	pipelist *pl, **prevp;
	XFILE *fp;

	if (!(prevp = searchpipe(fd))) return(NULL);
	pl = *prevp;
	if (!(fp = Xfdopen(fd, "r"))) {
		safeclose(fd);
		safermtmpfile(pl -> file);
		Xfree(pl -> file);
		*prevp = pl -> next;
		Xfree(pl);
		return(NULL);
	}
	pl -> fp = fp;

	return(fp);
}

#ifdef	DJGPP
int closepipe(fd, dupl)
int fd, dupl;
#else
int closepipe(fd)
int fd;
#endif
{
#if	!MSDOS && defined (NOJOB)
	wait_pid_t w;
#endif
	pipelist *pl, **prevp;
	int ret, duperrno;

	if (!(prevp = searchpipe(fd))) return(-1);
	duperrno = errno;
	pl = *prevp;
#ifndef	USEFAKEPIPE
	freestree(pl -> trp);
	Xfree(pl -> trp);
#endif
#ifndef	NOJOB
	if (pl -> pid > (p_id_t)0 && stoppedjob(pl -> pid) > 0) {
		errno = duperrno;
		return(RET_SUCCESS);
	}
#endif

	if (pl -> old >= 0) {
		Xdup2(pl-> old, pl -> fd);
		safeclose(pl -> old);
	}
	if (fd != STDIN_FILENO &&
	fd != STDOUT_FILENO && fd != STDERR_FILENO) {
		if (pl -> fp) safefclose(pl -> fp);
		else safeclose(pl -> new);
	}

	ret = RET_SUCCESS;
	if (pl -> file) {
		if (rmtmpfile(pl -> file) < 0) {
#ifdef	DJGPP
			if (errno == EACCES && dupl >= 0 && Xclose(dupl) >= 0
			&& rmtmpfile(pl -> file) >= 0)
				/*EMPTY*/;
			else
#endif
			doperror(NULL, pl -> file);
		}
		Xfree(pl -> file);
		ret = pl -> ret;
	}
#if	!MSDOS
	else if (!(pl -> pid)) {
		prepareexit(1);
		Xexit(RET_SUCCESS);
	}
	else if (pl -> pid > (p_id_t)0) {
# ifdef	NOJOB
		VOID_C kill(pl -> pid, SIGPIPE);
		while (!(ret = waitjob(pl -> pid, &w, WUNTRACED)))
			if (interrupted) break;
		if (ret < 0) {
			if (errno == ECHILD) ret = errno = 0;
		}
		else if (WIFSTOPPED(w)) ret = -1;
		else if (WIFSIGNALED(w) && WTERMSIG(w) != SIGPIPE)
			ret = -1;
# else	/* !NOJOB */
		ret = waitchild(pl -> pid, NULL);
		if (ret == 128 + SIGPIPE) ret = RET_SUCCESS;
# endif	/* !NOJOB */
	}
#endif	/* !MSDOS */

	*prevp = pl -> next;
	Xfree(pl);
	errno = duperrno;

	return(ret);
}

#ifndef	_NOUSEHASH
static VOID NEAR disphash(VOID_A)
{
	hashlist *hp;
	char buf[7 + 1];
	int i, j;

	Xfputs("hits    cost    command", Xstdout);
	VOID_C fputnl(Xstdout);
	if (hashtable) for (i = 0; i < MAXHASH; i++)
		for (hp = hashtable[i]; hp; hp = hp -> next) {
			j = Xsnprintf(buf, sizeof(buf), "%d", hp -> hits);
			buf[j++] = (hp -> type & CM_RECALC) ? '*' : ' ';
			while (j < 7) buf[j++] = ' ';
			buf[j] = '\0';
			VOID_C Xprintf("%s %-7d %k\n",
				buf, hp -> cost, hp -> path);
		}
}
#endif	/* !_NOUSEHASH */

char *evalbackquote(arg)
CONST char *arg;
{
	XFILE *fp;
	char *buf;
	ALLOC_T len;
	int duptrapok;

	duptrapok = trapok;
	trapok = -1;
	fp = _dopopen(arg);
	trapok = duptrapok;
	if (!fp) {
		if (errno) doperror(NULL, NULL);
		buf = NULL;
		ret_status = RET_NOTEXEC;
	}
	else if (!(buf = readfile(Xfileno(fp), &len))) {
		doperror(NULL, NULL);
		ret_status = RET_FATALERR;
	}
	else {
#if	defined (BASHSTYLE) || defined (STRICTPOSIX)
	/* bash & POSIX ignore any following newlines */
		while (len > 0 && buf[--len] == '\n') buf[len] = '\0';
#else
		if (len > 0 && buf[--len] == '\n') buf[len] = '\0';
#endif
		ret_status = closepipe2(Xfileno(fp), -1);
	}

	return(buf);
}

#ifdef	NOALIAS
int checktype(s, idp, func)
CONST char *s;
int *idp, func;
#else
int checktype(s, idp, alias, func)
CONST char *s;
int *idp, alias, func;
#endif
{
	int i;

	if (!s || !*s) {
		if (idp) *idp = 0;
		return(CT_NONE);
	}
	else if (!strdelim(s, 1)) {
#ifndef	NOALIAS
		if (alias) {
			for (i = 0; shellalias[i].ident; i++)
			if (!strcommcmp(s, shellalias[i].ident)) {
				if (idp) *idp = i;
				return(CT_ALIAS);
			}
		}
#endif
#ifdef	STRICTPOSIX
		for (i = 0; i < SHBUILTINSIZ; i++) {
# ifndef	MINIMUMSHELL
			if (shbuiltinlist[i].flags & BT_DISABLE) continue;
# endif
			if (!(shbuiltinlist[i].flags & BT_POSIXSPECIAL))
				continue;
			if (!strcommcmp(s, shbuiltinlist[i].ident)) {
				if (idp) *idp = i;
				return(CT_BUILTIN);
			}
		}
#endif
		if (func) {
			for (i = 0; shellfunc[i].ident; i++)
			if (!strcommcmp(s, shellfunc[i].ident)) {
				if (idp) *idp = i;
				return(CT_FUNCTION);
			}
		}
#if	MSDOS
		if (_dospath(s) && (!s[2] || (s[2] == _SC_ && !s[3]))) {
			if (idp) *idp = *s;
			return(CT_LOGDRIVE);
		}
#endif
		for (i = 0; i < SHBUILTINSIZ; i++) {
#ifndef	MINIMUMSHELL
			if (shbuiltinlist[i].flags & BT_DISABLE) continue;
#endif
#ifdef	STRICTPOSIX
			if (shbuiltinlist[i].flags & BT_POSIXSPECIAL)
				continue;
#endif
			if (!strcommcmp(s, shbuiltinlist[i].ident)) {
				if (idp) *idp = i;
				return(CT_BUILTIN);
			}
		}
#ifdef	FD
		if ((i = checkbuiltin(s)) >= 0) {
			if (idp) *idp = i;
			return(CT_FDORIGINAL);
		}
		if (!shellmode && (i = checkinternal(s)) >= 0) {
			if (idp) *idp = i;
			return(CT_FDINTERNAL);
		}
#endif
	}
	if (idp) *idp = 0;

	return(CT_COMMAND);
}

#if	!defined (FDSH) && !defined (_NOCOMPLETE)
int completeshellvar(s, len, argc, argvp)
CONST char *s;
int len, argc;
char ***argvp;
{
	char *cp;
	int i;

	if (shellvar) for (i = 0; shellvar[i]; i++) {
		if (strnenvcmp(s, shellvar[i], len)
		|| !(cp = Xstrchr(shellvar[i], '=')))
			continue;
		argc = addcompletion(NULL,
			Xstrndup(shellvar[i], cp - shellvar[i]), argc, argvp);
	}

	return(argc);
}

int completeshellcomm(s, len, argc, argvp)
CONST char *s;
int len, argc;
char ***argvp;
{
	int i;

# ifndef	NOALIAS
	for (i = 0; shellalias[i].ident; i++) {
		if (strncommcmp(s, shellalias[i].ident, len)) continue;
		argc = addcompletion(shellalias[i].ident, NULL, argc, argvp);
	}
# endif	/* !NOALIAS */
	for (i = 0; shellfunc[i].ident; i++) {
		if (strncommcmp(s, shellfunc[i].ident, len)) continue;
		argc = addcompletion(shellfunc[i].ident, NULL, argc, argvp);
	}
	for (i = 0; i < SHBUILTINSIZ; i++) {
# ifndef	MINIMUMSHELL
		if (shbuiltinlist[i].flags & BT_DISABLE) continue;
# endif
		if (strncommcmp(s, shbuiltinlist[i].ident, len)) continue;
		argc = addcompletion(shbuiltinlist[i].ident,
			NULL, argc, argvp);
	}
# ifdef	FD
	argc = completebuiltin(s, len, argc, argvp);
	if (!shellmode) argc = completeinternal(s, len, argc, argvp);
# endif
	for (i = 0; i < STATEMENTSIZ; i++) {
		if (strncommcmp(s, statementlist[i].ident, len)) continue;
		argc = addcompletion(statementlist[i].ident,
			NULL, argc, argvp);
	}

	return(argc);
}
#endif	/* !FDSH && !_NOCOMPLETE */

#ifdef	BASHSTYLE
static char *NEAR quotemeta(s)
char *s;
{
	char *cp, *buf;
	ALLOC_T ptr, len;

	cp = Xstrchr(s, '=');
	if (!cp || !strpbrk(++cp, METACHAR)) return(s);
	len = strlen(s) + 2;
	buf = Xmalloc(len + 1);
	ptr = cp - s;
	memcpy(buf, s, ptr);
	buf[ptr++] = '\'';
	while (*cp) {
		if (*cp != '\'') buf[ptr++] = *(cp++);
		else {
			len += 3;
			buf = Xrealloc(buf, len + 1);
			buf[ptr++] = '\'';
			buf[ptr++] = PESCAPE;
			buf[ptr++] = *(cp++);
			buf[ptr++] = '\'';
		}
	}
	buf[ptr++] = '\'';
	buf[ptr] = '\0';
	Xfree(s);

	return(buf);
}
#endif	/* BASHSTYLE */

int getsubst(argc, argv, substp, lenp)
int argc;
char **argv, ***substp;
int **lenp;
{
	int i, n, len;

	*substp = (char **)Xmalloc((argc + 1) * sizeof(char *));
	*lenp = (int *)Xmalloc(argc * sizeof(int));

	len = 1;
	for (i = n = 0; i < argc; i++) {
		len = (freeenviron || len > 0) ? identcheck(argv[i], '=') : 0;
		if (len > 0) {
			(*substp)[n] = argv[i];
			(*lenp)[n] = len;
			memmove((char *)(&(argv[i])),
				(char *)(&(argv[i + 1])),
				(argc-- - i) * sizeof(char *));
			n++;
			i--;
		}
	}
	(*substp)[n] = NULL;

	return(argc);
}

static int NEAR substvar(argv, flags)
char **argv;
int flags;
{
	char *tmp, *arg;
	int i;

	for (i = 0; argv[i]; i++) {
		if (trapok >= 0) trapok = 1;
		tmp = evalarg(argv[i], flags);
		if (trapok >= 0) trapok = 0;
		if (!tmp) {
			arg = argv[i];
			if (i && *arg && (flags & EA_EVALIFS)) {
				while (argv[i]) i++;
				argv = duplvar(argv, 2);
				argv = checkshellbuiltinargv(i, argv);
				freevar(argv);
			}
			execerror(NULL, arg, ER_BADSUBST, 0);
			return(-1);
		}
		Xfree(argv[i]);
		argv[i] = tmp;
	}

	return(i);
}

static int NEAR evalargv(comm, typep, contp)
command_t *comm;
int *typep, *contp;
{
	char *tmp;
	int i, id, glob;

	if (!comm) return(-1);
	else if (comm -> type) {
		*typep = comm -> type;
		return(comm -> id);
	}

	if (contp && substvar(comm -> argv, EA_BACKQ | EA_EVALIFS) < 0)
		return(-1);

	if (trapok >= 0) trapok = 1;
#ifdef	BASHSTYLE
	/* bash does not use IFS as a command separator */
	comm -> argc = evalifs(comm -> argc, &(comm -> argv), IFS_SET);
#else
	comm -> argc = evalifs(comm -> argc, &(comm -> argv), getifs());
#endif
	if (!(comm -> argc)) {
		*typep = CT_NONE;
		id = 0;
	}
	else {
		stripquote(tmp = Xstrdup(comm -> argv[0]), EA_STRIPQ);
		*typep = checktype2(tmp, &id, 0, 1);
		Xfree(tmp);
	}

#ifdef	FD
	if (contp) {
		i = replaceargs(&(comm -> argc), &(comm -> argv),
			exportvar, (*typep == CT_COMMAND) ? 1 : 0);
		if (i < 0) return(-1);
		*contp = i;
	}
#endif

	if (noglob) glob = 0;
#ifdef	FD
	else if (*typep == CT_FDINTERNAL) glob = 0;
#endif
	else if (*typep != CT_BUILTIN) glob = 1;
	else if (shbuiltinlist[id].flags & BT_NOGLOB) glob = 0;
	else glob = 1;

	if (glob) {
#ifdef	DEP_FILECONV
		if (*typep == CT_FDORIGINAL) i = 0;
		else if (*typep != CT_BUILTIN) i = 1;
		else if (shbuiltinlist[id].flags & BT_NOKANJIFGET) i = 1;
		else i = 0;
		if (i) nokanjifconv++;
#endif
#if	MSDOS
		comm -> argc = evalglob(comm -> argc, &(comm -> argv),
			(*typep == CT_COMMAND) ? 0 : EA_STRIPQ);
#else
		comm -> argc = evalglob(comm -> argc, &(comm -> argv),
			EA_STRIPQ);
#endif
#ifdef	DEP_FILECONV
		if (i) nokanjifconv--;
#endif
	}
	else {
		i = 0;
#if	MSDOS
		stripquote(comm -> argv[i++], EA_STRIPQ);
		if (*typep == CT_COMMAND) while (i < comm -> argc)
			stripquote(comm -> argv[i++], 0);
		else
#endif
		while (i < comm -> argc)
			stripquote(comm -> argv[i++], EA_STRIPQ);
	}
	if (trapok >= 0) trapok = 0;

	return(id);
}

static char *NEAR evalexternal(comm)
command_t *comm;
{
	char *path;
	int type;

	type = searchhash(&(comm -> hash), comm -> argv[0], NULL);
	if (type & CM_NOTFOUND) {
		execerror(NULL, comm -> argv[0], ER_COMNOFOUND, 1);
		ret_status = RET_NOTFOUND;
		return(NULL);
	}
	if (restricted && (type & CM_FULLPATH)) {
		execerror(NULL, comm -> argv[0], ER_RESTRICTED, 2);
		ret_status = RET_FAIL;
		return(NULL);
	}
	if (type & CM_FULLPATH) {
#if	MSDOS
		if (type & CM_ADDEXT)
			path = comm -> argv[0] = addext(comm -> argv[0], type);
		else
#endif
		path = comm -> argv[0];
	}
	else {
#ifdef	_NOUSEHASH
		path = (char *)(comm -> hash);
#else
		((comm -> hash) -> hits)++;
		path = (comm -> hash) -> path;
#endif
	}
#if	MSDOS
	if (type & CM_BATCH)
		comm -> argv = replacebat(&path, comm -> argv);
#endif

	return(path);
}

static VOID NEAR printindent(n, fp)
int n;
XFILE *fp;
{
	while (n-- > 0) Xfputs("  ", fp);
}

static VOID NEAR printnewline(n, fp)
int n;
XFILE *fp;
{
	if (n < 0) Xfputc(' ', fp);
	else {
		VOID_C fputnl(fp);
		printindent(n, fp);
	}
}

static int NEAR printredirect(rp, fp)
redirectlist *rp;
XFILE *fp;
{
	heredoc_t *hdp;
	int ret;

	if (!rp) return(0);
	ret = printredirect(rp -> next, fp);

	Xfputc(' ', fp);
	switch (rp -> type & MD_RDWR) {
		case MD_READ:
			if (rp -> fd != STDIN_FILENO)
				VOID_C Xfprintf(fp, "%d", rp -> fd);
			Xfputc('<', fp);
			if (rp -> type & MD_HEREDOC) {
				Xfputc('<', fp);
				if (rp -> type & MD_APPEND) Xfputc('-', fp);
			}
			break;
		case MD_WRITE:
			if (rp -> fd != STDOUT_FILENO)
				VOID_C Xfprintf(fp, "%d", rp -> fd);
			if (rp -> type & MD_WITHERR) Xfputc('&', fp);
			Xfputc('>', fp);
			if (rp -> type & MD_APPEND) Xfputc('>', fp);
#ifndef	MINIMUMSHELL
			else if (rp -> type & MD_FORCED) Xfputc('|', fp);
#endif
			break;
		case MD_RDWR:
			if (rp -> fd != STDOUT_FILENO)
				VOID_C Xfprintf(fp, "%d", rp -> fd);
			Xfputs("<>", fp);
			break;
		default:
			break;
	}
	if (!(rp -> filename)) Xfputc('-', fp);
	else {
		if (rp -> type & MD_FILEDESC) Xfputc('&', fp);
		else Xfputc(' ', fp);
		if (!(rp -> type & MD_HEREDOC)) argfputs(rp -> filename, fp);
		else {
			hdp = (heredoc_t *)(rp -> filename);
#ifdef	MINIMUMSHELL
			argfputs(hdp -> filename, fp);
#else
			argfputs(hdp -> eof, fp);
#endif
			ret = 1;
		}
	}

	return(ret);
}

VOID printstree(trp, indent, fp)
syntaxtree *trp;
int indent;
XFILE *fp;
#ifndef	MINIMUMSHELL
{
	redirectlist **rlist;
	int i;

	rlist = _printstree(trp, NULL, indent, fp);
	if (rlist) {
		for (i = 0; rlist[i]; i++) printheredoc(rlist[i], fp);
		Xfree(rlist);
	}
}

static VOID NEAR printheredoc(rp, fp)
redirectlist *rp;
XFILE *fp;
{
	heredoc_t *hdp;
	char *buf;
	int fd;

	if (!rp) return;
	printheredoc(rp -> next, fp);
	if (!(rp -> type & MD_HEREDOC)) return;

	hdp = (heredoc_t *)(rp -> filename);
	fd = newdup(Xopen(hdp -> filename, O_BINARY | O_RDONLY, 0666));
	if (fd >= 0) {
		VOID_C fputnl(Xstdout);
		while ((buf = readline(fd, '\0')) != vnullstr) {
			if (!buf) break;
			Xfputs(buf, Xstdout);
			VOID_C fputnl(Xstdout);
			Xfree(buf);
		}
		safeclose(fd);
		argfputs(hdp -> eof, fp);
	}
}

static redirectlist **NEAR _printstree(trp, rlist, indent, fp)
syntaxtree *trp;
redirectlist **rlist;
int indent;
XFILE *fp;
#endif	/* !MINIMUMSHELL */
{
#ifndef	MINIMUMSHELL
	redirectlist **rlist2;
	int nl;
#endif
	syntaxtree *tmptr;
	int i, j, id, prev, ind2;

#ifdef	MINIMUMSHELL
	if (!trp) return;
#else
	if (!trp) return(rlist);
#endif
	prev = getstatid(tmptr = parentstree(trp)) + 1;

#ifndef	MINIMUMSHELL
	if (isopnot(trp)) {
		Xfputc('!', fp);
		return(_printstree(trp -> next, rlist, indent, fp));
	}
	rlist2 = NULL;
#endif

	if (trp -> flags & ST_NODE)
#ifdef	MINIMUMSHELL
		printstree((syntaxtree *)(trp -> comm), indent, fp);
#else
		rlist2 = _printstree((syntaxtree *)(trp -> comm),
			NULL, indent, fp);
#endif
	else if (!(trp -> comm)) {
		if (tmptr && !(tmptr -> next) && prev > 0) {
			for (i = 0; i < STATEMENTSIZ; i++) {
				if (!(statementlist[i].type & STT_NEEDNONE))
					continue;
				for (j = 0; j < SMPREV; j++) {
					if (!(statementlist[i].prev[j]))
						continue;
					if (prev == statementlist[i].prev[j])
						break;
				}
				if (j < SMPREV) break;
			}
			if (i < STATEMENTSIZ) {
				printnewline(indent - 1, fp);
				Xfputs(statementlist[i].ident, fp);
			}
		}
	}
	else {
		if (isstatement(trp -> comm)) {
			id = (trp -> comm) -> id;
			i = id - 1;
			if (id == SM_CHILD) {
				Xfputs("( ", fp);
#ifdef	MINIMUMSHELL
				printstree(statementbody(trp), indent, fp);
#else
				rlist2 = _printstree(statementbody(trp),
					NULL, indent, fp);
#endif
				Xfputs(" )", fp);
			}
			else if (id == SM_STATEMENT)
#ifdef	MINIMUMSHELL
				printstree(statementbody(trp), indent, fp);
#else
				rlist2 = _printstree(statementbody(trp),
					NULL, indent, fp);
#endif
			else {
				Xfputs(statementlist[i].ident, fp);
				switch (id) {
					case SM_THEN:
					case SM_ELSE:
					case SM_DO:
					case SM_RPAREN:
					case SM_LIST:
						ind2 = 1;
						break;
					case SM_INCASE:
						if (indent >= 0) indent++;
						ind2 = 0;
						break;
					case SM_CASEEND:
						if (trp -> next) ind2 = 0;
						else ind2 = -1;
						break;
					case SM_FUNC:
#ifdef	BASHSTYLE
	/* bash type pretty print */
						ind2 = 0;
						break;
#endif
					case SM_LPAREN:
						ind2 = -1;
						break;
					default:
						ind2 = -1;
						Xfputc(' ', fp);
						break;
				}

				tmptr = statementbody(trp);
				if (ind2 < 0) ind2 = indent;
				else {
					if (indent < 0) ind2 = indent;
					else ind2 += indent;
					if (tmptr && tmptr -> comm)
						printnewline(ind2, fp);
				}
#ifdef	MINIMUMSHELL
				printstree(tmptr, ind2, fp);
#else
				rlist2 = _printstree(tmptr, NULL, ind2, fp);
#endif

				switch (id) {
					case SM_IF:
					case SM_ELIF:
					case SM_WHILE:
					case SM_UNTIL:
					case SM_IN:
#ifdef	BASHSTYLE
	/* bash type pretty print */
						Xfputc(' ', fp);
						break;
#endif
					case SM_THEN:
					case SM_RPAREN:
						if (!(trp -> next)) break;
						printnewline(indent, fp);
						break;
					case SM_FOR:
					case SM_CASE:
						Xfputc(' ', fp);
						break;
					default:
						break;
				}
			}
		}
		else if ((trp -> comm) -> argc > 0) {
			argfputs((trp -> comm) -> argv[0], fp);
			for (i = 1; i < (trp -> comm) -> argc; i++) {
				Xfputc(' ', fp);
				if (prev == SM_INCASE || prev == SM_CASEEND)
					Xfputs("| ", fp);
				argfputs((trp -> comm) -> argv[i], fp);
			}
		}

#ifdef	MINIMUMSHELL
		printredirect((trp -> comm) -> redp, fp);
#else
		if (printredirect((trp -> comm) -> redp, fp)) {
			if (!rlist) i = 0;
			else for (i = 0; rlist[i]; i++) /*EMPTY*/;
			rlist = (redirectlist **)Xrealloc(rlist,
				(i + 2) * sizeof(redirectlist *));
			rlist[i++] = (trp -> comm) -> redp;
			rlist[i] = NULL;
		}
#endif
	}

#ifndef	MINIMUMSHELL
	if (rlist2) {
		if (!rlist) i = 0;
		else for (i = 0; rlist[i]; i++) /*EMPTY*/;
		for (j = 0; rlist2[j]; j++) /*EMPTY*/;
		rlist = (redirectlist **)Xrealloc(rlist,
			(i + j + 1) * sizeof(redirectlist *));
		for (j = 0; rlist2[j]; j++) rlist[i + j] = rlist2[j];
		rlist[i + j] = NULL;
		Xfree(rlist2);
	}
#endif

	for (id = 0; id < OPELISTSIZ; id++)
		if (trp -> type == opelist[id].op) break;
	if (id < OPELISTSIZ && !isopfg(trp))
		VOID_C Xfprintf(fp, " %s", opelist[id].symbol);
#ifndef	MINIMUMSHELL
	if (!rlist || opelist[id].level < 4) nl = 0;
	else {
		for (i = 0; rlist[i]; i++) printheredoc(rlist[i], fp);
		Xfree(rlist);
		rlist = NULL;
		nl = 1;
	}
#endif

	if (trp -> next && (trp -> next) -> comm) {
#ifndef	MINIMUMSHELL
		if (nl) {
			VOID_C fputnl(fp);
			printindent(indent, fp);
		}
		else
#endif
		if (isopfg(trp)) {
#ifdef	BASHSTYLE
	/* bash type pretty print */
			Xfputc(';', fp);
			printnewline(indent, fp);
#else
			if (indent < 0) Xfputc(';', fp);
			else {
				VOID_C fputnl(fp);
				printindent(indent, fp);
			}
#endif
		}
		else if (id < OPELISTSIZ) {
#ifndef	BASHSTYLE
	/* bash type pretty print */
			if (opelist[id].level >= 4) printnewline(indent, fp);
			else
#endif
			Xfputc(' ', fp);
		}
	}
#ifndef	MINIMUMSHELL
	else if (nl) /*EMPTY*/;
#endif
	else if (isopfg(trp)) {
#ifdef	BASHSTYLE
	/* bash type pretty print */
		Xfputc(';', fp);
#else
		if (indent < 0) Xfputc(';', fp);
#endif
	}

#ifdef	MINIMUMSHELL
	if (trp -> next) printstree(trp -> next, indent, fp);
	Xfflush(fp);
#else
	if (trp -> next) rlist = _printstree(trp -> next, rlist, indent, fp);
	Xfflush(fp);

	return(rlist);
#endif
}

static VOID NEAR printshfunc(f, fp)
shfunctable *f;
XFILE *fp;
{
#ifdef	BASHSTYLE
	/* bash type pretty print */
	VOID_C Xfprintf(fp, "%k ()\n", f -> ident);
#else
	VOID_C Xfprintf(fp, "%k()", f -> ident);
#endif
	if (getstatid(statementcheck(f -> func, SM_STATEMENT)) == SM_LIST - 1)
		printstree(f -> func, 0, fp);
	else {
		Xfputs("{\n", fp);
		if (f -> func) {
			printindent(1, fp);
			printstree(f -> func, 1, fp);
		}
		Xfputs("\n}", fp);
	}
}

int tinygetopt(trp, opt, nump)
syntaxtree *trp;
CONST char *opt;
int *nump;
{
	char **argv;
	int i, n, f, argc, noopt;

	argv = (trp -> comm) -> argv;
	argc = (trp -> comm) -> argc;
	f = '\0';
	noopt = 0;
	if (*opt == '-') {
		opt++;
		noopt++;
	}

	for (n = 1; n < argc; n++) {
		if (!argv[n] || argv[n][0] != '-') break;
		if (argv[n][1] == '-' && !(argv[n][2])) {
			n++;
			break;
		}

		for (i = 1; argv[n][i]; i++) {
			if (Xstrchr(opt, argv[n][i])) /*EMPTY*/;
			else if (noopt) break;
			else {
				execerror(argv, argv[n], ER_BADOPTIONS, 2);
				return(-1);
			}
			f = argv[n][i];
		}
		if (!f) break;
	}
	if (nump) *nump = n;

	return(f);
}

static int NEAR dochild(trp)
syntaxtree *trp;
{
#if	MSDOS
# ifdef	DEP_PSEUDOPATH
	int drv;
# endif
	char **svar, **evar, **elist, **rlist, cwd[MAXPATHLEN];
	u_long esize;
	int blevel, clevel;
#else	/* !MSDOS */
	syntaxtree *body;
	p_id_t pid;
#endif	/* !MSDOS */
	int ret;

#if	MSDOS
	if (!Xgetwd(cwd)) return(RET_FAIL);
# ifdef	DEP_PSEUDOPATH
	if ((drv = preparedrv(cwd, NULL, NULL)) < 0) {
		doperror(NULL, cwd);
		return(RET_FAIL);
	}
# endif
	svar = shellvar;
	evar = exportvar;
	esize = exportsize;
	elist = exportlist;
	rlist = ronlylist;
	blevel = breaklevel;
	clevel = continuelevel;
	shellvar = duplvar(shellvar, 0);
	exportvar = duplvar(exportvar, 0);
	exportlist = duplvar(exportlist, 0);
	ronlylist = duplvar(ronlylist, 0);
	breaklevel = continuelevel = 0;
	childdepth++;
	ret = exec_stree(statementbody(trp), 0);
	childdepth--;
	freevar(shellvar);
	freevar(exportvar);
	freevar(exportlist);
	freevar(ronlylist);
	shellvar = svar;
	exportvar = evar;
	exportsize = esize;
	exportlist = elist;
	ronlylist = rlist;
	breaklevel = blevel;
	continuelevel = clevel;
	if (chdir3(cwd, 1) < 0) ret = RET_FAIL;
# ifdef	DEP_PSEUDOPATH
	shutdrv(drv);
# endif

	return(ret);
#else	/* !MSDOS */
	body = statementbody(trp);
	if ((pid = makechild(1, (p_id_t)-1)) < (p_id_t)0) return(-1);
	else if (!pid) {
		childdepth++;
# ifndef	NOJOB
		stackjob(mypid, 0, -1, body);
# endif
		ret = exec_stree(body, 0);
		prepareexit(1);
		Xexit((ret >= 0) ? ret : RET_NOTEXEC);
	}
	else {
# ifndef	NOJOB
		stackjob(pid, 0, -1, body);
# endif
		if (isopbg(trp) || isopnown(trp)) return(RET_SUCCESS);
		ret = waitchild(pid, trp);
		if (ret == RET_NOTEXEC) ret = -1;
		if (ret < 0) breaklevel = loopdepth;
	}

	return(ret);
#endif	/* !MSDOS */
}

static int NEAR doif(trp)
syntaxtree *trp;
{
	syntaxtree *cond, *body;
	int ret;

	cond = statementbody(trp);
	if (!(body = statementcheck(trp -> next, SM_THEN))) return(-1);

	if ((ret = exec_stree(cond, 1)) < 0 || returnlevel) return(ret);
	if (interrupted) return(RET_INTR);
	if (ret_status == RET_SUCCESS) return(exec_stree(body, 0));
	if (!(trp = (trp -> next) -> next)) return(RET_SUCCESS);

	body = statementbody(trp);
	if (interrupted) return(RET_INTR);
	if ((trp -> comm) -> id == SM_ELSE) return(exec_stree(body, 0));
	if ((trp -> comm) -> id == SM_ELIF) return(doif(trp));

	return(seterrno(EINVAL));
}

static int NEAR dowhile(trp)
syntaxtree *trp;
{
	syntaxtree *cond, *body;
	int ret, tmp;

	cond = statementbody(trp);
	if (!(body = statementcheck(trp -> next, SM_DO))) return(-1);

	ret = RET_SUCCESS;
	loopdepth++;
	for (;;) {
		if ((tmp = exec_stree(cond, 1)) < 0 || returnlevel) {
			ret = tmp;
			break;
		}
		if (ret_status != RET_SUCCESS) break;
		if (interrupted) {
			ret = RET_INTR;
			break;
		}
		if ((ret = exec_stree(body, 0)) < 0 || returnlevel) break;
		if (interrupted) {
			ret = RET_INTR;
			break;
		}
		if (breaklevel) {
			breaklevel--;
			break;
		}
		if (continuelevel && --continuelevel) break;
	}
	loopdepth--;

	return(ret);
}

static int NEAR dountil(trp)
syntaxtree *trp;
{
	syntaxtree *cond, *body;
	int ret, tmp;

	cond = statementbody(trp);
	if (!(body = statementcheck(trp -> next, SM_DO))) return(-1);

	ret = RET_SUCCESS;
	loopdepth++;
	for (;;) {
		if ((tmp = exec_stree(cond, 1)) < 0 || returnlevel) {
			ret = tmp;
			break;
		}
#ifndef	BASHSTYLE
	/* bash ignores "break" in the "until" condition */
		if (breaklevel) {
			breaklevel--;
			break;
		}
#endif
		if (ret_status == RET_SUCCESS) break;
		if (interrupted) {
			ret = RET_INTR;
			break;
		}
		if ((ret = exec_stree(body, 0)) < 0 || returnlevel) break;
		if (interrupted) {
			ret = RET_INTR;
			break;
		}
#ifdef	BASHSTYLE
	/* bash ignores "break" in the "until" condition */
		if (breaklevel) {
			breaklevel--;
			break;
		}
#endif
		if (continuelevel && --continuelevel) break;
	}
	loopdepth--;

	return(ret);
}

static int NEAR dofor(trp)
syntaxtree *trp;
{
	syntaxtree *var, *body;
	command_t *comm;
	char *tmp, *ident, **argv;
	int i, argc, ret;

	var = statementbody(trp);
	body = statementbody(trp -> next);
	comm = var -> comm;

	ident = comm -> argv[0];
	if (identcheck(ident, '\0') <= 0) {
		execerror(NULL, ident, ER_NOTIDENT, 0);
		return(RET_FAIL);
	}
	trp = trp -> next;
	if ((trp -> comm) -> id != SM_IN)
		argv = duplvar(&(argvar[1]), 0);
	else {
		if (!(comm = body -> comm)) return(seterrno(EINVAL));
		trp = trp -> next;

		argv = (char **)Xmalloc((comm -> argc + 1) * sizeof(char *));
		for (i = 0; i < comm -> argc; i++) {
			tmp = evalvararg(comm -> argv[i], EA_BACKQ, 0);
			if (!tmp) {
				while (--i >= 0) Xfree(argv[i]);
				Xfree(argv);
				return(RET_FAIL);
			}
			argv[i] = tmp;
		}
		argv[i] = NULL;
		argc = evalifs(comm -> argc, &argv, getifs());
		if (!noglob) {
#ifdef	DEP_FILECONV
			nokanjifconv++;
#endif
			argc = evalglob(argc, &argv, EA_STRIPQ);
#ifdef	DEP_FILECONV
			nokanjifconv--;
#endif
		}
		else for (i = 0; i < argc; i++) stripquote(argv[i], EA_STRIPQ);
#ifdef	FD
		if (replaceargs(&argc, &argv, NULL, -1) < 0) {
			freevar(argv);
			return(-1);
		}
#endif
	}

	if (!(body = statementcheck(trp, SM_DO))) {
		freevar(argv);
		return(-1);
	}

	ret = RET_SUCCESS;
	loopdepth++;
	for (i = 0; argv[i]; i++) {
		if (interrupted) {
			ret = RET_INTR;
			break;
		}
		if (setenv2(ident, argv[i], 0) < 0) break;
		if ((ret = exec_stree(body, 0)) < 0 || returnlevel) break;
		if (interrupted) {
			ret = RET_INTR;
			break;
		}
		if (breaklevel) {
			breaklevel--;
			break;
		}
		if (continuelevel && --continuelevel) break;
	}
	loopdepth--;
	freevar(argv);

	return(ret);
}

static int NEAR docase(trp)
syntaxtree *trp;
{
	syntaxtree *var, *body;
	command_t *comm;
	reg_ex_t *re;
	char *tmp, *key;
	int i, n, ret;

	var = statementbody(trp);
	comm = var -> comm;
	key = evalvararg(comm -> argv[0], EA_BACKQ | EA_STRIPQLATER, 0);
	if (!key) return(RET_FAIL);
#ifdef	FD
	demacroarg(&key);
#endif

	ret = RET_SUCCESS;
	for (trp = trp -> next; trp; trp = (trp -> next) -> next) {
		if (!(trp -> next)) break;
		if (interrupted) {
			ret = RET_INTR;
			break;
		}
		var = statementbody(trp);
		if (!(body = statementcheck(trp -> next, SM_RPAREN))) {
			ret = -1;
			break;
		}
		if (!(comm = var -> comm)) break;
		ret = -1;
		for (i = 0; i < comm -> argc; i++) {
			tmp = evalvararg(comm -> argv[i], EA_BACKQ, 0);
			if (!tmp) {
				ret = RET_FAIL;
				break;
			}
#ifdef	FD
			demacroarg(&tmp);
#endif
			re = regexp_init(tmp, -1);
			Xfree(tmp);
			if (!re) continue;
			n = regexp_exec(re, key, 0);
			regexp_free(re);
			if (n) {
				ret = exec_stree(body, 0);
				break;
			}
			if (interrupted) {
				ret = RET_INTR;
				break;
			}
		}
		if (ret >= 0) break;
		ret = RET_SUCCESS;
	}
	Xfree(key);

	return(ret);
}

static int NEAR dolist(trp)
syntaxtree *trp;
{
	return(exec_stree(statementbody(trp), 0));
}

/*ARGSUSED*/
static int NEAR donull(trp)
syntaxtree *trp;
{
	return(RET_SUCCESS);
}

static int NEAR dobreak(trp)
syntaxtree *trp;
{
	int n;

	if (!loopdepth) return(RET_FAIL);
	else if ((trp -> comm) -> argc <= 1) breaklevel = 1;
	else if ((n = isnumeric((trp -> comm) -> argv[1])) < 0) {
		execerror((trp -> comm) -> argv,
			(trp -> comm) -> argv[1], ER_BADNUMBER, 0);
		return(RET_FAIL);
	}
#ifndef	BASHSTYLE
	/* bash ignores "break 0" */
	else if (!n) breaklevel = -1;
#endif
	else if (n > loopdepth) breaklevel = loopdepth;
	else breaklevel = n;

	return(RET_SUCCESS);
}

static int NEAR docontinue(trp)
syntaxtree *trp;
{
	int n;

	if (!loopdepth) return(RET_FAIL);
	else if ((trp -> comm) -> argc <= 1) continuelevel = 1;
	else if ((n = isnumeric((trp -> comm) -> argv[1])) < 0) {
		execerror((trp -> comm) -> argv,
			(trp -> comm) -> argv[1], ER_BADNUMBER, 0);
		return(RET_FAIL);
	}
#ifndef	BASHSTYLE
	/* bash ignores "break 0" */
	else if (!n) continuelevel = -1;
#endif
	else if (n > loopdepth) continuelevel = loopdepth;
	else continuelevel = n;

	return(RET_SUCCESS);
}

static int NEAR doreturn(trp)
syntaxtree *trp;
{
	int ret;

	if (!shfunclevel) {
		execerror((trp -> comm) -> argv, NULL, ER_CANNOTRET, 0);
		return(RET_FAIL);
	}
	if ((trp -> comm) -> argc <= 1) ret = ret_status;
	else if ((ret = isnumeric((trp -> comm) -> argv[1])) < 0) {
		execerror((trp -> comm) -> argv,
			(trp -> comm) -> argv[1], ER_BADNUMBER, 0);
		ret = RET_FAIL;
#ifdef	BASHSTYLE
	/* bash ignores "return -1" */
		return(ret);
#endif
	}
	returnlevel = shfunclevel;

	return(ret);
}

static int NEAR execpath(comm, errexit)
command_t *comm;
int errexit;
{
	char *path, **evar;

	path = evalexternal(comm);
	if (!errexit && !path) return(RET_NOTFOUND);
	path = Xstrdup(path);
	evar = exportvar;
	exportvar = NULL;

	if (errexit && !path) {
#ifdef	DEBUG
		freevar(evar);
#endif
		Xexit2(RET_FAIL);
	}

#ifndef	NOJOB
	if (loginshell && interactive_io) killjob();
#endif
	prepareexit(1);
#ifdef	FD
	prepareexitfd(0);
#endif
	Xexecve2(path, comm -> argv, evar, 0);

	return(RET_NOTEXEC);
}

static int NEAR doexec(trp)
syntaxtree *trp;
{
	command_t *comm;

	comm = trp -> comm;
	if (cancelredirect(comm -> redp) < 0) return(RET_FAIL);
	if (comm -> argc >= 2) {
		if (restricted) {
			execerror(comm -> argv,
				comm -> argv[1], ER_RESTRICTED, 2);
			return(RET_FAIL);
		}
		Xfree(comm -> argv[0]);
		memmove((char *)(&(comm -> argv[0])),
			(char *)(&(comm -> argv[1])),
			(comm -> argc)-- * sizeof(char *));
		searchheredoc(trp, 1);
#ifdef	BASHSTYLE
	/* bash ignores the unexecutable external command */
		return(execpath(comm, 0));
#else
		return(execpath(comm, 1));
#endif
	}

	if (!isopbg(trp) && !isopnown(trp) && comm -> redp) {
		if (ttyio < 0) /*EMPTY*/;
		else if (definput == ttyio && !isatty(STDIN_FILENO))
			definput = STDIN_FILENO;
		else if (definput == STDIN_FILENO && isatty(STDIN_FILENO))
			definput = ttyio;
	}

	return(RET_SUCCESS);
}

#ifndef	MINIMUMSHELL
/*ARGSUSED*/
static int NEAR dologin(trp)
syntaxtree *trp;
{
# if	MSDOS
	return(RET_SUCCESS);
# else	/* !MSDOS */
	searchheredoc(trp, 1);

	return(execpath(trp -> comm, 0));
# endif	/* !MSDOS */
}

static int NEAR dologout(trp)
syntaxtree *trp;
{
	if ((!loginshell && interactive_io) || exit_status < 0) {
		execerror((trp -> comm) -> argv, NULL, ER_NOTLOGINSH, 0);
		return(RET_FAIL);
	}

	return(doexit(trp));
}
#endif	/* !MINIMUMSHELL */

static int NEAR doeval(trp)
syntaxtree *trp;
{
#ifndef	MINIMUMSHELL
	long dupshlineno;
#endif
	syntaxtree *stree;
	char *cp;
	int ret;

	if ((trp -> comm) -> argc <= 1
	|| !(cp = catvar(&((trp -> comm) -> argv[1]), ' ')))
		return(RET_SUCCESS);

#ifdef	BASHSTYLE
	/* bash displays arguments of "eval", in -v mode */
	if (verboseinput) {
		argfputs(cp, Xstderr);
		VOID_C fputnl(Xstderr);
	}
#endif
#ifndef	MINIMUMSHELL
	dupshlineno = shlineno;
	setshlineno(1L);
#endif
	trp = stree = newstree(NULL);

#ifndef	NOJOB
	childpgrp = (p_id_t)-1;
#endif
	trp = execline(cp, stree, trp, 1);
	execline((char *)-1, stree, trp, 1);
	ret = (syntaxerrno) ? RET_SYNTAXERR : ret_status;
	Xfree(stree);
	Xfree(cp);
#ifndef	MINIMUMSHELL
	setshlineno(dupshlineno);
#endif

	return(ret);
}

static int NEAR doexit(trp)
syntaxtree *trp;
{
	int ret;

	if ((trp -> comm) -> argc <= 1) ret = ret_status;
	else if ((ret = isnumeric((trp -> comm) -> argv[1])) < 0) {
		execerror((trp -> comm) -> argv,
			(trp -> comm) -> argv[1], ER_BADNUMBER, 0);
		ret = RET_FAIL;
#ifdef	BASHSTYLE
	/* bash ignores "exit -1" */
		return(ret);
#endif
	}
	if (exit_status < 0) exit_status = ret;
	else Xexit2(ret);

	return(RET_SUCCESS);
}

static int NEAR doread(trp)
syntaxtree *trp;
{
	char *cp, *next, *buf, *ifs;
	int n, top, opt, ret;

	if ((opt = tinygetopt(trp, "N", &top)) < 0) return(RET_FAIL);

	for (n = top; n < (trp -> comm) -> argc; n++) {
		if (identcheck((trp -> comm) -> argv[n], '\0') <= 0) {
			execerror((trp -> comm) -> argv,
				(trp -> comm) -> argv[n], ER_NOTIDENT, 0);
			return(RET_FAIL);
		}
	}

	ifs = getifs();
	ret = RET_SUCCESS;
	buf = readline(STDIN_FILENO, opt);
	if (buf == vnullstr) ret = RET_FAIL;
	else if (!buf) {
		if (errno != EINTR) doperror((trp -> comm) -> argv[0], NULL);
		ret = RET_FAIL;
	}
	else if ((trp -> comm) -> argc > top) {
		cp = buf;
		for (n = top; n < (trp -> comm) -> argc - 1; n++) {
			if (!*cp) next = cp;
			else if (!(next = strpbrk(cp, ifs)))
				next = cp + strlen(cp);
			else do {
				*(next++) = '\0';
			} while (Xstrchr(ifs, *next));
			if (setenv2((trp -> comm) -> argv[n], cp, 0) < 0) {
				ret = RET_FAIL;
				cp = NULL;
				break;
			}
			cp = next;
		}
		if (cp && setenv2((trp -> comm) -> argv[n], cp, 0) < 0)
			ret = RET_FAIL;
	}
#ifdef	BASHSTYLE
	/* bash set the variable REPLY without any argument */
	else if (setenv2(ENVREPLY, buf, 0) < 0) ret = RET_FAIL;
#endif
	if (buf != vnullstr) Xfree(buf);

	return(ret);
}

static int NEAR doshift(trp)
syntaxtree *trp;
{
	int i, n, ret;

	if ((trp -> comm) -> argc <= 1) n = 1;
	else if ((n = isnumeric((trp -> comm) -> argv[1])) < 0) {
		execerror((trp -> comm) -> argv,
			(trp -> comm) -> argv[1], ER_BADNUMBER, 0);
		return(RET_FAIL);
	}
	else if (!n) return(RET_SUCCESS);

	for (i = 0; i < n; i++) {
		if (!argvar[i + 1]) break;
		Xfree(argvar[i + 1]);
	}
	ret = (i >= n) ? RET_SUCCESS : RET_FAIL;
	n = i;
	for (i = 0; argvar[i + n + 1]; i++) argvar[i + 1] = argvar[i + n + 1];
	argvar[i + 1] = NULL;
	if (ret != RET_SUCCESS)
		execerror((trp -> comm) -> argv, NULL, ER_CANNOTSHIFT, 0);

	return(ret);
}

static int NEAR doset(trp)
syntaxtree *trp;
{
	shfunctable *func;
	char **var, **argv;
	int i, n, argc;

	argc = (trp -> comm) -> argc;
	argv = (trp -> comm) -> argv;
	if (argc <= 1) {
		var = duplvar(shellvar, 0);
		i = countvar(var);
		if (i > 1) qsort(var, i, sizeof(char *), cmppath);
		for (i = 0; var[i]; i++) {
#ifdef	BASHSTYLE
	/* bash's "set" quotes the value which includes any meta chacters */
			var[i] = quotemeta(var[i]);
#endif
			kanjifputs(var[i], Xstdout);
			VOID_C fputnl(Xstdout);
		}
		freevar(var);

		func = duplfunc(shellfunc);
		for (i = 0; func[i].ident; i++) /*EMPTY*/;
		if (i > 1) qsort(func, i, sizeof(shfunctable), cmpfunc);
		for (i = 0; func[i].ident; i++) {
			printshfunc(&(func[i]), Xstdout);
			VOID_C fputnl(Xstdout);
		}
		freefunc(func);
		return(RET_SUCCESS);
	}

	if ((n = getoption(argc, argv, 0)) < 0) return(RET_FAIL);
#ifdef	BASHSTYLE
	/* bash makes -e option effective immediately */
	errorexit = tmperrorexit;
#endif
	if (n > 2) {
		if (argc <= 2) {
			for (i = 0; i < FLAGSSIZ; i++) {
				if (!(shflaglist[i].ident)) continue;
				VOID_C Xprintf("%-16.16s%s\n",
					shflaglist[i].ident,
					(*(shflaglist[i].var)) ? "on" : "off");
			}
		}
		else {
			for (i = 0; i < FLAGSSIZ; i++) {
				if (!(shflaglist[i].ident)) continue;
				if (!strcmp(argv[2], shflaglist[i].ident))
					break;
			}
			if (i >= FLAGSSIZ) {
				execerror(argv, argv[2], ER_BADOPTIONS, 0);
				return(RET_FAIL);
			}

			setshflag(i, (n <= 3) ? 1 : 0);
		}
		n = 3;
	}
	if (n >= argc) return(RET_SUCCESS);

	var = argvar;
	argvar = (char **)Xmalloc((argc - n + 2) * sizeof(char *));
	argvar[0] = Xstrdup(var[0]);
	for (i = 1; n < argc; i++, n++) argvar[i] = Xstrdup(argv[n]);
	argvar[i] = NULL;
	freevar(var);
#ifdef	DEP_PTY
	sendparent(TE_SETVAR, &argvar, argvar);
#endif

	return(RET_SUCCESS);
}

static int NEAR dounset(trp)
syntaxtree *trp;
{
	char **argv;
	int i, n, len, ret;

	argv = (trp -> comm) -> argv;
	ret = RET_SUCCESS;
	for (i = 1; i < (trp -> comm) -> argc; i++) {
		len = strlen(argv[i]);
		if (getshellvar(argv[i], len)) n = unset(argv[i], len);
		else n = unsetshfunc(argv[i], len);
		if (n < 0) {
			ret = RET_FAIL;
			ERRBREAK;
		}
	}

	return(ret);
}

#ifndef	_NOUSEHASH
static int NEAR dohash(trp)
syntaxtree *trp;
{
	hashlist *hp;
	int i, n, ret;

	if ((trp -> comm) -> argc <= 1) {
		disphash();
		return(RET_SUCCESS);
	}

	if (!strcmp((trp -> comm) -> argv[1], "-r")) {
		VOID_C searchhash(NULL, NULL, NULL);
		return(RET_SUCCESS);
	}

	ret = RET_SUCCESS;
	for (i = 1; i < (trp -> comm) -> argc; i++) {
		n = searchhash(&hp, (trp -> comm) -> argv[i], NULL);
		if (n & CM_NOTFOUND) {
			execerror((trp -> comm) -> argv,
				(trp -> comm) -> argv[i], ER_NOTFOUND, 1);
			ret = RET_FAIL;
			ERRBREAK;
		}
		if (!(n & CM_HASH)) continue;
		hp -> hits = 0;
	}

	return(ret);
}
#endif	/* !_NOUSEHASH */

static int NEAR dochdir(trp)
syntaxtree *trp;
{
#ifdef	FD
	int opt, dupphysical_path;
#endif
	char *cp, *tmp, *dir, *path, *next;
	int n, dlen, len, size;

#ifdef	FD
	if ((opt = tinygetopt(trp, "LP", &n)) < 0) return(RET_FAIL);
#else
	n = 1;
#endif	/* !FD */

	if (n < (trp -> comm) -> argc) dir = (trp -> comm) -> argv[n];
	else if (!(dir = getconstvar(ENVHOME))) {
		execerror((trp -> comm) -> argv,
			(trp -> comm) -> argv[0], ER_NOHOMEDIR, 0);
		return(RET_FAIL);
	}
	else if (!*dir) return(RET_SUCCESS);

#ifdef	FD
	dupphysical_path = physical_path;
	if (opt) physical_path = (opt == 'P') ? 1 : 0;
#endif

	if (isrootdir(dir)) next = NULL;
#ifdef	DEP_DOSPATH
	else if (_dospath(dir)) next = NULL;
#endif
#ifdef	FD
# ifdef	DEP_DOSDRIVE
	else if (dir[0] && !dir[1] && Xstrchr(".?-@", dir[0])) next = NULL;
# else
	else if (dir[0] && !dir[1] && Xstrchr(".?-", dir[0])) next = NULL;
# endif
#endif	/* FD */
	else if ((next = getconstvar(ENVCDPATH))) {
		tmp = ((cp = strdelim(dir, 0)))
			? Xstrndup(dir, cp - dir) : dir;
		if (isdotdir(tmp)) next = NULL;
		if (tmp != dir) Xfree(tmp);
	}

	if (!next) {
		if (chdir4(dir, 0, NULL) >= 0) {
#ifdef	FD
			physical_path = dupphysical_path;
#endif
			return(RET_SUCCESS);
		}
	}
	else {
		len = strlen(dir);
		size = 0;
		path = NULL;
		for (cp = next; cp; cp = next) {
#ifdef	DEP_DOSPATH
			if (_dospath(cp)) next = Xstrchr(&(cp[2]), PATHDELIM);
			else
#endif
			next = Xstrchr(cp, PATHDELIM);
			dlen = (next) ? (next++) - cp : strlen(cp);
			if (!dlen) tmp = NULL;
			else {
				tmp = _evalpath(cp, cp + dlen, 0);
				dlen = strlen(tmp);
			}
			if (dlen + len + 1 + 1 > size) {
				size = dlen + len + 1 + 1;
				path = Xrealloc(path, size);
			}
			if (tmp) {
				Xstrncpy(path, tmp, dlen);
				Xfree(tmp);
				dlen = strcatdelim(path) - path;
			}
			Xstrncpy(&(path[dlen]), dir, len);
			if (chdir4(path, 1, NULL) >= 0) {
				kanjifputs(path, Xstdout);
				VOID_C fputnl(Xstdout);
				Xfree(path);
#ifdef	FD
				physical_path = dupphysical_path;
#endif
				return(RET_SUCCESS);
			}
		}
		Xfree(path);
	}
	execerror((trp -> comm) -> argv, dir, ER_BADDIR, 0);
#ifdef	FD
	physical_path = dupphysical_path;
#endif

	return(RET_FAIL);
}

static int NEAR dopwd(trp)
syntaxtree *trp;
{
#ifdef	FD
	int opt;
#endif
	char buf[MAXPATHLEN];

#ifdef	FD
	if ((opt = tinygetopt(trp, "LP", NULL)) < 0) return(RET_FAIL);

	if (!((opt) ? (opt == 'P') : physical_path)) Xstrcpy(buf, fullpath);
	else
#endif
	if (!Xgetwd(buf)) {
		execerror((trp -> comm) -> argv, NULL, ER_CANNOTSTAT, 0);
		return(RET_FAIL);
	}
	kanjifputs(buf, Xstdout);
	VOID_C fputnl(Xstdout);

	return(RET_SUCCESS);
}

static int NEAR dosource(trp)
syntaxtree *trp;
{
#ifdef	BASHSTYLE
	char **var, **dupargvar;
#endif
	hashlist *hp;
	CONST char *fname;
	int n, fd;

	if ((trp -> comm) -> argc <= 1) return(RET_SUCCESS);
	fname = (trp -> comm) -> argv[1];
	n = searchhash(&hp, fname, NULL);
	if (restricted && (n & CM_FULLPATH)) {
		execerror((trp -> comm) -> argv, fname, ER_RESTRICTED, 2);
		return(RET_FAIL);
	}
	if (n & CM_NOTFOUND) {
		execerror((trp -> comm) -> argv, fname, ER_NOTFOUND, 1);
		return(RET_FAIL);
	}
	if ((fd = newdup(Kopen(fname, O_TEXT | O_RDONLY, 0666))) < 0) {
		doperror((trp -> comm) -> argv[0], fname);
		return(RET_FAIL);
	}
#ifdef	BASHSTYLE
	/* bash sets positional parameters with source arguments */
	if ((trp -> comm) -> argc > 2) {
		dupargvar = argvar;
		argvar = (char **)Xmalloc((trp -> comm) -> argc
			* sizeof(char *));
		argvar[0] = Xstrdup(dupargvar[0]);
		for (n = 1; n < (trp -> comm) -> argc - 1; n++)
			argvar[n] = Xstrdup((trp -> comm) -> argv[n + 1]);
		argvar[n] = NULL;
		var = argvar;
		sourcefile(fd, fname, 0);
		safeclose(fd);
		if (var != argvar) freevar(dupargvar);
		else {
			freevar(argvar);
			argvar = dupargvar;
		}
	}
	else
#endif	/* BASHSTYLE */
	{
		sourcefile(fd, fname, 0);
		safeclose(fd);
	}

	return(ret_status);
}

static int NEAR expandlist(varp, ident)
char ***varp;
CONST char *ident;
{
#ifndef	MINIMUMSHELL
	char *cp;
#endif
	int len;

#ifdef	MINIMUMSHELL
	len = identcheck(ident, '\0');
#else
	len = identcheck(ident, '=');
#endif
	if (!len) {
		execerror(NULL, ident, ER_NOTIDENT, 0);
		return(-1);
	}
#ifndef	MINIMUMSHELL
	else if (len < 0) len = -len;
	else if (_putshellvar(cp = Xstrdup(ident), len) < 0) {
		Xfree(cp);
		return(-1);
	}
#endif
	*varp = expandvar(*varp, ident, len);

	return(len);
}

int setexport(ident)
CONST char *ident;
{
	int i, len;

	if ((len = expandlist(&exportlist, ident)) < 0) return(-1);
	if ((i = searchvar(shellvar, ident, len, '=')) >= 0)
		exportvar = putvar(exportvar, Xstrdup(shellvar[i]), len);

	return(len);
}

int setronly(ident)
CONST char *ident;
{
	return(expandlist(&ronlylist, ident));
}

static int NEAR doexport(trp)
syntaxtree *trp;
{
	char **argv;
	int i, n, ret;

	argv = (trp -> comm) -> argv;
	if ((trp -> comm) -> argc <= 1) {
		for (i = 0; exportlist[i]; i++)
			VOID_C Xprintf("export %k\n", exportlist[i]);
		return(RET_SUCCESS);
	}

	ret = RET_SUCCESS;
	for (n = 1; n < (trp -> comm) -> argc; n++) {
		if (setexport(argv[n]) < 0) {
			ret = RET_FAIL;
			ERRBREAK;
		}
#ifdef	DEP_PTY
		sendparent(TE_SETEXPORT, argv[n]);
#endif
	}

	return(ret);
}

static int NEAR doreadonly(trp)
syntaxtree *trp;
{
	char **argv;
	int i, n, ret;

	argv = (trp -> comm) -> argv;
	if ((trp -> comm) -> argc <= 1) {
		for (i = 0; ronlylist[i]; i++) {
			VOID_C Xprintf("readonly %k\n", ronlylist[i]);
		}
		return(RET_SUCCESS);
	}

	ret = RET_SUCCESS;
	for (n = 1; n < (trp -> comm) -> argc; n++) {
		if (setronly(argv[n]) < 0) {
			ret = RET_FAIL;
			ERRBREAK;
		}
#ifdef	DEP_PTY
		sendparent(TE_SETRONLY, argv[n]);
#endif
	}

	return(ret);
}

/*ARGSUSED*/
static int NEAR dotimes(trp)
syntaxtree *trp;
{
	time_t usrtime, systime;
#ifdef	USEGETRUSAGE
	struct rusage ru;

	getrusage(RUSAGE_CHILDREN, &ru);
	usrtime = ru.ru_utime.tv_sec;
	systime = ru.ru_stime.tv_sec;
#else	/* !USEGETRUSAGE */
# ifdef	USETIMES
	struct tms buf;
	int clk;

	times(&buf);
	clk = CLKPERSEC;
	usrtime = buf.tms_cutime / clk;
	if (buf.tms_cutime % clk > clk / 2) usrtime++;
	systime = buf.tms_cstime / clk;
	if (buf.tms_cstime % clk > clk / 2) systime++;
# else
	usrtime = systime = (time_t)0;
# endif
#endif	/* !USEGETRUSAGE */
	VOID_C Xprintf("%dm%ds %dm%ds\n",
		(int)(usrtime / 60), (int)(usrtime % 60),
		(int)(systime / 60), (int)(systime % 60));

	return(RET_SUCCESS);
}

/*ARGSUSED*/
static int NEAR dowait(trp)
syntaxtree *trp;
{
#if	MSDOS
	return(RET_SUCCESS);
#else	/* !MSDOS */
	char *s;
	p_id_t pid;
# ifndef	NOJOB
	int i, n;
# endif

	if ((trp -> comm) -> argc <= 1) {
# ifdef	NOJOB
		pid = lastpid;
# else
		for (;;) {
			for (n = 0; n < maxjobs; n++) {
				if (!(joblist[n].pids)) continue;
				i = joblist[n].npipe;
				if (!(joblist[n].stats[i])) break;
			}
			if (n >= maxjobs) return(RET_SUCCESS);
			VOID_C waitchild(joblist[n].pids[i], NULL);
		}
# endif
	}
	else {
		s = (trp -> comm) -> argv[1];
# ifndef	NOJOB
		if (*s == '%') {
			checkjob(NULL);
			if ((n = getjob(s)) < 0) pid = (p_id_t)-1;
			else {
				i = joblist[n].npipe;
				pid = joblist[n].pids[i];
			}
		}
		else
# endif	/* !NOJOB */
		if ((pid = isnumeric(s)) < (p_id_t)0) {
			execerror((trp -> comm) -> argv, s, ER_BADNUMBER, 0);
			return(RET_FAIL);
		}
# ifndef	NOJOB
		else {
			checkjob(NULL);
			if (!joblist || (n = searchjob(pid, &i)) < 0) {
				pid = (p_id_t)-1;
#  ifdef	FAKEUNINIT
				n = -1;		/* fake for -Wuninitialized */
#  endif
			}
		}

		if (pid < (p_id_t)0) {
			execerror((trp -> comm) -> argv, s, ER_NOSUCHJOB, 2);
			return(RET_FAIL);
		}
		if (joblist[n].stats[i]) return(RET_SUCCESS);
# endif	/* !NOJOB */
	}

	return(waitchild(pid, NULL));
#endif	/* !MSDOS */
}

static int NEAR doumask(trp)
syntaxtree *trp;
{
	char *s;
	int i, n;

	if ((trp -> comm) -> argc <= 1) {
		n = umask(022);
		VOID_C umask(n);
#ifdef	BASHSTYLE
		VOID_C Xprintf("%03o\n", n & 0777);
#else
		VOID_C Xprintf("%04o\n", n & 0777);
#endif
	}
	else {
		s = (trp -> comm) -> argv[1];
		n = 0;
		for (i = 0; s[i] >= '0' && s[i] <= '7'; i++)
			n = (n << 3) + s[i] - '0';
#ifdef	BASHSTYLE
		if (n & ~0777) {
			execerror((trp -> comm) -> argv, s, ER_NUMOUTRANGE, 0);
			return(RET_FAIL);
		}
#else
		n &= 0777;
#endif
		VOID_C umask(n);
	}

	return(RET_SUCCESS);
}

/*ARGSUSED*/
static int NEAR doulimit(trp)
syntaxtree *trp;
{
#ifdef	USERESOURCEH
	struct rlimit lim;
	XFILE *fp;
	u_long flags;
	int i, j, n, err, hs, res, inf;
#endif
#if	defined (USERESOURCEH) || defined (USEULIMITH)
	long val;
	char **argv;
	int argc;
#endif

#if	!defined (USERESOURCEH) && !defined (USEULIMITH)
	execerror((trp -> comm) -> argv, NULL, ER_BADULIMIT, 1);

	return(RET_FAIL);
#else	/* USERESOURCEH || USEULIMITH */
	argc = (trp -> comm) -> argc;
	argv = (trp -> comm) -> argv;
# ifdef	USERESOURCEH
	flags = (u_long)0;
	err = hs = 0;
	n = 1;
	if (argc > 1 && argv[1][0] == '-') {
		for (i = 1; !err && argv[1][i]; i++) switch (argv[1][i]) {
			case 'a':
				flags = ((u_long)1 << ULIMITSIZ) - 1;
				break;
			case 'H':
				hs |= 1;
				break;
			case 'S':
				hs |= 2;
				break;
			default:
				for (j = 0; j < ULIMITSIZ; j++)
					if (argv[1][i] == ulimitlist[j].opt)
						break;
				if (j >= ULIMITSIZ) err = 1;
				else flags |= ((u_long)1 << j);
				break;
		}
		n++;
	}
	res = -1;
	if (!flags)
	for (i = 0, flags = (u_long)1; i < ULIMITSIZ; i++, flags <<= 1) {
		if (ulimitlist[i].res == RLIMIT_FSIZE) {
			res = i;
			break;
		}
	}
	else for (i = 0; i < ULIMITSIZ; i++) {
		if (flags & ((u_long)1 << i)) {
			if (res < 0) res = i;
			else {
				res = -1;
				break;
			}
		}
	}

#  ifdef	FAKEUNINIT
	val = 0L;	/* fake for -Wuninitialized */
#  endif
	if (n >= argc) {
#  ifdef	FAKEUNINIT
		inf = 0;	/* fake for -Wuninitialized */
#  endif
		if (!hs) hs = 2;
	}
	else {
		inf = 0;
		if (!hs) hs = (1 | 2);
		if (res < 0) err = 1;
		else if (!strcmp(argv[n], UNLIMITED)) inf = 1;
		else {
			if ((val = isnumeric(argv[n])) < 0L) {
				execerror(argv, argv[n], ER_BADULIMIT, 1);
				return(RET_FAIL);
			}
			val *= ulimitlist[res].unit;
		}
	}
	if (err) {
#  ifdef	BASHSTYLE
		fp = Xstderr;
#  else
		fp = Xstdout;
#  endif
		VOID_C Xfprintf(fp, "usage: %k [ -HSa", argv[0]);
		for (j = 0; j < ULIMITSIZ; j++)
			Xfputc(ulimitlist[j].opt, fp);
		Xfputs(" ] [ limit ]", fp);
		VOID_C fputnl(fp);
#  ifdef	BASHSTYLE
		return(RET_SYNTAXERR);
#  else
		return(RET_SUCCESS);
#  endif
	}

	if (n >= argc) {
		for (i = 0; i < ULIMITSIZ; i++) if (flags & ((u_long)1 << i)) {
			if (res < 0)
				VOID_C Xprintf("%s ", ulimitlist[i].mes);
			if (getrlimit(ulimitlist[i].res, &lim) < 0) {
				execerror(argv, NULL, ER_BADULIMIT, 1);
				return(RET_FAIL);
			}
			if (hs & 2) {
				if (lim.rlim_cur == RLIM_INFINITY)
					Xfputs(UNLIMITED, Xstdout);
				else if (lim.rlim_cur) VOID_C Xprintf("%ld",
					lim.rlim_cur / ulimitlist[i].unit);
			}
			if (hs & 1) {
				if (hs & 2) Xfputc(':', Xstdout);
				if (lim.rlim_max == RLIM_INFINITY)
					Xfputs(UNLIMITED, Xstdout);
				else if (lim.rlim_max) VOID_C Xprintf("%ld",
					lim.rlim_max / ulimitlist[i].unit);
			}
			VOID_C fputnl(Xstdout);
		}
	}
	else {
		if (getrlimit(ulimitlist[res].res, &lim) < 0) {
			execerror(argv, NULL, ER_BADULIMIT, 1);
			return(RET_FAIL);
		}
		if (hs & 1) lim.rlim_max = (inf) ? RLIM_INFINITY : val;
		if (hs & 2) lim.rlim_cur = (inf) ? RLIM_INFINITY : val;
		if (setrlimit(ulimitlist[res].res, &lim) < 0) {
			execerror(argv, NULL, ER_BADULIMIT, 1);
			return(RET_FAIL);
		}
	}
# else	/* !USERESOURCEH */
	if (argc <= 1) {
		if ((val = ulimit(UL_GETFSIZE, 0L)) < 0L) {
			execerror(argv, NULL, ER_BADULIMIT, 1);
			return(RET_FAIL);
		}
		if (val == RLIM_INFINITY) Xfputs(UNLIMITED, Xstdout);
		else VOID_C Xprintf("%ld", val * 512L);
		VOID_C fputnl(Xstdout);
	}
	else {
		if (!strcmp(argv[1], UNLIMITED)) val = RLIM_INFINITY;
		else {
			if ((val = isnumeric(argv[1])) < 0L) {
				execerror(argv, argv[1], ER_BADULIMIT, 1);
				return(RET_FAIL);
			}
			val /= 512L;
		}
		if (ulimit(UL_SETFSIZE, val) < 0L) {
			execerror(argv, NULL, ER_BADULIMIT, 1);
			return(RET_FAIL);
		}
	}
# endif	/* !USERESOURCEH */

	return(RET_SUCCESS);
#endif	/* USERESOURCEH || USEULIMITH */
}

static int NEAR dotrap(trp)
syntaxtree *trp;
{
	char *comm, **argv;
	int i, n, sig;

	argv = (trp -> comm) -> argv;
	if ((trp -> comm) -> argc <= 1) {
		for (i = 0; i < NSIG; i++) {
			if ((trapmode[i] & TR_STAT) != TR_TRAP) continue;
			VOID_C Xprintf("%d: %k\n", i, trapcomm[i]);
		}
		return(RET_SUCCESS);
	}

	n = 1;
	comm = (isnumeric(argv[n]) < 0) ? argv[n++] : NULL;

	for (; n < (trp -> comm) -> argc; n++) {
		if ((sig = isnumeric(argv[n])) < 0) {
			execerror(argv, argv[n], ER_BADNUMBER, 0);
			return(RET_FAIL);
		}
		if (sig >= NSIG) {
			execerror(argv, argv[n], ER_BADTRAP, 0);
			return(RET_FAIL);
		}

		if (!sig) {
			trapmode[0] = (comm) ? TR_TRAP : 0;
			Xfree(trapcomm[0]);
			trapcomm[0] = Xstrdup(comm);
			continue;
		}

		for (i = 0; signallist[i].sig >= 0; i++)
			if (sig == signallist[i].sig) break;
		if (signallist[i].sig < 0
		|| (signallist[i].flags & TR_NOTRAP)) {
			execerror(argv, argv[n], ER_BADTRAP, 0);
			return(RET_FAIL);
		}

		signal2(sig, SIG_IGN);
		Xfree(trapcomm[sig]);
		if (!comm) {
			trapmode[sig] = signallist[i].flags;
			if (trapmode[sig] & TR_BLOCK)
				signal2(sig, (sigcst_t)(signallist[i].func));
			else signal2(sig, oldsigfunc[sig]);
			trapcomm[sig] = NULL;
		}
		else {
			trapmode[sig] = TR_TRAP;
			trapcomm[sig] = Xstrdup(comm);
			if ((signallist[i].flags & TR_STAT) == TR_STOP)
				signal2(sig, SIG_DFL);
			else signal2(sig, (sigcst_t)(signallist[i].func));
		}
	}

	return(RET_SUCCESS);
}

#ifndef	NOJOB
static int NEAR dojobs(trp)
syntaxtree *trp;
{
	return(posixjobs(trp));
}

static int NEAR dofg(trp)
syntaxtree *trp;
{
	return(posixfg(trp));
}

static int NEAR dobg(trp)
syntaxtree *trp;
{
	return(posixbg(trp));
}

static int NEAR dodisown(trp)
syntaxtree *trp;
{
	char *s;
	int n;

	s = ((trp -> comm) -> argc > 1) ? (trp -> comm) -> argv[1] : NULL;
	checkjob(NULL);
	if ((n = getjob(s)) < 0) {
		execerror((trp -> comm) -> argv,
			(trp -> comm) -> argv[1],
			(n < -1) ? ER_TERMINATED : ER_NOSUCHJOB, 2);
		return(RET_FAIL);
	}
	freejob(n);

	return(RET_SUCCESS);
}
#endif	/* !NOJOB */

int typeone(s, fp)
CONST char *s;
XFILE *fp;
{
	hashlist *hp;
	int type, id;

	kanjifputs(s, fp);
	type = checktype2(s, &id, 1, 1);

	if (type == CT_BUILTIN) Xfputs(" is a shell builtin", fp);
#ifdef	FD
	else if (type == CT_FDORIGINAL) Xfputs(" is a FDclone builtin", fp);
	else if (type == CT_FDINTERNAL)
		Xfputs(" is a FDclone internal", fp);
#endif
#if	MSDOS
	else if (type == CT_LOGDRIVE)
		Xfputs(" is a change drive command", fp);
#endif
	else if (type == CT_FUNCTION) {
		Xfputs(" is a function\n", fp);
		printshfunc(&(shellfunc[id]), fp);
	}
#ifndef	NOALIAS
	else if (type == CT_ALIAS)
		VOID_C Xfprintf(fp,
			" is a aliased to `%k'", shellalias[id].comm);
#endif
	else {
		type = searchhash(&hp, s, NULL);
#ifdef	_NOUSEHASH
		if (!(type & CM_FULLPATH)) s = (char *)hp;
#else
		if (type & CM_HASH) s = hp -> path;
#endif
		if (restricted && (type & CM_FULLPATH))
			Xfputs(": restricted", fp);
		else if ((type & CM_NOTFOUND) || Xaccess(s, X_OK) < 0) {
			Xfputs(" not found", fp);
			VOID_C fputnl(fp);
			return(-1);
		}
		else VOID_C Xfprintf(fp, " is %k", s);
	}
	VOID_C fputnl(fp);

	return(0);
}

static int NEAR dotype(trp)
syntaxtree *trp;
{
	int i, ret;

	if ((trp -> comm) -> argc <= 1) return(RET_SUCCESS);

#ifdef	BASHSTYLE
	ret = RET_FAIL;
#else
	ret = RET_SUCCESS;
#endif
	for (i = 1; i < (trp -> comm) -> argc; i++) {
		if (typeone((trp -> comm) -> argv[i], Xstdout) >= 0)
#ifdef	BASHSTYLE
			ret = RET_SUCCESS;
#else
			/*EMPTY*/;
		else ret = RET_FAIL;
#endif
	}

	return(ret);
}

#ifdef	DOSCOMMAND
static int NEAR dodir(trp)
syntaxtree *trp;
{
	return(doscomdir((trp -> comm) -> argc, (trp -> comm) -> argv));
}

static int NEAR domkdir(trp)
syntaxtree *trp;
{
	return(doscommkdir((trp -> comm) -> argc, (trp -> comm) -> argv));
}

static int NEAR dormdir(trp)
syntaxtree *trp;
{
	return(doscomrmdir((trp -> comm) -> argc, (trp -> comm) -> argv));
}

static int NEAR doerase(trp)
syntaxtree *trp;
{
	return(doscomerase((trp -> comm) -> argc, (trp -> comm) -> argv));
}

static int NEAR dorename(trp)
syntaxtree *trp;
{
	return(doscomrename((trp -> comm) -> argc, (trp -> comm) -> argv));
}

static int NEAR docopy(trp)
syntaxtree *trp;
{
	return(doscomcopy((trp -> comm) -> argc, (trp -> comm) -> argv));
}

static int NEAR docls(trp)
syntaxtree *trp;
{
	return(doscomcls((trp -> comm) -> argc, (trp -> comm) -> argv));
}

static int NEAR dodtype(trp)
syntaxtree *trp;
{
	return(doscomtype((trp -> comm) -> argc, (trp -> comm) -> argv));
}
#endif	/* DOSCOMMAND */

#ifndef	NOALIAS
static int NEAR doalias(trp)
syntaxtree *trp;
{
	return(posixalias(trp));
}

static int NEAR dounalias(trp)
syntaxtree *trp;
{
	return(posixunalias(trp));
}
#endif	/* NOALIAS */

static int NEAR doecho(trp)
syntaxtree *trp;
{
	int i, n, opt;

	n = 1;
	if ((opt = tinygetopt(trp, "-nN", NULL)) < 0) return(RET_FAIL);
	if (opt) n++;

	for (i = n; i < (trp -> comm) -> argc; i++) {
		if (i > n) Xfputc(' ', Xstdout);
		if ((trp -> comm) -> argv[i])
			argfputs((trp -> comm) -> argv[i], Xstdout);
	}
	if (opt == 'n') /*EMPTY*/;
	else if (opt == 'N') Xfputs("\r\n", Xstdout);
	else VOID_C fputnl(Xstdout);
	Xfflush(Xstdout);

	return(RET_SUCCESS);
}

#ifndef	MINIMUMSHELL
static int NEAR dokill(trp)
syntaxtree *trp;
{
	return(posixkill(trp));
}

static int NEAR dotest(trp)
syntaxtree *trp;
{
	return(posixtest(trp));
}
#endif	/* !MINIMUMSHELL */

#ifndef	NOPOSIXUTIL
/*ARGSUSED*/
static int NEAR dofalse(trp)
syntaxtree *trp;
{
	return(RET_FAIL);
}

static int NEAR docommand(trp)
syntaxtree *trp;
{
	return(posixcommand(trp));
}

static int NEAR dogetopts(trp)
syntaxtree *trp;
{
	return(posixgetopts(trp));
}

/*ARGSUSED*/
static int NEAR donewgrp(trp)
syntaxtree *trp;
{
#if	MSDOS
	return(RET_SUCCESS);
#else	/* !MSDOS */
	searchheredoc(trp, 1);

	return(execpath(trp -> comm, 0));
#endif	/* !MSDOS */
}

# if	0				/* exists in FD original builtin */
static int NEAR dofc(trp)
syntaxtree *trp;
{
	return(RET_SUCCESS);
}
# endif
#endif	/* NOPOSIXUTIL */

#ifndef	MINIMUMSHELL
static int NEAR getworkdir(path)
char *path;
{
# ifdef	FD
	if (underhome(&(path[1]))) path[0] = '~';
	else Xstrcpy(path, fullpath);
# else
	if (!Xgetwd(path)) {
		execerror(NULL, NULL, ER_CANNOTSTAT, 0);
		return(-1);
	}
# endif

	return(0);
}

static int NEAR dopushd(trp)
syntaxtree *trp;
{
	char *cp, path[MAXPATHLEN];
	int n;

	if (getworkdir(path) < 0) return(RET_FAIL);
	if ((trp -> comm) -> argc < 2) {
		if (!dirstack || !dirstack[0]) {
			execerror((trp -> comm) -> argv, NULL, ER_DIREMPTY, 2);
			return(RET_FAIL);
		}
		cp = evalpath(Xstrdup(dirstack[0]), 0);
		n = chdir4(cp, 1, NULL);
		Xfree(cp);
		if (n < 0) {
			execerror((trp -> comm) -> argv,
				dirstack[0], ER_BADDIR, 0);
			return(RET_FAIL);
		}
		Xfree(dirstack[0]);
# ifdef	DEP_PTY
		sendparent(TE_POPVAR, &dirstack);
# endif
	}
	else {
		if (chdir4((trp -> comm) -> argv[1], 0, NULL) < 0) {
			execerror((trp -> comm) -> argv,
				(trp -> comm) -> argv[1], ER_BADDIR, 0);
			return(RET_FAIL);
		}
		n = countvar(dirstack);
		dirstack = (char **)Xrealloc(dirstack,
			(n + 1 + 1) * sizeof(char *));
		memmove((char *)&(dirstack[1]), (char *)&(dirstack[0]),
			n * sizeof(char *));
		dirstack[n + 1] = NULL;
	}
	dirstack[0] = Xstrdup(path);
	dodirs(trp);
# ifdef	DEP_PTY
	sendparent(TE_PUSHVAR, &dirstack, path);
# endif

	return(RET_SUCCESS);
}

static int NEAR dopopd(trp)
syntaxtree *trp;
{
	char *cp;
	int n;

	if (!dirstack || !dirstack[0]) {
		execerror((trp -> comm) -> argv, NULL, ER_DIREMPTY, 2);
		return(RET_FAIL);
	}
	cp = evalpath(Xstrdup(dirstack[0]), 0);
	n = chdir4(cp, 1, NULL);
	Xfree(cp);
	if (n < 0) {
		execerror((trp -> comm) -> argv, dirstack[0], ER_BADDIR, 0);
		return(RET_FAIL);
	}
	n = countvar(dirstack);
	Xfree(dirstack[0]);
	memmove((char *)&(dirstack[0]), (char *)&(dirstack[1]),
		n * sizeof(char *));
	dodirs(trp);
# ifdef	DEP_PTY
	sendparent(TE_POPVAR, &dirstack);
# endif

	return(RET_SUCCESS);
}

/*ARGSUSED*/
static int NEAR dodirs(trp)
syntaxtree *trp;
{
	char path[MAXPATHLEN];
	int i;

	if (getworkdir(path) < 0) return(RET_FAIL);
	kanjifputs(path, Xstdout);
	if (dirstack)
		for (i = 0; dirstack[i]; i++)
			VOID_C Xprintf(" %k", dirstack[i]);
	VOID_C fputnl(Xstdout);

	return(RET_SUCCESS);
}

static int NEAR doenable(trp)
syntaxtree *trp;
{
	char **argv;
	int i, j, n, ret;

	argv = (trp -> comm) -> argv;
	n = 1;
	if ((trp -> comm) -> argc > 1 && argv[n][0] == '-'
	&& argv[n][1] == 'n' && !(argv[n][2]))
		n++;

	ret = RET_SUCCESS;
	if (n >= (trp -> comm) -> argc) for (j = 0; j < SHBUILTINSIZ; j++) {
		if (n == 1) {
			if (shbuiltinlist[j].flags & BT_DISABLE) continue;
		}
		else {
			if (!(shbuiltinlist[j].flags & BT_DISABLE)) continue;
		}

		for (i = 0; i < (trp -> comm) -> argc; i++)
			VOID_C Xprintf("%k ", argv[i]);
		VOID_C Xprintf("%k\n", shbuiltinlist[j].ident);
	}
	else for (i = n; i < (trp -> comm) -> argc; i++) {
		for (j = 0; j < SHBUILTINSIZ; j++)
			if (!strcommcmp(argv[i], shbuiltinlist[j].ident))
				break;
		if (j >= SHBUILTINSIZ) {
			execerror(argv, argv[i], ER_COMNOFOUND, 1);
			ret = RET_FAIL;
			continue;
		}

		if (n == 1) shbuiltinlist[j].flags &= ~BT_DISABLE;
		else shbuiltinlist[j].flags |= BT_DISABLE;
	}

	return(ret);
}

static int NEAR dobuiltin(trp)
syntaxtree *trp;
{
	char **argv;
	int i, ret, argc;

	argc = (trp -> comm) -> argc;
	argv = (trp -> comm) -> argv;

	if (interrupted) return(RET_INTR);
	if (argc <= 1) return(ret_status);

	for (i = 0; i < SHBUILTINSIZ; i++) {
		if (shbuiltinlist[i].flags & BT_DISABLE) continue;
		if (!strcommcmp(argv[1], shbuiltinlist[i].ident)) break;
	}

	if (i >= SHBUILTINSIZ) {
		execerror(argv, argv[1], ER_COMNOFOUND, 1);
		return(RET_FAIL);
	}

	if (restricted && (shbuiltinlist[i].flags & BT_RESTRICT)) {
		execerror(argv, argv[1], ER_RESTRICTED, 0);
		return(RET_FAIL);
	}
	if (!shbuiltinlist[i].func) return(RET_FAIL);
	(trp -> comm) -> argc--;
	(trp -> comm) -> argv++;
	ret = (*(shbuiltinlist[i].func))(trp);

	(trp -> comm) -> argc = argc;
	(trp -> comm) -> argv = argv;

	return(ret);
}

static int NEAR doaddcr(trp)
syntaxtree *trp;
{
	CONST char *fname;
	char *buf;
	int n, fd, ret, opt;

	if ((opt = tinygetopt(trp, "1", &n)) < 0) return(RET_FAIL);
	fname = (trp -> comm) -> argv[n];

	if (!fname) fd = STDIN_FILENO;
	else if ((fd = newdup(Kopen(fname, O_TEXT | O_RDONLY, 0666))) < 0) {
		doperror((trp -> comm) -> argv[0], fname);
		return(RET_FAIL);
	}

	ret = RET_SUCCESS;
	while ((buf = readline(fd, 'N')) != vnullstr) {
		if (!buf) {
			if (errno != EINTR)
				doperror((trp -> comm) -> argv[0], fname);
			ret = RET_FAIL;
			break;
		}

		Xfputs(buf, Xstdout);
		Xfree(buf);
		Xfputs("\r\n", Xstdout);
		Xfflush(Xstdout);

		if (opt == '1') break;
	}
	safeclose(fd);

	return(ret);
}
#endif	/* !MINIMUMSHELL */

#ifdef	WITHSOCKET
static int NEAR doaccept(trp)
syntaxtree *trp;
{
	CONST char *cp;
	int s, fd;

	if (!(cp = (trp -> comm) -> argv[1])) s = STDIN_FILENO;
	else if ((s = isnumeric(cp)) < 0) {
		execerror((trp -> comm) -> argv, cp, ER_BADNUMBER, 0);
		return(RET_FAIL);
	}

	if (issocket(s) < 0) {
		execerror((trp -> comm) -> argv, cp, ER_NOTSOCKET, 2);
		return(RET_FAIL);
	}
	if ((fd = sockaccept(s, SCK_THROUGHPUT)) < 0) {
		doperror((trp -> comm) -> argv[0], cp);
		return(RET_FAIL);
	}
	s = Xdup2(fd, s);
	safeclose(fd);
	if (s < 0) {
		doperror((trp -> comm) -> argv[0], cp);
		return(RET_FAIL);
	}

	return(RET_SUCCESS);
}

static int NEAR dosocketinfo(trp)
syntaxtree *trp;
{
	char addr[SCK_ADDRSIZE + 1];
	CONST char *cp;
	int n, s, opt, port;

	if ((opt = tinygetopt(trp, "apAP", &n)) < 0) return(RET_FAIL);

	if (!(cp = (trp -> comm) -> argv[n])) s = STDIN_FILENO;
	else if ((s = isnumeric(cp)) < 0) {
		execerror((trp -> comm) -> argv, cp, ER_BADNUMBER, 0);
		return(RET_FAIL);
	}

	if (issocket(s) < 0) {
		execerror((trp -> comm) -> argv, NULL, ER_NOTSOCKET, 2);
		return(RET_FAIL);
	}
	if (!Xisupper(opt)) {
		if (getsockinfo(s, addr, sizeof(addr), &port, 1) >= 0) {
			if (opt == 'a') VOID_C Xprintf("%s\n", addr);
			else if (opt == 'p') VOID_C Xprintf("%d\n", port);
			else VOID_C Xprintf("Remote: %s:%d\n", addr, port);
		}
# ifdef	ENOTCONN
		else if (errno == ENOTCONN) {
			if (opt) VOID_C Xprintf("%s\n", nullstr);
			else VOID_C Xprintf("Remote: (not connected)\n");
		}
# endif
		else {
			execerror((trp -> comm) -> argv,
				NULL, ER_INVALSOCKET, 2);
			return(RET_FAIL);
		}
	}
	if (!Xislower(opt)) {
		if (getsockinfo(s, addr, sizeof(addr), &port, 0) >= 0) {
			if (opt == 'A') VOID_C Xprintf("%s\n", addr);
			else if (opt == 'P') VOID_C Xprintf("%d\n", port);
			else VOID_C Xprintf("Local: %s:%d\n", addr, port);
		}
		else {
			execerror((trp -> comm) -> argv,
				NULL, ER_INVALSOCKET, 2);
			return(RET_FAIL);
		}
	}

	return(RET_SUCCESS);
}
#endif	/* WITHSOCKET */

#ifdef	FD
static int NEAR dofd(trp)
syntaxtree *trp;
{
	int n, mode;

	if (!shellmode || exit_status < 0) {
		execerror((trp -> comm) -> argv, NULL, ER_RECURSIVEFD, 0);
		return(RET_FAIL);
	}
	else if (dumbterm > 1) {
		execerror((trp -> comm) -> argv,
			getconstvar(ENVTERM), ER_INVALTERMFD, 1);
		return(RET_FAIL);
	}
	else if (!interactive || nottyout) {
		execerror((trp -> comm) -> argv, NULL, ER_INVALTERMFD, 1);
		return(RET_FAIL);
	}
	else {
		n = sigvecset(1);
		ttyiomode(0);
		mode = termmode(1);
		shellmode = 0;
		main_fd(&((trp -> comm) -> argv[1]), 1);
		shellmode = 1;
		VOID_C termmode(mode);
		stdiomode();
		VOID_C fputnl(Xstderr);
		sigvecset(n);
	}

	return(RET_SUCCESS);
}
#endif	/* FD */

static int NEAR doshfunc(trp, n)
syntaxtree *trp;
int n;
{
#ifndef	MINIMUMSHELL
	long dupshlineno;
#endif
	char **avar;
	int ret;

#ifndef	MINIMUMSHELL
	dupshlineno = shlineno;
#endif
	avar = argvar;
	argvar = duplvar((trp -> comm) -> argv, 0);
	shfunclevel++;
	ret = exec_stree(shellfunc[n].func, 0);
	if (returnlevel >= shfunclevel) returnlevel = 0;
	shfunclevel--;
	freevar(argvar);
	argvar = avar;
#ifndef	MINIMUMSHELL
	setshlineno(dupshlineno);
#endif

	return(ret);
}

#ifndef	FDSH
char **getsimpleargv(trp)
syntaxtree *trp;
{
	char **argv;

	if ((trp -> flags & ST_NODE) || trp -> cont) return(NULL);
	if (!(trp -> comm) || isstatement(trp -> comm)) return(NULL);
	argv = (trp -> comm) -> argv;
	while (trp -> next) {
		trp = trp -> next;
		if ((trp -> flags & ST_NODE) || trp -> comm) return(NULL);
	}

	return(argv);
}
#endif	/* !FDSH */

#ifdef	SHOWSTREE
static VOID NEAR show_stree(trp, lvl)
syntaxtree *trp;
int lvl;
{
	redirectlist *rp;
	int i, id;

	if (!trp) {
		printindent(lvl, Xstdout);
		Xfputs("(null):", Xstdout);
		VOID_C fputnl(Xstdout);
		return;
	}

	if (trp -> flags & ST_NODE) {
		printindent(lvl, Xstdout);
		Xfputs("node:\n", Xstdout);
		show_stree((syntaxtree *)(trp -> comm), lvl + 1);
	}
	else if (!(trp -> comm)) {
		printindent(lvl, Xstdout);
		Xfputs("body: NULL", Xstdout);
		VOID_C fputnl(Xstdout);
	}
	else {
		printindent(lvl, Xstdout);
		if (isstatement(trp -> comm)) {
			id = (trp -> comm) -> id;
			i = id - 1;
			if (id == SM_CHILD) Xfputs("sub shell", Xstdout);
			else if ((id == SM_STATEMENT))
				Xfputs("statement", Xstdout);
			else Xfputs(statementlist[i].ident, Xstdout);
			Xfputs(":\n", Xstdout);
			show_stree(statementbody(trp), lvl + 1);
		}
		else for (i = 0; i <= (trp -> comm) -> argc; i++) {
			if (!((trp -> comm) -> argv)
			|| !((trp -> comm) -> argv[i]))
				Xfputs("NULL", Xstdout);
			else if (!i && isbuiltin(trp -> comm))
				argfputs((trp -> comm) -> argv[i], Xstdout);
			else VOID_C Xprintf("\"%a\"",
				(trp -> comm) -> argv[i]);
			Xfputc((i < (trp -> comm) -> argc) ? ' ' : '\n',
				Xstdout);
		}
		for (rp = (trp -> comm) -> redp; rp; rp = rp -> next) {
			printindent(lvl, Xstdout);
			VOID_C Xprintf("redirect %d", rp -> fd);
			if (!(rp -> filename)) Xfputs(">-: ", Xstdout);
			else if (rp -> type & MD_HEREDOC)
				VOID_C Xprintf(">> \"%a\": ",
					((heredoc_t *)(rp -> filename))
						-> eof);
			else VOID_C Xprintf("> \"%a\": ", rp -> filename);
			VOID_C Xprintf("%06o\n", (int)(rp -> type));
		}
	}
	if (trp -> type) {
		for (i = 0; i < OPELISTSIZ; i++)
			if (trp -> type == opelist[i].op) break;
		if (i < OPELISTSIZ) {
			printindent(lvl, Xstdout);
			Xfputs(opelist[i].symbol, Xstdout);
			VOID_C fputnl(Xstdout);
		}
	}
	if (trp -> next) {
		if (trp -> cont & (CN_QUOT | CN_ESCAPE)) {
			rp = (redirectlist *)(trp -> next);
			printindent(lvl, Xstdout);
			VOID_C Xprintf("continuing...\"%a\"\n",
				rp -> filename);
		}
# ifndef	MINIMUMSHELL
		else if (trp -> flags & ST_BUSY);
# endif
		else show_stree(trp -> next, lvl);
	}
}
#endif	/* SHOWSTREE */

VOID setshfunc(ident, trp)
char *ident;
syntaxtree *trp;
{
	int i;

	for (i = 0; shellfunc[i].ident; i++)
		if (!strcommcmp(ident, shellfunc[i].ident)) break;
	if (shellfunc[i].ident) {
		Xfree(shellfunc[i].ident);
		freestree(shellfunc[i].func);
		Xfree(shellfunc[i].func);
	}
	else {
		shellfunc = (shfunctable *)Xrealloc(shellfunc,
			(i + 2) * sizeof(shfunctable));
		shellfunc[i + 1].ident = NULL;
	}
	shellfunc[i].ident = ident;
	shellfunc[i].func = trp;
#ifndef	_NOUSEHASH
	if (hashahead) check_stree(trp);
#endif
}

static int NEAR dosetshfunc(ident, trp)
CONST char *ident;
syntaxtree *trp;
{
	syntaxtree *functr;
	char *new;
	int len;

#ifndef	BASHSTYLE
	/* bash allows any character in the function identifier */
	if (identcheck(ident, '\0') <= 0) {
		execerror(NULL, ident, ER_NOTIDENT, 0);
		return(-1);
	}
#endif
	new = Xstrdup(ident);
	stripquote(new, EA_STRIPQ);
	len = strlen(new);
#ifndef	BASHSTYLE
	/* bash distinguishes the same named function and variable */
	if (unset(new, len) < 0) {
		Xfree(new);
		return(RET_FAIL);
	}
#endif

	trp = trp -> next;
	if (!(functr = statementbody(trp))) {
		Xfree(new);
		return(RET_FAIL);
	}
	(trp -> comm) -> argv =
		(char **)duplstree2(functr, NULL, functr -> lineno);
	functr -> flags |= ST_TOP;
	setshfunc(new, functr);
#ifdef	DEP_PTY
	if (parentfd >= 0 && mypid == shellpid) {
		sendparent(TE_ADDFUNCTION, ident, functr);
		nownstree(functr);
	}
#endif

	return(RET_SUCCESS);
}

int unsetshfunc(ident, len)
CONST char *ident;
int len;
{
	int i;

	if (checkronly(ident, len) < 0) return(-1);
	for (i = 0; shellfunc[i].ident; i++)
		if (!strncommcmp(ident, shellfunc[i].ident, len)
		&& !shellfunc[i].ident[len])
			break;
	if (!shellfunc[i].ident) return(0);
	Xfree(shellfunc[i].ident);
	freestree(shellfunc[i].func);
	Xfree(shellfunc[i].func);
	for (; shellfunc[i + 1].ident; i++) {
		shellfunc[i].ident = shellfunc[i + 1].ident;
		shellfunc[i].func = shellfunc[i + 1].func;
	}
	shellfunc[i].ident = NULL;
#ifdef	DEP_PTY
	sendparent(TE_DELETEFUNCTION, ident, len);
#endif

	return(0);
}

static int NEAR exec_statement(trp)
syntaxtree *trp;
{
	int id;

	if ((id = getstatid(trp = statementbody(trp))) < 0)
		return(seterrno(EINVAL));

	return((*(statementlist[id].func))(trp));
}

#if	MSDOS
int exec_simplecom(trp, type, id)
syntaxtree *trp;
int type, id;
#else
int exec_simplecom(trp, type, id, bg)
syntaxtree *trp;
int type, id, bg;
#endif
{
#ifdef	DEP_FILECONV
	int i;
#endif
#if	!MSDOS
	p_id_t pid;
#endif
	command_t *comm;
	char *path;
	int ret;

	comm = trp -> comm;

#ifdef	DEP_FILECONV
	if (defaultkcode == NOCNV || defaultkcode == DEFCODE) /*EMPTY*/;
	else if (type == CT_FDORIGINAL || type == CT_FDINTERNAL
	|| (type == CT_BUILTIN && (shbuiltinlist[id].flags & BT_FILENAME))) {
		for (i = 1; i < comm -> argc; i++)
			renewkanjiconv(&(comm -> argv[i]),
				defaultkcode, DEFCODE, L_FNAME);
	}
#endif

	if (type == CT_NONE) return(ret_status);
	if (type == CT_BUILTIN) {
		if (!shbuiltinlist[id].func) return(RET_FAIL);
		if (!restricted || !(shbuiltinlist[id].flags & BT_RESTRICT))
			return((*(shbuiltinlist[id].func))(trp));
		execerror(comm -> argv, NULL, ER_RESTRICTED, 0);
		return(RET_FAIL);
	}
#ifdef	FD
	if (type == CT_FDORIGINAL)
		return(execbuiltin(id, comm -> argc, comm -> argv));
	if (type == CT_FDINTERNAL) {
		if (shellmode) return(RET_FAIL);
		resetsignal(0);
		ret = execinternal(id, comm -> argc, comm -> argv);
		setsignal();
		return(ret);
	}
#endif
#if	MSDOS
	if (type == CT_LOGDRIVE) {
		if (setcurdrv(id, 1) >= 0) return(RET_SUCCESS);
		execerror(comm -> argv, NULL, ER_INVALDRIVE, 2);
		return(RET_FAIL);
	}
#endif
	if (type == CT_FUNCTION) {
		if (!shellfunc[id].func) return(RET_FAIL);
		return(doshfunc(trp, id));
	}

	if (!(path = evalexternal(trp -> comm))) return(ret_status);
#if	MSDOS
	if ((ret = spawnve(P_WAIT, path, comm -> argv, exportvar)) < 0
	&& errno == ENOENT) {
		execerror(comm -> argv, NULL, ER_COMNOFOUND, 1);
		return(RET_NOTFOUND);
	}
#else	/* !MSDOS */
# ifndef	BASHSTYLE
	/* bash does fork() all processes even in a sub shell */
	if (childdepth && !(trp -> next)) bg = 1;
# endif
	if (bg || isopbg(trp) || isopnown(trp)) {
		searchheredoc(trp, 1);
		Xexecve2(path, comm -> argv, exportvar, 1);
	}

	if ((pid = makechild(1, (p_id_t)-1)) < (p_id_t)0) return(-1);
	if (!pid) Xexecve2(path, comm -> argv, exportvar, 1);

	ret = waitchild(pid, trp);
#endif	/* !MSDOS */

	return(ret);
}

static char **NEAR checkshellbuiltinargv(argc, argv)
int argc;
char **argv;
{
	char *tmp;
	int id, type;

	isshellbuiltin = 0;
#ifdef	BASHSTYLE
	/* bash does not use IFS as a command separator */
	argc = evalifs(argc, &argv, " \t");
#else
	argc = evalifs(argc, &argv, getifs());
#endif
	if (!argc) return(argv);

	stripquote(tmp = Xstrdup(argv[0]), EA_STRIPQ);
	type = checktype2(tmp, &id, 0, 1);
	Xfree(tmp);
#ifdef	STRICTPOSIX
	if (type == CT_BUILTIN && (shbuiltinlist[id].flags & BT_POSIXSPECIAL))
		isshellbuiltin = 1;
#else
	if (type != CT_NONE && type != CT_COMMAND) isshellbuiltin = 1;
#endif

	return(argv);
}

static int NEAR checkshellbuiltin(trp)
syntaxtree *trp;
{
	syntaxtree *next;
	char **argv, **subst;
	int ret, *len, argc, duperrno;

	duperrno = errno;
	isshellbuiltin = 0;
	if (!trp || !(trp -> comm) || isstatement(trp -> comm)) return(0);
	next = statementcheck(trp -> next, SM_STATEMENT);
	if (getstatid(next) == SM_LPAREN - 1) {
		errno = duperrno;
		return(0);
	}

	argv = duplvar((trp -> comm) -> argv, 2);
	argc = getsubst((trp -> comm) -> argc, argv, &subst, &len);

	if (substvar(argv, EA_BACKQ | EA_EVALIFS) < 0) ret = -1;
	else {
		argv = checkshellbuiltinargv(argc, argv);
		ret = 0;
	}

	freevar(subst);
	Xfree(len);
	freevar(argv);
	errno = duperrno;

	return(ret);
}

#if	MSDOS
static int NEAR exec_command(trp, contp)
syntaxtree *trp;
int *contp;
#else
static int NEAR exec_command(trp, contp, bg)
syntaxtree *trp;
int *contp, bg;
#endif
{
#ifndef	_NOUSEHASH
	hashlist **htable;
	char *pathvar;
#endif
	command_t *comm;
	char *ps, **argv, **subst, **svar, **evar;
	u_long esize;
	int i, ret, id, type, argc, nsubst, keepvar, *len;

	isshellbuiltin = execerrno = 0;
	comm = trp -> comm;
	argc = comm -> argc;
	argv = comm -> argv;
	if (interrupted) return(RET_INTR);
	if (argc <= 0) return(ret_status);

	type = 0;
	comm -> argv = duplvar(comm -> argv, 2);
	comm -> argc = getsubst(comm -> argc, comm -> argv, &subst, &len);

	id = evalargv(comm, &type, contp);
	if (id < 0 || (nsubst = substvar(subst, EA_BACKQ)) < 0) {
		freevar(subst);
		Xfree(len);
		comm -> argc = argc;
		freevar(comm -> argv);
		comm -> argv = argv;
		return(RET_FAIL);
	}
	if (nsubst > 0 && type == CT_NONE) ret_status = RET_SUCCESS;

	keepvar = 0;
#ifdef	BASHSTYLE
	/* bash treats the substitution before builtin as ineffective */
	if (type == CT_BUILTIN) keepvar = -1;
	else if (type != CT_NONE) keepvar = 1;
#else
	if (type == CT_COMMAND) keepvar = 1;
	/* bash treats the substitution before a function as effective */
	else if (type == CT_FUNCTION) keepvar = -1;
#endif
#ifdef	STRICTPOSIX
	if (type == CT_BUILTIN) {
		if (shbuiltinlist[id].flags & BT_POSIXSPECIAL)
			isshellbuiltin = 1;
		else keepvar = -1;
	}
#else
	if (type != CT_NONE && type != CT_COMMAND) isshellbuiltin = 1;
#endif

	if (keepvar < 0) {
		for (i = 0; subst[i]; i++) Xfree(subst[i]);
		subst[0] = NULL;
		keepvar = nsubst = 0;
	}

	if (keepvar) {
		shellvar = duplvar(svar = shellvar, 0);
		exportvar = duplvar(evar = exportvar, 0);
		esize = exportsize;
#ifndef	_NOUSEHASH
		htable = duplhash(hashtable);
		pathvar = getconstvar(ENVPATH);
#endif
	}
#ifdef	FAKEUNINIT
	else {
		/* fake for -Wuninitialized */
		svar = evar = NULL;
		esize = 0L;
# ifndef	_NOUSEHASH
		htable = NULL;
		pathvar = NULL;
# endif
	}
#endif

#ifdef	MINIMUMSHELL
	ps = PS4STR;
#else	/* !MINIMUMSHELL */
# ifdef	FD
	if ((ps = getconstvar(ENVPS4))) evalprompt(&ps, ps);
# else
	if ((ps = getconstvar(ENVPS4))) ps = evalvararg(ps, EA_BACKQ, 0);
# endif
#endif	/* !MINIMUMSHELL */
	while (nsubst--) {
#ifdef	FD
		demacroarg(&(subst[nsubst]));
#endif
		stripquote(subst[nsubst], EA_STRIPQ);
		if (putshellvar(subst[nsubst], len[nsubst]) < 0) {
			while (nsubst >= 0) Xfree(subst[nsubst--]);
			Xfree(subst);
			Xfree(len);
			comm -> argc = argc;
			freevar(comm -> argv);
			comm -> argv = argv;
			return(RET_FAIL);
		}
		if (type == CT_COMMAND)
			exportvar = putvar(exportvar,
				Xstrdup(subst[nsubst]), len[nsubst]);
		if (verboseexec) {
#ifdef	BASHSTYLE
	/* bash displays "+ " with substitutions, in -x mode */
			if (ps) Xfputs(ps, Xstderr);
			argfputs(subst[nsubst], Xstderr);
#else
			argfputs(subst[nsubst], Xstderr);
			if (nsubst) Xfputc(' ', Xstderr);
			else
#endif
			VOID_C fputnl(Xstderr);
		}
	}
	Xfree(subst);
	Xfree(len);

	if (verboseexec && comm -> argc) {
		if (ps) Xfputs(ps, Xstderr);
		argfputs(comm -> argv[0], Xstderr);
		for (i = 1; i < comm -> argc; i++)
			VOID_C Xfprintf(Xstderr, " %a", comm -> argv[i]);
		VOID_C fputnl(Xstderr);
	}
#ifndef	MINIMUMSHELL
	Xfree(ps);
#endif

	ret = exec_simplecom2(trp, type, id, bg);
	Xfpurge(Xstdin);
	Xfflush(Xstdout);
	Xfflush(Xstderr);

	if (keepvar) {
#ifndef	_NOUSEHASH
		if (pathvar != getconstvar(ENVPATH)) {
			VOID_C searchhash(NULL, NULL, NULL);
			hashtable = htable;
		}
		else if (htable) VOID_C searchhash(htable, NULL, NULL);
#endif
		freevar(shellvar);
		freevar(exportvar);
		shellvar = svar;
		exportvar = evar;
		exportsize = esize;
	}
#ifdef	FD
	if (type != CT_NONE && type != CT_FDINTERNAL && type != CT_FUNCTION)
		internal_status = FNC_FAIL;
#endif

	comm -> argc = argc;
	freevar(comm -> argv);
	comm -> argv = argv;
#if	!MSDOS && defined (FD)
	if (autosavetty) savestdio(0);
#endif

	return(ret);
}

#ifdef	USEFAKEPIPE
static int NEAR exec_process(trp)
syntaxtree *trp;
#else
static int NEAR exec_process(trp, pipein)
syntaxtree *trp;
p_id_t pipein;
#endif
{
#if	!MSDOS
	syntaxtree *tmptr;
	p_id_t pid;
	int bg, sub;
#endif
#ifndef	MINIMUMSHELL
	long dupshlineno;
#endif
	syntaxtree *next;
	int cont, ret;

	next = statementcheck(trp -> next, SM_STATEMENT);
	if (getstatid(next) != SM_LPAREN - 1) {
#if	!MSDOS
		tmptr = trp;
#endif
		next = NULL;
	}
#if	!MSDOS
	else tmptr = trp -> next;
#endif

#if	!MSDOS
	sub = 0;
# if	!defined (BASHSTYLE) && !defined (USEFAKEPIPE)
	/* bash does not treat the end of pipeline as sub shell */
	if (tmptr -> next || !(tmptr -> flags & ST_NEXT)) /*EMPTY*/;
	else if ((tmptr = getparent(trp)) && isoppipe(tmptr))
		sub = 1;
# endif

	if (!isstatement(trp -> comm)) /*EMPTY*/;
	else if ((trp -> comm) -> id == SM_CHILD) sub = 0;
# if	!defined (NOJOB) && defined (CHILDSTATEMENT)
	else if (jobok && mypid == orgpgrp) sub = 1;
# endif
# ifndef	BASHSTYLE
	/* bash does not treat redirected statements as sub shell */
	else if ((trp -> comm) -> redp) sub = 1;
# endif

	if (!sub) /*EMPTY*/;
	else if ((pid = makechild(1, (p_id_t)-1)) < (p_id_t)0) return(-1);
	else if (pid) {
# ifndef	NOJOB
		stackjob(pid, 0, -1, trp);
# endif
		return(waitchild(pid, trp));
	}
# ifndef	NOJOB
	else stackjob(mypid, 0, -1, trp);
# endif
#endif	/* !MSDOS */

	if (isstatement(trp -> comm)) {
		if ((trp -> comm) -> id == SM_CHILD) ret = dochild(trp);
		else ret = exec_statement(trp);
	}
	else if (next) ret = dosetshfunc((trp -> comm) -> argv[0], next);
	else for (;;) {
		cont = 0;
#ifndef	MINIMUMSHELL
		dupshlineno = shlineno;
		setshlineno(trp -> lineno);
#endif
#if	!MSDOS
		if (sub) bg = 1;
# ifndef	USEFAKEPIPE
		else if (!pipein) bg = 1;
# endif
# ifndef	NOJOB
		else if (ttypgrp < (p_id_t)0 && !(trp -> next)) bg = 1;
# endif
		else bg = 0;
#endif	/* !MSDOS */
		ret = exec_command2(trp, &cont, bg);

#ifndef	MINIMUMSHELL
		setshlineno(dupshlineno);
#endif
		if (ret < 0) break;
#ifdef	FD
		if (cont) continue;
#endif
		break;
	}

#if	!MSDOS
	if (sub) {
		prepareexit(1);
		Xexit((ret >= 0) ? ret : RET_NOTEXEC);
	}
#endif

	return(ret);
}

static int NEAR exec_stree(trp, cond)
syntaxtree *trp;
int cond;
{
#if	!MSDOS
	p_id_t pid;
#endif
	syntaxtree *tmptr;
	redirectlist *errp;
	p_id_t pipein;
	char *tmp;
	int ret, fd;

	exectrapcomm();
	if (breaklevel || continuelevel) {
		ret_status = RET_SUCCESS;
		return(ret_status);
	}

#ifndef	MINIMUMSHELL
	if (isopnot(trp)) {
		trp = skipfuncbody(trp);
		if (!(trp -> next)) return(RET_FAIL);
		if ((ret = exec_stree(trp -> next, cond)) < 0) return(-1);
		ret_status =
			(ret_status != RET_SUCCESS) ? RET_SUCCESS : RET_FAIL;
		return(ret_status);
	}
#endif

	fd = -1;
#if	!MSDOS
	if (!isopbg(trp) && !isopnown(trp)) pid = (p_id_t)-1;
	else if ((pid = makechild(0, (p_id_t)-1)) < (p_id_t)0) return(-1);
	else if (!pid) {
		nownstree(trp -> next);
# ifndef	NOJOB
		ttypgrp = (p_id_t)-1;
		if (!isopnown(trp)) stackjob(mypid, 0, -1, trp);
		if (jobok) /*EMPTY*/;
		else
# endif
		fd = Xopen(_PATH_DEVNULL, O_BINARY | O_RDONLY, 0666);
		if (fd >= 0) {
			Xdup2(fd, STDIN_FILENO);
			safeclose(fd);
		}
	}
	else {
		tmptr = trp -> next;
		trp -> next = NULL;
		nownstree(trp);
		trp -> next = tmptr;
# ifndef	NOJOB
		childpgrp = (p_id_t)-1;
		if (!isopnown(trp)) {
			prevjob = lastjob;
			lastjob = stackjob(pid, 0, -1, trp);
		}
# endif
		if (interactive && !nottyout) {
# ifndef	NOJOB
			if (jobok && !isopnown(trp)) {
				if (mypid == orgpgrp)
					VOID_C Xfprintf(Xstderr,
						"[%d] %id\n",
						lastjob + 1, pid);
			}
			else
# endif	/* !NOJOB */
			VOID_C Xfprintf(Xstderr, "%id\n", pid);
		}
		lastpid = pid;
		trp = skipfuncbody(trp);
		ret_status = RET_SUCCESS;
		if (!(trp -> next)) return(ret_status);
		return(exec_stree(trp -> next, cond));
	}
#endif	/* !MSDOS */

	tmptr = skipfuncbody(trp);
	if (!isoppipe(tmptr)) pipein = (p_id_t)-1;
	else if ((fd = openpipe2(&pipein, STDIN_FILENO, 0, 1, (p_id_t)-1)) < 0)
		return(-1);

#ifdef	FD
	replaceargs(NULL, NULL, NULL, 0);
#endif

	if (!(trp -> comm) || pipein > (p_id_t)0) {
#ifndef	NOJOB
		if (pipein > (p_id_t)0) stackjob(pipein, 0, fd, trp);
#endif
		ret = ret_status;
	}
	else if (trp -> flags & ST_NODE)
		ret = exec_stree((syntaxtree *)(trp -> comm), cond);
	else if (!((trp -> comm) -> redp)) {
		ret = exec_process2(trp, pipein);
		ret_status = (ret >= 0) ? ret : RET_FAIL;
	}
	else for (;;) {
		if (!(errp = doredirect((trp -> comm) -> redp)))
			ret = exec_process2(trp, pipein);
		else {
			if (checkshellbuiltin(trp) < 0) /*EMPTY*/;
			else if (errno < 0) /*EMPTY*/;
			else if (errp -> type & MD_HEREDOC)
				doperror(NULL, NULL);
			else {
				if (!(errp -> filename)) tmp = Xstrdup("-");
				else tmp = evalvararg(errp -> filename,
					EA_BACKQ | EA_STRIPQLATER, 0);
				if (tmp && !*tmp && *(errp -> filename)) {
					Xfree(tmp);
					tmp = Xstrdup(errp -> filename);
				}
				if (tmp) {
					if (errno) doperror(NULL, tmp);
					else execerror(NULL,
						tmp, ER_RESTRICTED, 2);
					Xfree(tmp);
				}
			}
			ret = RET_FAIL;
		}
		closeredirect((trp -> comm) -> redp);
		ret_status = (ret >= 0) ? ret : RET_FAIL;
#ifdef	FD
		if (!errp && ((trp -> comm) -> redp) -> type & MD_REST)
			continue;
#endif
		break;
	}
	trp = tmptr;

	if (returnlevel || ret < 0) /*EMPTY*/;
	else if (pipein >= (p_id_t)0
	&& (fd = reopenpipe(fd, RET_SUCCESS)) < 0) {
		pipein = (p_id_t)-1;
		ret = -1;
	}
	else {
#if	!MSDOS
		if (!pid) {
			searchheredoc(trp, 1);
			prepareexit(1);
			Xexit(RET_SUCCESS);
		}
#endif
#ifndef	NOJOB
		if (pipein > (p_id_t)0) stackjob(pipein, 0, fd, trp);
#endif

		if (interrupted) {
			breaklevel = loopdepth;
			ret = RET_INTR;
		}
		else if (!(trp -> next)) /*EMPTY*/;
		else if (errorexit && !cond && isopfg(trp) && ret_status)
			breaklevel = loopdepth;
		else if (isopand(trp) && ret_status) {
			if (errorexit && !cond) {
				breaklevel = loopdepth;
				ret = RET_SUCCESS;
			}
		}
		else if (isopor(trp) && !ret_status) /*EMPTY*/;
		else ret = exec_stree(trp -> next, cond);
	}
	if (pipein >= (p_id_t)0) VOID_C closepipe2(fd, -1);
	exectrapcomm();

	return(ret);
}

static syntaxtree *NEAR execline(command, stree, trp, noexit)
CONST char *command;
syntaxtree *stree, *trp;
int noexit;
{
	int ret;

	if (command == (char *)-1) {
		if (!(trp = analyzeeof(trp))) {
			freestree(stree);
			return(NULL);
		}
	}
	else {
		if (!(trp = analyze(command, trp, 0))) {
			freestree(stree);
			if (errorexit && !noexit) {
				Xfree(stree);
				Xexit2(RET_SYNTAXERR);
			}
			return(NULL);
		}
		if (trp -> cont) return(trp);
	}

	if (notexec) {
		ret = RET_SUCCESS;
#ifdef	SHOWSTREE
		show_stree(stree, 0);
#endif
	}
#ifndef	_NOUSEHASH
	else if (hashahead && check_stree(stree) < 0)
		ret = ret_status = RET_FAIL;
#endif
	else ret = exec_stree(stree, 0);
	errorexit = tmperrorexit;
	freestree(stree);

	if (ret < 0 && errno) doperror(NULL, NULL);
	if ((errorexit && ret && !noexit) || terminated) {
		Xfree(stree);
		Xexit2(ret);
	}

	return(NULL);
}

static int NEAR exec_line(command)
CONST char *command;
{
	static syntaxtree *stree = NULL;
	static syntaxtree *trp = NULL;
	int ret;

	if (!command) {
		freestree(stree);
		Xfree(stree);
		stree = trp = NULL;
		return(0);
	}

	setsignal();
	if (verboseinput && command != (char *)-1) {
		argfputs(command, Xstderr);
		VOID_C fputnl(Xstderr);
	}
	if (!stree) trp = stree = newstree(NULL);

#ifndef	NOJOB
	childpgrp = (p_id_t)-1;
#endif
	if ((trp = execline(command, stree, trp, 0))) ret = trp -> cont;
	else {
		Xfree(stree);
		stree = trp = NULL;
		ret = 0;
	}
	resetsignal(0);

	return(ret);
}

static int NEAR _dosystem(command)
CONST char *command;
{
	static syntaxtree *trp = NULL;
	int ret;

	if (!command) ret = 0;
	else if (!(trp = analyzeline(command))) ret = RET_SYNTAXERR;
	else {
		if (notexec) {
			ret = RET_SUCCESS;
#ifdef	SHOWSTREE
			show_stree(trp, 0);
#endif
		}
#ifndef	_NOUSEHASH
		else if (hashahead && check_stree(trp) < 0)
			ret = RET_FAIL;
#endif
		else ret = exec_stree(trp, 0);
		errorexit = tmperrorexit;
	}
	freestree(trp);
	Xfree(trp);
	trp = NULL;

	return((ret >= 0) ? ret : RET_FAIL);
}

#ifndef	FDSH
int dosystem(command)
CONST char *command;
{
	int ret;

	shellpid = mypid;
# ifndef	NOJOB
	orgpgrp = mypid;
	childpgrp = (p_id_t)-1;
# endif
# ifdef	DEP_PTY
	if (!command) return(shell_loop((isshptymode()) ? 0 : 1));
# else
	if (!command) return(shell_loop(1));
# endif
	setsignal();
	ret = _dosystem(command);
	resetsignal(0);

	return(ret);
}
#endif	/* !FDSH */

static XFILE *NEAR _dopopen(command)
CONST char *command;
{
#ifndef	USEFAKEPIPE
	pipelist **prevp;
#endif
#ifdef	CYGWIN
	struct timeval tv;
#endif
	syntaxtree *trp;
	p_id_t pipein;
	int fd;

	trp = (command) ? analyzeline(command) : NULL;

	nottyout++;
	fd = openpipe2(&pipein, STDIN_FILENO, 1, 1, mypid);
#ifndef	USEFAKEPIPE
	prevp = searchpipe(fd);
#endif

	if (fd < 0) /*EMPTY*/;
	else if (pipein) {
#ifdef	CYGWIN
	/* a trick for buggy terminal emulation */
		tv.tv_sec = 0L;
		tv.tv_usec = 500000L;	/* maybe enough waiting limit */
		VOID_C readselect(1, &fd, NULL, &tv);
#endif
	}
	else {
#ifndef	USEFAKEPIPE
		if (prevp && !((*prevp) -> file)) nownstree(trp);
		prevp = NULL;
#endif
		if (!trp) ret_status = RET_SYNTAXERR;
		else if (!(trp -> comm)) ret_status = RET_SUCCESS;
		else if (notexec) {
#ifdef	SHOWSTREE
			show_stree(trp, 0);
#endif
			pipein = (p_id_t)-1;
		}
#ifndef	_NOUSEHASH
		else if (hashahead && check_stree(trp) < 0)
			pipein = (p_id_t)-1;
#endif
		else if (exec_stree(trp, 0) < 0) pipein = (p_id_t)-1;

		searchheredoc(trp, 1);
		if (pipein >= (p_id_t)0) fd = reopenpipe(fd, ret_status);
		else {
			VOID_C closepipe2(fd, -1);
			fd = -1;
		}
	}

#ifndef	USEFAKEPIPE
	if (prevp) (*prevp) -> trp = trp;
	else
#endif
	{
		freestree(trp);
		Xfree(trp);
	}
	errorexit = tmperrorexit;
	nottyout--;

	return((fd >= 0) ? fdopenpipe(fd) : NULL);
}

#ifndef	FDSH
XFILE *dopopen(command)
CONST char *command;
{
	XFILE *fp;

	shellpid = mypid;
# ifndef	NOJOB
	orgpgrp = mypid;
	childpgrp = (p_id_t)-1;
# endif
	setsignal();
	fp = _dopopen(command);
	resetsignal(0);

	return(fp);
}

int dopclose(fp)
XFILE *fp;
{
	return(closepipe2(Xfileno(fp), -1));
}
#endif	/* !FDSH */

static int NEAR sourcefile(fd, fname, verbose)
int fd;
CONST char *fname;
int verbose;
{
#ifndef	MINIMUMSHELL
	long dupshlineno;
#endif
	syntaxtree *stree, *trp;
	char *buf;
	long n;
	int ret, dupinteractive;

	dupinteractive = interactive;
	interactive = 0;
#ifndef	MINIMUMSHELL
	dupshlineno = shlineno;
	setshlineno(1L);
#endif
	trp = stree = newstree(NULL);
	n = 0L;
	ret = errno = 0;
#if	!MSDOS
	closeonexec(fd);
#endif
	while ((buf = readline(fd, '\0')) != vnullstr) {
		if (!buf) {
			if (errno != EINTR) doperror(NULL, fname);
			ret++;
			ret_status = RET_FAIL;
			break;
		}

		if (!trp) trp = stree;
		trp = execline(buf, stree, trp, 1);
		n++;
		if (ret_status == RET_NOTICE || syntaxerrno || execerrno) {
			ret++;
			if (verbose)
				VOID_C Xfprintf(Xstderr,
					"%k:%ld: %s\n", fname, n, buf);
			ret_status = RET_FAIL;
		}
		Xfree(buf);
		if (syntaxerrno) break;
#ifndef	BASHSTYLE
		if (execerrno) break;
#endif
#ifndef	MINIMUMSHELL
		setshlineno(shlineno + 1L);
#endif
	}

	execline((char *)-1, stree, trp, 1);
	if (syntaxerrno) ret_status = RET_SYNTAXERR;
	Xfree(stree);
	interactive = dupinteractive;
#ifndef	MINIMUMSHELL
	setshlineno(dupshlineno);
#endif

	return(ret);
}

int execruncom(fname, verbose)
CONST char *fname;
int verbose;
{
#ifdef	MINIMUMSHELL
	char *cp, path[MAXPATHLEN];
#endif
	lockbuf_t *lck;
	char *new, **dupargvar;
	int ret, duprestricted;

	setsignal();
#ifdef	MINIMUMSHELL
	if (*fname == '~' && fname[1] == _SC_ && (cp = getconstvar(ENVHOME))) {
		strcatdelim2(path, cp, &(fname[2]));
		fname = path;
	}
#endif
	new = Xstrdup(fname);
#if	MSDOS && !defined (BSPATHDELIM)
	new = adjustpname(new);
#endif
	fname = new = evalpath(new, 0);
	ret = RET_SUCCESS;
	if (noruncom || !isrootdir(fname)) lck = NULL;
	else if (!(lck = lockopen(fname, O_TEXT | O_RDONLY, 0666))) {
		doperror(NULL, fname);
		ret = RET_FAIL;
	}
	else if (lck -> fd >= 0) {
#ifdef	FD
		inruncom = 1;
#endif
		duprestricted = restricted;
		restricted = 0;
		dupargvar = argvar;
		argvar = (char **)Xmalloc(2 * sizeof(char *));
		argvar[0] = Xstrdup(fname);
		argvar[1] = NULL;
		ret = sourcefile(lck -> fd, fname, verbose);
		freevar(argvar);
		argvar = dupargvar;
		restricted = duprestricted;
#ifdef	FD
		inruncom = 0;
#endif
	}
	lockclose(lck);
	resetsignal(0);
	Xfree(new);

	return(ret);
}

#if	MSDOS && !defined (BSPATHDELIM)
static VOID NEAR adjustdelim(var)
char *CONST *var;
{
	char *cp;
	int i;

	if (!var) return;
	for (i = 0; var[i]; i++) {
		if (!(cp = Xstrchr(var[i], '='))) continue;
		if (searchvar2(ADJUSTVARSIZ, adjustvar, var[i], cp - var[i]))
			adjustpname(cp);
	}
}
#endif	/* MSDOS && !BSPATHDELIM */

VOID setshellvar(envp)
char *CONST *envp;
{
	char *cp;
	int i, len;

	shellvar = duplvar(envp, 0);
	exportvar = duplvar(envp, 0);
#if	MSDOS && !defined (BSPATHDELIM)
	adjustdelim(shellvar);
	adjustdelim(exportvar);
#endif
	exportsize = (u_long)0;
	for (i = 0; exportvar[i]; i++)
		exportsize += (u_long)strlen(exportvar[i]) + 1;
	exportlist = (char **)Xmalloc((i + 1) * sizeof(char *));
	for (i = 0; exportvar[i]; i++) {
		len = ((cp = Xstrchr(exportvar[i], '=')))
			? cp - exportvar[i] : strlen(exportvar[i]);
		exportlist[i] = Xstrndup(exportvar[i], len);
	}
	exportlist[i] = NULL;
	ronlylist = duplvar(NULL, 0);
}

int prepareterm(VOID_A)
{
#ifdef	FD
	CONST char *term;
#endif

	if (ttyio >= 0 && ttyout) return(0);

	if (opentty(&ttyio, &ttyout) < 0) {
		closetty(&ttyio, &ttyout);
		nottyout++;
		if (interactive) return(-1);
	}

#ifndef	NOJOB
	if (!interactive) oldttypgrp = (p_id_t)-1;
	else if (gettcpgrp(ttyio, &oldttypgrp) < (p_id_t)0) {
		closetty(&ttyio, &ttyout);
		if ((ttyio = newdup(Xdup(STDIN_FILENO))) < 0
		|| opentty(&ttyio, &ttyout) < 0) {
			closetty(&ttyio, &ttyout);
			return(-1);
		}
	}
#endif	/* !NOJOB */

#ifdef	FD
	if (ttyio >= 0) inittty(0);
	if (!(term = getconstvar(ENVTERM))) term = nullstr;
	getterment(term);
#endif

	return(0);
}

static VOID NEAR initrc(verbose)
int verbose;
{
#if	!defined (MINIMUMSHELL) \
|| (MSDOS && defined (FD) && !defined (BSPATHDELIM))
	CONST char *cp;
#endif

#ifdef	FD
	execruncom(DEFRC, verbose);
#else
	if (loginshell) {
		execruncom(LSH_DEFRC, verbose);
		execruncom(LSH_RCFILE, verbose);
	}
#endif
#ifndef	MINIMUMSHELL
# if	!MSDOS
	if (getuid() != geteuid() || getgid() != getegid()) /*EMPTY*/;
	else
# endif
	if ((cp = getconstvar(ENVENV))) execruncom(cp, verbose);
#endif	/* !MINIMUMSHELL */
#ifdef	FD
	if (loginshell) execruncom(SH_RCFILE, verbose);
	else execruncom(FD_RCFILE, verbose);
# ifdef	DEP_PTY
	if (interactive) shptymode = tmpshptymode;
# endif
#endif	/* FD */
}

int initshell(argc, argv)
int argc;
char *CONST *argv;
{
#if	MSDOS
	char *tmp;
#else	/* !MSDOS */
# if	!defined (NOJOB) && defined (NTTYDISC) && defined (ldisc)
	ldiscioctl_t tty;
# endif
# ifndef	MINIMUMSHELL
	char buf[MAXLONGWIDTH + 1];
# endif
	sigmask_t mask;
#endif	/* !MSDOS */
	CONST char *cp, *cp2;
	int i, n, isstdin, tmprestricted;

	shellname = argv[0];
	getshellname(getbasename(shellname), &loginshell, &tmprestricted);

	definput = STDIN_FILENO;
	if (!interactive && isatty(STDIN_FILENO) && isatty(STDOUT_FILENO))
		interactive = 1;
	orgpid = shellpid = mypid = getpid();
#ifndef	NOJOB
	ttypgrp = mypid;
	if ((orgpgrp = getpgroup()) < (p_id_t)0) {
		doperror(NULL, NULL);
		return(-1);
	}
#endif

	if ((n = getoption(argc, argv, 1)) < 0) return(-1);
#if	!defined (MINIMUMSHELL) && defined (BASHSTYLE)
	noclobber = 0;
#endif
	errorexit = tmperrorexit;
#ifdef	DEP_PTY
	shptymode = tmpshptymode;
#endif
	isstdin = forcedstdin;

	if (n > 2) {
		shellmode = 1;
		interactive = interactive_io;
	}
	else if (n >= argc || isstdin) isstdin = 1;
	else {
		definput = newdup(Xopen(argv[n], O_BINARY | O_RDONLY, 0666));
		if (definput < 0) {
			VOID_C Xfprintf(Xstderr, "%k: cannot open\n", argv[n]);
			return(-1);
		}
		if (!interactive_io || !isatty(definput)) {
			interactive = 0;
#if	!MSDOS
			closeonexec(definput);
#endif
		}
		else {
			if (Xdup2(definput, STDIN_FILENO) < 0) {
				VOID_C Xfprintf(Xstderr,
					"%k: cannot open\n", argv[n]);
				return(-1);
			}
			safeclose(definput);
			definput = STDIN_FILENO;
		}
	}
	interactive_io = interactive;

	if (prepareterm() < 0) {
		doperror(NULL, NULL);
		return(-1);
	}
#if	!MSDOS && defined (FD)
	if (autosavetty) savestdio(0);
#endif

	Xsetbuf(Xstdin);
	Xsetlinebuf(Xstderr);
	Xsetlinebuf(Xstdout);
#if	!MSDOS
	Xsigemptyset(mask);
	Xsigblock(oldsigmask, mask);
#endif	/* !MSDOS */

	if (ttyio >= 0 && definput == STDIN_FILENO && isatty(STDIN_FILENO))
		definput = ttyio;

	getvarfunc = getshellvar;
	putvarfunc = putshellvar;
	getretvalfunc = getretval;
	getpidfunc = getorgpid;
	getlastpidfunc = getlastpid;
	getflagfunc = getflagstr;
	checkundeffunc = checkundefvar;
	exitfunc = safeexit;
	backquotefunc = evalbackquote;
#ifndef	MINIMUMSHELL
	posixsubstfunc = evalposixsubst;
#endif
	shellfunc = (shfunctable *)Xmalloc(1 * sizeof(shfunctable));
	shellfunc[0].ident = NULL;
#ifndef	NOALIAS
	shellalias = (shaliastable *)Xmalloc(1 * sizeof(shaliastable));
	shellalias[0].ident = NULL;
#endif
#ifndef	_NOUSEHASH
	VOID_C searchhash(NULL, NULL, NULL);
#endif
	if ((!interactive || isstdin) && n < argc) {
		argvar = (char **)Xmalloc((argc - n + 1 + isstdin)
			* sizeof(char *));
		for (i = 0; i < argc - n + 1; i++)
			argvar[i + isstdin] = Xstrdup(argv[i + n]);
		if (isstdin) argvar[0] = Xstrdup(argv[0]);
	}
	else {
		argvar = (char **)Xmalloc(2 * sizeof(char *));
		argvar[0] = Xstrdup(argv[0]);
		argvar[1] = NULL;
	}

#if	MSDOS
	if (!(tmp = getconstvar(ENVPATH))) setenv2(ENVPATH, DEFPATH, 1);
	else for (i = 0; tmp[i]; i++) if (tmp[i] == ';') tmp[i] = PATHDELIM;
#else
	if (!getconstvar(ENVPATH)) setenv2(ENVPATH, DEFPATH, 1);
	if (!getconstvar(ENVTERM)) setenv2(ENVTERM, DEFTERM, 1);
#endif

	if (interactive) {
		if (getconstvar(ENVPS1)) /*EMPTY*/;
#if	!MSDOS
		else if (!getuid()) setenv2(ENVPS1, PS1ROOT, 0);
#endif
		else setenv2(ENVPS1, PS1STR, 0);
		if (!getconstvar(ENVPS2)) setenv2(ENVPS2, PS2STR, 0);
	}
	else {
		for (i = 0; i < PRIMALVARSIZ; i++)
			if (!strenvcmp(primalvar[i], ENVPS1)
			|| !strenvcmp(primalvar[i], ENVPS2))
				primalvar[i] = NULL;
		unset(ENVPS1, strsize(ENVPS1));
		unset(ENVPS2, strsize(ENVPS2));
	}
#ifndef	MINIMUMSHELL
	if (!getconstvar(ENVPS4)) setenv2(ENVPS4, PS4STR, 0);
	setshlineno(1L);
# if	!MSDOS
	VOID_C Xsnprintf(buf, sizeof(buf), "%id", getppid());
	setenv2(ENVPPID, buf, 0);
# endif
#endif
	setenv2(ENVIFS, IFS_SET, 0);
	if (loginshell) {
#ifndef	NOUID
		getlogininfo(&cp, &cp2);
#endif
		if (!getconstvar(ENVHOME)) {
#ifndef	NOUID
			if (cp) /*EMPTY*/;
			else
#endif
			cp = rootpath;
			setenv2(ENVHOME, cp, 1);
		}
		if (!getconstvar(ENVSHELL)) {
#ifndef	NOUID
			if (cp2) /*EMPTY*/;
			else
#endif
			cp2 = shellname;
			setenv2(ENVSHELL, cp2, 1);
		}
	}
#if	!MSDOS && !defined (MINIMUMSHELL)
# ifdef	BASHSTYLE
	if (!getconstvar(ENVMAILCHECK)) setenv2(ENVMAILCHECK, "60", 0);
# else
	if (!getconstvar(ENVMAILCHECK)) setenv2(ENVMAILCHECK, "600", 0);
# endif
	if ((cp = getconstvar(ENVMAILPATH))) replacemailpath(cp, 1);
	else if ((cp = getconstvar(ENVMAIL))) replacemailpath(cp, 0);
#endif	/* !MSDOS && !MINIMUMSHELL */
#ifndef	NOPOSIXUTIL
	setenv2(ENVOPTIND, "1", 0);
#endif

	for (i = 0; i < NSIG; i++) {
		trapmode[i] = 0;
		trapcomm[i] = NULL;
		if ((oldsigfunc[i] = (sigcst_t)signal2(i, SIG_DFL)) == SIG_ERR)
			oldsigfunc[i] = SIG_DFL;
		else signal2(i, oldsigfunc[i]);
	}
	for (i = 0; signallist[i].sig >= 0; i++)
		trapmode[signallist[i].sig] = signallist[i].flags;
#ifndef	NOJOB
	if (!interactive || ttyio < 0) jobok = 0;
	else {
		if (!orgpgrp) {
			orgpgrp = mypid;
			if (setpgroup(0, mypid) < (p_id_t)0
			|| settcpgrp(ttyio, orgpgrp) < 0) {
				doperror(NULL, NULL);
				return(-1);
			}
		}
		VOID_C gettcpgrp(ttyio, &ttypgrp);
# ifdef	SIGTTIN
		while (ttypgrp >= (p_id_t)0 && ttypgrp != orgpgrp) {
			signal2(SIGTTIN, SIG_DFL);
			VOID_C kill(0, SIGTTIN);
			signal2(SIGTTIN, oldsigfunc[SIGTTIN]);
			VOID_C gettcpgrp(ttyio, &ttypgrp);
		}
# endif
# if	defined (NTTYDISC) && defined (ldisc)
		if (tioctl(ttyio, REQGETD, &tty) < 0) {
			doperror(NULL, NULL);
			return(-1);
		}
		else if (ldisc(tty) != NTTYDISC && (ldisc(tty) = NTTYDISC) > 0
		&& tioctl(ttyio, REQSETD, &tty) < 0) {
#  ifdef	USESGTTY
			doperror(NULL, NULL);
			return(-1);
#  else	/* !USESGTTY */
#   ifdef	ENODEV
			if (errno == ENODEV) /*EMPTY*/;
			else
#   endif
			jobok = 0;
#  endif	/* !USESGTTY */
		}
# endif	/* NTTYDISC && ldisc */
		if (orgpgrp != mypid) {
			orgpgrp = mypid;
			if (setpgroup(0, mypid) < (p_id_t)0
			|| (mypid != ttypgrp && gettermio(orgpgrp, 1) < 0)) {
				doperror(NULL, NULL);
				return(-1);
			}
		}

		if (jobok < 0) jobok = 1;
	}
#endif	/* !NOJOB */
#if	!MSDOS
	closeonexec(ttyio);
#endif

	if (n > 2) {
#ifdef	FD
		initenv();
# ifdef	DEP_PTY
		if (isshptymode()) checkscreen(0, 0);
# endif
# ifndef	_NOCUSTOMIZE
		saveorigenviron();
# endif
#endif	/* FD */
		initrc(0);
#ifdef	FD
		initfd(argv);
#endif
		if (verboseinput) {
			kanjifputs(argv[2], Xstderr);
#ifdef	BASHSTYLE
	/* bash displays a newline with single string command, in -v mode */
			VOID_C fputnl(Xstderr);
#endif
			Xfflush(Xstderr);
		}
#ifdef	DEP_PTY
		if (isshptymode()) {
			shellpid = (p_id_t)-1;
			n = ptymacro(argv[2], NULL, F_DOSYSTEM);
		}
		else
#endif
		{
			setsignal();
			n = _dosystem(argv[2]);
			resetsignal(0);
		}
		Xexit2(n);
	}

	n = 0;
#ifdef	BASHSTYLE
	/* bash changes the current directory if the variable PWD is set */
	if ((cp = getconstvar(ENVPWD))) n = chdir2(cp);
	else
#endif
	if (loginshell && interactive) n = chdir2(getconstvar(ENVHOME));
	if (n < 0) {
#ifdef	FD
		initfd(argv);
#endif
		Xfputs("No directory", Xstderr);
		VOID_C fputnl(Xstderr);
		Xexit2(RET_FAIL);
	}

	if (!restricted) restricted = tmprestricted;
	if (!restricted) {
#ifdef	FD
		cp = getenv2("FD_SHELL");
#else
		cp = getconstvar(ENVSHELL);
#endif
		if (cp) getshellname(getbasename(cp), NULL, &restricted);
	}
#ifdef	FD
	fd_restricted = restricted;
#endif

	return(0);
}

int shell_loop(pseudoexit)
int pseudoexit;
{
	char *ps, *buf;
	int cont;

	shellmode = 1;
	setsignal();
	cont = 0;
	if (pseudoexit) exit_status = -1;
	for (;;) {
		if (trapok >= 0) trapok = 1;
		if (!interactive) buf = readline(definput, '\0');
		else {
#if	!MSDOS
			checkmail(0);
#endif
			ps = (cont)
				? getconstvar(ENVPS2) : getconstvar(ENVPS1);
#ifdef	FD
			if (dumbterm > 1) {
				evalprompt(&buf, ps);
				kanjifputs(buf, Xstderr);
				Xfree(buf);
				Xfflush(Xstderr);
				buf = readline(definput, '\0');
			}
			else {
				ttyiomode(1);
				buf = inputshellstr(ps, -1, NULL);
				stdiomode();
				if (!buf) continue;
			}
#else	/* !FD */
			kanjifputs(ps, Xstderr);
			Xfflush(Xstderr);
			buf = readline(definput, '\0');
#endif	/* !FD */
		}
		if (trapok >= 0) trapok = 0;
		if (!buf) {
			if (errno != EINTR) doperror(NULL, "stdin");
			exec_line(NULL);
			break;
		}
		if (buf == vnullstr) {
#ifndef	MINIMUMSHELL
			if (interactive && ignoreeof) {
				if (cont) {
					exec_line(NULL);
					syntaxerrno = ER_UNEXPEOF;
					syntaxerror(nullstr);
				}
				else VOID_C Xfprintf(Xstderr,
					"Use \"%s\" to leave to the shell.\n",
					(loginshell) ? "logout" : "exit");
				cont = 0;
				continue;
			}
#endif	/* !MINIMUMSHELL */
			if (!cont) break;
			exec_line((char *)-1);
			cont = 0;
			ERRBREAK;
		}

#ifdef	DEP_FILECONV
		renewkanjiconv(&buf, DEFCODE, defaultkcode, L_FNAME);
		printf_defkanji++;
#endif
		cont = exec_line(buf);
#ifdef	DEP_FILECONV
		printf_defkanji--;
#endif
		Xfree(buf);
		if (pseudoexit && exit_status >= 0) break;
#ifndef	MINIMUMSHELL
		setshlineno(shlineno + 1L);
#endif
	}
	resetsignal(0);
	if (exit_status >= 0) ret_status = exit_status;
	shellmode = 0;

	return(ret_status);
}

int main_shell(argc, argv, envp)
int argc;
char *CONST *argv, *CONST *envp;
{
	shellmode = 1;
	setshellvar(envp);
	if (initshell(argc, argv) < 0) return(RET_FAIL);
#ifdef	FD
	initenv();
# if	MSDOS
	inittty(1);
# endif
	if (interactive) checkscreen(0, 0);
# ifndef	_NOCUSTOMIZE
	saveorigenviron();
# endif
#endif	/* FD */
#if	defined (DEP_URLPATH) && defined (WITHNETWORK)
	urldrive = 1;
#endif
	ret_status = RET_SUCCESS;
	initrc(!loginshell);
#ifdef	FD
	initfd(argv);
#endif
#ifdef	DEP_PTY
	if (isshptymode()) {
		shellpid = (p_id_t)-1;
		ret_status = ptymacro(NULL, NULL, F_DOSYSTEM);
	}
	else
#endif
	shell_loop(0);

	return(ret_status);
}

#ifdef	FDSH
int main(argc, argv, envp)
int argc;
char *CONST argv[], *CONST envp[];
{
	int ret;

# ifdef	DEBUG
	mtrace();
# endif
	ret = main_shell(argc, argv, envp);
	Xexit2(ret);

	return(0);
}
#endif	/* FDSH */
#endif	/* DEP_ORIGSHELL */
