/* vi: set sw=4 ts=4: */
/*
 * ash shell port for busybox
 *
 * Copyright (c) 1989, 1991, 1993, 1994
 *      The Regents of the University of California.  All rights reserved.
 *
 * Copyright (c) 1997-2003 Herbert Xu <herbert@debian.org>
 * was re-ported from NetBSD and debianized.
 *
 *
 * This code is derived from software contributed to Berkeley by
 * Kenneth Almquist.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 *
 * Modified by Vladimir Oleynik <dzo@simtreas.ru> to be used in busybox
 *
 *
 * Original BSD copyright notice is retained at the end of this file.
 */

/*
 * The follow should be set to reflect the type of system you have:
 *      JOBS -> 1 if you have Berkeley job control, 0 otherwise.
 *      define SYSV if you are running under System V.
 *      define DEBUG=1 to compile in debugging ('set -o debug' to turn on)
 *      define DEBUG=2 to compile in and turn on debugging.
 *
 * When debugging is on, debugging info will be written to ./trace and
 * a quit signal will generate a core dump.
 */



#define IFS_BROKEN

#define PROFILE 0

#ifdef DEBUG
#define _GNU_SOURCE
#endif

#include <sys/types.h>
#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <stdarg.h>
#include <stddef.h>
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <paths.h>
#include <setjmp.h>
#include <signal.h>
#include <stdint.h>
#include <sysexits.h>

#include <fnmatch.h>
#include <glob.h>



#include "busybox.h"
#include "pwd_.h"

#ifdef CONFIG_ASH_JOB_CONTROL
#define JOBS 1
#else
#undef JOBS
#endif

#if JOBS
#include <termios.h>
#endif

#include "cmdedit.h"

#ifdef __GLIBC__
/* glibc sucks */
static int *dash_errno;
#undef errno
#define errno (*dash_errno)
#endif

#if defined(__uClinux__)
#error "Do not even bother, ash will not run on uClinux"
#endif

#ifdef DEBUG
#define _DIAGASSERT(assert_expr) assert(assert_expr)
#else
#define _DIAGASSERT(assert_expr)
#endif


#ifdef CONFIG_ASH_ALIAS
/*      $NetBSD: alias.h,v 1.5 2002/11/24 22:35:38 christos Exp $       */

#define ALIASINUSE      1
#define ALIASDEAD       2

struct alias {
	struct alias *next;
	char *name;
	char *val;
	int flag;
};

static struct alias *lookupalias(const char *, int);
static int aliascmd(int, char **);
static int unaliascmd(int, char **);
static void rmaliases(void);
static int unalias(const char *);
static void printalias(const struct alias *);
#endif

/*      $NetBSD: cd.h,v 1.3 2002/11/24 22:35:39 christos Exp $  */


static void    setpwd(const char *, int);

/*      $NetBSD: error.h,v 1.15 2002/11/24 22:35:39 christos Exp $      */


/*
 * Types of operations (passed to the errmsg routine).
 */


static const char not_found_msg[] = "%s: not found";


#define E_OPEN  "No such file"          /* opening a file */
#define E_CREAT "Directory nonexistent" /* creating a file */
#define E_EXEC  not_found_msg+4         /* executing a program */

/*
 * We enclose jmp_buf in a structure so that we can declare pointers to
 * jump locations.  The global variable handler contains the location to
 * jump to when an exception occurs, and the global variable exception
 * contains a code identifying the exeception.  To implement nested
 * exception handlers, the user should save the value of handler on entry
 * to an inner scope, set handler to point to a jmploc structure for the
 * inner scope, and restore handler on exit from the scope.
 */

struct jmploc {
	jmp_buf loc;
};

static struct jmploc *handler;
static int exception;
static volatile int suppressint;
static volatile sig_atomic_t intpending;

static int exerrno;            /* Last exec error, error for EXEXEC */

/* exceptions */
#define EXINT 0         /* SIGINT received */
#define EXERROR 1       /* a generic error */
#define EXSHELLPROC 2   /* execute a shell procedure */
#define EXEXEC 3        /* command execution failed */
#define EXEXIT 4        /* exit the shell */
#define EXSIG 5         /* trapped signal in wait(1) */


/* do we generate EXSIG events */
static int exsig;
/* last pending signal */
static volatile sig_atomic_t pendingsigs;

/*
 * These macros allow the user to suspend the handling of interrupt signals
 * over a period of time.  This is similar to SIGHOLD to or sigblock, but
 * much more efficient and portable.  (But hacking the kernel is so much
 * more fun than worrying about efficiency and portability. :-))
 */

#define barrier() ({ __asm__ __volatile__ ("": : :"memory"); })
#define INTOFF \
	({ \
		suppressint++; \
		barrier(); \
		0; \
	})
#define SAVEINT(v) ((v) = suppressint)
#define RESTOREINT(v) \
	({ \
		barrier(); \
		if ((suppressint = (v)) == 0 && intpending) onint(); \
		0; \
	})
#define EXSIGON() \
	({ \
		exsig++; \
		barrier(); \
		if (pendingsigs) \
			exraise(EXSIG); \
		0; \
	})
/* EXSIG is turned off by evalbltin(). */


static void exraise(int) __attribute__((__noreturn__));
static void onint(void) __attribute__((__noreturn__));

static void error(const char *, ...) __attribute__((__noreturn__));
static void exerror(int, const char *, ...) __attribute__((__noreturn__));

static void sh_warnx(const char *, ...);

#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
static void
inton(void) {
	if (--suppressint == 0 && intpending) {
		onint();
	}
}
#define INTON inton()
static void forceinton(void)
{
	suppressint = 0;
	if (intpending)
		onint();
}
#define FORCEINTON forceinton()
#else
#define INTON \
	({ \
		barrier(); \
		if (--suppressint == 0 && intpending) onint(); \
		0; \
	})
#define FORCEINTON \
	({ \
		barrier(); \
		suppressint = 0; \
		if (intpending) onint(); \
		0; \
	})
#endif /* CONFIG_ASH_OPTIMIZE_FOR_SIZE */

/*
 * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
 * so we use _setjmp instead.
 */

#if defined(BSD) && !defined(__SVR4) && !defined(__GLIBC__)
#define setjmp(jmploc)  _setjmp(jmploc)
#define longjmp(jmploc, val)    _longjmp(jmploc, val)
#endif

/*      $NetBSD: expand.h,v 1.13 2002/11/24 22:35:40 christos Exp $     */

struct strlist {
	struct strlist *next;
	char *text;
};


struct arglist {
	struct strlist *list;
	struct strlist **lastp;
};

/*
 * expandarg() flags
 */
#define EXP_FULL        0x1     /* perform word splitting & file globbing */
#define EXP_TILDE       0x2     /* do normal tilde expansion */
#define EXP_VARTILDE    0x4     /* expand tildes in an assignment */
#define EXP_REDIR       0x8     /* file glob for a redirection (1 match only) */
#define EXP_CASE        0x10    /* keeps quotes around for CASE pattern */
#define EXP_RECORD      0x20    /* need to record arguments for ifs breakup */
#define EXP_VARTILDE2   0x40    /* expand tildes after colons only */
#define EXP_WORD        0x80    /* expand word in parameter expansion */
#define EXP_QWORD       0x100   /* expand word in quoted parameter expansion */


union node;
static void expandarg(union node *, struct arglist *, int);
#define rmescapes(p) _rmescapes((p), 0)
static char *_rmescapes(char *, int);
static int casematch(union node *, char *);

#ifdef CONFIG_ASH_MATH_SUPPORT
static void expari(int);
#endif

/*      $NetBSD: eval.h,v 1.13 2002/11/24 22:35:39 christos Exp $       */

static char *commandname;              /* currently executing command */
static struct strlist *cmdenviron;     /* environment for builtin command */
static int exitstatus;                 /* exit status of last command */
static int back_exitstatus;            /* exit status of backquoted command */


struct backcmd {                /* result of evalbackcmd */
	int fd;                 /* file descriptor to read from */
	char *buf;              /* buffer */
	int nleft;              /* number of chars in buffer */
	struct job *jp;         /* job structure for command */
};

/*
 * This file was generated by the mknodes program.
 */

#define NCMD 0
#define NPIPE 1
#define NREDIR 2
#define NBACKGND 3
#define NSUBSHELL 4
#define NAND 5
#define NOR 6
#define NSEMI 7
#define NIF 8
#define NWHILE 9
#define NUNTIL 10
#define NFOR 11
#define NCASE 12
#define NCLIST 13
#define NDEFUN 14
#define NARG 15
#define NTO 16
#define NCLOBBER 17
#define NFROM 18
#define NFROMTO 19
#define NAPPEND 20
#define NTOFD 21
#define NFROMFD 22
#define NHERE 23
#define NXHERE 24
#define NNOT 25



struct ncmd {
      int type;
      union node *assign;
      union node *args;
      union node *redirect;
};


struct npipe {
      int type;
      int backgnd;
      struct nodelist *cmdlist;
};


struct nredir {
      int type;
      union node *n;
      union node *redirect;
};


struct nbinary {
      int type;
      union node *ch1;
      union node *ch2;
};


struct nif {
      int type;
      union node *test;
      union node *ifpart;
      union node *elsepart;
};


struct nfor {
      int type;
      union node *args;
      union node *body;
      char *var;
};


struct ncase {
      int type;
      union node *expr;
      union node *cases;
};


struct nclist {
      int type;
      union node *next;
      union node *pattern;
      union node *body;
};


struct narg {
      int type;
      union node *next;
      char *text;
      struct nodelist *backquote;
};


struct nfile {
      int type;
      union node *next;
      int fd;
      union node *fname;
      char *expfname;
};


struct ndup {
      int type;
      union node *next;
      int fd;
      int dupfd;
      union node *vname;
};


struct nhere {
      int type;
      union node *next;
      int fd;
      union node *doc;
};


struct nnot {
      int type;
      union node *com;
};


union node {
      int type;
      struct ncmd ncmd;
      struct npipe npipe;
      struct nredir nredir;
      struct nbinary nbinary;
      struct nif nif;
      struct nfor nfor;
      struct ncase ncase;
      struct nclist nclist;
      struct narg narg;
      struct nfile nfile;
      struct ndup ndup;
      struct nhere nhere;
      struct nnot nnot;
};


struct nodelist {
	struct nodelist *next;
	union node *n;
};


struct funcnode {
	int count;
	union node n;
};


static void freefunc(struct funcnode *);
/*      $NetBSD: parser.h,v 1.15 2002/11/24 22:35:42 christos Exp $     */

/* control characters in argument strings */
#define CTL_FIRST '\201'        /* first 'special' character */
#define CTLESC '\201'           /* escape next character */
#define CTLVAR '\202'           /* variable defn */
#define CTLENDVAR '\203'
#define CTLBACKQ '\204'
#define CTLQUOTE 01             /* ored with CTLBACKQ code if in quotes */
/*      CTLBACKQ | CTLQUOTE == '\205' */
#define CTLARI  '\206'          /* arithmetic expression */
#define CTLENDARI '\207'
#define CTLQUOTEMARK '\210'
#define CTL_LAST '\210'         /* last 'special' character */

/* variable substitution byte (follows CTLVAR) */
#define VSTYPE  0x0f            /* type of variable substitution */
#define VSNUL   0x10            /* colon--treat the empty string as unset */
#define VSQUOTE 0x80            /* inside double quotes--suppress splitting */

/* values of VSTYPE field */
#define VSNORMAL        0x1             /* normal variable:  $var or ${var} */
#define VSMINUS         0x2             /* ${var-text} */
#define VSPLUS          0x3             /* ${var+text} */
#define VSQUESTION      0x4             /* ${var?message} */
#define VSASSIGN        0x5             /* ${var=text} */
#define VSTRIMRIGHT     0x6             /* ${var%pattern} */
#define VSTRIMRIGHTMAX  0x7             /* ${var%%pattern} */
#define VSTRIMLEFT      0x8             /* ${var#pattern} */
#define VSTRIMLEFTMAX   0x9             /* ${var##pattern} */
#define VSLENGTH        0xa             /* ${#var} */

/* values of checkkwd variable */
#define CHKALIAS        0x1
#define CHKKWD          0x2
#define CHKNL           0x4

#define IBUFSIZ (BUFSIZ + 1)

/*
 * NEOF is returned by parsecmd when it encounters an end of file.  It
 * must be distinct from NULL, so we use the address of a variable that
 * happens to be handy.
 */
static int plinno = 1;                  /* input line number */

/* number of characters left in input buffer */
static int parsenleft;                  /* copy of parsefile->nleft */
static int parselleft;                  /* copy of parsefile->lleft */

/* next character in input buffer */
static char *parsenextc;               /* copy of parsefile->nextc */
static struct parsefile basepf;         /* top level input file */
static char basebuf[IBUFSIZ];           /* buffer for top level input file */
static struct parsefile *parsefile = &basepf;  /* current input file */


static int tokpushback;                 /* last token pushed back */
#define NEOF ((union node *)&tokpushback)
static int parsebackquote;             /* nonzero if we are inside backquotes */
static int doprompt;                   /* if set, prompt the user */
static int needprompt;                 /* true if interactive and at start of line */
static int lasttoken;                  /* last token read */
static char *wordtext;                 /* text of last word returned by readtoken */
static int checkkwd;
static struct nodelist *backquotelist;
static union node *redirnode;
static struct heredoc *heredoc;
static int quoteflag;                  /* set if (part of) last token was quoted */
static int startlinno;                 /* line # where last token started */

static union node *parsecmd(int);
static void fixredir(union node *, const char *, int);
static const char *const *findkwd(const char *);
static char *endofname(const char *);

/*      $NetBSD: shell.h,v 1.16 2003/01/22 20:36:04 dsl Exp $   */

typedef void *pointer;

static char nullstr[1];                /* zero length string */
static const char spcstr[] = " ";
static const char snlfmt[] = "%s\n";
static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
static const char illnum[] = "Illegal number: %s";
static const char homestr[] = "HOME";

#ifdef DEBUG
#define TRACE(param)    trace param
#define TRACEV(param)   tracev param
#else
#define TRACE(param)
#define TRACEV(param)
#endif

#if defined(__GNUC__) && __GNUC__ < 3
#define va_copy __va_copy
#endif

#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
#define __builtin_expect(x, expected_value) (x)
#endif

#define likely(x)       __builtin_expect((x),1)
#define unlikely(x)     __builtin_expect((x),0)

#define TEOF 0
#define TNL 1
#define TREDIR 2
#define TWORD 3
#define TSEMI 4
#define TBACKGND 5
#define TAND 6
#define TOR 7
#define TPIPE 8
#define TLP 9
#define TRP 10
#define TENDCASE 11
#define TENDBQUOTE 12
#define TNOT 13
#define TCASE 14
#define TDO 15
#define TDONE 16
#define TELIF 17
#define TELSE 18
#define TESAC 19
#define TFI 20
#define TFOR 21
#define TIF 22
#define TIN 23
#define TTHEN 24
#define TUNTIL 25
#define TWHILE 26
#define TBEGIN 27
#define TEND 28

/* first char is indicating which tokens mark the end of a list */
static const char *const tokname_array[] = {
	"\1end of file",
	"\0newline",
	"\0redirection",
	"\0word",
	"\0;",
	"\0&",
	"\0&&",
	"\0||",
	"\0|",
	"\0(",
	"\1)",
	"\1;;",
	"\1`",
#define KWDOFFSET 13
	/* the following are keywords */
	"\0!",
	"\0case",
	"\1do",
	"\1done",
	"\1elif",
	"\1else",
	"\1esac",
	"\1fi",
	"\0for",
	"\0if",
	"\0in",
	"\1then",
	"\0until",
	"\0while",
	"\0{",
	"\1}",
};

static const char *tokname(int tok)
{
	static char buf[16];

	if (tok >= TSEMI)
		buf[0] = '"';
	sprintf(buf + (tok >= TSEMI), "%s%c",
			tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
	return buf;
}

/*      $NetBSD: machdep.h,v 1.10 2002/10/07 14:26:08 christos Exp $    */

/*
 * Most machines require the value returned from malloc to be aligned
 * in some way.  The following macro will get this right on many machines.
 */

#define SHELL_SIZE (sizeof(union {int i; char *cp; double d; }) - 1)
/*
 * It appears that grabstackstr() will barf with such alignments
 * because stalloc() will return a string allocated in a new stackblock.
 */
#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)

/*
 * This file was generated by the mksyntax program.
 */


/* Syntax classes */
#define CWORD 0                 /* character is nothing special */
#define CNL 1                   /* newline character */
#define CBACK 2                 /* a backslash character */
#define CSQUOTE 3               /* single quote */
#define CDQUOTE 4               /* double quote */
#define CENDQUOTE 5             /* a terminating quote */
#define CBQUOTE 6               /* backwards single quote */
#define CVAR 7                  /* a dollar sign */
#define CENDVAR 8               /* a '}' character */
#define CLP 9                   /* a left paren in arithmetic */
#define CRP 10                  /* a right paren in arithmetic */
#define CENDFILE 11             /* end of file */
#define CCTL 12                 /* like CWORD, except it must be escaped */
#define CSPCL 13                /* these terminate a word */
#define CIGN 14                 /* character should be ignored */

#ifdef CONFIG_ASH_ALIAS
#define SYNBASE 130
#define PEOF -130
#define PEOA -129
#define PEOA_OR_PEOF PEOA
#else
#define SYNBASE 129
#define PEOF -129
#define PEOA_OR_PEOF PEOF
#endif

#define is_digit(c)     ((unsigned)((c) - '0') <= 9)
#define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
#define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))

/*
 * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
 * (assuming ascii char codes, as the original implementation did)
 */
#define is_special(c) \
    ( (((unsigned int)c) - 33 < 32) \
			 && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))

#define digit_val(c)    ((c) - '0')

/*
 * This file was generated by the mksyntax program.
 */

#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
#define USE_SIT_FUNCTION
#endif

/* number syntax index */
#define  BASESYNTAX  0  /* not in quotes */
#define  DQSYNTAX    1  /* in double quotes */
#define  SQSYNTAX    2  /* in single quotes */
#define  ARISYNTAX   3  /* in arithmetic */

#ifdef CONFIG_ASH_MATH_SUPPORT
static const char S_I_T[][4] = {
#ifdef CONFIG_ASH_ALIAS
	{CSPCL, CIGN, CIGN, CIGN},              /* 0, PEOA */
#endif
	{CSPCL, CWORD, CWORD, CWORD},           /* 1, ' ' */
	{CNL, CNL, CNL, CNL},                   /* 2, \n */
	{CWORD, CCTL, CCTL, CWORD},             /* 3, !*-/:=?[]~ */
	{CDQUOTE, CENDQUOTE, CWORD, CWORD},     /* 4, '"' */
	{CVAR, CVAR, CWORD, CVAR},              /* 5, $ */
	{CSQUOTE, CWORD, CENDQUOTE, CWORD},     /* 6, "'" */
	{CSPCL, CWORD, CWORD, CLP},             /* 7, ( */
	{CSPCL, CWORD, CWORD, CRP},             /* 8, ) */
	{CBACK, CBACK, CCTL, CBACK},            /* 9, \ */
	{CBQUOTE, CBQUOTE, CWORD, CBQUOTE},     /* 10, ` */
	{CENDVAR, CENDVAR, CWORD, CENDVAR},     /* 11, } */
#ifndef USE_SIT_FUNCTION
	{CENDFILE, CENDFILE, CENDFILE, CENDFILE},       /* 12, PEOF */
	{CWORD, CWORD, CWORD, CWORD},           /* 13, 0-9A-Za-z */
	{CCTL, CCTL, CCTL, CCTL}                /* 14, CTLESC ... */
#endif
};
#else
static const char S_I_T[][3] = {
#ifdef CONFIG_ASH_ALIAS
	{CSPCL, CIGN, CIGN},                    /* 0, PEOA */
#endif
	{CSPCL, CWORD, CWORD},                  /* 1, ' ' */
	{CNL, CNL, CNL},                        /* 2, \n */
	{CWORD, CCTL, CCTL},                    /* 3, !*-/:=?[]~ */
	{CDQUOTE, CENDQUOTE, CWORD},            /* 4, '"' */
	{CVAR, CVAR, CWORD},                    /* 5, $ */
	{CSQUOTE, CWORD, CENDQUOTE},            /* 6, "'" */
	{CSPCL, CWORD, CWORD},                  /* 7, ( */
	{CSPCL, CWORD, CWORD},                  /* 8, ) */
	{CBACK, CBACK, CCTL},                   /* 9, \ */
	{CBQUOTE, CBQUOTE, CWORD},              /* 10, ` */
	{CENDVAR, CENDVAR, CWORD},              /* 11, } */
#ifndef USE_SIT_FUNCTION
	{CENDFILE, CENDFILE, CENDFILE},         /* 12, PEOF */
	{CWORD, CWORD, CWORD},                  /* 13, 0-9A-Za-z */
	{CCTL, CCTL, CCTL}                      /* 14, CTLESC ... */
#endif
};
#endif /* CONFIG_ASH_MATH_SUPPORT */

#ifdef USE_SIT_FUNCTION

#define U_C(c) ((unsigned char)(c))

static int SIT(int c, int syntax)
{
	static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
#ifdef CONFIG_ASH_ALIAS
	static const char syntax_index_table[] = {
		1, 2, 1, 3, 4, 5, 1, 6,         /* "\t\n !\"$&'" */
		7, 8, 3, 3, 3, 3, 1, 1,         /* "()*-/:;<" */
		3, 1, 3, 3, 9, 3, 10, 1,        /* "=>?[\\]`|" */
		11, 3                           /* "}~" */
	};
#else
	static const char syntax_index_table[] = {
		0, 1, 0, 2, 3, 4, 0, 5,         /* "\t\n !\"$&'" */
		6, 7, 2, 2, 2, 2, 0, 0,         /* "()*-/:;<" */
		2, 0, 2, 2, 8, 2, 9, 0,         /* "=>?[\\]`|" */
		10, 2                           /* "}~" */
	};
#endif
	const char *s;
	int indx;

	if (c == PEOF)          /* 2^8+2 */
		return CENDFILE;
#ifdef CONFIG_ASH_ALIAS
	if (c == PEOA)          /* 2^8+1 */
		indx = 0;
	else
#endif
		if (U_C(c) >= U_C(CTLESC) && U_C(c) <= U_C(CTLQUOTEMARK))
			return CCTL;
	else {
		s = strchr(spec_symbls, c);
		if (s == 0 || *s == 0)
			return CWORD;
		indx = syntax_index_table[(s - spec_symbls)];
	}
	return S_I_T[indx][syntax];
}

#else                                                   /* USE_SIT_FUNCTION */

#define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]

#ifdef CONFIG_ASH_ALIAS
#define CSPCL_CIGN_CIGN_CIGN                           0
#define CSPCL_CWORD_CWORD_CWORD                        1
#define CNL_CNL_CNL_CNL                                2
#define CWORD_CCTL_CCTL_CWORD                          3
#define CDQUOTE_CENDQUOTE_CWORD_CWORD                  4
#define CVAR_CVAR_CWORD_CVAR                           5
#define CSQUOTE_CWORD_CENDQUOTE_CWORD                  6
#define CSPCL_CWORD_CWORD_CLP                          7
#define CSPCL_CWORD_CWORD_CRP                          8
#define CBACK_CBACK_CCTL_CBACK                         9
#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE                 10
#define CENDVAR_CENDVAR_CWORD_CENDVAR                 11
#define CENDFILE_CENDFILE_CENDFILE_CENDFILE           12
#define CWORD_CWORD_CWORD_CWORD                       13
#define CCTL_CCTL_CCTL_CCTL                           14
#else
#define CSPCL_CWORD_CWORD_CWORD                        0
#define CNL_CNL_CNL_CNL                                1
#define CWORD_CCTL_CCTL_CWORD                          2
#define CDQUOTE_CENDQUOTE_CWORD_CWORD                  3
#define CVAR_CVAR_CWORD_CVAR                           4
#define CSQUOTE_CWORD_CENDQUOTE_CWORD                  5
#define CSPCL_CWORD_CWORD_CLP                          6
#define CSPCL_CWORD_CWORD_CRP                          7
#define CBACK_CBACK_CCTL_CBACK                         8
#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE                  9
#define CENDVAR_CENDVAR_CWORD_CENDVAR                 10
#define CENDFILE_CENDFILE_CENDFILE_CENDFILE           11
#define CWORD_CWORD_CWORD_CWORD                       12
#define CCTL_CCTL_CCTL_CCTL                           13
#endif

static const char syntax_index_table[258] = {
	/* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
	/*   0  PEOF */      CENDFILE_CENDFILE_CENDFILE_CENDFILE,
#ifdef CONFIG_ASH_ALIAS
	/*   1  PEOA */      CSPCL_CIGN_CIGN_CIGN,
#endif
	/*   2  -128 0x80 */ CWORD_CWORD_CWORD_CWORD,
	/*   3  -127 CTLESC       */ CCTL_CCTL_CCTL_CCTL,
	/*   4  -126 CTLVAR       */ CCTL_CCTL_CCTL_CCTL,
	/*   5  -125 CTLENDVAR    */ CCTL_CCTL_CCTL_CCTL,
	/*   6  -124 CTLBACKQ     */ CCTL_CCTL_CCTL_CCTL,
	/*   7  -123 CTLQUOTE     */ CCTL_CCTL_CCTL_CCTL,
	/*   8  -122 CTLARI       */ CCTL_CCTL_CCTL_CCTL,
	/*   9  -121 CTLENDARI    */ CCTL_CCTL_CCTL_CCTL,
	/*  10  -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
	/*  11  -119      */ CWORD_CWORD_CWORD_CWORD,
	/*  12  -118      */ CWORD_CWORD_CWORD_CWORD,
	/*  13  -117      */ CWORD_CWORD_CWORD_CWORD,
	/*  14  -116      */ CWORD_CWORD_CWORD_CWORD,
	/*  15  -115      */ CWORD_CWORD_CWORD_CWORD,
	/*  16  -114      */ CWORD_CWORD_CWORD_CWORD,
	/*  17  -113      */ CWORD_CWORD_CWORD_CWORD,
	/*  18  -112      */ CWORD_CWORD_CWORD_CWORD,
	/*  19  -111      */ CWORD_CWORD_CWORD_CWORD,
	/*  20  -110      */ CWORD_CWORD_CWORD_CWORD,
	/*  21  -109      */ CWORD_CWORD_CWORD_CWORD,
	/*  22  -108      */ CWORD_CWORD_CWORD_CWORD,
	/*  23  -107      */ CWORD_CWORD_CWORD_CWORD,
	/*  24  -106      */ CWORD_CWORD_CWORD_CWORD,
	/*  25  -105      */ CWORD_CWORD_CWORD_CWORD,
	/*  26  -104      */ CWORD_CWORD_CWORD_CWORD,
	/*  27  -103      */ CWORD_CWORD_CWORD_CWORD,
	/*  28  -102      */ CWORD_CWORD_CWORD_CWORD,
	/*  29  -101      */ CWORD_CWORD_CWORD_CWORD,
	/*  30  -100      */ CWORD_CWORD_CWORD_CWORD,
	/*  31   -99      */ CWORD_CWORD_CWORD_CWORD,
	/*  32   -98      */ CWORD_CWORD_CWORD_CWORD,
	/*  33   -97      */ CWORD_CWORD_CWORD_CWORD,
	/*  34   -96      */ CWORD_CWORD_CWORD_CWORD,
	/*  35   -95      */ CWORD_CWORD_CWORD_CWORD,
	/*  36   -94      */ CWORD_CWORD_CWORD_CWORD,
	/*  37   -93      */ CWORD_CWORD_CWORD_CWORD,
	/*  38   -92      */ CWORD_CWORD_CWORD_CWORD,
	/*  39   -91      */ CWORD_CWORD_CWORD_CWORD,
	/*  40   -90      */ CWORD_CWORD_CWORD_CWORD,
	/*  41   -89      */ CWORD_CWORD_CWORD_CWORD,
	/*  42   -88      */ CWORD_CWORD_CWORD_CWORD,
	/*  43   -87      */ CWORD_CWORD_CWORD_CWORD,
	/*  44   -86      */ CWORD_CWORD_CWORD_CWORD,
	/*  45   -85      */ CWORD_CWORD_CWORD_CWORD,
	/*  46   -84      */ CWORD_CWORD_CWORD_CWORD,
	/*  47   -83      */ CWORD_CWORD_CWORD_CWORD,
	/*  48   -82      */ CWORD_CWORD_CWORD_CWORD,
	/*  49   -81      */ CWORD_CWORD_CWORD_CWORD,
	/*  50   -80      */ CWORD_CWORD_CWORD_CWORD,
	/*  51   -79      */ CWORD_CWORD_CWORD_CWORD,
	/*  52   -78      */ CWORD_CWORD_CWORD_CWORD,
	/*  53   -77      */ CWORD_CWORD_CWORD_CWORD,
	/*  54   -76      */ CWORD_CWORD_CWORD_CWORD,
	/*  55   -75      */ CWORD_CWORD_CWORD_CWORD,
	/*  56   -74      */ CWORD_CWORD_CWORD_CWORD,
	/*  57   -73      */ CWORD_CWORD_CWORD_CWORD,
	/*  58   -72      */ CWORD_CWORD_CWORD_CWORD,
	/*  59   -71      */ CWORD_CWORD_CWORD_CWORD,
	/*  60   -70      */ CWORD_CWORD_CWORD_CWORD,
	/*  61   -69      */ CWORD_CWORD_CWORD_CWORD,
	/*  62   -68      */ CWORD_CWORD_CWORD_CWORD,
	/*  63   -67      */ CWORD_CWORD_CWORD_CWORD,
	/*  64   -66      */ CWORD_CWORD_CWORD_CWORD,
	/*  65   -65      */ CWORD_CWORD_CWORD_CWORD,
	/*  66   -64      */ CWORD_CWORD_CWORD_CWORD,
	/*  67   -63      */ CWORD_CWORD_CWORD_CWORD,
	/*  68   -62      */ CWORD_CWORD_CWORD_CWORD,
	/*  69   -61      */ CWORD_CWORD_CWORD_CWORD,
	/*  70   -60      */ CWORD_CWORD_CWORD_CWORD,
	/*  71   -59      */ CWORD_CWORD_CWORD_CWORD,
	/*  72   -58      */ CWORD_CWORD_CWORD_CWORD,
	/*  73   -57      */ CWORD_CWORD_CWORD_CWORD,
	/*  74   -56      */ CWORD_CWORD_CWORD_CWORD,
	/*  75   -55      */ CWORD_CWORD_CWORD_CWORD,
	/*  76   -54      */ CWORD_CWORD_CWORD_CWORD,
	/*  77   -53      */ CWORD_CWORD_CWORD_CWORD,
	/*  78   -52      */ CWORD_CWORD_CWORD_CWORD,
	/*  79   -51      */ CWORD_CWORD_CWORD_CWORD,
	/*  80   -50      */ CWORD_CWORD_CWORD_CWORD,
	/*  81   -49      */ CWORD_CWORD_CWORD_CWORD,
	/*  82   -48      */ CWORD_CWORD_CWORD_CWORD,
	/*  83   -47      */ CWORD_CWORD_CWORD_CWORD,
	/*  84   -46      */ CWORD_CWORD_CWORD_CWORD,
	/*  85   -45      */ CWORD_CWORD_CWORD_CWORD,
	/*  86   -44      */ CWORD_CWORD_CWORD_CWORD,
	/*  87   -43      */ CWORD_CWORD_CWORD_CWORD,
	/*  88   -42      */ CWORD_CWORD_CWORD_CWORD,
	/*  89   -41      */ CWORD_CWORD_CWORD_CWORD,
	/*  90   -40      */ CWORD_CWORD_CWORD_CWORD,
	/*  91   -39      */ CWORD_CWORD_CWORD_CWORD,
	/*  92   -38      */ CWORD_CWORD_CWORD_CWORD,
	/*  93   -37      */ CWORD_CWORD_CWORD_CWORD,
	/*  94   -36      */ CWORD_CWORD_CWORD_CWORD,
	/*  95   -35      */ CWORD_CWORD_CWORD_CWORD,
	/*  96   -34      */ CWORD_CWORD_CWORD_CWORD,
	/*  97   -33      */ CWORD_CWORD_CWORD_CWORD,
	/*  98   -32      */ CWORD_CWORD_CWORD_CWORD,
	/*  99   -31      */ CWORD_CWORD_CWORD_CWORD,
	/* 100   -30      */ CWORD_CWORD_CWORD_CWORD,
	/* 101   -29      */ CWORD_CWORD_CWORD_CWORD,
	/* 102   -28      */ CWORD_CWORD_CWORD_CWORD,
	/* 103   -27      */ CWORD_CWORD_CWORD_CWORD,
	/* 104   -26      */ CWORD_CWORD_CWORD_CWORD,
	/* 105   -25      */ CWORD_CWORD_CWORD_CWORD,
	/* 106   -24      */ CWORD_CWORD_CWORD_CWORD,
	/* 107   -23      */ CWORD_CWORD_CWORD_CWORD,
	/* 108   -22      */ CWORD_CWORD_CWORD_CWORD,
	/* 109   -21      */ CWORD_CWORD_CWORD_CWORD,
	/* 110   -20      */ CWORD_CWORD_CWORD_CWORD,
	/* 111   -19      */ CWORD_CWORD_CWORD_CWORD,
	/* 112   -18      */ CWORD_CWORD_CWORD_CWORD,
	/* 113   -17      */ CWORD_CWORD_CWORD_CWORD,
	/* 114   -16      */ CWORD_CWORD_CWORD_CWORD,
	/* 115   -15      */ CWORD_CWORD_CWORD_CWORD,
	/* 116   -14      */ CWORD_CWORD_CWORD_CWORD,
	/* 117   -13      */ CWORD_CWORD_CWORD_CWORD,
	/* 118   -12      */ CWORD_CWORD_CWORD_CWORD,
	/* 119   -11      */ CWORD_CWORD_CWORD_CWORD,
	/* 120   -10      */ CWORD_CWORD_CWORD_CWORD,
	/* 121    -9      */ CWORD_CWORD_CWORD_CWORD,
	/* 122    -8      */ CWORD_CWORD_CWORD_CWORD,
	/* 123    -7      */ CWORD_CWORD_CWORD_CWORD,
	/* 124    -6      */ CWORD_CWORD_CWORD_CWORD,
	/* 125    -5      */ CWORD_CWORD_CWORD_CWORD,
	/* 126    -4      */ CWORD_CWORD_CWORD_CWORD,
	/* 127    -3      */ CWORD_CWORD_CWORD_CWORD,
	/* 128    -2      */ CWORD_CWORD_CWORD_CWORD,
	/* 129    -1      */ CWORD_CWORD_CWORD_CWORD,
	/* 130     0      */ CWORD_CWORD_CWORD_CWORD,
	/* 131     1      */ CWORD_CWORD_CWORD_CWORD,
	/* 132     2      */ CWORD_CWORD_CWORD_CWORD,
	/* 133     3      */ CWORD_CWORD_CWORD_CWORD,
	/* 134     4      */ CWORD_CWORD_CWORD_CWORD,
	/* 135     5      */ CWORD_CWORD_CWORD_CWORD,
	/* 136     6      */ CWORD_CWORD_CWORD_CWORD,
	/* 137     7      */ CWORD_CWORD_CWORD_CWORD,
	/* 138     8      */ CWORD_CWORD_CWORD_CWORD,
	/* 139     9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
	/* 140    10 "\n" */ CNL_CNL_CNL_CNL,
	/* 141    11      */ CWORD_CWORD_CWORD_CWORD,
	/* 142    12      */ CWORD_CWORD_CWORD_CWORD,
	/* 143    13      */ CWORD_CWORD_CWORD_CWORD,
	/* 144    14      */ CWORD_CWORD_CWORD_CWORD,
	/* 145    15      */ CWORD_CWORD_CWORD_CWORD,
	/* 146    16      */ CWORD_CWORD_CWORD_CWORD,
	/* 147    17      */ CWORD_CWORD_CWORD_CWORD,
	/* 148    18      */ CWORD_CWORD_CWORD_CWORD,
	/* 149    19      */ CWORD_CWORD_CWORD_CWORD,
	/* 150    20      */ CWORD_CWORD_CWORD_CWORD,
	/* 151    21      */ CWORD_CWORD_CWORD_CWORD,
	/* 152    22      */ CWORD_CWORD_CWORD_CWORD,
	/* 153    23      */ CWORD_CWORD_CWORD_CWORD,
	/* 154    24      */ CWORD_CWORD_CWORD_CWORD,
	/* 155    25      */ CWORD_CWORD_CWORD_CWORD,
	/* 156    26      */ CWORD_CWORD_CWORD_CWORD,
	/* 157    27      */ CWORD_CWORD_CWORD_CWORD,
	/* 158    28      */ CWORD_CWORD_CWORD_CWORD,
	/* 159    29      */ CWORD_CWORD_CWORD_CWORD,
	/* 160    30      */ CWORD_CWORD_CWORD_CWORD,
	/* 161    31      */ CWORD_CWORD_CWORD_CWORD,
	/* 162    32  " " */ CSPCL_CWORD_CWORD_CWORD,
	/* 163    33  "!" */ CWORD_CCTL_CCTL_CWORD,
	/* 164    34  """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
	/* 165    35  "#" */ CWORD_CWORD_CWORD_CWORD,
	/* 166    36  "$" */ CVAR_CVAR_CWORD_CVAR,
	/* 167    37  "%" */ CWORD_CWORD_CWORD_CWORD,
	/* 168    38  "&" */ CSPCL_CWORD_CWORD_CWORD,
	/* 169    39  "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
	/* 170    40  "(" */ CSPCL_CWORD_CWORD_CLP,
	/* 171    41  ")" */ CSPCL_CWORD_CWORD_CRP,
	/* 172    42  "*" */ CWORD_CCTL_CCTL_CWORD,
	/* 173    43  "+" */ CWORD_CWORD_CWORD_CWORD,
	/* 174    44  "," */ CWORD_CWORD_CWORD_CWORD,
	/* 175    45  "-" */ CWORD_CCTL_CCTL_CWORD,
	/* 176    46  "." */ CWORD_CWORD_CWORD_CWORD,
	/* 177    47  "/" */ CWORD_CCTL_CCTL_CWORD,
	/* 178    48  "0" */ CWORD_CWORD_CWORD_CWORD,
	/* 179    49  "1" */ CWORD_CWORD_CWORD_CWORD,
	/* 180    50  "2" */ CWORD_CWORD_CWORD_CWORD,
	/* 181    51  "3" */ CWORD_CWORD_CWORD_CWORD,
	/* 182    52  "4" */ CWORD_CWORD_CWORD_CWORD,
	/* 183    53  "5" */ CWORD_CWORD_CWORD_CWORD,
	/* 184    54  "6" */ CWORD_CWORD_CWORD_CWORD,
	/* 185    55  "7" */ CWORD_CWORD_CWORD_CWORD,
	/* 186    56  "8" */ CWORD_CWORD_CWORD_CWORD,
	/* 187    57  "9" */ CWORD_CWORD_CWORD_CWORD,
	/* 188    58  ":" */ CWORD_CCTL_CCTL_CWORD,
	/* 189    59  ";" */ CSPCL_CWORD_CWORD_CWORD,
	/* 190    60  "<" */ CSPCL_CWORD_CWORD_CWORD,
	/* 191    61  "=" */ CWORD_CCTL_CCTL_CWORD,
	/* 192    62  ">" */ CSPCL_CWORD_CWORD_CWORD,
	/* 193    63  "?" */ CWORD_CCTL_CCTL_CWORD,
	/* 194    64  "@" */ CWORD_CWORD_CWORD_CWORD,
	/* 195    65  "A" */ CWORD_CWORD_CWORD_CWORD,
	/* 196    66  "B" */ CWORD_CWORD_CWORD_CWORD,
	/* 197    67  "C" */ CWORD_CWORD_CWORD_CWORD,
	/* 198    68  "D" */ CWORD_CWORD_CWORD_CWORD,
	/* 199    69  "E" */ CWORD_CWORD_CWORD_CWORD,
	/* 200    70  "F" */ CWORD_CWORD_CWORD_CWORD,
	/* 201    71  "G" */ CWORD_CWORD_CWORD_CWORD,
	/* 202    72  "H" */ CWORD_CWORD_CWORD_CWORD,
	/* 203    73  "I" */ CWORD_CWORD_CWORD_CWORD,
	/* 204    74  "J" */ CWORD_CWORD_CWORD_CWORD,
	/* 205    75  "K" */ CWORD_CWORD_CWORD_CWORD,
	/* 206    76  "L" */ CWORD_CWORD_CWORD_CWORD,
	/* 207    77  "M" */ CWORD_CWORD_CWORD_CWORD,
	/* 208    78  "N" */ CWORD_CWORD_CWORD_CWORD,
	/* 209    79  "O" */ CWORD_CWORD_CWORD_CWORD,
	/* 210    80  "P" */ CWORD_CWORD_CWORD_CWORD,
	/* 211    81  "Q" */ CWORD_CWORD_CWORD_CWORD,
	/* 212    82  "R" */ CWORD_CWORD_CWORD_CWORD,
	/* 213    83  "S" */ CWORD_CWORD_CWORD_CWORD,
	/* 214    84  "T" */ CWORD_CWORD_CWORD_CWORD,
	/* 215    85  "U" */ CWORD_CWORD_CWORD_CWORD,
	/* 216    86  "V" */ CWORD_CWORD_CWORD_CWORD,
	/* 217    87  "W" */ CWORD_CWORD_CWORD_CWORD,
	/* 218    88  "X" */ CWORD_CWORD_CWORD_CWORD,
	/* 219    89  "Y" */ CWORD_CWORD_CWORD_CWORD,
	/* 220    90  "Z" */ CWORD_CWORD_CWORD_CWORD,
	/* 221    91  "[" */ CWORD_CCTL_CCTL_CWORD,
	/* 222    92  "\" */ CBACK_CBACK_CCTL_CBACK,
	/* 223    93  "]" */ CWORD_CCTL_CCTL_CWORD,
	/* 224    94  "^" */ CWORD_CWORD_CWORD_CWORD,
	/* 225    95  "_" */ CWORD_CWORD_CWORD_CWORD,
	/* 226    96  "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
	/* 227    97  "a" */ CWORD_CWORD_CWORD_CWORD,
	/* 228    98  "b" */ CWORD_CWORD_CWORD_CWORD,
	/* 229    99  "c" */ CWORD_CWORD_CWORD_CWORD,
	/* 230   100  "d" */ CWORD_CWORD_CWORD_CWORD,
	/* 231   101  "e" */ CWORD_CWORD_CWORD_CWORD,
	/* 232   102  "f" */ CWORD_CWORD_CWORD_CWORD,
	/* 233   103  "g" */ CWORD_CWORD_CWORD_CWORD,
	/* 234   104  "h" */ CWORD_CWORD_CWORD_CWORD,
	/* 235   105  "i" */ CWORD_CWORD_CWORD_CWORD,
	/* 236   106  "j" */ CWORD_CWORD_CWORD_CWORD,
	/* 237   107  "k" */ CWORD_CWORD_CWORD_CWORD,
	/* 238   108  "l" */ CWORD_CWORD_CWORD_CWORD,
	/* 239   109  "m" */ CWORD_CWORD_CWORD_CWORD,
	/* 240   110  "n" */ CWORD_CWORD_CWORD_CWORD,
	/* 241   111  "o" */ CWORD_CWORD_CWORD_CWORD,
	/* 242   112  "p" */ CWORD_CWORD_CWORD_CWORD,
	/* 243   113  "q" */ CWORD_CWORD_CWORD_CWORD,
	/* 244   114  "r" */ CWORD_CWORD_CWORD_CWORD,
	/* 245   115  "s" */ CWORD_CWORD_CWORD_CWORD,
	/* 246   116  "t" */ CWORD_CWORD_CWORD_CWORD,
	/* 247   117  "u" */ CWORD_CWORD_CWORD_CWORD,
	/* 248   118  "v" */ CWORD_CWORD_CWORD_CWORD,
	/* 249   119  "w" */ CWORD_CWORD_CWORD_CWORD,
	/* 250   120  "x" */ CWORD_CWORD_CWORD_CWORD,
	/* 251   121  "y" */ CWORD_CWORD_CWORD_CWORD,
	/* 252   122  "z" */ CWORD_CWORD_CWORD_CWORD,
	/* 253   123  "{" */ CWORD_CWORD_CWORD_CWORD,
	/* 254   124  "|" */ CSPCL_CWORD_CWORD_CWORD,
	/* 255   125  "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
	/* 256   126  "~" */ CWORD_CCTL_CCTL_CWORD,
	/* 257   127      */ CWORD_CWORD_CWORD_CWORD,
};

#endif                                                  /* USE_SIT_FUNCTION */

/*      $NetBSD: alias.c,v 1.11 2002/11/24 22:35:38 christos Exp $      */


#define ATABSIZE 39

static int     funcblocksize;          /* size of structures in function */
static int     funcstringsize;         /* size of strings in node */
static pointer funcblock;              /* block to allocate function from */
static char   *funcstring;             /* block to allocate strings from */

static const short nodesize[26] = {
      SHELL_ALIGN(sizeof (struct ncmd)),
      SHELL_ALIGN(sizeof (struct npipe)),
      SHELL_ALIGN(sizeof (struct nredir)),
      SHELL_ALIGN(sizeof (struct nredir)),
      SHELL_ALIGN(sizeof (struct nredir)),
      SHELL_ALIGN(sizeof (struct nbinary)),
      SHELL_ALIGN(sizeof (struct nbinary)),
      SHELL_ALIGN(sizeof (struct nbinary)),
      SHELL_ALIGN(sizeof (struct nif)),
      SHELL_ALIGN(sizeof (struct nbinary)),
      SHELL_ALIGN(sizeof (struct nbinary)),
      SHELL_ALIGN(sizeof (struct nfor)),
      SHELL_ALIGN(sizeof (struct ncase)),
      SHELL_ALIGN(sizeof (struct nclist)),
      SHELL_ALIGN(sizeof (struct narg)),
      SHELL_ALIGN(sizeof (struct narg)),
      SHELL_ALIGN(sizeof (struct nfile)),
      SHELL_ALIGN(sizeof (struct nfile)),
      SHELL_ALIGN(sizeof (struct nfile)),
      SHELL_ALIGN(sizeof (struct nfile)),
      SHELL_ALIGN(sizeof (struct nfile)),
      SHELL_ALIGN(sizeof (struct ndup)),
      SHELL_ALIGN(sizeof (struct ndup)),
      SHELL_ALIGN(sizeof (struct nhere)),
      SHELL_ALIGN(sizeof (struct nhere)),
      SHELL_ALIGN(sizeof (struct nnot)),
};


static void calcsize(union node *);
static void sizenodelist(struct nodelist *);
static union node *copynode(union node *);
static struct nodelist *copynodelist(struct nodelist *);
static char *nodesavestr(char *);



static void evalstring(char *, int);
union node;     /* BLETCH for ansi C */
static void evaltree(union node *, int);
static void evalbackcmd(union node *, struct backcmd *);

/* in_function returns nonzero if we are currently evaluating a function */
#define in_function()   funcnest
static int evalskip;                   /* set if we are skipping commands */
static int skipcount;           /* number of levels to skip */
static int funcnest;                   /* depth of function calls */

/* reasons for skipping commands (see comment on breakcmd routine) */
#define SKIPBREAK       1
#define SKIPCONT        2
#define SKIPFUNC        3
#define SKIPFILE        4

/*
 * This file was generated by the mkbuiltins program.
 */

#ifdef JOBS
static int bgcmd(int, char **);
#endif
static int breakcmd(int, char **);
static int cdcmd(int, char **);
#ifdef CONFIG_ASH_CMDCMD
static int commandcmd(int, char **);
#endif
static int dotcmd(int, char **);
static int evalcmd(int, char **);
static int execcmd(int, char **);
static int exitcmd(int, char **);
#ifdef CONFIG_ASH_MATH_SUPPORT
static int expcmd(int, char **);
#endif
static int exportcmd(int, char **);
static int falsecmd(int, char **);
#ifdef JOBS
static int fgcmd(int, char **);
#endif
#ifdef CONFIG_ASH_GETOPTS
static int getoptscmd(int, char **);
#endif
static int hashcmd(int, char **);
#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
static int helpcmd(int argc, char **argv);
#endif
#ifdef JOBS
static int jobscmd(int, char **);
#endif
static int localcmd(int, char **);
static int pwdcmd(int, char **);
static int readcmd(int, char **);
static int returncmd(int, char **);
static int setcmd(int, char **);
static int shiftcmd(int, char **);
static int timescmd(int, char **);
static int trapcmd(int, char **);
static int truecmd(int, char **);
static int typecmd(int, char **);
static int umaskcmd(int, char **);
static int unsetcmd(int, char **);
static int waitcmd(int, char **);
static int ulimitcmd(int, char **);
#ifdef JOBS
static int killcmd(int, char **);
#endif

/*      $NetBSD: mail.h,v 1.9 2002/11/24 22:35:40 christos Exp $        */

#ifdef CONFIG_ASH_MAIL
static void chkmail(void);
static void changemail(const char *);
#endif

/*      $NetBSD: exec.h,v 1.20 2003/01/22 20:36:04 dsl Exp $    */

/* values of cmdtype */
#define CMDUNKNOWN      -1      /* no entry in table for command */
#define CMDNORMAL       0       /* command is an executable program */
#define CMDFUNCTION     1       /* command is a shell function */
#define CMDBUILTIN      2       /* command is a shell builtin */

struct builtincmd {
	const char *name;
	int (*builtin)(int, char **);
	/* unsigned flags; */
};

#ifdef CONFIG_ASH_CMDCMD
# ifdef JOBS
#  ifdef CONFIG_ASH_ALIAS
#    define COMMANDCMD (builtincmd + 7)
#    define EXECCMD (builtincmd + 10)
#  else
#    define COMMANDCMD (builtincmd + 6)
#    define EXECCMD (builtincmd + 9)
#  endif
# else /* ! JOBS */
#  ifdef CONFIG_ASH_ALIAS
#    define COMMANDCMD (builtincmd + 6)
#    define EXECCMD (builtincmd + 9)
#  else
#    define COMMANDCMD (builtincmd + 5)
#    define EXECCMD (builtincmd + 8)
#  endif
# endif /* JOBS */
#else   /* ! CONFIG_ASH_CMDCMD */
# ifdef JOBS
#  ifdef CONFIG_ASH_ALIAS
#    define EXECCMD (builtincmd + 9)
#  else
#    define EXECCMD (builtincmd + 8)
#  endif
# else /* ! JOBS */
#  ifdef CONFIG_ASH_ALIAS
#    define EXECCMD (builtincmd + 8)
#  else
#    define EXECCMD (builtincmd + 7)
#  endif
# endif /* JOBS */
#endif /* CONFIG_ASH_CMDCMD */

#define BUILTIN_NOSPEC  "0"
#define BUILTIN_SPECIAL "1"
#define BUILTIN_REGULAR "2"
#define BUILTIN_SPEC_REG "3"
#define BUILTIN_ASSIGN  "4"
#define BUILTIN_SPEC_ASSG  "5"
#define BUILTIN_REG_ASSG   "6"
#define BUILTIN_SPEC_REG_ASSG   "7"

#define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1)
#define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2)

static const struct builtincmd builtincmd[] = {
	{ BUILTIN_SPEC_REG      ".", dotcmd },
	{ BUILTIN_SPEC_REG      ":", truecmd },
#ifdef CONFIG_ASH_ALIAS
	{ BUILTIN_REG_ASSG      "alias", aliascmd },
#endif
#ifdef JOBS
	{ BUILTIN_REGULAR       "bg", bgcmd },
#endif
	{ BUILTIN_SPEC_REG      "break", breakcmd },
	{ BUILTIN_REGULAR       "cd", cdcmd },
	{ BUILTIN_NOSPEC        "chdir", cdcmd },
#ifdef CONFIG_ASH_CMDCMD
	{ BUILTIN_REGULAR       "command", commandcmd },
#endif
	{ BUILTIN_SPEC_REG      "continue", breakcmd },
	{ BUILTIN_SPEC_REG      "eval", evalcmd },
	{ BUILTIN_SPEC_REG      "exec", execcmd },
	{ BUILTIN_SPEC_REG      "exit", exitcmd },
#ifdef CONFIG_ASH_MATH_SUPPORT
	{ BUILTIN_NOSPEC        "exp", expcmd },
#endif
	{ BUILTIN_SPEC_REG_ASSG "export", exportcmd },
	{ BUILTIN_REGULAR       "false", falsecmd },
#ifdef JOBS
	{ BUILTIN_REGULAR       "fg", fgcmd },
#endif
#ifdef CONFIG_ASH_GETOPTS
	{ BUILTIN_REGULAR       "getopts", getoptscmd },
#endif
	{ BUILTIN_NOSPEC        "hash", hashcmd },
#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
	{ BUILTIN_NOSPEC        "help", helpcmd },
#endif
#ifdef JOBS
	{ BUILTIN_REGULAR       "jobs", jobscmd },
	{ BUILTIN_REGULAR       "kill", killcmd },
#endif
#ifdef CONFIG_ASH_MATH_SUPPORT
	{ BUILTIN_NOSPEC        "let", expcmd },
#endif
	{ BUILTIN_ASSIGN        "local", localcmd },
	{ BUILTIN_NOSPEC        "pwd", pwdcmd },
	{ BUILTIN_REGULAR       "read", readcmd },
	{ BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
	{ BUILTIN_SPEC_REG      "return", returncmd },
	{ BUILTIN_SPEC_REG      "set", setcmd },
	{ BUILTIN_SPEC_REG      "shift", shiftcmd },
	{ BUILTIN_SPEC_REG      "times", timescmd },
	{ BUILTIN_SPEC_REG      "trap", trapcmd },
	{ BUILTIN_REGULAR       "true", truecmd },
	{ BUILTIN_NOSPEC        "type", typecmd },
	{ BUILTIN_NOSPEC        "ulimit", ulimitcmd },
	{ BUILTIN_REGULAR       "umask", umaskcmd },
#ifdef CONFIG_ASH_ALIAS
	{ BUILTIN_REGULAR       "unalias", unaliascmd },
#endif
	{ BUILTIN_SPEC_REG      "unset", unsetcmd },
	{ BUILTIN_REGULAR       "wait", waitcmd },
};

#define NUMBUILTINS  (sizeof (builtincmd) / sizeof (struct builtincmd) )



struct cmdentry {
	int cmdtype;
	union param {
		int index;
		const struct builtincmd *cmd;
		struct funcnode *func;
	} u;
};


/* action to find_command() */
#define DO_ERR          0x01    /* prints errors */
#define DO_ABS          0x02    /* checks absolute paths */
#define DO_NOFUNC       0x04    /* don't return shell functions, for command */
#define DO_ALTPATH      0x08    /* using alternate path */
#define DO_ALTBLTIN     0x20    /* %builtin in alt. path */

static const char *pathopt;     /* set by padvance */

static void shellexec(char **, const char *, int)
    __attribute__((__noreturn__));
static char *padvance(const char **, const char *);
static void find_command(char *, struct cmdentry *, int, const char *);
static struct builtincmd *find_builtin(const char *);
static void hashcd(void);
static void changepath(const char *);
static void defun(char *, union node *);
static void unsetfunc(const char *);

#ifdef CONFIG_ASH_MATH_SUPPORT
/* From arith.y */
static int dash_arith(const char *);
#endif

/*      $NetBSD: init.h,v 1.9 2002/11/24 22:35:40 christos Exp $        */

static void reset(void);

/*      $NetBSD: var.h,v 1.21 2003/01/22 20:36:04 dsl Exp $     */

/*
 * Shell variables.
 */

/* flags */
#define VEXPORT         0x01    /* variable is exported */
#define VREADONLY       0x02    /* variable cannot be modified */
#define VSTRFIXED       0x04    /* variable struct is statically allocated */
#define VTEXTFIXED      0x08    /* text is statically allocated */
#define VSTACK          0x10    /* text is allocated on the stack */
#define VUNSET          0x20    /* the variable is not set */
#define VNOFUNC         0x40    /* don't call the callback function */
#define VNOSET          0x80    /* do not set variable - just readonly test */
#define VNOSAVE         0x100   /* when text is on the heap before setvareq */


struct var {
	struct var *next;               /* next entry in hash list */
	int flags;                      /* flags are defined above */
	const char *text;               /* name=value */
	void (*func)(const char *);
					/* function to be called when  */
					/* the variable gets set/unset */
};

struct localvar {
	struct localvar *next;          /* next local variable in list */
	struct var *vp;                 /* the variable that was made local */
	int flags;                      /* saved flags */
	const char *text;               /* saved text */
};


static struct localvar *localvars;

/*
 * Shell variables.
 */

#ifdef CONFIG_ASH_GETOPTS
static void getoptsreset(const char *);
#endif

#ifdef CONFIG_LOCALE_SUPPORT
#include <locale.h>
static void change_lc_all(const char *value);
static void change_lc_ctype(const char *value);
#endif

#define VTABSIZE 39

static const char defpathvar[] =
	"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
#ifdef IFS_BROKEN
static const char defifsvar[] = "IFS= \t\n";
#define defifs (defifsvar + 4)
#else
static const char defifs[] = " \t\n";
#endif


static struct var varinit[] = {
#ifdef IFS_BROKEN
	{ 0,    VSTRFIXED|VTEXTFIXED,           defifsvar,      0 },
#else
	{ 0,    VSTRFIXED|VTEXTFIXED|VUNSET,    "IFS\0",        0 },
#endif

#ifdef CONFIG_ASH_MAIL
	{ 0,    VSTRFIXED|VTEXTFIXED|VUNSET,    "MAIL\0",       changemail },
	{ 0,    VSTRFIXED|VTEXTFIXED|VUNSET,    "MAILPATH\0",   changemail },
#endif

	{ 0,    VSTRFIXED|VTEXTFIXED,           defpathvar,     changepath },
	{ 0,    VSTRFIXED|VTEXTFIXED,           "PS1=$ ",       0 },
	{ 0,    VSTRFIXED|VTEXTFIXED,           "PS2=> ",       0 },
	{ 0,    VSTRFIXED|VTEXTFIXED,           "PS4=+ ",       0 },
#ifdef CONFIG_ASH_GETOPTS
	{ 0,    VSTRFIXED|VTEXTFIXED,           "OPTIND=1",     getoptsreset },
#endif
#ifdef CONFIG_LOCALE_SUPPORT
	{0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL=", change_lc_all},
	{0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE=", change_lc_ctype},
#endif
#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
	{0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE=", NULL},
#endif
};

#define vifs varinit[0]
#ifdef CONFIG_ASH_MAIL
#define vmail (&vifs)[1]
#define vmpath (&vmail)[1]
#else
#define vmpath vifs
#endif
#define vpath (&vmpath)[1]
#define vps1 (&vpath)[1]
#define vps2 (&vps1)[1]
#define vps4 (&vps2)[1]
#define voptind (&vps4)[1]

#define defpath (defpathvar + 5)

/*
 * The following macros access the values of the above variables.
 * They have to skip over the name.  They return the null string
 * for unset variables.
 */

#define ifsval()        (vifs.text + 4)
#define ifsset()        ((vifs.flags & VUNSET) == 0)
#define mailval()       (vmail.text + 5)
#define mpathval()      (vmpath.text + 9)
#define pathval()       (vpath.text + 5)
#define ps1val()        (vps1.text + 4)
#define ps2val()        (vps2.text + 4)
#define ps4val()        (vps4.text + 4)
#define optindval()     (voptind.text + 7)

#define mpathset()      ((vmpath.flags & VUNSET) == 0)

static void setvar(const char *, const char *, int);
static void setvareq(char *, int);
static void listsetvar(struct strlist *, int);
static char *lookupvar(const char *);
static char *bltinlookup(const char *);
static char **listvars(int, int, char ***);
#define environment() listvars(VEXPORT, VUNSET, 0)
static int showvars(const char *, int, int);
static void poplocalvars(void);
static int unsetvar(const char *);
#ifdef CONFIG_ASH_GETOPTS
static int setvarsafe(const char *, const char *, int);
#endif
static int varcmp(const char *, const char *);
static struct var **hashvar(const char *);


static inline int varequal(const char *a, const char *b) {
	return !varcmp(a, b);
}


static int loopnest;            /* current loop nesting level */

struct strpush {
	struct strpush *prev;   /* preceding string on stack */
	char *prevstring;
	int prevnleft;
#ifdef CONFIG_ASH_ALIAS
	struct alias *ap;       /* if push was associated with an alias */
#endif
	char *string;           /* remember the string since it may change */
};

struct parsefile {
	struct parsefile *prev; /* preceding file on stack */
	int linno;              /* current line */
	int fd;                 /* file descriptor (or -1 if string) */
	int nleft;              /* number of chars left in this line */
	int lleft;              /* number of chars left in this buffer */
	char *nextc;            /* next char in buffer */
	char *buf;              /* input buffer */
	struct strpush *strpush; /* for pushing strings at this level */
	struct strpush basestrpush; /* so pushing one is fast */
};

/*
 * The parsefile structure pointed to by the global variable parsefile
 * contains information about the current file being read.
 */


struct redirtab {
	struct redirtab *next;
	int renamed[10];
	int nullredirs;
};

static struct redirtab *redirlist;
static int nullredirs;

extern char **environ;

/*      $NetBSD: output.h,v 1.16 2002/11/24 22:35:42 christos Exp $     */


static void outstr(const char *, FILE *);
static void outcslow(int, FILE *);
static void flushall(void);
static void flushout(FILE *);
static int  out1fmt(const char *, ...)
    __attribute__((__format__(__printf__,1,2)));
static int fmtstr(char *, size_t, const char *, ...)
    __attribute__((__format__(__printf__,3,4)));
static void xwrite(int, const void *, size_t);


#define outerr(f)       ferror(f)
#define out2c(c)        outcslow((c), stderr)

static void out1str(const char *p)
{
	outstr(p, stdout);
}

static void out2str(const char *p)
{
	outstr(p, stderr);
}

static void out1c(char c)
{
	char s[2];

	s[0] = c;
	s[1] = 0;
	outstr(s, stdout);
}

/*
 * Initialization code.
 */

/*
 * This routine initializes the builtin variables.
 */

static inline void
initvar(void)
{
	struct var *vp;
	struct var *end;
	struct var **vpp;

	/*
	 * PS1 depends on uid
	 */
#if defined(CONFIG_FEATURE_COMMAND_EDITING) && defined(CONFIG_FEATURE_SH_FANCY_PROMPT)
	vps1.text = "PS1=\\w \\$ ";
#else
	if (!geteuid())
		vps1.text = "PS1=# ";
#endif
	vp = varinit;
	end = vp + sizeof(varinit) / sizeof(varinit[0]);
	do {
		vpp = hashvar(vp->text);
		vp->next = *vpp;
		*vpp = vp;
	} while (++vp < end);
}

static inline void
init(void)
{

      /* from input.c: */
      {
	      basepf.nextc = basepf.buf = basebuf;
      }

      /* from trap.c: */
      {
	      signal(SIGCHLD, SIG_DFL);
      }

      /* from var.c: */
      {
	      char **envp;
	      char ppid[32];

	      initvar();
	      for (envp = environ ; *envp ; envp++) {
		      if (strchr(*envp, '=')) {
			      setvareq(*envp, VEXPORT|VTEXTFIXED);
		      }
	      }

	      snprintf(ppid, sizeof(ppid), "%d", (int) getppid());
	      setvar("PPID", ppid, 0);
	      setpwd(0, 0);
      }
}

/* PEOF (the end of file marker) */

/*
 * The input line number.  Input.c just defines this variable, and saves
 * and restores it when files are pushed and popped.  The user of this
 * package must set its value.
 */

static int pgetc(void);
static int pgetc2(void);
static int preadbuffer(void);
static void pungetc(void);
static void pushstring(char *, void *);
static void popstring(void);
static void setinputfile(const char *, int);
static void setinputfd(int, int);
static void setinputstring(char *);
static void popfile(void);
static void popallfiles(void);
static void closescript(void);


/*      $NetBSD: jobs.h,v 1.17 2003/01/22 20:36:04 dsl Exp $    */


/* Mode argument to forkshell.  Don't change FORK_FG or FORK_BG. */
#define FORK_FG 0
#define FORK_BG 1
#define FORK_NOJOB 2

/* mode flags for showjob(s) */
#define SHOW_PGID       0x01    /* only show pgid - for jobs -p */
#define SHOW_PID        0x04    /* include process pid */
#define SHOW_CHANGED    0x08    /* only jobs whose state has changed */


/*
 * A job structure contains information about a job.  A job is either a
 * single process or a set of processes contained in a pipeline.  In the
 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
 * array of pids.
 */

struct procstat {
	pid_t   pid;            /* process id */
	int     status;         /* last process status from wait() */
	char    *cmd;           /* text of command being run */
};

struct job {
	struct procstat ps0;    /* status of process */
	struct procstat *ps;    /* status or processes when more than one */
#if JOBS
	int stopstatus;         /* status of a stopped job */
#endif
	uint32_t
		nprocs: 16,     /* number of processes */
		state: 8,
#define JOBRUNNING      0       /* at least one proc running */
#define JOBSTOPPED      1       /* all procs are stopped */
#define JOBDONE         2       /* all procs are completed */
#if JOBS
		sigint: 1,      /* job was killed by SIGINT */
		jobctl: 1,      /* job running under job control */
#endif
		waited: 1,      /* true if this entry has been waited for */
		used: 1,        /* true if this entry is in used */
		changed: 1;     /* true if status has changed */
	struct job *prev_job;   /* previous job */
};

static pid_t backgndpid;        /* pid of last background process */
static int job_warning;         /* user was warned about stopped jobs */
#if JOBS
static int jobctl;              /* true if doing job control */
#endif

static struct job *makejob(union node *, int);
static int forkshell(struct job *, union node *, int);
static int waitforjob(struct job *);
static int stoppedjobs(void);

#if ! JOBS
#define setjobctl(on)   /* do nothing */
#else
static void setjobctl(int);
static void showjobs(FILE *, int);
#endif

/*      $NetBSD: main.h,v 1.9 2002/11/24 22:35:41 christos Exp $        */


/* pid of main shell */
static int rootpid;
/* true if we aren't a child of the main shell */
static int rootshell;

static void readcmdfile(char *);
static void cmdloop(int);

/*      $NetBSD: memalloc.h,v 1.13 2003/01/22 20:36:04 dsl Exp $        */


struct stackmark {
	struct stack_block *stackp;
	char *stacknxt;
	size_t stacknleft;
	struct stackmark *marknext;
};

/* minimum size of a block */
#define MINSIZE SHELL_ALIGN(504)

struct stack_block {
	struct stack_block *prev;
	char space[MINSIZE];
};

static struct stack_block stackbase;
static struct stack_block *stackp = &stackbase;
static struct stackmark *markp;
static char *stacknxt = stackbase.space;
static size_t stacknleft = MINSIZE;
static char *sstrend = stackbase.space + MINSIZE;
static int herefd = -1;


static pointer ckmalloc(size_t);
static pointer ckrealloc(pointer, size_t);
static char *savestr(const char *);
static pointer stalloc(size_t);
static void stunalloc(pointer);
static void setstackmark(struct stackmark *);
static void popstackmark(struct stackmark *);
static void growstackblock(void);
static void *growstackstr(void);
static char *makestrspace(size_t, char *);
static char *stnputs(const char *, size_t, char *);
static char *stputs(const char *, char *);


static inline char *_STPUTC(char c, char *p) {
	if (p == sstrend)
		p = growstackstr();
	*p++ = c;
	return p;
}

#define stackblock() ((void *)stacknxt)
#define stackblocksize() stacknleft
#define STARTSTACKSTR(p) ((p) = stackblock())
#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
#define CHECKSTRSPACE(n, p) \
	({ \
		char *q = (p); \
		size_t l = (n); \
		size_t m = sstrend - q; \
		if (l > m) \
			(p) = makestrspace(l, q); \
		0; \
	})
#define USTPUTC(c, p)   (*p++ = (c))
#define STACKSTRNUL(p)  ((p) == sstrend? (p = growstackstr(), *p = '\0') : (*p = '\0'))
#define STUNPUTC(p)     (--p)
#define STTOPC(p)       p[-1]
#define STADJUST(amount, p)     (p += (amount))

#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
#define ungrabstackstr(s, p) stunalloc((s))
#define stackstrend() ((void *)sstrend)

#define ckfree(p)       free((pointer)(p))

/*      $NetBSD: mystring.h,v 1.10 2002/11/24 22:35:42 christos Exp $   */


#define DOLATSTRLEN 4

static char *prefix(const char *, const char *);
static int number(const char *);
static int is_number(const char *);
static char *single_quote(const char *);
static char *sstrdup(const char *);

#define equal(s1, s2)   (strcmp(s1, s2) == 0)
#define scopy(s1, s2)   ((void)strcpy(s2, s1))

/*      $NetBSD: options.h,v 1.16 2003/01/22 20:36:04 dsl Exp $ */

struct shparam {
	int nparam;             /* # of positional parameters (without $0) */
	unsigned char malloc;   /* if parameter list dynamically allocated */
	char **p;               /* parameter list */
#ifdef CONFIG_ASH_GETOPTS
	int optind;             /* next parameter to be processed by getopts */
	int optoff;             /* used by getopts */
#endif
};


#define eflag optlist[0]
#define fflag optlist[1]
#define Iflag optlist[2]
#define iflag optlist[3]
#define mflag optlist[4]
#define nflag optlist[5]
#define sflag optlist[6]
#define xflag optlist[7]
#define vflag optlist[8]
#define Cflag optlist[9]
#define aflag optlist[10]
#define bflag optlist[11]
#define uflag optlist[12]
#define qflag optlist[13]

#ifdef DEBUG
#define nolog optlist[14]
#define debug optlist[15]
#define NOPTS   16
#else
#define NOPTS   14
#endif

/*      $NetBSD: options.c,v 1.33 2003/01/22 20:36:04 dsl Exp $ */


static const char *const optletters_optnames[NOPTS] = {
	"e"   "errexit",
	"f"   "noglob",
	"I"   "ignoreeof",
	"i"   "interactive",
	"m"   "monitor",
	"n"   "noexec",
	"s"   "stdin",
	"x"   "xtrace",
	"v"   "verbose",
	"C"   "noclobber",
	"a"   "allexport",
	"b"   "notify",
	"u"   "nounset",
	"q"   "quietprofile",
#ifdef DEBUG
	"\0"  "nolog",
	"\0"  "debug",
#endif
};

#define optletters(n) optletters_optnames[(n)][0]
#define optnames(n) (&optletters_optnames[(n)][1])


static char optlist[NOPTS];


static char *arg0;                     /* value of $0 */
static struct shparam shellparam;      /* $@ current positional parameters */
static char **argptr;                  /* argument list for builtin commands */
static char *optionarg;                /* set by nextopt (like getopt) */
static char *optptr;                   /* used by nextopt */

static char *minusc;                   /* argument to -c option */


static void procargs(int, char **);
static void optschanged(void);
static void setparam(char **);
static void freeparam(volatile struct shparam *);
static int shiftcmd(int, char **);
static int setcmd(int, char **);
static int nextopt(const char *);

/*      $NetBSD: redir.h,v 1.14 2002/11/24 22:35:43 christos Exp $      */

/* flags passed to redirect */
#define REDIR_PUSH 01           /* save previous values of file descriptors */

union node;
static void redirect(union node *, int);
static void popredir(int);
static void clearredir(int);
static int copyfd(int, int);
static int redirectsafe(union node *, int);

/*      $NetBSD: show.h,v 1.6 2003/01/22 20:36:04 dsl Exp $     */


#ifdef DEBUG
static void showtree(union node *);
static void trace(const char *, ...);
static void tracev(const char *, va_list);
static void trargs(char **);
static void trputc(int);
static void trputs(const char *);
static void opentrace(void);
#endif

/*      $NetBSD: trap.h,v 1.16 2002/11/24 22:35:43 christos Exp $       */


/* trap handler commands */
static char *trap[NSIG];
/* current value of signal */
static char sigmode[NSIG - 1];
/* indicates specified signal received */
static char gotsig[NSIG - 1];

static void clear_traps(void);
static void setsignal(int);
static void ignoresig(int);
static void onsig(int);
static void dotrap(void);
static void setinteractive(int);
static void exitshell(void) __attribute__((__noreturn__));
static int decode_signal(const char *, int);

/*
 * This routine is called when an error or an interrupt occurs in an
 * interactive shell and control is returned to the main command loop.
 */

static void
reset(void)
{
      /* from eval.c: */
      {
	      evalskip = 0;
	      loopnest = 0;
	      funcnest = 0;
      }

      /* from input.c: */
      {
	      parselleft = parsenleft = 0;      /* clear input buffer */
	      popallfiles();
      }

      /* from parser.c: */
      {
	      tokpushback = 0;
	      checkkwd = 0;
      }

      /* from redir.c: */
      {
	      clearredir(0);
      }

}

#ifdef CONFIG_ASH_ALIAS
static struct alias *atab[ATABSIZE];

static void setalias(const char *, const char *);
static struct alias *freealias(struct alias *);
static struct alias **__lookupalias(const char *);

static void
setalias(const char *name, const char *val)
{
	struct alias *ap, **app;

	app = __lookupalias(name);
	ap = *app;
	INTOFF;
	if (ap) {
		if (!(ap->flag & ALIASINUSE)) {
			ckfree(ap->val);
		}
		ap->val = savestr(val);
		ap->flag &= ~ALIASDEAD;
	} else {
		/* not found */
		ap = ckmalloc(sizeof (struct alias));
		ap->name = savestr(name);
		ap->val = savestr(val);
		ap->flag = 0;
		ap->next = 0;
		*app = ap;
	}
	INTON;
}

static int
unalias(const char *name)
{
	struct alias **app;

	app = __lookupalias(name);

	if (*app) {
		INTOFF;
		*app = freealias(*app);
		INTON;
		return (0);
	}

	return (1);
}

static void
rmaliases(void)
{
	struct alias *ap, **app;
	int i;

	INTOFF;
	for (i = 0; i < ATABSIZE; i++) {
		app = &atab[i];
		for (ap = *app; ap; ap = *app) {
			*app = freealias(*app);
			if (ap == *app) {
				app = &ap->next;
			}
		}
	}
	INTON;
}

static struct alias *
lookupalias(const char *name, int check)
{
	struct alias *ap = *__lookupalias(name);

	if (check && ap && (ap->flag & ALIASINUSE))
		return (NULL);
	return (ap);
}

/*
 * TODO - sort output
 */
static int
aliascmd(int argc, char **argv)
{
	char *n, *v;
	int ret = 0;
	struct alias *ap;

	if (argc == 1) {
		int i;

		for (i = 0; i < ATABSIZE; i++)
			for (ap = atab[i]; ap; ap = ap->next) {
				printalias(ap);
			}
		return (0);
	}
	while ((n = *++argv) != NULL) {
		if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */
			if ((ap = *__lookupalias(n)) == NULL) {
				fprintf(stderr, "%s: %s not found\n", "alias", n);
				ret = 1;
			} else
				printalias(ap);
		} else {
			*v++ = '\0';
			setalias(n, v);
		}
	}

	return (ret);
}

static int
unaliascmd(int argc, char **argv)
{
	int i;

	while ((i = nextopt("a")) != '\0') {
		if (i == 'a') {
			rmaliases();
			return (0);
		}
	}
	for (i = 0; *argptr; argptr++) {
		if (unalias(*argptr)) {
			fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
			i = 1;
		}
	}

	return (i);
}

static struct alias *
freealias(struct alias *ap) {
	struct alias *next;

	if (ap->flag & ALIASINUSE) {
		ap->flag |= ALIASDEAD;
		return ap;
	}

	next = ap->next;
	ckfree(ap->name);
	ckfree(ap->val);
	ckfree(ap);
	return next;
}

static void
printalias(const struct alias *ap) {
	out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
}

static struct alias **
__lookupalias(const char *name) {
	unsigned int hashval;
	struct alias **app;
	const char *p;
	unsigned int ch;

	p = name;

	ch = (unsigned char)*p;
	hashval = ch << 4;
	while (ch) {
		hashval += ch;
		ch = (unsigned char)*++p;
	}
	app = &atab[hashval % ATABSIZE];

	for (; *app; app = &(*app)->next) {
		if (equal(name, (*app)->name)) {
			break;
		}
	}

	return app;
}
#endif /* CONFIG_ASH_ALIAS */


/*      $NetBSD: cd.c,v 1.30 2003/01/22 20:36:03 dsl Exp $      */

/*
 * The cd and pwd commands.
 */

#define CD_PHYSICAL 1
#define CD_PRINT 2

static int docd(const char *, int);
static int cdopt(void);

static char *curdir = nullstr;          /* current working directory */
static char *physdir = nullstr;         /* physical working directory */

static int
cdopt(void)
{
	int flags = 0;
	int i, j;

	j = 'L';
	while ((i = nextopt("LP"))) {
		if (i != j) {
			flags ^= CD_PHYSICAL;
			j = i;
		}
	}

	return flags;
}

static int
cdcmd(int argc, char **argv)
{
	const char *dest;
	const char *path;
	const char *p;
	char c;
	struct stat statb;
	int flags;

	flags = cdopt();
	dest = *argptr;
	if (!dest)
		dest = bltinlookup(homestr);
	else if (dest[0] == '-' && dest[1] == '\0') {
		dest = bltinlookup("OLDPWD");
		flags |= CD_PRINT;
		goto step7;
	}
	if (!dest)
		dest = nullstr;
	if (*dest == '/')
		goto step7;
	if (*dest == '.') {
		c = dest[1];
dotdot:
		switch (c) {
		case '\0':
		case '/':
			goto step6;
		case '.':
			c = dest[2];
			if (c != '.')
				goto dotdot;
		}
	}
	if (!*dest)
		dest = ".";
	if (!(path = bltinlookup("CDPATH"))) {
step6:
step7:
		p = dest;
		goto docd;
	}
	do {
		c = *path;
		p = padvance(&path, dest);
		if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
			if (c && c != ':')
				flags |= CD_PRINT;
docd:
			if (!docd(p, flags))
				goto out;
			break;
		}
	} while (path);
	error("can't cd to %s", dest);
	/* NOTREACHED */
out:
	if (flags & CD_PRINT)
		out1fmt(snlfmt, curdir);
	return 0;
}


/*
 * Update curdir (the name of the current directory) in response to a
 * cd command.
 */

static inline const char *
updatepwd(const char *dir)
{
	char *new;
	char *p;
	char *cdcomppath;
	const char *lim;

	cdcomppath = sstrdup(dir);
	STARTSTACKSTR(new);
	if (*dir != '/') {
		if (curdir == nullstr)
			return 0;
		new = stputs(curdir, new);
	}
	new = makestrspace(strlen(dir) + 2, new);
	lim = stackblock() + 1;
	if (*dir != '/') {
		if (new[-1] != '/')
			USTPUTC('/', new);
		if (new > lim && *lim == '/')
			lim++;
	} else {
		USTPUTC('/', new);
		cdcomppath++;
		if (dir[1] == '/' && dir[2] != '/') {
			USTPUTC('/', new);
			cdcomppath++;
			lim++;
		}
	}
	p = strtok(cdcomppath, "/");
	while (p) {
		switch(*p) {
		case '.':
			if (p[1] == '.' && p[2] == '\0') {
				while (new > lim) {
					STUNPUTC(new);
					if (new[-1] == '/')
						break;
				}
				break;
			} else if (p[1] == '\0')
				break;
			/* fall through */
		default:
			new = stputs(p, new);
			USTPUTC('/', new);
		}
		p = strtok(0, "/");
	}
	if (new > lim)
		STUNPUTC(new);
	*new = 0;
	return stackblock();
}

/*
 * Actually do the chdir.  We also call hashcd to let the routines in exec.c
 * know that the current directory has changed.
 */

static int
docd(const char *dest, int flags)
{
	const char *dir = 0;
	int err;

	TRACE(("docd(\"%s\", %d) called\n", dest, flags));

	INTOFF;
	if (!(flags & CD_PHYSICAL)) {
		dir = updatepwd(dest);
		if (dir)
			dest = dir;
	}
	err = chdir(dest);
	if (err)
		goto out;
	setpwd(dir, 1);
	hashcd();
out:
	INTON;
	return err;
}

/*
 * Find out what the current directory is. If we already know the current
 * directory, this routine returns immediately.
 */
static inline char *
getpwd(void)
{
	char *dir = getcwd(0, 0);
	return dir ? dir : nullstr;
}

static int
pwdcmd(int argc, char **argv)
{
	int flags;
	const char *dir = curdir;

	flags = cdopt();
	if (flags) {
		if (physdir == nullstr)
			setpwd(dir, 0);
		dir = physdir;
	}
	out1fmt(snlfmt, dir);
	return 0;
}

static void
setpwd(const char *val, int setold)
{
	char *oldcur, *dir;

	oldcur = dir = curdir;

	if (setold) {
		setvar("OLDPWD", oldcur, VEXPORT);
	}
	INTOFF;
	if (physdir != nullstr) {
		if (physdir != oldcur)
			free(physdir);
		physdir = nullstr;
	}
	if (oldcur == val || !val) {
		char *s = getpwd();
		physdir = s;
		if (!val)
			dir = s;
	} else
		dir = savestr(val);
	if (oldcur != dir && oldcur != nullstr) {
		free(oldcur);
	}
	curdir = dir;
	INTON;
	setvar("PWD", dir, VEXPORT);
}

/*      $NetBSD: error.c,v 1.30 2003/01/22 20:36:03 dsl Exp $   */

/*
 * Errors and exceptions.
 */

/*
 * Code to handle exceptions in C.
 */



static void exverror(int, const char *, va_list)
    __attribute__((__noreturn__));

/*
 * Called to raise an exception.  Since C doesn't include exceptions, we
 * just do a longjmp to the exception handler.  The type of exception is
 * stored in the global variable "exception".
 */

static void
exraise(int e)
{
#ifdef DEBUG
	if (handler == NULL)
		abort();
#endif
	INTOFF;

	exception = e;
	longjmp(handler->loc, 1);
}


/*
 * Called from trap.c when a SIGINT is received.  (If the user specifies
 * that SIGINT is to be trapped or ignored using the trap builtin, then
 * this routine is not called.)  Suppressint is nonzero when interrupts
 * are held using the INTOFF macro.  (The test for iflag is just
 * defensive programming.)
 */

static void
onint(void) {
	int i;

	intpending = 0;
	sigsetmask(0);
	i = EXSIG;
	if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
		if (!(rootshell && iflag)) {
			signal(SIGINT, SIG_DFL);
			raise(SIGINT);
		}
		i = EXINT;
	}
	exraise(i);
	/* NOTREACHED */
}

static void
exvwarning(const char *msg, va_list ap)
{
	FILE *errs;
	const char *name;
	const char *fmt;

	errs = stderr;
	name = arg0;
	fmt = "%s: ";
	if (commandname) {
		name = commandname;
		fmt = "%s: %d: ";
	}
	fprintf(errs, fmt, name, startlinno);
	vfprintf(errs, msg, ap);
	outcslow('\n', errs);
}

/*
 * Exverror is called to raise the error exception.  If the second argument
 * is not NULL then error prints an error message using printf style
 * formatting.  It then raises the error exception.
 */
static void
exverror(int cond, const char *msg, va_list ap)
{
#ifdef DEBUG
	if (msg) {
		TRACE(("exverror(%d, \"", cond));
		TRACEV((msg, ap));
		TRACE(("\") pid=%d\n", getpid()));
	} else
		TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
	if (msg)
#endif
		exvwarning(msg, ap);

	flushall();
	exraise(cond);
	/* NOTREACHED */
}


static void
error(const char *msg, ...)
{
	va_list ap;

	va_start(ap, msg);
	exverror(EXERROR, msg, ap);
	/* NOTREACHED */
	va_end(ap);
}


static void
exerror(int cond, const char *msg, ...)
{
	va_list ap;

	va_start(ap, msg);
	exverror(cond, msg, ap);
	/* NOTREACHED */
	va_end(ap);
}

/*
 * error/warning routines for external builtins
 */

static void
sh_warnx(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	exvwarning(fmt, ap);
	va_end(ap);
}


/*
 * Return a string describing an error.  The returned string may be a
 * pointer to a static buffer that will be overwritten on the next call.
 * Action describes the operation that got the error.
 */

static const char *
errmsg(int e, const char *em)
{
	if(e == ENOENT || e == ENOTDIR) {

		return em;
	}
	return strerror(e);
}


/*      $NetBSD: eval.c,v 1.71 2003/01/23 03:33:16 rafal Exp $  */

/*
 * Evaluate a command.
 */

/* flags in argument to evaltree */
#define EV_EXIT 01              /* exit after evaluating tree */
#define EV_TESTED 02            /* exit status is checked; ignore -e flag */
#define EV_BACKCMD 04           /* command executing within back quotes */


static void evalloop(union node *, int);
static void evalfor(union node *, int);
static void evalcase(union node *, int);
static void evalsubshell(union node *, int);
static void expredir(union node *);
static void evalpipe(union node *, int);
static void evalcommand(union node *, int);
static int evalbltin(const struct builtincmd *, int, char **);
static int evalfun(struct funcnode *, int, char **, int);
static void prehash(union node *);
static int eprintlist(struct strlist *, int);
static int bltincmd(int, char **);


static const struct builtincmd bltin = {
	"\0\0", bltincmd
};


/*
 * Called to reset things after an exception.
 */

/*
 * The eval commmand.
 */

static int
evalcmd(int argc, char **argv)
{
	char *p;
	char *concat;
	char **ap;

	if (argc > 1) {
		p = argv[1];
		if (argc > 2) {
			STARTSTACKSTR(concat);
			ap = argv + 2;
			for (;;) {
				concat = stputs(p, concat);
				if ((p = *ap++) == NULL)
					break;
				STPUTC(' ', concat);
			}
			STPUTC('\0', concat);
			p = grabstackstr(concat);
		}
		evalstring(p, EV_TESTED);
	}
	return exitstatus;
}


/*
 * Execute a command or commands contained in a string.
 */

static void
evalstring(char *s, int flag)
{
	union node *n;
	struct stackmark smark;

	setstackmark(&smark);
	setinputstring(s);

	while ((n = parsecmd(0)) != NEOF) {
		evaltree(n, flag);
		popstackmark(&smark);
		if (evalskip)
			break;
	}
	popfile();
	popstackmark(&smark);
}



/*
 * Evaluate a parse tree.  The value is left in the global variable
 * exitstatus.
 */

static void
evaltree(union node *n, int flags)
{
	int checkexit = 0;
	void (*evalfn)(union node *, int);
	unsigned isor;
	int status;
	if (n == NULL) {
		TRACE(("evaltree(NULL) called\n"));
		goto out;
	}
	TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
	    getpid(), n, n->type, flags));
	switch (n->type) {
	default:
#ifdef DEBUG
		out1fmt("Node type = %d\n", n->type);
		flushout(stdout);
		break;
#endif
	case NNOT:
		evaltree(n->nnot.com, EV_TESTED);
		status = !exitstatus;
		goto setstatus;
	case NREDIR:
		expredir(n->nredir.redirect);
		status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
		if (!status) {
			evaltree(n->nredir.n, flags & EV_TESTED);
			status = exitstatus;
		}
		popredir(0);
		goto setstatus;
	case NCMD:
		evalfn = evalcommand;
checkexit:
		if (eflag && !(flags & EV_TESTED))
			checkexit = ~0;
		goto calleval;
	case NFOR:
		evalfn = evalfor;
		goto calleval;
	case NWHILE:
	case NUNTIL:
		evalfn = evalloop;
		goto calleval;
	case NSUBSHELL:
	case NBACKGND:
		evalfn = evalsubshell;
		goto calleval;
	case NPIPE:
		evalfn = evalpipe;
		goto checkexit;
	case NCASE:
		evalfn = evalcase;
		goto calleval;
	case NAND:
	case NOR:
	case NSEMI:
#if NAND + 1 != NOR
#error NAND + 1 != NOR
#endif
#if NOR + 1 != NSEMI
#error NOR + 1 != NSEMI
#endif
		isor = n->type - NAND;
		evaltree(
			n->nbinary.ch1,
			(flags | ((isor >> 1) - 1)) & EV_TESTED
		);
		if (!exitstatus == isor)
			break;
		if (!evalskip) {
			n = n->nbinary.ch2;
evaln:
			evalfn = evaltree;
calleval:
			evalfn(n, flags);
			break;
		}
		break;
	case NIF:
		evaltree(n->nif.test, EV_TESTED);
		if (evalskip)
			break;
		if (exitstatus == 0) {
			n = n->nif.ifpart;
			goto evaln;
		} else if (n->nif.elsepart) {
			n = n->nif.elsepart;
			goto evaln;
		}
		goto success;
	case NDEFUN:
		defun(n->narg.text, n->narg.next);
success:
		status = 0;
setstatus:
		exitstatus = status;
		break;
	}
out:
	if (pendingsigs)
		dotrap();
	if (flags & EV_EXIT || checkexit & exitstatus)
		exraise(EXEXIT);
}


#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
static
#endif
void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));


static void
evalloop(union node *n, int flags)
{
	int status;

	loopnest++;
	status = 0;
	flags &= EV_TESTED;
	for (;;) {
		int i;

		evaltree(n->nbinary.ch1, EV_TESTED);
		if (evalskip) {
skipping:         if (evalskip == SKIPCONT && --skipcount <= 0) {
				evalskip = 0;
				continue;
			}
			if (evalskip == SKIPBREAK && --skipcount <= 0)
				evalskip = 0;
			break;
		}
		i = exitstatus;
		if (n->type != NWHILE)
			i = !i;
		if (i != 0)
			break;
		evaltree(n->nbinary.ch2, flags);
		status = exitstatus;
		if (evalskip)
			goto skipping;
	}
	loopnest--;
	exitstatus = status;
}



static void
evalfor(union node *n, int flags)
{
	struct arglist arglist;
	union node *argp;
	struct strlist *sp;
	struct stackmark smark;

	setstackmark(&smark);
	arglist.lastp = &arglist.list;
	for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
		/* XXX */
		if (evalskip)
			goto out;
	}
	*arglist.lastp = NULL;

	exitstatus = 0;
	loopnest++;
	flags &= EV_TESTED;
	for (sp = arglist.list ; sp ; sp = sp->next) {
		setvar(n->nfor.var, sp->text, 0);
		evaltree(n->nfor.body, flags);
		if (evalskip) {
			if (evalskip == SKIPCONT && --skipcount <= 0) {
				evalskip = 0;
				continue;
			}
			if (evalskip == SKIPBREAK && --skipcount <= 0)
				evalskip = 0;
			break;
		}
	}
	loopnest--;
out:
	popstackmark(&smark);
}



static void
evalcase(union node *n, int flags)
{
	union node *cp;
	union node *patp;
	struct arglist arglist;
	struct stackmark smark;

	setstackmark(&smark);
	arglist.lastp = &arglist.list;
	expandarg(n->ncase.expr, &arglist, EXP_TILDE);
	exitstatus = 0;
	for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
		for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
			if (casematch(patp, arglist.list->text)) {
				if (evalskip == 0) {
					evaltree(cp->nclist.body, flags);
				}
				goto out;
			}
		}
	}
out:
	popstackmark(&smark);
}



/*
 * Kick off a subshell to evaluate a tree.
 */

static void
evalsubshell(union node *n, int flags)
{
	struct job *jp;
	int backgnd = (n->type == NBACKGND);
	int status;

	expredir(n->nredir.redirect);
	if (!backgnd && flags & EV_EXIT && !trap[0])
		goto nofork;
	INTOFF;
	jp = makejob(n, 1);
	if (forkshell(jp, n, backgnd) == 0) {
		INTON;
		flags |= EV_EXIT;
		if (backgnd)
			flags &=~ EV_TESTED;
nofork:
		redirect(n->nredir.redirect, 0);
		evaltreenr(n->nredir.n, flags);
		/* never returns */
	}
	status = 0;
	if (! backgnd)
		status = waitforjob(jp);
	exitstatus = status;
	INTON;
}



/*
 * Compute the names of the files in a redirection list.
 */

static void
expredir(union node *n)
{
	union node *redir;

	for (redir = n ; redir ; redir = redir->nfile.next) {
		struct arglist fn;
		fn.lastp = &fn.list;
		switch (redir->type) {
		case NFROMTO:
		case NFROM:
		case NTO:
		case NCLOBBER:
		case NAPPEND:
			expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
			redir->nfile.expfname = fn.list->text;
			break;
		case NFROMFD:
		case NTOFD:
			if (redir->ndup.vname) {
				expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
				fixredir(redir, fn.list->text, 1);
			}
			break;
		}
	}
}



/*
 * Evaluate a pipeline.  All the processes in the pipeline are children
 * of the process creating the pipeline.  (This differs from some versions
 * of the shell, which make the last process in a pipeline the parent
 * of all the rest.)
 */

static void
evalpipe(union node *n, int flags)
{
	struct job *jp;
	struct nodelist *lp;
	int pipelen;
	int prevfd;
	int pip[2];

	TRACE(("evalpipe(0x%lx) called\n", (long)n));
	pipelen = 0;
	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
		pipelen++;
	flags |= EV_EXIT;
	INTOFF;
	jp = makejob(n, pipelen);
	prevfd = -1;
	for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
		prehash(lp->n);
		pip[1] = -1;
		if (lp->next) {
			if (pipe(pip) < 0) {
				close(prevfd);
				error("Pipe call failed");
			}
		}
		if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
			INTON;
			if (pip[1] >= 0) {
				close(pip[0]);
			}
			if (prevfd > 0) {
				dup2(prevfd, 0);
				close(prevfd);
			}
			if (pip[1] > 1) {
				dup2(pip[1], 1);
				close(pip[1]);
			}
			evaltreenr(lp->n, flags);
			/* never returns */
		}
		if (prevfd >= 0)
			close(prevfd);
		prevfd = pip[0];
		close(pip[1]);
	}
	if (n->npipe.backgnd == 0) {
		exitstatus = waitforjob(jp);
		TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
	}
	INTON;
}



/*
 * Execute a command inside back quotes.  If it's a builtin command, we
 * want to save its output in a block obtained from malloc.  Otherwise
 * we fork off a subprocess and get the output of the command via a pipe.
 * Should be called with interrupts off.
 */

static void
evalbackcmd(union node *n, struct backcmd *result)
{
	int saveherefd;

	result->fd = -1;
	result->buf = NULL;
	result->nleft = 0;
	result->jp = NULL;
	if (n == NULL) {
		goto out;
	}

	saveherefd = herefd;
	herefd = -1;

	{
		int pip[2];
		struct job *jp;

		if (pipe(pip) < 0)
			error("Pipe call failed");
		jp = makejob(n, 1);
		if (forkshell(jp, n, FORK_NOJOB) == 0) {
			FORCEINTON;
			close(pip[0]);
			if (pip[1] != 1) {
				close(1);
				copyfd(pip[1], 1);
				close(pip[1]);
			}
			eflag = 0;
			evaltreenr(n, EV_EXIT);
			/* NOTREACHED */
		}
		close(pip[1]);
		result->fd = pip[0];
		result->jp = jp;
	}
	herefd = saveherefd;
out:
	TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
		result->fd, result->buf, result->nleft, result->jp));
}

#ifdef CONFIG_ASH_CMDCMD
static inline char **
parse_command_args(char **argv, const char **path)
{
	char *cp, c;

	for (;;) {
		cp = *++argv;
		if (!cp)
			return 0;
		if (*cp++ != '-')
			break;
		if (!(c = *cp++))
			break;
		if (c == '-' && !*cp) {
			argv++;
			break;
		}
		do {
			switch (c) {
			case 'p':
				*path = defpath;
				break;
			default:
				/* run 'typecmd' for other options */
				return 0;
			}
		} while ((c = *cp++));
	}
	return argv;
}
#endif



/*
 * Execute a simple command.
 */

static void
evalcommand(union node *cmd, int flags)
{
	struct stackmark smark;
	union node *argp;
	struct arglist arglist;
	struct arglist varlist;
	char **argv;
	int argc;
	struct strlist *sp;
	struct cmdentry cmdentry;
	struct job *jp;
	char *lastarg;
	const char *path;
	int spclbltin;
	int cmd_is_exec;
	int status;
	char **nargv;

	/* First expand the arguments. */
	TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
	setstackmark(&smark);
	back_exitstatus = 0;

	cmdentry.cmdtype = CMDBUILTIN;
	cmdentry.u.cmd = &bltin;
	varlist.lastp = &varlist.list;
	*varlist.lastp = NULL;
	arglist.lastp = &arglist.list;
	*arglist.lastp = NULL;

	argc = 0;
	for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
		struct strlist **spp;

		spp = arglist.lastp;
		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
		for (sp = *spp; sp; sp = sp->next)
			argc++;
	}

	argv = nargv = stalloc(sizeof (char *) * (argc + 1));
	for (sp = arglist.list ; sp ; sp = sp->next) {
		TRACE(("evalcommand arg: %s\n", sp->text));
		*nargv++ = sp->text;
	}
	*nargv = NULL;

	lastarg = NULL;
	if (iflag && funcnest == 0 && argc > 0)
		lastarg = nargv[-1];

	expredir(cmd->ncmd.redirect);
	status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH);

	path = vpath.text;
	for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
		struct strlist **spp;
		char *p;

		spp = varlist.lastp;
		expandarg(argp, &varlist, EXP_VARTILDE);

		/*
		 * Modify the command lookup path, if a PATH= assignment
		 * is present
		 */
		p = (*spp)->text;
		if (varequal(p, path))
			path = p;
	}

	/* Print the command if xflag is set. */
	if (xflag) {
		int sep;

		out2str(ps4val());
		sep = 0;
		sep = eprintlist(varlist.list, sep);
		eprintlist(arglist.list, sep);
		out2c('\n');
		flushall();
	}

	cmd_is_exec = 0;
	spclbltin = -1;

	/* Now locate the command. */
	if (argc) {
		const char *oldpath;
		int cmd_flag = DO_ERR;

		path += 5;
		oldpath = path;
		for (;;) {
			find_command(argv[0], &cmdentry, cmd_flag, path);
			if (cmdentry.cmdtype == CMDUNKNOWN) {
				status = 127;
				flushout(stderr);
				goto bail;
			}

			/* implement bltin and command here */
			if (cmdentry.cmdtype != CMDBUILTIN)
				break;
			if (spclbltin < 0)
				spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
			if (cmdentry.u.cmd == EXECCMD)
				cmd_is_exec++;
#ifdef CONFIG_ASH_CMDCMD
			if (cmdentry.u.cmd == COMMANDCMD) {

				path = oldpath;
				nargv = parse_command_args(argv, &path);
				if (!nargv)
					break;
				argc -= nargv - argv;
				argv = nargv;
				cmd_flag |= DO_NOFUNC;
			} else
#endif
				break;
		}
	}

	if (status) {
		/* We have a redirection error. */
		if (spclbltin > 0)
			exraise(EXERROR);
bail:
		exitstatus = status;
		goto out;
	}

	/* Execute the command. */
	switch (cmdentry.cmdtype) {
	default:
		/* Fork off a child process if necessary. */
		if (!(flags & EV_EXIT) || trap[0]) {
			INTOFF;
			jp = makejob(cmd, 1);
			if (forkshell(jp, cmd, FORK_FG) != 0) {
				exitstatus = waitforjob(jp);
				INTON;
				break;
			}
			FORCEINTON;
		}
		listsetvar(varlist.list, VEXPORT|VSTACK);
		shellexec(argv, path, cmdentry.u.index);
		/* NOTREACHED */

	case CMDBUILTIN:
		cmdenviron = varlist.list;
		if (cmdenviron) {
			struct strlist *list = cmdenviron;
			int i = VNOSET;
			if (spclbltin > 0 || argc == 0) {
				i = 0;
				if (cmd_is_exec && argc > 1)
					i = VEXPORT;
			}
			listsetvar(list, i);
		}
		if (evalbltin(cmdentry.u.cmd, argc, argv)) {
			int exit_status;
			int i, j;

			i = exception;
			if (i == EXEXIT)
				goto raise;

			exit_status = 2;
			j = 0;
			if (i == EXINT)
				j = SIGINT;
			if (i == EXSIG)
				j = pendingsigs;
			if (j)
				exit_status = j + 128;
			exitstatus = exit_status;

			if (i == EXINT || spclbltin > 0) {
raise:
				longjmp(handler->loc, 1);
			}
			FORCEINTON;
		}
		break;

	case CMDFUNCTION:
		listsetvar(varlist.list, 0);
		if (evalfun(cmdentry.u.func, argc, argv, flags))
			goto raise;
		break;
	}

out:
	popredir(cmd_is_exec);
	if (lastarg)
		/* dsl: I think this is intended to be used to support
		 * '_' in 'vi' command mode during line editing...
		 * However I implemented that within libedit itself.
		 */
		setvar("_", lastarg, 0);
	popstackmark(&smark);
}

static int
evalbltin(const struct builtincmd *cmd, int argc, char **argv) {
	char *volatile savecmdname;
	struct jmploc *volatile savehandler;
	struct jmploc jmploc;
	int i;

	savecmdname = commandname;
	if ((i = setjmp(jmploc.loc)))
		goto cmddone;
	savehandler = handler;
	handler = &jmploc;
	commandname = argv[0];
	argptr = argv + 1;
	optptr = NULL;                  /* initialize nextopt */
	exitstatus = (*cmd->builtin)(argc, argv);
	flushall();
cmddone:
	exitstatus |= outerr(stdout);
	commandname = savecmdname;
	exsig = 0;
	handler = savehandler;

	return i;
}

static int
evalfun(struct funcnode *func, int argc, char **argv, int flags)
{
	volatile struct shparam saveparam;
	struct localvar *volatile savelocalvars;
	struct jmploc *volatile savehandler;
	struct jmploc jmploc;
	int e;

	saveparam = shellparam;
	savelocalvars = localvars;
	if ((e = setjmp(jmploc.loc))) {
		goto funcdone;
	}
	INTOFF;
	savehandler = handler;
	handler = &jmploc;
	localvars = NULL;
	shellparam.malloc = 0;
	func->count++;
	INTON;
	shellparam.nparam = argc - 1;
	shellparam.p = argv + 1;
#ifdef CONFIG_ASH_GETOPTS
	shellparam.optind = 1;
	shellparam.optoff = -1;
#endif
	funcnest++;
	evaltree(&func->n, flags & EV_TESTED);
	funcnest--;
funcdone:
	INTOFF;
	freefunc(func);
	poplocalvars();
	localvars = savelocalvars;
	freeparam(&shellparam);
	shellparam = saveparam;
	handler = savehandler;
	INTON;
	if (evalskip == SKIPFUNC) {
		evalskip = 0;
		skipcount = 0;
	}
	return e;
}


/*
 * Search for a command.  This is called before we fork so that the
 * location of the command will be available in the parent as well as
 * the child.
 */

static void
prehash(union node *n)
{
	struct cmdentry entry;

	if (n->type == NCMD && n->ncmd.args)
		find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
}



/*
 * Builtin commands.  Builtin commands whose functions are closely
 * tied to evaluation are implemented here.
 */

/*
 * No command given.
 */

static int
bltincmd(int argc, char **argv)
{
	/*
	 * Preserve exitstatus of a previous possible redirection
	 * as POSIX mandates
	 */
	return back_exitstatus;
}


/*
 * Handle break and continue commands.  Break, continue, and return are
 * all handled by setting the evalskip flag.  The evaluation routines
 * above all check this flag, and if it is set they start skipping
 * commands rather than executing them.  The variable skipcount is
 * the number of loops to break/continue, or the number of function
 * levels to return.  (The latter is always 1.)  It should probably
 * be an error to break out of more loops than exist, but it isn't
 * in the standard shell so we don't make it one here.
 */

static int
breakcmd(int argc, char **argv)
{
	int n = argc > 1 ? number(argv[1]) : 1;

	if (n <= 0)
		error(illnum, argv[1]);
	if (n > loopnest)
		n = loopnest;
	if (n > 0) {
		evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
		skipcount = n;
	}
	return 0;
}


/*
 * The return command.
 */

static int
returncmd(int argc, char **argv)
{
	int ret = argc > 1 ? number(argv[1]) : exitstatus;

	if (funcnest) {
		evalskip = SKIPFUNC;
		skipcount = 1;
		return ret;
	}
	else {
		/* Do what ksh does; skip the rest of the file */
		evalskip = SKIPFILE;
		skipcount = 1;
		return ret;
	}
}


static int
falsecmd(int argc, char **argv)
{
	return 1;
}


static int
truecmd(int argc, char **argv)
{
	return 0;
}


static int
execcmd(int argc, char **argv)
{
	if (argc > 1) {
		iflag = 0;              /* exit on error */
		mflag = 0;
		optschanged();
		shellexec(argv + 1, pathval(), 0);
	}
	return 0;
}


static int
eprintlist(struct strlist *sp, int sep)
{
	while (sp) {
		const char *p;

		p = " %s" + (1 - sep);
		sep |= 1;
		fprintf(stderr, p, sp->text);
		sp = sp->next;
	}

	return sep;
}
/*      $NetBSD: exec.c,v 1.35 2003/01/22 20:36:04 dsl Exp $    */

/*
 * When commands are first encountered, they are entered in a hash table.
 * This ensures that a full path search will not have to be done for them
 * on each invocation.
 *
 * We should investigate converting to a linear search, even though that
 * would make the command name "hash" a misnomer.
 */

#define CMDTABLESIZE 31         /* should be prime */
#define ARB 1                   /* actual size determined at run time */



struct tblentry {
	struct tblentry *next;  /* next entry in hash chain */
	union param param;      /* definition of builtin function */
	short cmdtype;          /* index identifying command */
	char rehash;            /* if set, cd done since entry created */
	char cmdname[ARB];      /* name of command */
};


static struct tblentry *cmdtable[CMDTABLESIZE];
static int builtinloc = -1;             /* index in path of %builtin, or -1 */


static void tryexec(char *, char **, char **);
static void printentry(struct tblentry *);
static void clearcmdentry(int);
static struct tblentry *cmdlookup(const char *, int);
static void delete_cmd_entry(void);


/*
 * Exec a program.  Never returns.  If you change this routine, you may
 * have to change the find_command routine as well.
 */

static void
shellexec(char **argv, const char *path, int idx)
{
	char *cmdname;
	int e;
	char **envp;

	clearredir(1);
	envp = environment();
	if (strchr(argv[0], '/') != NULL
#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
		|| find_applet_by_name(argv[0])
#endif
						) {
		tryexec(argv[0], argv, envp);
		e = errno;
	} else {
		e = ENOENT;
		while ((cmdname = padvance(&path, argv[0])) != NULL) {
			if (--idx < 0 && pathopt == NULL) {
				tryexec(cmdname, argv, envp);
				if (errno != ENOENT && errno != ENOTDIR)
					e = errno;
			}
			stunalloc(cmdname);
		}
	}

	/* Map to POSIX errors */
	switch (e) {
	case EACCES:
		exerrno = 126;
		break;
	case ENOENT:
		exerrno = 127;
		break;
	default:
		exerrno = 2;
		break;
	}
	TRACE(("shellexec failed for %s, errno %d, suppressint %d\n",
		argv[0], e, suppressint ));
	exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
	/* NOTREACHED */
}


static void
tryexec(char *cmd, char **argv, char **envp)
{
	int repeated = 0;
#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
	int flg_bb = 0;
	char *name = cmd;

#ifdef CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN
	name = bb_get_last_path_component(name);
	if(find_applet_by_name(name) != NULL)
		flg_bb = 1;
#else
	if(strchr(name, '/') == NULL && find_applet_by_name(name) != NULL) {
		flg_bb = 1;
	}
#endif
	if(flg_bb) {
		char **ap;
		char **new;

		*argv = name;
		if(strcmp(name, "busybox")) {
			for (ap = argv; *ap; ap++);
			ap = new = xmalloc((ap - argv + 2) * sizeof(char *));
			*ap++ = cmd = "/bin/busybox";
			while ((*ap++ = *argv++));
			argv = new;
			repeated++;
		} else {
			cmd = "/bin/busybox";
		}
	}
#endif

repeat:
#ifdef SYSV
	do {
		execve(cmd, argv, envp);
	} while (errno == EINTR);
#else
	execve(cmd, argv, envp);
#endif
	if (repeated++) {
		ckfree(argv);
	} else if (errno == ENOEXEC) {
		char **ap;
		char **new;

		for (ap = argv; *ap; ap++)
			;
		ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
		*ap++ = cmd = "/bin/sh";
		while ((*ap++ = *argv++))
			;
		argv = new;
		goto repeat;
	}
}



/*
 * Do a path search.  The variable path (passed by reference) should be
 * set to the start of the path before the first call; padvance will update
 * this value as it proceeds.  Successive calls to padvance will return
 * the possible path expansions in sequence.  If an option (indicated by
 * a percent sign) appears in the path entry then the global variable
 * pathopt will be set to point to it; otherwise pathopt will be set to
 * NULL.
 */

static char *
padvance(const char **path, const char *name)
{
	const char *p;
	char *q;
	const char *start;
	size_t len;

	if (*path == NULL)
		return NULL;
	start = *path;
	for (p = start ; *p && *p != ':' && *p != '%' ; p++);
	len = p - start + strlen(name) + 2;     /* "2" is for '/' and '\0' */
	while (stackblocksize() < len)
		growstackblock();
	q = stackblock();
	if (p != start) {
		memcpy(q, start, p - start);
		q += p - start;
		*q++ = '/';
	}
	strcpy(q, name);
	pathopt = NULL;
	if (*p == '%') {
		pathopt = ++p;
		while (*p && *p != ':')  p++;
	}
	if (*p == ':')
		*path = p + 1;
	else
		*path = NULL;
	return stalloc(len);
}



/*** Command hashing code ***/


static int
hashcmd(int argc, char **argv)
{
	struct tblentry **pp;
	struct tblentry *cmdp;
	int c;
	struct cmdentry entry;
	char *name;

	while ((c = nextopt("r")) != '\0') {
		clearcmdentry(0);
		return 0;
	}
	if (*argptr == NULL) {
		for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
			for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
				if (cmdp->cmdtype == CMDNORMAL)
					printentry(cmdp);
			}
		}
		return 0;
	}
	c = 0;
	while ((name = *argptr) != NULL) {
		if ((cmdp = cmdlookup(name, 0)) != NULL
		 && (cmdp->cmdtype == CMDNORMAL
		     || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
			delete_cmd_entry();
		find_command(name, &entry, DO_ERR, pathval());
		if (entry.cmdtype == CMDUNKNOWN)
			c = 1;
		argptr++;
	}
	return c;
}


static void
printentry(struct tblentry *cmdp)
{
	int idx;
	const char *path;
	char *name;

	idx = cmdp->param.index;
	path = pathval();
	do {
		name = padvance(&path, cmdp->cmdname);
		stunalloc(name);
	} while (--idx >= 0);
	out1str(name);
	out1fmt(snlfmt, cmdp->rehash ? "*" : nullstr);
}



/*
 * Resolve a command name.  If you change this routine, you may have to
 * change the shellexec routine as well.
 */

static void
find_command(char *name, struct cmdentry *entry, int act, const char *path)
{
	struct tblentry *cmdp;
	int idx;
	int prev;
	char *fullname;
	struct stat statb;
	int e;
	int updatetbl;
	struct builtincmd *bcmd;

	/* If name contains a slash, don't use PATH or hash table */
	if (strchr(name, '/') != NULL) {
		entry->u.index = -1;
		if (act & DO_ABS) {
			while (stat(name, &statb) < 0) {
#ifdef SYSV
				if (errno == EINTR)
					continue;
#endif
				entry->cmdtype = CMDUNKNOWN;
				return;
			}
		}
		entry->cmdtype = CMDNORMAL;
		return;
	}

#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
	if (find_applet_by_name(name)) {
		entry->cmdtype = CMDNORMAL;
		entry->u.index = -1;
		return;
	}
#endif

	updatetbl = (path == pathval());
	if (!updatetbl) {
		act |= DO_ALTPATH;
		if (strstr(path, "%builtin") != NULL)
			act |= DO_ALTBLTIN;
	}

	/* If name is in the table, check answer will be ok */
	if ((cmdp = cmdlookup(name, 0)) != NULL) {
		int bit;

		switch (cmdp->cmdtype) {
		default:
#if DEBUG
			abort();
#endif
		case CMDNORMAL:
			bit = DO_ALTPATH;
			break;
		case CMDFUNCTION:
			bit = DO_NOFUNC;
			break;
		case CMDBUILTIN:
			bit = DO_ALTBLTIN;
			break;
		}
		if (act & bit) {
			updatetbl = 0;
			cmdp = NULL;
		} else if (cmdp->rehash == 0)
			/* if not invalidated by cd, we're done */
			goto success;
	}

	/* If %builtin not in path, check for builtin next */
	bcmd = find_builtin(name);
	if (bcmd && (IS_BUILTIN_REGULAR(bcmd) || (
		act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc <= 0
	)))
		goto builtin_success;

	/* We have to search path. */
	prev = -1;              /* where to start */
	if (cmdp && cmdp->rehash) {     /* doing a rehash */
		if (cmdp->cmdtype == CMDBUILTIN)
			prev = builtinloc;
		else
			prev = cmdp->param.index;
	}

	e = ENOENT;
	idx = -1;
loop:
	while ((fullname = padvance(&path, name)) != NULL) {
		stunalloc(fullname);
		idx++;
		if (pathopt) {
			if (prefix(pathopt, "builtin")) {
				if (bcmd)
					goto builtin_success;
				continue;
			} else if (!(act & DO_NOFUNC) &&
				   prefix(pathopt, "func")) {
				/* handled below */
			} else {
				/* ignore unimplemented options */
				continue;
			}
		}
		/* if rehash, don't redo absolute path names */
		if (fullname[0] == '/' && idx <= prev) {
			if (idx < prev)
				continue;
			TRACE(("searchexec \"%s\": no change\n", name));
			goto success;
		}
		while (stat(fullname, &statb) < 0) {
#ifdef SYSV
			if (errno == EINTR)
				continue;
#endif
			if (errno != ENOENT && errno != ENOTDIR)
				e = errno;
			goto loop;
		}
		e = EACCES;     /* if we fail, this will be the error */
		if (!S_ISREG(statb.st_mode))
			continue;
		if (pathopt) {          /* this is a %func directory */
			stalloc(strlen(fullname) + 1);
			readcmdfile(fullname);
			if ((cmdp = cmdlookup(name, 0)) == NULL ||
			    cmdp->cmdtype != CMDFUNCTION)
				error("%s not defined in %s", name, fullname);
			stunalloc(fullname);
			goto success;
		}
		TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
		if (!updatetbl) {
			entry->cmdtype = CMDNORMAL;
			entry->u.index = idx;
			return;
		}
		INTOFF;
		cmdp = cmdlookup(name, 1);
		cmdp->cmdtype = CMDNORMAL;
		cmdp->param.index = idx;
		INTON;
		goto success;
	}

	/* We failed.  If there was an entry for this command, delete it */
	if (cmdp && updatetbl)
		delete_cmd_entry();
	if (act & DO_ERR)
		sh_warnx("%s: %s", name, errmsg(e, E_EXEC));
	entry->cmdtype = CMDUNKNOWN;
	return;

builtin_success:
	if (!updatetbl) {
		entry->cmdtype = CMDBUILTIN;
		entry->u.cmd = bcmd;
		return;
	}
	INTOFF;
	cmdp = cmdlookup(name, 1);
	cmdp->cmdtype = CMDBUILTIN;
	cmdp->param.cmd = bcmd;
	INTON;
success:
	cmdp->rehash = 0;
	entry->cmdtype = cmdp->cmdtype;
	entry->u = cmdp->param;
}


/*
 * Wrapper around strcmp for qsort/bsearch/...
 */
static int pstrcmp(const void *a, const void *b)
{
	return strcmp((const char *) a, (*(const char *const *) b) + 1);
}

/*
 * Search the table of builtin commands.
 */

static struct builtincmd *
find_builtin(const char *name)
{
	struct builtincmd *bp;

	bp = bsearch(
		name, builtincmd, NUMBUILTINS, sizeof(struct builtincmd),
		pstrcmp
	);
	return bp;
}



/*
 * Called when a cd is done.  Marks all commands so the next time they
 * are executed they will be rehashed.
 */

static void
hashcd(void)
{
	struct tblentry **pp;
	struct tblentry *cmdp;

	for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
		for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
			if (cmdp->cmdtype == CMDNORMAL || (
				cmdp->cmdtype == CMDBUILTIN &&
				!(IS_BUILTIN_REGULAR(cmdp->param.cmd)) &&
				builtinloc > 0
			))
				cmdp->rehash = 1;
		}
	}
}



/*
 * Fix command hash table when PATH changed.
 * Called before PATH is changed.  The argument is the new value of PATH;
 * pathval() still returns the old value at this point.
 * Called with interrupts off.
 */

static void
changepath(const char *newval)
{
	const char *old, *new;
	int idx;
	int firstchange;
	int idx_bltin;

	old = pathval();
	new = newval;
	firstchange = 9999;     /* assume no change */
	idx = 0;
	idx_bltin = -1;
	for (;;) {
		if (*old != *new) {
			firstchange = idx;
			if ((*old == '\0' && *new == ':')
			 || (*old == ':' && *new == '\0'))
				firstchange++;
			old = new;      /* ignore subsequent differences */
		}
		if (*new == '\0')
			break;
		if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
			idx_bltin = idx;
		if (*new == ':') {
			idx++;
		}
		new++, old++;
	}
	if (builtinloc < 0 && idx_bltin >= 0)
		builtinloc = idx_bltin;             /* zap builtins */
	if (builtinloc >= 0 && idx_bltin < 0)
		firstchange = 0;
	clearcmdentry(firstchange);
	builtinloc = idx_bltin;
}


/*
 * Clear out command entries.  The argument specifies the first entry in
 * PATH which has changed.
 */

static void
clearcmdentry(int firstchange)
{
	struct tblentry **tblp;
	struct tblentry **pp;
	struct tblentry *cmdp;

	INTOFF;
	for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
		pp = tblp;
		while ((cmdp = *pp) != NULL) {
			if ((cmdp->cmdtype == CMDNORMAL &&
			     cmdp->param.index >= firstchange)
			 || (cmdp->cmdtype == CMDBUILTIN &&
			     builtinloc >= firstchange)) {
				*pp = cmdp->next;
				ckfree(cmdp);
			} else {
				pp = &cmdp->next;
			}
		}
	}
	INTON;
}



/*
 * Locate a command in the command hash table.  If "add" is nonzero,
 * add the command to the table if it is not already present.  The
 * variable "lastcmdentry" is set to point to the address of the link
 * pointing to the entry, so that delete_cmd_entry can delete the
 * entry.
 *
 * Interrupts must be off if called with add != 0.
 */

static struct tblentry **lastcmdentry;


static struct tblentry *
cmdlookup(const char *name, int add)
{
	unsigned int hashval;
	const char *p;
	struct tblentry *cmdp;
	struct tblentry **pp;

	p = name;
	hashval = (unsigned char)*p << 4;
	while (*p)
		hashval += (unsigned char)*p++;
	hashval &= 0x7FFF;
	pp = &cmdtable[hashval % CMDTABLESIZE];
	for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
		if (equal(cmdp->cmdname, name))
			break;
		pp = &cmdp->next;
	}
	if (add && cmdp == NULL) {
		cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
					+ strlen(name) + 1);
		cmdp->next = NULL;
		cmdp->cmdtype = CMDUNKNOWN;
		strcpy(cmdp->cmdname, name);
	}
	lastcmdentry = pp;
	return cmdp;
}

/*
 * Delete the command entry returned on the last lookup.
 */

static void
delete_cmd_entry(void)
{
	struct tblentry *cmdp;

	INTOFF;
	cmdp = *lastcmdentry;
	*lastcmdentry = cmdp->next;
	if (cmdp->cmdtype == CMDFUNCTION)
		freefunc(cmdp->param.func);
	ckfree(cmdp);
	INTON;
}


/*
 * Add a new command entry, replacing any existing command entry for
 * the same name - except special builtins.
 */

static inline void
addcmdentry(char *name, struct cmdentry *entry)
{
	struct tblentry *cmdp;

	cmdp = cmdlookup(name, 1);
	if (cmdp->cmdtype == CMDFUNCTION) {
		freefunc(cmdp->param.func);
	}
	cmdp->cmdtype = entry->cmdtype;
	cmdp->param = entry->u;
	cmdp->rehash = 0;
}

/*
 * Make a copy of a parse tree.
 */

static inline struct funcnode *
copyfunc(union node *n)
{
	struct funcnode *f;
	size_t blocksize;

	funcblocksize = offsetof(struct funcnode, n);
	funcstringsize = 0;
	calcsize(n);
	blocksize = funcblocksize;
	f = ckmalloc(blocksize + funcstringsize);
	funcblock = (char *) f + offsetof(struct funcnode, n);
	funcstring = (char *) f + blocksize;
	copynode(n);
	f->count = 0;
	return f;
}

/*
 * Define a shell function.
 */

static void
defun(char *name, union node *func)
{
	struct cmdentry entry;

	INTOFF;
	entry.cmdtype = CMDFUNCTION;
	entry.u.func = copyfunc(func);
	addcmdentry(name, &entry);
	INTON;
}


/*
 * Delete a function if it exists.
 */

static void
unsetfunc(const char *name)
{
	struct tblentry *cmdp;

	if ((cmdp = cmdlookup(name, 0)) != NULL &&
	    cmdp->cmdtype == CMDFUNCTION)
		delete_cmd_entry();
}

/*
 * Locate and print what a word is...
 */


#ifdef CONFIG_ASH_CMDCMD
static int
describe_command(char *command, int describe_command_verbose)
#else
#define describe_command_verbose 1
static int
describe_command(char *command)
#endif
{
	struct cmdentry entry;
	struct tblentry *cmdp;
#ifdef CONFIG_ASH_ALIAS
	const struct alias *ap;
#endif
	const char *path = pathval();

	if (describe_command_verbose) {
		out1str(command);
	}

	/* First look at the keywords */
	if (findkwd(command)) {
		out1str(describe_command_verbose ? " is a shell keyword" : command);
		goto out;
	}

#ifdef CONFIG_ASH_ALIAS
	/* Then look at the aliases */
	if ((ap = lookupalias(command, 0)) != NULL) {
		if (describe_command_verbose) {
			out1fmt(" is an alias for %s", ap->val);
		} else {
			out1str("alias ");
			printalias(ap);
			return 0;
		}
		goto out;
	}
#endif
	/* Then check if it is a tracked alias */
	if ((cmdp = cmdlookup(command, 0)) != NULL) {
		entry.cmdtype = cmdp->cmdtype;
		entry.u = cmdp->param;
	} else {
		/* Finally use brute force */
		find_command(command, &entry, DO_ABS, path);
	}

	switch (entry.cmdtype) {
	case CMDNORMAL: {
		int j = entry.u.index;
		char *p;
		if (j == -1) {
			p = command;
		} else {
			do {
				p = padvance(&path, command);
				stunalloc(p);
			} while (--j >= 0);
		}
		if (describe_command_verbose) {
			out1fmt(" is%s %s",
				(cmdp ? " a tracked alias for" : nullstr), p
			);
		} else {
			out1str(p);
		}
		break;
	}

	case CMDFUNCTION:
		if (describe_command_verbose) {
			out1str(" is a shell function");
		} else {
			out1str(command);
		}
		break;

	case CMDBUILTIN:
		if (describe_command_verbose) {
			out1fmt(" is a %sshell builtin",
				IS_BUILTIN_SPECIAL(entry.u.cmd) ?
					"special " : nullstr
			);
		} else {
			out1str(command);
		}
		break;

	default:
		if (describe_command_verbose) {
			out1str(": not found\n");
		}
		return 127;
	}

out:
	out1c('\n');
	return 0;
}

static int
typecmd(int argc, char **argv)
{
	int i;
	int err = 0;

	for (i = 1; i < argc; i++) {
#ifdef CONFIG_ASH_CMDCMD
		err |= describe_command(argv[i], 1);
#else
		err |= describe_command(argv[i]);
#endif
	}
	return err;
}

#ifdef CONFIG_ASH_CMDCMD
static int
commandcmd(int argc, char **argv)
{
	int c;
	int default_path = 0;
	int verify_only = 0;
	int verbose_verify_only = 0;

	while ((c = nextopt("pvV")) != '\0')
		switch (c) {
		default:
#ifdef DEBUG
			fprintf(stderr,
"command: nextopt returned character code 0%o\n", c);
			return EX_SOFTWARE;
#endif
		case 'p':
			default_path = 1;
			break;
		case 'v':
			verify_only = 1;
			break;
		case 'V':
			verbose_verify_only = 1;
			break;
		}

	if (default_path + verify_only + verbose_verify_only > 1 ||
	    !*argptr) {
			fprintf(stderr,
				"command [-p] command [arg ...]\n"
				"command {-v|-V} command\n");
			return EX_USAGE;
	}

	if (verify_only || verbose_verify_only) {
		return describe_command(*argptr, verbose_verify_only);
	}

	return 0;
}
#endif

/*      $NetBSD: expand.c,v 1.56 2002/11/24 22:35:39 christos Exp $     */

/*
 * Routines to expand arguments to commands.  We have to deal with
 * backquotes, shell variables, and file metacharacters.
 */

/*
 * _rmescape() flags
 */
#define RMESCAPE_ALLOC  0x1     /* Allocate a new string */
#define RMESCAPE_GLOB   0x2     /* Add backslashes for glob */
#define RMESCAPE_QUOTED 0x4     /* Remove CTLESC unless in quotes */
#define RMESCAPE_GROW   0x8     /* Grow strings instead of stalloc */
#define RMESCAPE_HEAP   0x10    /* Malloc strings instead of stalloc */

/*
 * Structure specifying which parts of the string should be searched
 * for IFS characters.
 */

struct ifsregion {
	struct ifsregion *next; /* next region in list */
	int begoff;             /* offset of start of region */
	int endoff;             /* offset of end of region */
	int nulonly;            /* search for nul bytes only */
};

/* output of current string */
static char *expdest;
/* list of back quote expressions */
static struct nodelist *argbackq;
/* first struct in list of ifs regions */
static struct ifsregion ifsfirst;
/* last struct in list */
static struct ifsregion *ifslastp;
/* holds expanded arg list */
static struct arglist exparg;

static void argstr(char *, int);
static char *exptilde(char *, char *, int);
static void expbackq(union node *, int, int);
static const char *subevalvar(char *, char *, int, int, int, int, int);
static char *evalvar(char *, int);
static int varisset(char *, int);
static void strtodest(const char *, int, int);
static void memtodest(const char *p, size_t len, int syntax, int quotes);
static void varvalue(char *, int, int);
static void recordregion(int, int, int);
static void removerecordregions(int);
static void ifsbreakup(char *, struct arglist *);
static void ifsfree(void);
static void expandmeta(struct strlist *, int);
static void addglob(const glob_t *);
static void addfname(char *);
static int patmatch(char *, const char *);

static int cvtnum(long);
static size_t esclen(const char *, const char *);
static char *scanleft(char *, char *, char *, char *, int, int);
static char *scanright(char *, char *, char *, char *, int, int);
static void varunset(const char *, const char *, const char *, int)
	__attribute__((__noreturn__));


#define pmatch(a, b) !fnmatch((a), (b), 0)
/*
 * Prepare a pattern for a glob(3) call.
 *
 * Returns an stalloced string.
 */

static inline char *
preglob(const char *pattern, int quoted, int flag) {
	flag |= RMESCAPE_GLOB;
	if (quoted) {
		flag |= RMESCAPE_QUOTED;
	}
	return _rmescapes((char *)pattern, flag);
}


static size_t
esclen(const char *start, const char *p) {
	size_t esc = 0;

	while (p > start && *--p == CTLESC) {
		esc++;
	}
	return esc;
}


/*
 * Expand shell variables and backquotes inside a here document.
 */

static inline void
expandhere(union node *arg, int fd)
{
	herefd = fd;
	expandarg(arg, (struct arglist *)NULL, 0);
	xwrite(fd, stackblock(), expdest - (char *)stackblock());
}


/*
 * Perform variable substitution and command substitution on an argument,
 * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
 * perform splitting and file name expansion.  When arglist is NULL, perform
 * here document expansion.
 */

void
expandarg(union node *arg, struct arglist *arglist, int flag)
{
	struct strlist *sp;
	char *p;

	argbackq = arg->narg.backquote;
	STARTSTACKSTR(expdest);
	ifsfirst.next = NULL;
	ifslastp = NULL;
	argstr(arg->narg.text, flag);
	if (arglist == NULL) {
		return;                 /* here document expanded */
	}
	STPUTC('\0', expdest);
	p = grabstackstr(expdest);
	exparg.lastp = &exparg.list;
	/*
	 * TODO - EXP_REDIR
	 */
	if (flag & EXP_FULL) {
		ifsbreakup(p, &exparg);
		*exparg.lastp = NULL;
		exparg.lastp = &exparg.list;
		expandmeta(exparg.list, flag);
	} else {
		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
			rmescapes(p);
		sp = (struct strlist *)stalloc(sizeof (struct strlist));
		sp->text = p;
		*exparg.lastp = sp;
		exparg.lastp = &sp->next;
	}
	if (ifsfirst.next)
		ifsfree();
	*exparg.lastp = NULL;
	if (exparg.list) {
		*arglist->lastp = exparg.list;
		arglist->lastp = exparg.lastp;
	}
}



/*
 * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
 * characters to allow for further processing.  Otherwise treat
 * $@ like $* since no splitting will be performed.
 */

static void
argstr(char *p, int flag)
{
	static const char spclchars[] = {
		'=',
		':',
		CTLQUOTEMARK,
		CTLENDVAR,
		CTLESC,
		CTLVAR,
		CTLBACKQ,
		CTLBACKQ | CTLQUOTE,
#ifdef CONFIG_ASH_MATH_SUPPORT
		CTLENDARI,
#endif
		0
	};
	const char *reject = spclchars;
	int c;
	int quotes = flag & (EXP_FULL | EXP_CASE);      /* do CTLESC */
	int breakall = flag & EXP_WORD;
	int inquotes;
	size_t length;
	int startloc;

	if (!(flag & EXP_VARTILDE)) {
		reject += 2;
	} else if (flag & EXP_VARTILDE2) {
		reject++;
	}
	inquotes = 0;
	length = 0;
	if (flag & EXP_TILDE) {
		char *q;

		flag &= ~EXP_TILDE;
tilde:
		q = p;
		if (*q == CTLESC && (flag & EXP_QWORD))
			q++;
		if (*q == '~')
			p = exptilde(p, q, flag);
	}
start:
	startloc = expdest - (char *)stackblock();
	for (;;) {
		length += strcspn(p + length, reject);
		c = p[length];
		if (c && (!(c & 0x80)
#ifdef CONFIG_ASH_MATH_SUPPORT
					|| c == CTLENDARI
#endif
		   )) {
			/* c == '=' || c == ':' || c == CTLENDARI */
			length++;
		}
		if (length > 0) {
			int newloc;
			expdest = stnputs(p, length, expdest);
			newloc = expdest - (char *)stackblock();
			if (breakall && !inquotes && newloc > startloc) {
				recordregion(startloc, newloc, 0);
			}
			startloc = newloc;
		}
		p += length + 1;
		length = 0;

		switch (c) {
		case '\0':
			goto breakloop;
		case '=':
			if (flag & EXP_VARTILDE2) {
				p--;
				continue;
			}
			flag |= EXP_VARTILDE2;
			reject++;
			/* fall through */
		case ':':
			/*
			 * sort of a hack - expand tildes in variable
			 * assignments (after the first '=' and after ':'s).
			 */
			if (*--p == '~') {
				goto tilde;
			}
			continue;
		}

		switch (c) {
		case CTLENDVAR: /* ??? */
			goto breakloop;
		case CTLQUOTEMARK:
			/* "$@" syntax adherence hack */
			if (
				!inquotes &&
				!memcmp(p, dolatstr, DOLATSTRLEN) &&
				(p[4] == CTLQUOTEMARK || (
					p[4] == CTLENDVAR &&
					p[5] == CTLQUOTEMARK
				))
			) {
				p = evalvar(p + 1, flag) + 1;
				goto start;
			}
			inquotes = !inquotes;
addquote:
			if (quotes) {
				p--;
				length++;
				startloc++;
			}
			break;
		case CTLESC:
			startloc++;
			length++;
			goto addquote;
		case CTLVAR:
			p = evalvar(p, flag);
			goto start;
		case CTLBACKQ:
			c = 0;
		case CTLBACKQ|CTLQUOTE:
			expbackq(argbackq->n, c, quotes);
			argbackq = argbackq->next;
			goto start;
#ifdef CONFIG_ASH_MATH_SUPPORT
		case CTLENDARI:
			p--;
			expari(quotes);
			goto start;
#endif
		}
	}
breakloop:
	;
}

static char *
exptilde(char *startp, char *p, int flag)
{
	char c;
	char *name;
	struct passwd *pw;
	const char *home;
	int quotes = flag & (EXP_FULL | EXP_CASE);
	int startloc;

	name = p + 1;

	while ((c = *++p) != '\0') {
		switch(c) {
		case CTLESC:
			return (startp);
		case CTLQUOTEMARK:
			return (startp);
		case ':':
			if (flag & EXP_VARTILDE)
				goto done;
			break;
		case '/':
		case CTLENDVAR:
			goto done;
		}
	}
done:
	*p = '\0';
	if (*name == '\0') {
		if ((home = lookupvar(homestr)) == NULL)
			goto lose;
	} else {
		if ((pw = getpwnam(name)) == NULL)
			goto lose;
		home = pw->pw_dir;
	}
	if (*home == '\0')
		goto lose;
	*p = c;
	startloc = expdest - (char *)stackblock();
	strtodest(home, SQSYNTAX, quotes);
	recordregion(startloc, expdest - (char *)stackblock(), 0);
	return (p);
lose:
	*p = c;
	return (startp);
}


static void
removerecordregions(int endoff)
{
	if (ifslastp == NULL)
		return;

	if (ifsfirst.endoff > endoff) {
		while (ifsfirst.next != NULL) {
			struct ifsregion *ifsp;
			INTOFF;
			ifsp = ifsfirst.next->next;
			ckfree(ifsfirst.next);
			ifsfirst.next = ifsp;
			INTON;
		}
		if (ifsfirst.begoff > endoff)
			ifslastp = NULL;
		else {
			ifslastp = &ifsfirst;
			ifsfirst.endoff = endoff;
		}
		return;
	}

	ifslastp = &ifsfirst;
	while (ifslastp->next && ifslastp->next->begoff < endoff)
		ifslastp=ifslastp->next;
	while (ifslastp->next != NULL) {
		struct ifsregion *ifsp;
		INTOFF;
		ifsp = ifslastp->next->next;
		ckfree(ifslastp->next);
		ifslastp->next = ifsp;
		INTON;
	}
	if (ifslastp->endoff > endoff)
		ifslastp->endoff = endoff;
}


#ifdef CONFIG_ASH_MATH_SUPPORT
/*
 * Expand arithmetic expression.  Backup to start of expression,
 * evaluate, place result in (backed up) result, adjust string position.
 */
void
expari(int quotes)
{
	char *p, *start;
	int begoff;
	int flag;
	int len;

	/*      ifsfree(); */

	/*
	 * This routine is slightly over-complicated for
	 * efficiency.  Next we scan backwards looking for the
	 * start of arithmetic.
	 */
	start = stackblock();
	p = expdest - 1;
	*p = '\0';
	p--;
	do {
		int esc;

		while (*p != CTLARI) {
			p--;
#ifdef DEBUG
			if (p < start) {
				error("missing CTLARI (shouldn't happen)");
			}
#endif
		}

		esc = esclen(start, p);
		if (!(esc % 2)) {
			break;
		}

		p -= esc + 1;
	} while (1);

	begoff = p - start;

	removerecordregions(begoff);

	flag = p[1];

	expdest = p;

	if (quotes)
		rmescapes(p + 2);

	len = cvtnum(dash_arith(p + 2));

	if (flag != '"')
		recordregion(begoff, begoff + len, 0);
}
#endif

/*
 * Expand stuff in backwards quotes.
 */

static void
expbackq(union node *cmd, int quoted, int quotes)
{
	struct backcmd in;
	int i;
	char buf[128];
	char *p;
	char *dest;
	int startloc;
	int syntax = quoted? DQSYNTAX : BASESYNTAX;
	struct stackmark smark;

	INTOFF;
	setstackmark(&smark);
	dest = expdest;
	startloc = dest - (char *)stackblock();
	grabstackstr(dest);
	evalbackcmd(cmd, (struct backcmd *) &in);
	popstackmark(&smark);

	p = in.buf;
	i = in.nleft;
	if (i == 0)
		goto read;
	for (;;) {
		memtodest(p, i, syntax, quotes);
read:
		if (in.fd < 0)
			break;
		i = safe_read(in.fd, buf, sizeof buf);
		TRACE(("expbackq: read returns %d\n", i));
		if (i <= 0)
			break;
		p = buf;
	}

	if (in.buf)
		ckfree(in.buf);
	if (in.fd >= 0) {
		close(in.fd);
		back_exitstatus = waitforjob(in.jp);
	}
	INTON;

	/* Eat all trailing newlines */
	dest = expdest;
	for (; dest > (char *)stackblock() && dest[-1] == '\n';)
		STUNPUTC(dest);
	expdest = dest;

	if (quoted == 0)
		recordregion(startloc, dest - (char *)stackblock(), 0);
	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
		(dest - (char *)stackblock()) - startloc,
		(dest - (char *)stackblock()) - startloc,
		stackblock() + startloc));
}


static char *
scanleft(
	char *startp, char *rmesc, char *rmescend, char *str, int quotes,
	int zero
) {
	char *loc;
	char *loc2;
	char c;

	loc = startp;
	loc2 = rmesc;
	do {
		int match;
		const char *s = loc2;
		c = *loc2;
		if (zero) {
			*loc2 = '\0';
			s = rmesc;
		}
		match = pmatch(str, s);
		*loc2 = c;
		if (match)
			return loc;
		if (quotes && *loc == CTLESC)
			loc++;
		loc++;
		loc2++;
	} while (c);
	return 0;
}


static char *
scanright(
	char *startp, char *rmesc, char *rmescend, char *str, int quotes,
	int zero
) {
	int esc = 0;
	char *loc;
	char *loc2;

	for (loc = str - 1, loc2 = rmescend; loc >= startp; loc2--) {
		int match;
		char c = *loc2;
		const char *s = loc2;
		if (zero) {
			*loc2 = '\0';
			s = rmesc;
		}
		match = pmatch(str, s);
		*loc2 = c;
		if (match)
			return loc;
		loc--;
		if (quotes) {
			if (--esc < 0) {
				esc = esclen(startp, loc);
			}
			if (esc % 2) {
				esc--;
				loc--;
			}
		}
	}
	return 0;
}

static const char *
subevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags, int quotes)
{
	char *startp;
	char *loc;
	int saveherefd = herefd;
	struct nodelist *saveargbackq = argbackq;
	int amount;
	char *rmesc, *rmescend;
	int zero;
	char *(*scan)(char *, char *, char *, char *, int , int);

	herefd = -1;
	argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
	STPUTC('\0', expdest);
	herefd = saveherefd;
	argbackq = saveargbackq;
	startp = stackblock() + startloc;

	switch (subtype) {
	case VSASSIGN:
		setvar(str, startp, 0);
		amount = startp - expdest;
		STADJUST(amount, expdest);
		return startp;

	case VSQUESTION:
		varunset(p, str, startp, varflags);
		/* NOTREACHED */
	}

	subtype -= VSTRIMRIGHT;
#ifdef DEBUG
	if (subtype < 0 || subtype > 3)
		abort();
#endif

	rmesc = startp;
	rmescend = stackblock() + strloc;
	if (quotes) {
		rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
		if (rmesc != startp) {
			rmescend = expdest;
			startp = stackblock() + startloc;
		}
	}
	rmescend--;
	str = stackblock() + strloc;
	preglob(str, varflags & VSQUOTE, 0);

	/* zero = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX */
	zero = subtype >> 1;
	/* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
	scan = (subtype & 1) ^ zero ? scanleft : scanright;

	loc = scan(startp, rmesc, rmescend, str, quotes, zero);
	if (loc) {
		if (zero) {
			memmove(startp, loc, str - loc);
			loc = startp + (str - loc) - 1;
		}
		*loc = '\0';
		amount = loc - expdest;
		STADJUST(amount, expdest);
	}
	return loc;
}


/*
 * Expand a variable, and return a pointer to the next character in the
 * input string.
 */
static char *
evalvar(char *p, int flag)
{
	int subtype;
	int varflags;
	char *var;
	int patloc;
	int c;
	int set;
	int startloc;
	size_t varlen;
	int easy;
	int quotes;
	int quoted;

	quotes = flag & (EXP_FULL | EXP_CASE);
	varflags = *p++;
	subtype = varflags & VSTYPE;
	quoted = varflags & VSQUOTE;
	var = p;
	easy = (!quoted || (*var == '@' && shellparam.nparam));
	varlen = 0;
	startloc = expdest - (char *)stackblock();
	p = strchr(p, '=') + 1;

	if (!is_name(*var)) {
		set = varisset(var, varflags & VSNUL);
		set--;
		if (subtype == VSPLUS)
			goto vsplus;
		if (++set) {
			varvalue(var, quoted, flag);
			if (subtype == VSLENGTH) {
				varlen =
					expdest - (char *)stackblock() -
					startloc;
				STADJUST(-varlen, expdest);
				goto vslen;
			}
		}
	} else {
		const char *val;
again:
		/* jump here after setting a variable with ${var=text} */
		val = lookupvar(var);
		set = !val || ((varflags & VSNUL) && !*val);
		if (subtype == VSPLUS)
			goto vsplus;
		if (--set) {
			varlen = strlen(val);
			if (subtype == VSLENGTH)
				goto vslen;
			memtodest(
				val, varlen, quoted ? DQSYNTAX : BASESYNTAX,
				quotes
			);
		}
	}


	if (subtype == VSMINUS) {
vsplus:
		if (!set) {
			argstr(
				p, flag | EXP_TILDE |
					(quoted ?  EXP_QWORD : EXP_WORD)
			);
			goto end;
		}
		if (easy)
			goto record;
		goto end;
	}

	if (subtype == VSASSIGN || subtype == VSQUESTION) {
		if (!set) {
			if (subevalvar(p, var, 0, subtype, startloc,
				       varflags, 0)) {
				varflags &= ~VSNUL;
				/*
				 * Remove any recorded regions beyond
				 * start of variable
				 */
				removerecordregions(startloc);
				goto again;
			}
			goto end;
		}
		if (easy)
			goto record;
		goto end;
	}

	if (!set && uflag)
		varunset(p, var, 0, 0);

	if (subtype == VSLENGTH) {
vslen:
		cvtnum(varlen);
		goto record;
	}

	if (subtype == VSNORMAL) {
		if (!easy)
			goto end;
record:
		recordregion(startloc, expdest - (char *)stackblock(), quoted);
		goto end;
	}

#ifdef DEBUG
	switch (subtype) {
	case VSTRIMLEFT:
	case VSTRIMLEFTMAX:
	case VSTRIMRIGHT:
	case VSTRIMRIGHTMAX:
		break;
	default:
		abort();
	}
#endif

	if (set) {
		/*
		 * Terminate the string and start recording the pattern
		 * right after it
		 */
		STPUTC('\0', expdest);
		patloc = expdest - (char *)stackblock();
		if (subevalvar(p, NULL, patloc, subtype,
			       startloc, varflags, quotes) == 0) {
			int amount = expdest - (
				(char *)stackblock() + patloc - 1
			);
			STADJUST(-amount, expdest);
		}
		/* Remove any recorded regions beyond start of variable */
		removerecordregions(startloc);
		goto record;
	}

end:
	if (subtype != VSNORMAL) {      /* skip to end of alternative */
		int nesting = 1;
		for (;;) {
			if ((c = *p++) == CTLESC)
				p++;
			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
				if (set)
					argbackq = argbackq->next;
			} else if (c == CTLVAR) {
				if ((*p++ & VSTYPE) != VSNORMAL)
					nesting++;
			} else if (c == CTLENDVAR) {
				if (--nesting == 0)
					break;
			}
		}
	}
	return p;
}



/*
 * Test whether a specialized variable is set.
 */

static int
varisset(char *name, int nulok)
{
	if (*name == '!')
		return backgndpid != 0;
	else if (*name == '@' || *name == '*') {
		if (*shellparam.p == NULL)
			return 0;

		if (nulok) {
			char **av;

			for (av = shellparam.p; *av; av++)
				if (**av != '\0')
					return 1;
			return 0;
		}
	} else if (is_digit(*name)) {
		char *ap;
		int num = atoi(name);

		if (num > shellparam.nparam)
			return 0;

		if (num == 0)
			ap = arg0;
		else
			ap = shellparam.p[num - 1];

		if (nulok && (ap == NULL || *ap == '\0'))
			return 0;
	}
	return 1;
}



/*
 * Put a string on the stack.
 */

static void
memtodest(const char *p, size_t len, int syntax, int quotes) {
	char *q = expdest;

	q = makestrspace(len * 2, q);

	while (len--) {
		int c = *p++;
		if (!c)
			continue;
		if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK))
			USTPUTC(CTLESC, q);
		USTPUTC(c, q);
	}

	expdest = q;
}


static void
strtodest(const char *p, int syntax, int quotes)
{
	memtodest(p, strlen(p), syntax, quotes);
}



/*
 * Add the value of a specialized variable to the stack string.
 */

static void
varvalue(char *name, int quoted, int flags)
{
	int num;
	char *p;
	int i;
	int sep;
	int sepq = 0;
	char **ap;
	int syntax;
	int allow_split = flags & EXP_FULL;
	int quotes = flags & (EXP_FULL | EXP_CASE);

	syntax = quoted ? DQSYNTAX : BASESYNTAX;
	switch (*name) {
	case '$':
		num = rootpid;
		goto numvar;
	case '?':
		num = exitstatus;
		goto numvar;
	case '#':
		num = shellparam.nparam;
		goto numvar;
	case '!':
		num = backgndpid;
numvar:
		cvtnum(num);
		break;
	case '-':
		for (i = 0 ; i < NOPTS ; i++) {
			if (optlist[i])
				STPUTC(optletters(i), expdest);
		}
		break;
	case '@':
		if (allow_split && quoted) {
			sep = 1 << CHAR_BIT;
			goto param;
		}
		/* fall through */
	case '*':
		sep = ifsset() ? ifsval()[0] : ' ';
		if (quotes) {
			sepq = (SIT(sep, syntax) == CCTL) || (SIT(sep, syntax) == CBACK);
		}
param:
		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
			strtodest(p, syntax, quotes);
			if (*ap && sep) {
				p = expdest;
				if (sepq)
					STPUTC(CTLESC, p);
				STPUTC(sep, p);
				expdest = p;
			}
		}
		break;
	case '0':
		strtodest(arg0, syntax, quotes);
		break;
	default:
		num = atoi(name);
		if (num > 0 && num <= shellparam.nparam) {
			strtodest(shellparam.p[num - 1], syntax, quotes);
		}
		break;
	}
}



/*
 * Record the fact that we have to scan this region of the
 * string for IFS characters.
 */

static void
recordregion(int start, int end, int nulonly)
{
	struct ifsregion *ifsp;

	if (ifslastp == NULL) {
		ifsp = &ifsfirst;
	} else {
		INTOFF;
		ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
		ifsp->next = NULL;
		ifslastp->next = ifsp;
		INTON;
	}
	ifslastp = ifsp;
	ifslastp->begoff = start;
	ifslastp->endoff = end;
	ifslastp->nulonly = nulonly;
}



/*
 * Break the argument string into pieces based upon IFS and add the
 * strings to the argument list.  The regions of the string to be
 * searched for IFS characters have been stored by recordregion.
 */
static void
ifsbreakup(char *string, struct arglist *arglist)
{
	struct ifsregion *ifsp;
	struct strlist *sp;
	char *start;
	char *p;
	char *q;
	const char *ifs, *realifs;
	int ifsspc;
	int nulonly;


	start = string;
	if (ifslastp != NULL) {
		ifsspc = 0;
		nulonly = 0;
		realifs = ifsset() ? ifsval() : defifs;
		ifsp = &ifsfirst;
		do {
			p = string + ifsp->begoff;
			nulonly = ifsp->nulonly;
			ifs = nulonly ? nullstr : realifs;
			ifsspc = 0;
			while (p < string + ifsp->endoff) {
				q = p;
				if (*p == CTLESC)
					p++;
				if (strchr(ifs, *p)) {
					if (!nulonly)
						ifsspc = (strchr(defifs, *p) != NULL);
					/* Ignore IFS whitespace at start */
					if (q == start && ifsspc) {
						p++;
						start = p;
						continue;
					}
					*q = '\0';
					sp = (struct strlist *)stalloc(sizeof *sp);
					sp->text = start;
					*arglist->lastp = sp;
					arglist->lastp = &sp->next;
					p++;
					if (!nulonly) {
						for (;;) {
							if (p >= string + ifsp->endoff) {
								break;
							}
							q = p;
							if (*p == CTLESC)
								p++;
							if (strchr(ifs, *p) == NULL ) {
								p = q;
								break;
							} else if (strchr(defifs, *p) == NULL) {
								if (ifsspc) {
									p++;
									ifsspc = 0;
								} else {
									p = q;
									break;
								}
							} else
								p++;
						}
					}
					start = p;
				} else
					p++;
			}
		} while ((ifsp = ifsp->next) != NULL);
		if (nulonly)
			goto add;
	}

	if (!*start)
		return;

add:
	sp = (struct strlist *)stalloc(sizeof *sp);
	sp->text = start;
	*arglist->lastp = sp;
	arglist->lastp = &sp->next;
}

static void
ifsfree(void)
{
	struct ifsregion *p;

	INTOFF;
	p = ifsfirst.next;
	do {
		struct ifsregion *ifsp;
		ifsp = p->next;
		ckfree(p);
		p = ifsp;
	} while (p);
	ifslastp = NULL;
	ifsfirst.next = NULL;
	INTON;
}



/*
 * Expand shell metacharacters.  At this point, the only control characters
 * should be escapes.  The results are stored in the list exparg.
 */

static void
expandmeta(str, flag)
	struct strlist *str;
	int flag;
{
	/* TODO - EXP_REDIR */

	while (str) {
		const char *p;
		glob_t pglob;
		int i;

		if (fflag)
			goto nometa;
		INTOFF;
		p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
		i = glob(p, GLOB_NOMAGIC, 0, &pglob);
		if (p != str->text)
			ckfree(p);
		switch (i) {
		case 0:
			if (!(pglob.gl_flags & GLOB_MAGCHAR))
				goto nometa2;
			addglob(&pglob);
			globfree(&pglob);
			INTON;
			break;
		case GLOB_NOMATCH:
nometa2:
			globfree(&pglob);
			INTON;
nometa:
			*exparg.lastp = str;
			rmescapes(str->text);
			exparg.lastp = &str->next;
			break;
		default:        /* GLOB_NOSPACE */
			error(bb_msg_memory_exhausted);
		}
		str = str->next;
	}
}


/*
 * Add the result of glob(3) to the list.
 */

static void
addglob(pglob)
	const glob_t *pglob;
{
	char **p = pglob->gl_pathv;

	do {
		addfname(*p);
	} while (*++p);
}


/*
 * Add a file name to the list.
 */

static void
addfname(char *name)
{
	struct strlist *sp;

	sp = (struct strlist *)stalloc(sizeof *sp);
	sp->text = sstrdup(name);
	*exparg.lastp = sp;
	exparg.lastp = &sp->next;
}


/*
 * Returns true if the pattern matches the string.
 */

static inline int
patmatch(char *pattern, const char *string)
{
	return pmatch(preglob(pattern, 0, 0), string);
}


/*
 * Remove any CTLESC characters from a string.
 */

static char *
_rmescapes(char *str, int flag)
{
	char *p, *q, *r;
	static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
	unsigned inquotes;
	int notescaped;
	int globbing;

	p = strpbrk(str, qchars);
	if (!p) {
		return str;
	}
	q = p;
	r = str;
	if (flag & RMESCAPE_ALLOC) {
		size_t len = p - str;
		size_t fulllen = len + strlen(p) + 1;

		if (flag & RMESCAPE_GROW) {
			r = makestrspace(fulllen, expdest);
		} else if (flag & RMESCAPE_HEAP) {
			r = ckmalloc(fulllen);
		} else {
			r = stalloc(fulllen);
		}
		q = r;
		if (len > 0) {
			q = mempcpy(q, str, len);
		}
	}
	inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
	globbing = flag & RMESCAPE_GLOB;
	notescaped = globbing;
	while (*p) {
		if (*p == CTLQUOTEMARK) {
			inquotes = ~inquotes;
			p++;
			notescaped = globbing;
			continue;
		}
		if (*p == '\\') {
			/* naked back slash */
			notescaped = 0;
			goto copy;
		}
		if (*p == CTLESC) {
			p++;
			if (notescaped && inquotes && *p != '/') {
				*q++ = '\\';
			}
		}
		notescaped = globbing;
copy:
		*q++ = *p++;
	}
	*q = '\0';
	if (flag & RMESCAPE_GROW) {
		expdest = r;
		STADJUST(q - r + 1, expdest);
	}
	return r;
}



/*
 * See if a pattern matches in a case statement.
 */

int
casematch(union node *pattern, char *val)
{
	struct stackmark smark;
	int result;

	setstackmark(&smark);
	argbackq = pattern->narg.backquote;
	STARTSTACKSTR(expdest);
	ifslastp = NULL;
	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
	STACKSTRNUL(expdest);
	result = patmatch(stackblock(), val);
	popstackmark(&smark);
	return result;
}

/*
 * Our own itoa().
 */

static int
cvtnum(long num)
{
	int len;

	expdest = makestrspace(32, expdest);
	len = fmtstr(expdest, 32, "%ld", num);
	STADJUST(len, expdest);
	return len;
}

static void
varunset(const char *end, const char *var, const char *umsg, int varflags)
{
	const char *msg;
	const char *tail;

	tail = nullstr;
	msg = "parameter not set";
	if (umsg) {
		if (*end == CTLENDVAR) {
			if (varflags & VSNUL)
				tail = " or null";
		} else
			msg = umsg;
	}
	error("%.*s: %s%s", end - var - 1, var, msg, tail);
}
/*      $NetBSD: input.c,v 1.37 2002/11/24 22:35:40 christos Exp $      */



/*
 * This file implements the input routines used by the parser.
 */

#define EOF_NLEFT -99           /* value of parsenleft when EOF pushed back */
#define IBUFSIZ (BUFSIZ + 1)

static void pushfile(void);

/*
 * Read a line from the script.
 */

static inline char *
pfgets(char *line, int len)
{
	char *p = line;
	int nleft = len;
	int c;

	while (--nleft > 0) {
		c = pgetc2();
		if (c == PEOF) {
			if (p == line)
				return NULL;
			break;
		}
		*p++ = c;
		if (c == '\n')
			break;
	}
	*p = '\0';
	return line;
}


/*
 * Read a character from the script, returning PEOF on end of file.
 * Nul characters in the input are silently discarded.
 */

#define pgetc_as_macro()   (--parsenleft >= 0? *parsenextc++ : preadbuffer())

#ifdef CONFIG_ASH_OPTIMIZE_FOR_SIZE
#define pgetc_macro() pgetc()
static int
pgetc(void)
{
	return pgetc_as_macro();
}
#else
#define pgetc_macro()   pgetc_as_macro()
static int
pgetc(void)
{
	return pgetc_macro();
}
#endif


/*
 * Same as pgetc(), but ignores PEOA.
 */
#ifdef CONFIG_ASH_ALIAS
static int pgetc2(void)
{
	int c;

	do {
		c = pgetc_macro();
	} while (c == PEOA);
	return c;
}
#else
static inline int pgetc2(void)
{
	return pgetc_macro();
}
#endif


#ifdef CONFIG_FEATURE_COMMAND_EDITING
static const char *cmdedit_prompt;
static inline void putprompt(const char *s)
{
	cmdedit_prompt = s;
}
#else
static inline void putprompt(const char *s)
{
	out2str(s);
}
#endif

static inline int
preadfd(void)
{
	int nr;
	char *buf =  parsefile->buf;
	parsenextc = buf;

retry:
#ifdef CONFIG_FEATURE_COMMAND_EDITING
	if (!iflag || parsefile->fd)
		nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
	else {
		nr = cmdedit_read_input((char *) cmdedit_prompt, buf);
		if(nr == 0) {
			/* Ctrl+C presend */
			raise(SIGINT);
			goto retry;
		}
		if(nr < 0) {
			/* Ctrl+D presend */
			nr = 0;
		}
	}
#else
	nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
#endif

	if (nr < 0) {
		if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
			int flags = fcntl(0, F_GETFL, 0);
			if (flags >= 0 && flags & O_NONBLOCK) {
				flags &=~ O_NONBLOCK;
				if (fcntl(0, F_SETFL, flags) >= 0) {
					out2str("sh: turning off NDELAY mode\n");
					goto retry;
				}
			}
		}
	}
	return nr;
}

/*
 * Refill the input buffer and return the next input character:
 *
 * 1) If a string was pushed back on the input, pop it;
 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
 *    from a string so we can't refill the buffer, return EOF.
 * 3) If the is more stuff in this buffer, use it else call read to fill it.
 * 4) Process input up to the next newline, deleting nul characters.
 */

int
preadbuffer(void)
{
	char *p, *q;
	int more;
	char savec;

	while (parsefile->strpush) {
#ifdef CONFIG_ASH_ALIAS
		if (parsenleft == -1 && parsefile->strpush->ap &&
			parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
			return PEOA;
		}
#endif
		popstring();
		if (--parsenleft >= 0)
			return (*parsenextc++);
	}
	if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
		return PEOF;
	flushall();

again:
	if (parselleft <= 0) {
		if ((parselleft = preadfd()) <= 0) {
			parselleft = parsenleft = EOF_NLEFT;
			return PEOF;
		}
	}

	q = p = parsenextc;

	/* delete nul characters */
	for (more = 1; more;) {
		switch (*p) {
		case '\0':
			p++;    /* Skip nul */
			goto check;

		case '\n':
			parsenleft = q - parsenextc;
			more = 0; /* Stop processing here */
			break;

		}

		*q++ = *p++;
check:
		if (--parselleft <= 0 && more) {
			parsenleft = q - parsenextc - 1;
			if (parsenleft < 0)
				goto again;
			more = 0;
		}
	}

	savec = *q;
	*q = '\0';

	if (vflag) {
		out2str(parsenextc);
		flushout(stderr);
	}

	*q = savec;

	return *parsenextc++;
}

/*
 * Undo the last call to pgetc.  Only one character may be pushed back.
 * PEOF may be pushed back.
 */

void
pungetc(void)
{
	parsenleft++;
	parsenextc--;
}

/*
 * Push a string back onto the input at this current parsefile level.
 * We handle aliases this way.
 */
void
pushstring(char *s, void *ap)
{
	struct strpush *sp;
	size_t len;

	len = strlen(s);
	INTOFF;
/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
	if (parsefile->strpush) {
		sp = ckmalloc(sizeof (struct strpush));
		sp->prev = parsefile->strpush;
		parsefile->strpush = sp;
	} else
		sp = parsefile->strpush = &(parsefile->basestrpush);
	sp->prevstring = parsenextc;
	sp->prevnleft = parsenleft;
#ifdef CONFIG_ASH_ALIAS
	sp->ap = (struct alias *)ap;
	if (ap) {
		((struct alias *)ap)->flag |= ALIASINUSE;
		sp->string = s;
	}
#endif
	parsenextc = s;
	parsenleft = len;
	INTON;
}

void
popstring(void)
{
	struct strpush *sp = parsefile->strpush;

	INTOFF;
#ifdef CONFIG_ASH_ALIAS
	if (sp->ap) {
		if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
			checkkwd |= CHKALIAS;
		}
		if (sp->string != sp->ap->val) {
			ckfree(sp->string);
		}
		sp->ap->flag &= ~ALIASINUSE;
		if (sp->ap->flag & ALIASDEAD) {
			unalias(sp->ap->name);
		}
	}
#endif
	parsenextc = sp->prevstring;
	parsenleft = sp->prevnleft;
/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
	parsefile->strpush = sp->prev;
	if (sp != &(parsefile->basestrpush))
		ckfree(sp);
	INTON;
}

/*
 * Set the input to take input from a file.  If push is set, push the
 * old input onto the stack first.
 */

void
setinputfile(const char *fname, int push)
{
	int fd;
	int fd2;

	INTOFF;
	if ((fd = open(fname, O_RDONLY)) < 0)
		error("Can't open %s", fname);
	if (fd < 10) {
		fd2 = copyfd(fd, 10);
		close(fd);
		if (fd2 < 0)
			error("Out of file descriptors");
		fd = fd2;
	}
	setinputfd(fd, push);
	INTON;
}


/*
 * Like setinputfile, but takes an open file descriptor.  Call this with
 * interrupts off.
 */

static void
setinputfd(int fd, int push)
{
	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
	if (push) {
		pushfile();
		parsefile->buf = 0;
	}
	parsefile->fd = fd;
	if (parsefile->buf == NULL)
		parsefile->buf = ckmalloc(IBUFSIZ);
	parselleft = parsenleft = 0;
	plinno = 1;
}


/*
 * Like setinputfile, but takes input from a string.
 */

static void
setinputstring(char *string)
{
	INTOFF;
	pushfile();
	parsenextc = string;
	parsenleft = strlen(string);
	parsefile->buf = NULL;
	plinno = 1;
	INTON;
}



/*
 * To handle the "." command, a stack of input files is used.  Pushfile
 * adds a new entry to the stack and popfile restores the previous level.
 */

static void
pushfile(void)
{
	struct parsefile *pf;

	parsefile->nleft = parsenleft;
	parsefile->lleft = parselleft;
	parsefile->nextc = parsenextc;
	parsefile->linno = plinno;
	pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
	pf->prev = parsefile;
	pf->fd = -1;
	pf->strpush = NULL;
	pf->basestrpush.prev = NULL;
	parsefile = pf;
}


static void
popfile(void)
{
	struct parsefile *pf = parsefile;

	INTOFF;
	if (pf->fd >= 0)
		close(pf->fd);
	if (pf->buf)
		ckfree(pf->buf);
	while (pf->strpush)
		popstring();
	parsefile = pf->prev;
	ckfree(pf);
	parsenleft = parsefile->nleft;
	parselleft = parsefile->lleft;
	parsenextc = parsefile->nextc;
	plinno = parsefile->linno;
	INTON;
}


/*
 * Return to top level.
 */

static void
popallfiles(void)
{
	while (parsefile != &basepf)
		popfile();
}



/*
 * Close the file(s) that the shell is reading commands from.  Called
 * after a fork is done.
 */

static void
closescript(void)
{
	popallfiles();
	if (parsefile->fd > 0) {
		close(parsefile->fd);
		parsefile->fd = 0;
	}
}
/*      $NetBSD: jobs.c,v 1.56 2002/11/25 12:13:03 agc Exp $    */


/* mode flags for set_curjob */
#define CUR_DELETE 2
#define CUR_RUNNING 1
#define CUR_STOPPED 0

/* mode flags for dowait */
#define DOWAIT_NORMAL 0
#define DOWAIT_BLOCK 1

/* array of jobs */
static struct job *jobtab;
/* size of array */
static unsigned njobs;
#if JOBS
/* pgrp of shell on invocation */
static int initialpgrp;
static int ttyfd = -1;
#endif
/* current job */
static struct job *curjob;
/* number of presumed living untracked jobs */
static int jobless;

static void set_curjob(struct job *, unsigned);
#if JOBS
static int restartjob(struct job *, int);
static void xtcsetpgrp(int, pid_t);
static char *commandtext(union node *);
static void cmdlist(union node *, int);
static void cmdtxt(union node *);
static void cmdputs(const char *);
static void showpipe(struct job *, FILE *);
#endif
static int sprint_status(char *, int, int);
static void freejob(struct job *);
static struct job *getjob(const char *, int);
static struct job *growjobtab(void);
static void forkchild(struct job *, union node *, int);
static void forkparent(struct job *, union node *, int, pid_t);
static int dowait(int, struct job *);
static int getstatus(struct job *);

static void
set_curjob(struct job *jp, unsigned mode)
{
	struct job *jp1;
	struct job **jpp, **curp;

	/* first remove from list */
	jpp = curp = &curjob;
	do {
		jp1 = *jpp;
		if (jp1 == jp)
			break;
		jpp = &jp1->prev_job;
	} while (1);
	*jpp = jp1->prev_job;

	/* Then re-insert in correct position */
	jpp = curp;
	switch (mode) {
	default:
#ifdef DEBUG
		abort();
#endif
	case CUR_DELETE:
		/* job being deleted */
		break;
	case CUR_RUNNING:
		/* newly created job or backgrounded job,
		   put after all stopped jobs. */
		do {
			jp1 = *jpp;
#ifdef JOBS
			if (!jp1 || jp1->state != JOBSTOPPED)
#endif
				break;
			jpp = &jp1->prev_job;
		} while (1);
		/* FALLTHROUGH */
#ifdef JOBS
	case CUR_STOPPED:
#endif
		/* newly stopped job - becomes curjob */
		jp->prev_job = *jpp;
		*jpp = jp;
		break;
	}
}

#if JOBS
/*
 * Turn job control on and off.
 *
 * Note:  This code assumes that the third arg to ioctl is a character
 * pointer, which is true on Berkeley systems but not System V.  Since
 * System V doesn't have job control yet, this isn't a problem now.
 *
 * Called with interrupts off.
 */

void
setjobctl(int on)
{
	int fd;
	int pgrp;

	if (on == jobctl || rootshell == 0)
		return;
	if (on) {
		int ofd;
		ofd = fd = open(_PATH_TTY, O_RDWR);
		if (fd < 0) {
			fd += 3;
			while (!isatty(fd) && --fd >= 0)
				;
		}
		fd = fcntl(fd, F_DUPFD, 10);
		close(ofd);
		if (fd < 0)
			goto out;
		fcntl(fd, F_SETFD, FD_CLOEXEC);
		do { /* while we are in the background */
			if ((pgrp = tcgetpgrp(fd)) < 0) {
out:
				sh_warnx("can't access tty; job control turned off");
				mflag = on = 0;
				goto close;
			}
			if (pgrp == getpgrp())
				break;
			killpg(0, SIGTTIN);
		} while (1);
		initialpgrp = pgrp;

		setsignal(SIGTSTP);
		setsignal(SIGTTOU);
		setsignal(SIGTTIN);
		pgrp = rootpid;
		setpgid(0, pgrp);
		xtcsetpgrp(fd, pgrp);
	} else {
		/* turning job control off */
		fd = ttyfd;
		pgrp = initialpgrp;
		xtcsetpgrp(fd, pgrp);
		setpgid(0, pgrp);
		setsignal(SIGTSTP);
		setsignal(SIGTTOU);
		setsignal(SIGTTIN);
close:
		close(fd);
		fd = -1;
	}
	ttyfd = fd;
	jobctl = on;
}

static int
killcmd(argc, argv)
	int argc;
	char **argv;
{
	int signo = -1;
	int list = 0;
	int i;
	pid_t pid;
	struct job *jp;

	if (argc <= 1) {
usage:
		error(
"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
"kill -l [exitstatus]"
		);
	}

	if (**++argv == '-') {
		signo = decode_signal(*argv + 1, 1);
		if (signo < 0) {
			int c;

			while ((c = nextopt("ls:")) != '\0')
				switch (c) {
				default:
#ifdef DEBUG
					abort();
#endif
				case 'l':
					list = 1;
					break;
				case 's':
					signo = decode_signal(optionarg, 1);
					if (signo < 0) {
						error(
							"invalid signal number or name: %s",
							optionarg
						);
					}
					break;
				}
			argv = argptr;
		} else
			argv++;
	}

	if (!list && signo < 0)
		signo = SIGTERM;

	if ((signo < 0 || !*argv) ^ list) {
		goto usage;
	}

	if (list) {
		const char *name;

		if (!*argv) {
			for (i = 1; i < NSIG; i++) {
				name = u_signal_names(0, &i, 1);
				if (name)
					out1fmt(snlfmt, name);
			}
			return 0;
		}
		name = u_signal_names(*argptr, &signo, -1);
		if (name)
			out1fmt(snlfmt, name);
		else
			error("invalid signal number or exit status: %s", *argptr);
		return 0;
	}

	i = 0;
	do {
		if (**argv == '%') {
			jp = getjob(*argv, 0);
			pid = -jp->ps[0].pid;
		} else
			pid = number(*argv);
		if (kill(pid, signo) != 0) {
			sh_warnx("%m\n");
			i = 1;
		}
	} while (*++argv);

	return i;
}
#endif /* JOBS */

#if defined(JOBS) || defined(DEBUG)
static int
jobno(const struct job *jp)
{
	return jp - jobtab + 1;
}
#endif

#ifdef JOBS
static int
fgcmd(int argc, char **argv)
{
	struct job *jp;
	FILE *out;
	int mode;
	int retval;

	mode = (**argv == 'f') ? FORK_FG : FORK_BG;
	nextopt(nullstr);
	argv = argptr;
	out = stdout;
	do {
		jp = getjob(*argv, 1);
		if (mode == FORK_BG) {
			set_curjob(jp, CUR_RUNNING);
			fprintf(out, "[%d] ", jobno(jp));
		}
		outstr(jp->ps->cmd, out);
		showpipe(jp, out);
		retval = restartjob(jp, mode);
	} while (*argv && *++argv);
	return retval;
}

static int bgcmd(int, char **) __attribute__((__alias__("fgcmd")));


static int
restartjob(struct job *jp, int mode)
{
	struct procstat *ps;
	int i;
	int status;
	pid_t pgid;

	INTOFF;
	if (jp->state == JOBDONE)
		goto out;
	jp->state = JOBRUNNING;
	pgid = jp->ps->pid;
	if (mode == FORK_FG)
		xtcsetpgrp(ttyfd, pgid);
	killpg(pgid, SIGCONT);
	ps = jp->ps;
	i = jp->nprocs;
	do {
		if (WIFSTOPPED(ps->status)) {
			ps->status = -1;
		}
	} while (ps++, --i);
out:
	status = (mode == FORK_FG) ? waitforjob(jp) : 0;
	INTON;
	return status;
}
#endif

static int
sprint_status(char *s, int status, int sigonly)
{
	int col;
	int st;

	col = 0;
	st = WEXITSTATUS(status);
	if (!WIFEXITED(status)) {
		st = WSTOPSIG(status);
#if JOBS
		if (!WIFSTOPPED(status))
			st = WTERMSIG(status);
#endif
		if (sigonly) {
			if (st == SIGINT || st == SIGPIPE)
				goto out;
			if (WIFSTOPPED(status))
				goto out;
		}
		st &= 0x7f;
		col = fmtstr(s, 32, u_signal_names(NULL, &st, 0));
		if (WCOREDUMP(status)) {
			col += fmtstr(s + col, 16, " (core dumped)");
		}
	} else if (!sigonly) {
		if (st)
			col = fmtstr(s, 16, "Done(%d)", st);
		else
			col = fmtstr(s, 16, "Done");
	}

out:
	return col;
}

#if JOBS
static void
showjob(FILE *out, struct job *jp, int mode)
{
	struct procstat *ps;
	struct procstat *psend;
	int col;
	int indent;
	char s[80];

	ps = jp->ps;

	if (mode & SHOW_PGID) {
		/* just output process (group) id of pipeline */
		fprintf(out, "%d\n", ps->pid);
		return;
	}

	col = fmtstr(s, 16, "[%d]   ", jobno(jp));
	indent = col;

	if (jp == curjob)
		s[col - 2] = '+';
	else if (curjob && jp == curjob->prev_job)
		s[col - 2] = '-';

	if (mode & SHOW_PID)
		col += fmtstr(s + col, 16, "%d ", ps->pid);

	psend = ps + jp->nprocs;

	if (jp->state == JOBRUNNING) {
		scopy("Running", s + col);
		col += strlen("Running");
	} else {
		int status = psend[-1].status;
#if JOBS
		if (jp->state == JOBSTOPPED)
			status = jp->stopstatus;
#endif
		col += sprint_status(s + col, status, 0);
	}

	goto start;

	do {
		/* for each process */
		col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;

start:
		fprintf(
			out, "%s%*c%s",
			s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
		);
		if (!(mode & SHOW_PID)) {
			showpipe(jp, out);
			break;
		}
		if (++ps == psend) {
			outcslow('\n', out);
			break;
		}
	} while (1);

	jp->changed = 0;

	if (jp->state == JOBDONE) {
		TRACE(("showjob: freeing job %d\n", jobno(jp)));
		freejob(jp);
	}
}


static int
jobscmd(int argc, char **argv)
{
	int mode, m;
	FILE *out;

	mode = 0;
	while ((m = nextopt("lp")))
		if (m == 'l')
			mode = SHOW_PID;
		else
			mode = SHOW_PGID;

	out = stdout;
	argv = argptr;
	if (*argv)
		do
			showjob(out, getjob(*argv,0), mode);
		while (*++argv);
	else
		showjobs(out, mode);

	return 0;
}


/*
 * Print a list of jobs.  If "change" is nonzero, only print jobs whose
 * statuses have changed since the last call to showjobs.
 */

static void
showjobs(FILE *out, int mode)
{
	struct job *jp;

	TRACE(("showjobs(%x) called\n", mode));

	/* If not even one one job changed, there is nothing to do */
	while (dowait(DOWAIT_NORMAL, NULL) > 0)
		continue;

	for (jp = curjob; jp; jp = jp->prev_job) {
		if (!(mode & SHOW_CHANGED) || jp->changed)
			showjob(out, jp, mode);
	}
}
#endif /* JOBS */

/*
 * Mark a job structure as unused.
 */

static void
freejob(struct job *jp)
{
	struct procstat *ps;
	int i;

	INTOFF;
	for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
		if (ps->cmd != nullstr)
			ckfree(ps->cmd);
	}
	if (jp->ps != &jp->ps0)
		ckfree(jp->ps);
	jp->used = 0;
	set_curjob(jp, CUR_DELETE);
	INTON;
}


static int
waitcmd(int argc, char **argv)
{
	struct job *job;
	int retval;
	struct job *jp;

	EXSIGON();

	nextopt(nullstr);
	retval = 0;

	argv = argptr;
	if (!*argv) {
		/* wait for all jobs */
		for (;;) {
			jp = curjob;
			while (1) {
				if (!jp) {
					/* no running procs */
					goto out;
				}
				if (jp->state == JOBRUNNING)
					break;
				jp->waited = 1;
				jp = jp->prev_job;
			}
			dowait(DOWAIT_BLOCK, 0);
		}
	}

	retval = 127;
	do {
		if (**argv != '%') {
			pid_t pid = number(*argv);
			job = curjob;
			goto start;
			do {
				if (job->ps[job->nprocs - 1].pid == pid)
					break;
				job = job->prev_job;
start:
				if (!job)
					goto repeat;
			} while (1);
		} else
			job = getjob(*argv, 0);
		/* loop until process terminated or stopped */
		while (job->state == JOBRUNNING)
			dowait(DOWAIT_BLOCK, 0);
		job->waited = 1;
		retval = getstatus(job);
repeat:
		;
	} while (*++argv);

out:
	return retval;
}



/*
 * Convert a job name to a job structure.
 */

static struct job *
getjob(const char *name, int getctl)
{
	struct job *jp;
	struct job *found;
	const char *err_msg = "No such job: %s";
	unsigned num;
	int c;
	const char *p;
	char *(*match)(const char *, const char *);

	jp = curjob;
	p = name;
	if (!p)
		goto currentjob;

	if (*p != '%')
		goto err;

	c = *++p;
	if (!c)
		goto currentjob;

	if (!p[1]) {
		if (c == '+' || c == '%') {
currentjob:
			err_msg = "No current job";
			goto check;
		} else if (c == '-') {
			if (jp)
				jp = jp->prev_job;
			err_msg = "No previous job";
check:
			if (!jp)
				goto err;
			goto gotit;
		}
	}

	if (is_number(p)) {
		num = atoi(p);
		if (num < njobs) {
			jp = jobtab + num - 1;
			if (jp->used)
				goto gotit;
			goto err;
		}
	}

	match = prefix;
	if (*p == '?') {
		match = strstr;
		p++;
	}

	found = 0;
	while (1) {
		if (!jp)
			goto err;
		if (match(jp->ps[0].cmd, p)) {
			if (found)
				goto err;
			found = jp;
			err_msg = "%s: ambiguous";
		}
		jp = jp->prev_job;
	}

gotit:
#if JOBS
	err_msg = "job %s not created under job control";
	if (getctl && jp->jobctl == 0)
		goto err;
#endif
	return jp;
err:
	error(err_msg, name);
}



/*
 * Return a new job structure.
 * Called with interrupts off.
 */

static struct job *
makejob(union node *node, int nprocs)
{
	int i;
	struct job *jp;

	for (i = njobs, jp = jobtab ; ; jp++) {
		if (--i < 0) {
			jp = growjobtab();
			break;
		}
		if (jp->used == 0)
			break;
		if (jp->state != JOBDONE || !jp->waited)
			continue;
#if JOBS
		if (jobctl)
			continue;
#endif
		freejob(jp);
		break;
	}
	memset(jp, 0, sizeof(*jp));
#if JOBS
	if (jobctl)
		jp->jobctl = 1;
#endif
	jp->prev_job = curjob;
	curjob = jp;
	jp->used = 1;
	jp->ps = &jp->ps0;
	if (nprocs > 1) {
		jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
	}
	TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
	    jobno(jp)));
	return jp;
}

static struct job *
growjobtab(void)
{
	size_t len;
	ptrdiff_t offset;
	struct job *jp, *jq;

	len = njobs * sizeof(*jp);
	jq = jobtab;
	jp = ckrealloc(jq, len + 4 * sizeof(*jp));

	offset = (char *)jp - (char *)jq;
	if (offset) {
		/* Relocate pointers */
		size_t l = len;

		jq = (struct job *)((char *)jq + l);
		while (l) {
			l -= sizeof(*jp);
			jq--;
#define joff(p) ((struct job *)((char *)(p) + l))
#define jmove(p) (p) = (void *)((char *)(p) + offset)
			if (likely(joff(jp)->ps == &jq->ps0))
				jmove(joff(jp)->ps);
			if (joff(jp)->prev_job)
				jmove(joff(jp)->prev_job);
		}
		if (curjob)
			jmove(curjob);
#undef joff
#undef jmove
	}

	njobs += 4;
	jobtab = jp;
	jp = (struct job *)((char *)jp + len);
	jq = jp + 3;
	do {
		jq->used = 0;
	} while (--jq >= jp);
	return jp;
}


/*
 * Fork off a subshell.  If we are doing job control, give the subshell its
 * own process group.  Jp is a job structure that the job is to be added to.
 * N is the command that will be evaluated by the child.  Both jp and n may
 * be NULL.  The mode parameter can be one of the following:
 *      FORK_FG - Fork off a foreground process.
 *      FORK_BG - Fork off a background process.
 *      FORK_NOJOB - Like FORK_FG, but don't give the process its own
 *                   process group even if job control is on.
 *
 * When job control is turned off, background processes have their standard
 * input redirected to /dev/null (except for the second and later processes
 * in a pipeline).
 *
 * Called with interrupts off.
 */

static inline void
forkchild(struct job *jp, union node *n, int mode)
{
	int wasroot;

	TRACE(("Child shell %d\n", getpid()));
	wasroot = rootshell;
	rootshell = 0;

	closescript();
	clear_traps();
#if JOBS
	/* do job control only in root shell */
	jobctl = 0;
	if (mode != FORK_NOJOB && jp->jobctl && wasroot) {
		pid_t pgrp;

		if (jp->nprocs == 0)
			pgrp = getpid();
		else
			pgrp = jp->ps[0].pid;
		/* This can fail because we are doing it in the parent also */
		(void)setpgid(0, pgrp);
		if (mode == FORK_FG)
			xtcsetpgrp(ttyfd, pgrp);
		setsignal(SIGTSTP);
		setsignal(SIGTTOU);
	} else
#endif
	if (mode == FORK_BG) {
		ignoresig(SIGINT);
		ignoresig(SIGQUIT);
		if (jp->nprocs == 0) {
			close(0);
			if (open(_PATH_DEVNULL, O_RDONLY) != 0)
				error("Can't open %s", _PATH_DEVNULL);
		}
	}
	if (wasroot && iflag) {
		setsignal(SIGINT);
		setsignal(SIGQUIT);
		setsignal(SIGTERM);
	}
	for (jp = curjob; jp; jp = jp->prev_job)
		freejob(jp);
	jobless = 0;
}

static inline void
forkparent(struct job *jp, union node *n, int mode, pid_t pid)
{
	TRACE(("In parent shell:  child = %d\n", pid));
	if (!jp) {
		while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
		jobless++;
		return;
	}
#if JOBS
	if (mode != FORK_NOJOB && jp->jobctl) {
		int pgrp;

		if (jp->nprocs == 0)
			pgrp = pid;
		else
			pgrp = jp->ps[0].pid;
		/* This can fail because we are doing it in the child also */
		(void)setpgid(pid, pgrp);
	}
#endif
	if (mode == FORK_BG) {
		backgndpid = pid;               /* set $! */
		set_curjob(jp, CUR_RUNNING);
	}
	if (jp) {
		struct procstat *ps = &jp->ps[jp->nprocs++];
		ps->pid = pid;
		ps->status = -1;
		ps->cmd = nullstr;
#if JOBS
		if (jobctl && n)
			ps->cmd = commandtext(n);
#endif
	}
}

static int
forkshell(struct job *jp, union node *n, int mode)
{
	int pid;

	TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
	pid = fork();
	if (pid < 0) {
		TRACE(("Fork failed, errno=%d", errno));
		if (jp)
			freejob(jp);
		error("Cannot fork");
	}
	if (pid == 0)
		forkchild(jp, n, mode);
	else
		forkparent(jp, n, mode, pid);
	return pid;
}

/*
 * Wait for job to finish.
 *
 * Under job control we have the problem that while a child process is
 * running interrupts generated by the user are sent to the child but not
 * to the shell.  This means that an infinite loop started by an inter-
 * active user may be hard to kill.  With job control turned off, an
 * interactive user may place an interactive program inside a loop.  If
 * the interactive program catches interrupts, the user doesn't want
 * these interrupts to also abort the loop.  The approach we take here
 * is to have the shell ignore interrupt signals while waiting for a
 * forground process to terminate, and then send itself an interrupt
 * signal if the child process was terminated by an interrupt signal.
 * Unfortunately, some programs want to do a bit of cleanup and then
 * exit on interrupt; unless these processes terminate themselves by
 * sending a signal to themselves (instead of calling exit) they will
 * confuse this approach.
 *
 * Called with interrupts off.
 */

int
waitforjob(struct job *jp)
{
	int st;

	TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
	while (jp->state == JOBRUNNING) {
		dowait(DOWAIT_BLOCK, jp);
	}
	st = getstatus(jp);
#if JOBS
	if (jp->jobctl) {
		xtcsetpgrp(ttyfd, rootpid);
		/*
		 * This is truly gross.
		 * If we're doing job control, then we did a TIOCSPGRP which
		 * caused us (the shell) to no longer be in the controlling
		 * session -- so we wouldn't have seen any ^C/SIGINT.  So, we
		 * intuit from the subprocess exit status whether a SIGINT
		 * occurred, and if so interrupt ourselves.  Yuck.  - mycroft
		 */
		if (jp->sigint)
			raise(SIGINT);
	}
	if (jp->state == JOBDONE)
#endif
		freejob(jp);
	return st;
}


/*
 * Do a wait system call.  If job control is compiled in, we accept
 * stopped processes.  If block is zero, we return a value of zero
 * rather than blocking.
 *
 * System V doesn't have a non-blocking wait system call.  It does
 * have a SIGCLD signal that is sent to a process when one of it's
 * children dies.  The obvious way to use SIGCLD would be to install
 * a handler for SIGCLD which simply bumped a counter when a SIGCLD
 * was received, and have waitproc bump another counter when it got
 * the status of a process.  Waitproc would then know that a wait
 * system call would not block if the two counters were different.
 * This approach doesn't work because if a process has children that
 * have not been waited for, System V will send it a SIGCLD when it
 * installs a signal handler for SIGCLD.  What this means is that when
 * a child exits, the shell will be sent SIGCLD signals continuously
 * until is runs out of stack space, unless it does a wait call before
 * restoring the signal handler.  The code below takes advantage of
 * this (mis)feature by installing a signal handler for SIGCLD and
 * then checking to see whether it was called.  If there are any
 * children to be waited for, it will be.
 *
 * If neither SYSV nor BSD is defined, we don't implement nonblocking
 * waits at all.  In this case, the user will not be informed when
 * a background process until the next time she runs a real program
 * (as opposed to running a builtin command or just typing return),
 * and the jobs command may give out of date information.
 */

static inline int
waitproc(int block, int *status)
{
	int flags = 0;

#if JOBS
	if (jobctl)
		flags |= WUNTRACED;
#endif
	if (block == 0)
		flags |= WNOHANG;
	return wait3(status, flags, (struct rusage *)NULL);
}

/*
 * Wait for a process to terminate.
 */

static int
dowait(int block, struct job *job)
{
	int pid;
	int status;
	struct job *jp;
	struct job *thisjob;
	int state;

	TRACE(("dowait(%d) called\n", block));
	pid = waitproc(block, &status);
	TRACE(("wait returns pid %d, status=%d\n", pid, status));
	if (pid <= 0)
		return pid;
	INTOFF;
	thisjob = NULL;
	for (jp = curjob; jp; jp = jp->prev_job) {
		struct procstat *sp;
		struct procstat *spend;
		if (jp->state == JOBDONE)
			continue;
		state = JOBDONE;
		spend = jp->ps + jp->nprocs;
		sp = jp->ps;
		do {
			if (sp->pid == pid) {
				TRACE(("Job %d: changing status of proc %d from 0x%x to 0x%x\n", jobno(jp), pid, sp->status, status));
				sp->status = status;
				thisjob = jp;
			}
			if (sp->status == -1)
				state = JOBRUNNING;
#ifdef JOBS
			if (state == JOBRUNNING)
				continue;
			if (WIFSTOPPED(sp->status)) {
				jp->stopstatus = sp->status;
				state = JOBSTOPPED;
			}
#endif
		} while (++sp < spend);
		if (thisjob)
			goto gotjob;
	}
#ifdef JOBS
	if (!WIFSTOPPED(status))
#endif

		jobless--;
	goto out;

gotjob:
	if (state != JOBRUNNING) {
		thisjob->changed = 1;

		if (thisjob->state != state) {
			TRACE(("Job %d: changing state from %d to %d\n", jobno(thisjob), thisjob->state, state));
			thisjob->state = state;
#ifdef JOBS
			if (state == JOBSTOPPED) {
				set_curjob(thisjob, CUR_STOPPED);
			}
#endif
		}
	}

out:
	INTON;

	if (thisjob && thisjob == job) {
		char s[48 + 1];
		int len;

		len = sprint_status(s, status, 1);
		if (len) {
			s[len] = '\n';
			s[len + 1] = 0;
			out2str(s);
		}
	}
	return pid;
}



/*
 * return 1 if there are stopped jobs, otherwise 0
 */
int
stoppedjobs(void)
{
	struct job *jp;
	int retval;

	retval = 0;
	if (job_warning)
		goto out;
	jp = curjob;
	if (jp && jp->state == JOBSTOPPED) {
		out2str("You have stopped jobs.\n");
		job_warning = 2;
		retval++;
	}

out:
	return retval;
}

/*
 * Return a string identifying a command (to be printed by the
 * jobs command).
 */

#if JOBS
static char *cmdnextc;

static char *
commandtext(union node *n)
{
	char *name;

	STARTSTACKSTR(cmdnextc);
	cmdtxt(n);
	name = stackblock();
	TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
		name, cmdnextc, cmdnextc));
	return savestr(name);
}

static void
cmdtxt(union node *n)
{
	union node *np;
	struct nodelist *lp;
	const char *p;
	char s[2];

	switch (n->type) {
	default:
#if DEBUG
		abort();
#endif
	case NPIPE:
		lp = n->npipe.cmdlist;
		for (;;) {
			cmdtxt(lp->n);
			lp = lp->next;
			if (!lp)
				break;
			cmdputs(" | ");
		}
		break;
	case NSEMI:
		p = "; ";
		goto binop;
	case NAND:
		p = " && ";
		goto binop;
	case NOR:
		p = " || ";
binop:
		cmdtxt(n->nbinary.ch1);
		cmdputs(p);
		n = n->nbinary.ch2;
		goto donode;
	case NREDIR:
	case NBACKGND:
		n = n->nredir.n;
		goto donode;
	case NNOT:
		cmdputs("!");
		n = n->nnot.com;
donode:
		cmdtxt(n);
		break;
	case NIF:
		cmdputs("if ");
		cmdtxt(n->nif.test);
		cmdputs("; then ");
		n = n->nif.ifpart;
		if (n->nif.elsepart) {
			cmdtxt(n);
			cmdputs("; else ");
			n = n->nif.elsepart;
		}
		p = "; fi";
		goto dotail;
	case NSUBSHELL:
		cmdputs("(");
		n = n->nredir.n;
		p = ")";
		goto dotail;
	case NWHILE:
		p = "while ";
		goto until;
	case NUNTIL:
		p = "until ";
until:
		cmdputs(p);
		cmdtxt(n->nbinary.ch1);
		n = n->nbinary.ch2;
		p = "; done";
dodo:
		cmdputs("; do ");
dotail:
		cmdtxt(n);
		goto dotail2;
	case NFOR:
		cmdputs("for ");
		cmdputs(n->nfor.var);
		cmdputs(" in ");
		cmdlist(n->nfor.args, 1);
		n = n->nfor.body;
		p = "; done";
		goto dodo;
	case NDEFUN:
		cmdputs(n->narg.text);
		p = "() { ... }";
		goto dotail2;
	case NCMD:
		cmdlist(n->ncmd.args, 1);
		cmdlist(n->ncmd.redirect, 0);
		break;
	case NARG:
		p = n->narg.text;
dotail2:
		cmdputs(p);
		break;
	case NHERE:
	case NXHERE:
		p = "<<...";
		goto dotail2;
	case NCASE:
		cmdputs("case ");
		cmdputs(n->ncase.expr->narg.text);
		cmdputs(" in ");
		for (np = n->ncase.cases; np; np = np->nclist.next) {
			cmdtxt(np->nclist.pattern);
			cmdputs(") ");
			cmdtxt(np->nclist.body);
			cmdputs(";; ");
		}
		p = "esac";
		goto dotail2;
	case NTO:
		p = ">";
		goto redir;
	case NCLOBBER:
		p = ">|";
		goto redir;
	case NAPPEND:
		p = ">>";
		goto redir;
	case NTOFD:
		p = ">&";
		goto redir;
	case NFROM:
		p = "<";
		goto redir;
	case NFROMFD:
		p = "<&";
		goto redir;
	case NFROMTO:
		p = "<>";
redir:
		s[0] = n->nfile.fd + '0';
		s[1] = '\0';
		cmdputs(s);
		cmdputs(p);
		if (n->type == NTOFD || n->type == NFROMFD) {
			s[0] = n->ndup.dupfd + '0';
			p = s;
			goto dotail2;
		} else {
			n = n->nfile.fname;
			goto donode;
		}
	}
}

static void
cmdlist(union node *np, int sep)
{
	for (; np; np = np->narg.next) {
		if (!sep)
			cmdputs(spcstr);
		cmdtxt(np);
		if (sep && np->narg.next)
			cmdputs(spcstr);
	}
}


static void
cmdputs(const char *s)
{
	const char *p, *str;
	char c, cc[2] = " ";
	char *nextc;
	int subtype = 0;
	int quoted = 0;
	static const char *const vstype[16] = {
		nullstr, "}", "-", "+", "?", "=",
		"#", "##", "%", "%%"
	};

	nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
	p = s;
	while ((c = *p++) != 0) {
		str = 0;
		switch (c) {
		case CTLESC:
			c = *p++;
			break;
		case CTLVAR:
			subtype = *p++;
			if ((subtype & VSTYPE) == VSLENGTH)
				str = "${#";
			else
				str = "${";
			if (!(subtype & VSQUOTE) != !(quoted & 1)) {
				quoted ^= 1;
				c = '"';
			} else
				goto dostr;
			break;
		case CTLENDVAR:
			quoted >>= 1;
			subtype = 0;
			if (quoted & 1) {
				str = "\"}";
				goto dostr;
			}
			c = '}';
			break;
		case CTLBACKQ:
			str = "$(...)";
			goto dostr;
		case CTLBACKQ+CTLQUOTE:
			str = "\"$(...)\"";
			goto dostr;
#ifdef CONFIG_ASH_MATH_SUPPORT
		case CTLARI:
			str = "$((";
			goto dostr;
		case CTLENDARI:
			str = "))";
			goto dostr;
#endif
		case CTLQUOTEMARK:
			quoted ^= 1;
			c = '"';
			break;
		case '=':
			if (subtype == 0)
				break;
			str = vstype[subtype & VSTYPE];
			if (subtype & VSNUL)
				c = ':';
			else
				c = *str++;
			if (c != '}')
				quoted <<= 1;
			break;
		case '\'':
		case '\\':
		case '"':
		case '$':
			/* These can only happen inside quotes */
			cc[0] = c;
			str = cc;
			c = '\\';
			break;
		default:
			break;
		}
		USTPUTC(c, nextc);
		if (!str)
			continue;
dostr:
		while ((c = *str++)) {
			USTPUTC(c, nextc);
		}
	}
	if (quoted & 1) {
		USTPUTC('"', nextc);
	}
	*nextc = 0;
	cmdnextc = nextc;
}


static void
showpipe(struct job *jp, FILE *out)
{
	struct procstat *sp;
	struct procstat *spend;

	spend = jp->ps + jp->nprocs;
	for (sp = jp->ps + 1; sp < spend; sp++)
		fprintf(out, " | %s", sp->cmd);
	outcslow('\n', out);
	flushall();
}

static void
xtcsetpgrp(int fd, pid_t pgrp)
{
	if (tcsetpgrp(fd, pgrp))
		error("Cannot set tty process group (%m)");
}
#endif /* JOBS */

static int
getstatus(struct job *job) {
	int status;
	int retval;

	status = job->ps[job->nprocs - 1].status;
	retval = WEXITSTATUS(status);
	if (!WIFEXITED(status)) {
#if JOBS
		retval = WSTOPSIG(status);
		if (!WIFSTOPPED(status))
#endif
		{
			/* XXX: limits number of signals */
			retval = WTERMSIG(status);
#if JOBS
			if (retval == SIGINT)
				job->sigint = 1;
#endif
		}
		retval += 128;
	}
	TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n",
		jobno(job), job->nprocs, status, retval));
	return retval;
}

#ifdef CONFIG_ASH_MAIL
/*      $NetBSD: mail.c,v 1.15 2002/11/24 22:35:40 christos Exp $       */

/*
 * Routines to check for mail.  (Perhaps make part of main.c?)
 */

#define MAXMBOXES 10

/* times of mailboxes */
static time_t mailtime[MAXMBOXES];
/* Set if MAIL or MAILPATH is changed. */
static int mail_var_path_changed;



/*
 * Print appropriate message(s) if mail has arrived.
 * If mail_var_path_changed is set,
 * then the value of MAIL has mail_var_path_changed,
 * so we just update the values.
 */

static void
chkmail(void)
{
	const char *mpath;
	char *p;
	char *q;
	time_t *mtp;
	struct stackmark smark;
	struct stat statb;

	setstackmark(&smark);
	mpath = mpathset() ? mpathval() : mailval();
	for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
		p = padvance(&mpath, nullstr);
		if (p == NULL)
			break;
		if (*p == '\0')
			continue;
		for (q = p ; *q ; q++);
#ifdef DEBUG
		if (q[-1] != '/')
			abort();
#endif
		q[-1] = '\0';                   /* delete trailing '/' */
		if (stat(p, &statb) < 0) {
			*mtp = 0;
			continue;
		}
		if (!mail_var_path_changed && statb.st_mtime != *mtp) {
			fprintf(
				stderr, snlfmt,
				pathopt ? pathopt : "you have mail"
			);
		}
		*mtp = statb.st_mtime;
	}
	mail_var_path_changed = 0;
	popstackmark(&smark);
}


static void
changemail(const char *val)
{
	mail_var_path_changed++;
}

#endif /* CONFIG_ASH_MAIL */

/*      $NetBSD: main.c,v 1.46 2002/12/11 19:12:18 christos Exp $       */


#if PROFILE
static short profile_buf[16384];
extern int etext();
#endif

static int isloginsh;

static void read_profile(const char *);

/*
 * Main routine.  We initialize things, parse the arguments, execute
 * profiles if we're a login shell, and then call cmdloop to execute
 * commands.  The setjmp call sets up the location to jump to when an
 * exception occurs.  When an exception occurs the variable "state"
 * is used to figure out how far we had gotten.
 */

int
ash_main(int argc, char **argv)
{
	char *shinit;
	volatile int state;
	struct jmploc jmploc;
	struct stackmark smark;

#ifdef __GLIBC__
	dash_errno = __errno_location();
#endif

#if PROFILE
	monitor(4, etext, profile_buf, sizeof profile_buf, 50);
#endif
	state = 0;
	if (setjmp(jmploc.loc)) {
		int status;
		int e;

		reset();

		e = exception;
		switch (exception) {
		case EXEXEC:
			status = exerrno;
			break;

		case EXERROR:
			status = 2;
			break;

		default:
			status = exitstatus;
			break;
		}
		exitstatus = status;

		if (e == EXEXIT || state == 0 || iflag == 0 || ! rootshell)
			exitshell();

		if (e == EXINT ) {
			outcslow('\n', stderr);
		}
		popstackmark(&smark);
		FORCEINTON;                             /* enable interrupts */
		if (state == 1)
			goto state1;
		else if (state == 2)
			goto state2;
		else if (state == 3)
			goto state3;
		else
			goto state4;
	}
	handler = &jmploc;
#ifdef DEBUG
	opentrace();
	trputs("Shell args:  ");  trargs(argv);
#endif
	rootpid = getpid();
	rootshell = 1;
	init();
	setstackmark(&smark);
	procargs(argc, argv);
#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
	if ( iflag ) {
		const char *hp = lookupvar("HISTFILE");

		if(hp == NULL ) {
			hp = lookupvar("HOME");
			if(hp != NULL) {
				char *defhp = concat_path_file(hp, ".ash_history");
				setvar("HISTFILE", defhp, 0);
				free(defhp);
			}
		}
	}
#endif
	if (argv[0] && argv[0][0] == '-')
		isloginsh = 1;
	if (isloginsh) {
		state = 1;
		read_profile("/etc/profile");
state1:
		state = 2;
		read_profile(".profile");
	}
state2:
	state = 3;
	if (
#ifndef linux
		getuid() == geteuid() && getgid() == getegid() &&
#endif
		iflag
	) {
		if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
			read_profile(shinit);
		}
	}
state3:
	state = 4;
	if (minusc)
		evalstring(minusc, 0);

	if (sflag || minusc == NULL) {
state4: /* XXX ??? - why isn't this before the "if" statement */
#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
	    if ( iflag ) {
		const char *hp = lookupvar("HISTFILE");

		if(hp != NULL )
			load_history ( hp );
	    }
#endif
		cmdloop(1);
	}
#if PROFILE
	monitor(0);
#endif
#if GPROF
	{
		extern void _mcleanup(void);
		_mcleanup();
	}
#endif
	exitshell();
	/* NOTREACHED */
}


/*
 * Read and execute commands.  "Top" is nonzero for the top level command
 * loop; it turns on prompting if the shell is interactive.
 */

static void
cmdloop(int top)
{
	union node *n;
	struct stackmark smark;
	int inter;
	int numeof = 0;

	TRACE(("cmdloop(%d) called\n", top));
	setstackmark(&smark);
	for (;;) {
		if (pendingsigs)
			dotrap();
#if JOBS
		if (jobctl)
			showjobs(stderr, SHOW_CHANGED);
#endif
		inter = 0;
		if (iflag && top) {
			inter++;
#ifdef CONFIG_ASH_MAIL
			chkmail();
#endif
		}
		n = parsecmd(inter);
		/* showtree(n); DEBUG */
		if (n == NEOF) {
			if (!top || numeof >= 50)
				break;
			if (!stoppedjobs()) {
				if (!Iflag)
					break;
				out2str("\nUse \"exit\" to leave shell.\n");
			}
			numeof++;
		} else if (n != NULL && nflag == 0) {
			job_warning = (job_warning == 2) ? 1 : 0;
			numeof = 0;
			evaltree(n, 0);
		}
		popstackmark(&smark);
		setstackmark(&smark);
		if (evalskip == SKIPFILE) {
			evalskip = 0;
			break;
		}
	}
	popstackmark(&smark);
}



/*
 * Read /etc/profile or .profile.  Return on error.
 */

static void
read_profile(const char *name)
{
	int fd;
	int xflag_set = 0;
	int vflag_set = 0;

	INTOFF;
	if ((fd = open(name, O_RDONLY)) >= 0)
		setinputfd(fd, 1);
	INTON;
	if (fd < 0)
		return;
	/* -q turns off -x and -v just when executing init files */
	if (qflag)  {
	    if (xflag)
		    xflag = 0, xflag_set = 1;
	    if (vflag)
		    vflag = 0, vflag_set = 1;
	}
	cmdloop(0);
	if (qflag)  {
	    if (xflag_set)
		    xflag = 1;
	    if (vflag_set)
		    vflag = 1;
	}
	popfile();
}



/*
 * Read a file containing shell functions.
 */

static void
readcmdfile(char *name)
{
	int fd;

	INTOFF;
	if ((fd = open(name, O_RDONLY)) >= 0)
		setinputfd(fd, 1);
	else
		error("Can't open %s", name);
	INTON;
	cmdloop(0);
	popfile();
}


/*
 * Take commands from a file.  To be compatible we should do a path
 * search for the file, which is necessary to find sub-commands.
 */

static inline char *
find_dot_file(char *name)
{
	char *fullname;
	const char *path = pathval();
	struct stat statb;

	/* don't try this for absolute or relative paths */
	if (strchr(name, '/'))
		return name;

	while ((fullname = padvance(&path, name)) != NULL) {
		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
			/*
			 * Don't bother freeing here, since it will
			 * be freed by the caller.
			 */
			return fullname;
		}
		stunalloc(fullname);
	}

	/* not found in the PATH */
	error(not_found_msg, name);
	/* NOTREACHED */
}

int
dotcmd(int argc, char **argv)
{
	exitstatus = 0;

	if (argc >= 2) {                /* That's what SVR2 does */
		char *fullname;
		struct stackmark smark;

		setstackmark(&smark);
		fullname = find_dot_file(argv[1]);
		setinputfile(fullname, 1);
		commandname = fullname;
		cmdloop(0);
		popfile();
		popstackmark(&smark);
	}
	return exitstatus;
}


static int
exitcmd(int argc, char **argv)
{
	if (stoppedjobs())
		return 0;
	if (argc > 1)
		exitstatus = number(argv[1]);
	exraise(EXEXIT);
	/* NOTREACHED */
}

/*      $NetBSD: memalloc.c,v 1.27 2003/01/22 20:36:04 dsl Exp $        */

/*
 * Like malloc, but returns an error when out of space.
 */

static pointer
ckmalloc(size_t nbytes)
{
	pointer p;

	p = malloc(nbytes);
	if (p == NULL)
		error(bb_msg_memory_exhausted);
	return p;
}


/*
 * Same for realloc.
 */

static pointer
ckrealloc(pointer p, size_t nbytes)
{
	p = realloc(p, nbytes);
	if (p == NULL)
		error(bb_msg_memory_exhausted);
	return p;
}


/*
 * Make a copy of a string in safe storage.
 */

static char *
savestr(const char *s)
{
	char *p = strdup(s);
	if (!p)
		error(bb_msg_memory_exhausted);
	return p;
}


/*
 * Parse trees for commands are allocated in lifo order, so we use a stack
 * to make this more efficient, and also to avoid all sorts of exception
 * handling code to handle interrupts in the middle of a parse.
 *
 * The size 504 was chosen because the Ultrix malloc handles that size
 * well.
 */


static pointer
stalloc(size_t nbytes)
{
	char *p;
	size_t aligned;

	aligned = SHELL_ALIGN(nbytes);
	if (aligned > stacknleft) {
		size_t len;
		size_t blocksize;
		struct stack_block *sp;

		blocksize = aligned;
		if (blocksize < MINSIZE)
			blocksize = MINSIZE;
		len = sizeof(struct stack_block) - MINSIZE + blocksize;
		if (len < blocksize)
			error(bb_msg_memory_exhausted);
		INTOFF;
		sp = ckmalloc(len);
		sp->prev = stackp;
		stacknxt = sp->space;
		stacknleft = blocksize;
		sstrend = stacknxt + blocksize;
		stackp = sp;
		INTON;
	}
	p = stacknxt;
	stacknxt += aligned;
	stacknleft -= aligned;
	return p;
}


void
stunalloc(pointer p)
{
#ifdef DEBUG
	if (!p || (stacknxt < (char *)p) || ((char *)p < stackp->space)) {
		write(2, "stunalloc\n", 10);
		abort();
	}
#endif
	stacknleft += stacknxt - (char *)p;
	stacknxt = p;
}



void
setstackmark(struct stackmark *mark)
{
	mark->stackp = stackp;
	mark->stacknxt = stacknxt;
	mark->stacknleft = stacknleft;
	mark->marknext = markp;
	markp = mark;
}


void
popstackmark(struct stackmark *mark)
{
	struct stack_block *sp;

	INTOFF;
	markp = mark->marknext;
	while (stackp != mark->stackp) {
		sp = stackp;
		stackp = sp->prev;
		ckfree(sp);
	}
	stacknxt = mark->stacknxt;
	stacknleft = mark->stacknleft;
	sstrend = mark->stacknxt + mark->stacknleft;
	INTON;
}


/*
 * When the parser reads in a string, it wants to stick the string on the
 * stack and only adjust the stack pointer when it knows how big the
 * string is.  Stackblock (defined in stack.h) returns a pointer to a block
 * of space on top of the stack and stackblocklen returns the length of
 * this block.  Growstackblock will grow this space by at least one byte,
 * possibly moving it (like realloc).  Grabstackblock actually allocates the
 * part of the block that has been used.
 */

void
growstackblock(void)
{
	size_t newlen;

	newlen = stacknleft * 2;
	if (newlen < stacknleft)
		error(bb_msg_memory_exhausted);
	if (newlen < 128)
		newlen += 128;

	if (stacknxt == stackp->space && stackp != &stackbase) {
		struct stack_block *oldstackp;
		struct stackmark *xmark;
		struct stack_block *sp;
		struct stack_block *prevstackp;
		size_t grosslen;

		INTOFF;
		oldstackp = stackp;
		sp = stackp;
		prevstackp = sp->prev;
		grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
		sp = ckrealloc((pointer)sp, grosslen);
		sp->prev = prevstackp;
		stackp = sp;
		stacknxt = sp->space;
		stacknleft = newlen;
		sstrend = sp->space + newlen;

		/*
		 * Stack marks pointing to the start of the old block
		 * must be relocated to point to the new block
		 */
		xmark = markp;
		while (xmark != NULL && xmark->stackp == oldstackp) {
			xmark->stackp = stackp;
			xmark->stacknxt = stacknxt;
			xmark->stacknleft = stacknleft;
			xmark = xmark->marknext;
		}
		INTON;
	} else {
		char *oldspace = stacknxt;
		int oldlen = stacknleft;
		char *p = stalloc(newlen);

		/* free the space we just allocated */
		stacknxt = memcpy(p, oldspace, oldlen);
		stacknleft += newlen;
	}
}

static inline void
grabstackblock(size_t len)
{
	len = SHELL_ALIGN(len);
	stacknxt += len;
	stacknleft -= len;
}

/*
 * The following routines are somewhat easier to use than the above.
 * The user declares a variable of type STACKSTR, which may be declared
 * to be a register.  The macro STARTSTACKSTR initializes things.  Then
 * the user uses the macro STPUTC to add characters to the string.  In
 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
 * grown as necessary.  When the user is done, she can just leave the
 * string there and refer to it using stackblock().  Or she can allocate
 * the space for it using grabstackstr().  If it is necessary to allow
 * someone else to use the stack temporarily and then continue to grow
 * the string, the user should use grabstack to allocate the space, and
 * then call ungrabstr(p) to return to the previous mode of operation.
 *
 * USTPUTC is like STPUTC except that it doesn't check for overflow.
 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
 * is space for at least one character.
 */

void *
growstackstr(void)
{
	size_t len = stackblocksize();
	if (herefd >= 0 && len >= 1024) {
		xwrite(herefd, stackblock(), len);
		return stackblock();
	}
	growstackblock();
	return stackblock() + len;
}

/*
 * Called from CHECKSTRSPACE.
 */

char *
makestrspace(size_t newlen, char *p)
{
	size_t len = p - stacknxt;
	size_t size = stackblocksize();

	for (;;) {
		size_t nleft;

		size = stackblocksize();
		nleft = size - len;
		if (nleft >= newlen)
			break;
		growstackblock();
	}
	return stackblock() + len;
}

char *
stnputs(const char *s, size_t n, char *p)
{
	p = makestrspace(n, p);
	p = mempcpy(p, s, n);
	return p;
}

char *
stputs(const char *s, char *p)
{
	return stnputs(s, strlen(s), p);
}

/*      $NetBSD: mystring.c,v 1.15 2002/11/24 22:35:42 christos Exp $   */

/*
 * String functions.
 *
 *      number(s)               Convert a string of digits to an integer.
 *      is_number(s)            Return true if s is a string of digits.
 */

/*
 * prefix -- see if pfx is a prefix of string.
 */

char *
prefix(const char *string, const char *pfx)
{
	while (*pfx) {
		if (*pfx++ != *string++)
			return 0;
	}
	return (char *) string;
}


/*
 * Convert a string of digits to an integer, printing an error message on
 * failure.
 */

int
number(const char *s)
{

	if (! is_number(s))
		error(illnum, s);
	return atoi(s);
}



/*
 * Check for a valid number.  This should be elsewhere.
 */

int
is_number(const char *p)
{
	do {
		if (! is_digit(*p))
			return 0;
	} while (*++p != '\0');
	return 1;
}


/*
 * Produce a possibly single quoted string suitable as input to the shell.
 * The return string is allocated on the stack.
 */

char *
single_quote(const char *s) {
	char *p;

	STARTSTACKSTR(p);

	do {
		char *q;
		size_t len;

		len = strchrnul(s, '\'') - s;

		q = p = makestrspace(len + 3, p);

		*q++ = '\'';
		q = mempcpy(q, s, len);
		*q++ = '\'';
		s += len;

		STADJUST(q - p, p);

		len = strspn(s, "'");
		if (!len)
			break;

		q = p = makestrspace(len + 3, p);

		*q++ = '"';
		q = mempcpy(q, s, len);
		*q++ = '"';
		s += len;

		STADJUST(q - p, p);
	} while (*s);

	USTPUTC(0, p);

	return stackblock();
}

/*
 * Like strdup but works with the ash stack.
 */

char *
sstrdup(const char *p)
{
	size_t len = strlen(p) + 1;
	return memcpy(stalloc(len), p, len);
}


static void
calcsize(union node *n)
{
      if (n == NULL)
	    return;
      funcblocksize += nodesize[n->type];
      switch (n->type) {
      case NCMD:
	    calcsize(n->ncmd.redirect);
	    calcsize(n->ncmd.args);
	    calcsize(n->ncmd.assign);
	    break;
      case NPIPE:
	    sizenodelist(n->npipe.cmdlist);
	    break;
      case NREDIR:
      case NBACKGND:
      case NSUBSHELL:
	    calcsize(n->nredir.redirect);
	    calcsize(n->nredir.n);
	    break;
      case NAND:
      case NOR:
      case NSEMI:
      case NWHILE:
      case NUNTIL:
	    calcsize(n->nbinary.ch2);
	    calcsize(n->nbinary.ch1);
	    break;
      case NIF:
	    calcsize(n->nif.elsepart);
	    calcsize(n->nif.ifpart);
	    calcsize(n->nif.test);
	    break;
      case NFOR:
	    funcstringsize += strlen(n->nfor.var) + 1;
	    calcsize(n->nfor.body);
	    calcsize(n->nfor.args);
	    break;
      case NCASE:
	    calcsize(n->ncase.cases);
	    calcsize(n->ncase.expr);
	    break;
      case NCLIST:
	    calcsize(n->nclist.body);
	    calcsize(n->nclist.pattern);
	    calcsize(n->nclist.next);
	    break;
      case NDEFUN:
      case NARG:
	    sizenodelist(n->narg.backquote);
	    funcstringsize += strlen(n->narg.text) + 1;
	    calcsize(n->narg.next);
	    break;
      case NTO:
      case NCLOBBER:
      case NFROM:
      case NFROMTO:
      case NAPPEND:
	    calcsize(n->nfile.fname);
	    calcsize(n->nfile.next);
	    break;
      case NTOFD:
      case NFROMFD:
	    calcsize(n->ndup.vname);
	    calcsize(n->ndup.next);
	    break;
      case NHERE:
      case NXHERE:
	    calcsize(n->nhere.doc);
	    calcsize(n->nhere.next);
	    break;
      case NNOT:
	    calcsize(n->nnot.com);
	    break;
      };
}



static void
sizenodelist(struct nodelist *lp)
{
	while (lp) {
		funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
		calcsize(lp->n);
		lp = lp->next;
	}
}



static union node *
copynode(union node *n)
{
      union node *new;

      if (n == NULL)
	    return NULL;
      new = funcblock;
      funcblock = (char *) funcblock + nodesize[n->type];
      switch (n->type) {
      case NCMD:
	    new->ncmd.redirect = copynode(n->ncmd.redirect);
	    new->ncmd.args = copynode(n->ncmd.args);
	    new->ncmd.assign = copynode(n->ncmd.assign);
	    break;
      case NPIPE:
	    new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
	    new->npipe.backgnd = n->npipe.backgnd;
	    break;
      case NREDIR:
      case NBACKGND:
      case NSUBSHELL:
	    new->nredir.redirect = copynode(n->nredir.redirect);
	    new->nredir.n = copynode(n->nredir.n);
	    break;
      case NAND:
      case NOR:
      case NSEMI:
      case NWHILE:
      case NUNTIL:
	    new->nbinary.ch2 = copynode(n->nbinary.ch2);
	    new->nbinary.ch1 = copynode(n->nbinary.ch1);
	    break;
      case NIF:
	    new->nif.elsepart = copynode(n->nif.elsepart);
	    new->nif.ifpart = copynode(n->nif.ifpart);
	    new->nif.test = copynode(n->nif.test);
	    break;
      case NFOR:
	    new->nfor.var = nodesavestr(n->nfor.var);
	    new->nfor.body = copynode(n->nfor.body);
	    new->nfor.args = copynode(n->nfor.args);
	    break;
      case NCASE:
	    new->ncase.cases = copynode(n->ncase.cases);
	    new->ncase.expr = copynode(n->ncase.expr);
	    break;
      case NCLIST:
	    new->nclist.body = copynode(n->nclist.body);
	    new->nclist.pattern = copynode(n->nclist.pattern);
	    new->nclist.next = copynode(n->nclist.next);
	    break;
      case NDEFUN:
      case NARG:
	    new->narg.backquote = copynodelist(n->narg.backquote);
	    new->narg.text = nodesavestr(n->narg.text);
	    new->narg.next = copynode(n->narg.next);
	    break;
      case NTO:
      case NCLOBBER:
      case NFROM:
      case NFROMTO:
      case NAPPEND:
	    new->nfile.fname = copynode(n->nfile.fname);
	    new->nfile.fd = n->nfile.fd;
	    new->nfile.next = copynode(n->nfile.next);
	    break;
      case NTOFD:
      case NFROMFD:
	    new->ndup.vname = copynode(n->ndup.vname);
	    new->ndup.dupfd = n->ndup.dupfd;
	    new->ndup.fd = n->ndup.fd;
	    new->ndup.next = copynode(n->ndup.next);
	    break;
      case NHERE:
      case NXHERE:
	    new->nhere.doc = copynode(n->nhere.doc);
	    new->nhere.fd = n->nhere.fd;
	    new->nhere.next = copynode(n->nhere.next);
	    break;
      case NNOT:
	    new->nnot.com = copynode(n->nnot.com);
	    break;
      };
      new->type = n->type;
	return new;
}


static struct nodelist *
copynodelist(struct nodelist *lp)
{
	struct nodelist *start;
	struct nodelist **lpp;

	lpp = &start;
	while (lp) {
		*lpp = funcblock;
		funcblock = (char *) funcblock +
		    SHELL_ALIGN(sizeof(struct nodelist));
		(*lpp)->n = copynode(lp->n);
		lp = lp->next;
		lpp = &(*lpp)->next;
	}
	*lpp = NULL;
	return start;
}



static char *
nodesavestr(char   *s)
{
	char   *rtn = funcstring;

	funcstring = stpcpy(funcstring, s) + 1;
	return rtn;
}



/*
 * Free a parse tree.
 */

static void
freefunc(struct funcnode *f)
{
	if (f && --f->count < 0)
		ckfree(f);
}


static void options(int);
static void setoption(int, int);


/*
 * Process the shell command line arguments.
 */

void
procargs(int argc, char **argv)
{
	int i;
	const char *xminusc;
	char **xargv;

	xargv = argv;
	arg0 = xargv[0];
	if (argc > 0)
		xargv++;
	for (i = 0; i < NOPTS; i++)
		optlist[i] = 2;
	argptr = xargv;
	options(1);
	xargv = argptr;
	xminusc = minusc;
	if (*xargv == NULL) {
		if (xminusc)
			error("-c requires an argument");
		sflag = 1;
	}
	if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
		iflag = 1;
	if (mflag == 2)
		mflag = iflag;
	for (i = 0; i < NOPTS; i++)
		if (optlist[i] == 2)
			optlist[i] = 0;
#if DEBUG == 2
	debug = 1;
#endif
	/* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
	if (xminusc) {
		minusc = *xargv++;
		if (*xargv)
			goto setarg0;
	} else if (!sflag) {
		setinputfile(*xargv, 0);
setarg0:
		arg0 = *xargv++;
		commandname = arg0;
	}

	shellparam.p = xargv;
#ifdef CONFIG_ASH_GETOPTS
	shellparam.optind = 1;
	shellparam.optoff = -1;
#endif
	/* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
	while (*xargv) {
		shellparam.nparam++;
		xargv++;
	}
	optschanged();
}


void
optschanged(void)
{
#ifdef DEBUG
	opentrace();
#endif
	setinteractive(iflag);
	setjobctl(mflag);
}

static inline void
minus_o(char *name, int val)
{
	int i;

	if (name == NULL) {
		out1str("Current option settings\n");
		for (i = 0; i < NOPTS; i++)
			out1fmt("%-16s%s\n", optnames(i),
				optlist[i] ? "on" : "off");
	} else {
		for (i = 0; i < NOPTS; i++)
			if (equal(name, optnames(i))) {
				optlist[i] = val;
				return;
			}
		error("Illegal option -o %s", name);
	}
}

/*
 * Process shell options.  The global variable argptr contains a pointer
 * to the argument list; we advance it past the options.
 */

static void
options(int cmdline)
{
	char *p;
	int val;
	int c;

	if (cmdline)
		minusc = NULL;
	while ((p = *argptr) != NULL) {
		argptr++;
		if ((c = *p++) == '-') {
			val = 1;
			if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
				if (!cmdline) {
					/* "-" means turn off -x and -v */
					if (p[0] == '\0')
						xflag = vflag = 0;
					/* "--" means reset params */
					else if (*argptr == NULL)
						setparam(argptr);
				}
				break;    /* "-" or  "--" terminates options */
			}
		} else if (c == '+') {
			val = 0;
		} else {
			argptr--;
			break;
		}
		while ((c = *p++) != '\0') {
			if (c == 'c' && cmdline) {
				minusc = p;     /* command is after shell args*/
			} else if (c == 'o') {
				minus_o(*argptr, val);
				if (*argptr)
					argptr++;
			} else if (cmdline && (c == '-')) {     // long options
				if (strcmp(p, "login") == 0)
					isloginsh = 1;
				break;
			} else {
				setoption(c, val);
			}
		}
	}
}



static void
setoption(int flag, int val)
{
	int i;

	for (i = 0; i < NOPTS; i++)
		if (optletters(i) == flag) {
			optlist[i] = val;
			return;
		}
	error("Illegal option -%c", flag);
	/* NOTREACHED */
}



/*
 * Set the shell parameters.
 */

void
setparam(char **argv)
{
	char **newparam;
	char **ap;
	int nparam;

	for (nparam = 0 ; argv[nparam] ; nparam++);
	ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
	while (*argv) {
		*ap++ = savestr(*argv++);
	}
	*ap = NULL;
	freeparam(&shellparam);
	shellparam.malloc = 1;
	shellparam.nparam = nparam;
	shellparam.p = newparam;
#ifdef CONFIG_ASH_GETOPTS
	shellparam.optind = 1;
	shellparam.optoff = -1;
#endif
}


/*
 * Free the list of positional parameters.
 */

void
freeparam(volatile struct shparam *param)
{
	char **ap;

	if (param->malloc) {
		for (ap = param->p ; *ap ; ap++)
			ckfree(*ap);
		ckfree(param->p);
	}
}



/*
 * The shift builtin command.
 */

int
shiftcmd(int argc, char **argv)
{
	int n;
	char **ap1, **ap2;

	n = 1;
	if (argc > 1)
		n = number(argv[1]);
	if (n > shellparam.nparam)
		error("can't shift that many");
	INTOFF;
	shellparam.nparam -= n;
	for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
		if (shellparam.malloc)
			ckfree(*ap1);
	}
	ap2 = shellparam.p;
	while ((*ap2++ = *ap1++) != NULL);
#ifdef CONFIG_ASH_GETOPTS
	shellparam.optind = 1;
	shellparam.optoff = -1;
#endif
	INTON;
	return 0;
}



/*
 * The set command builtin.
 */

int
setcmd(int argc, char **argv)
{
	if (argc == 1)
		return showvars(nullstr, 0, VUNSET);
	INTOFF;
	options(0);
	optschanged();
	if (*argptr != NULL) {
		setparam(argptr);
	}
	INTON;
	return 0;
}


#ifdef CONFIG_ASH_GETOPTS
static void
getoptsreset(value)
	const char *value;
{
	shellparam.optind = number(value);
	shellparam.optoff = -1;
}
#endif

#ifdef CONFIG_LOCALE_SUPPORT
static void change_lc_all(const char *value)
{
	if (value != 0 && *value != 0)
		setlocale(LC_ALL, value);
}

static void change_lc_ctype(const char *value)
{
	if (value != 0 && *value != 0)
		setlocale(LC_CTYPE, value);
}

#endif

#ifdef CONFIG_ASH_GETOPTS
static int
getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
{
	char *p, *q;
	char c = '?';
	int done = 0;
	int err = 0;
	char s[10];
	char **optnext = optfirst + *param_optind - 1;

	if (*param_optind <= 1 || *optoff < 0 || !(*(optnext - 1)) ||
	    strlen(*(optnext - 1)) < *optoff)
		p = NULL;
	else
		p = *(optnext - 1) + *optoff;
	if (p == NULL || *p == '\0') {
		/* Current word is done, advance */
		if (optnext == NULL)
			return 1;
		p = *optnext;
		if (p == NULL || *p != '-' || *++p == '\0') {
atend:
			p = NULL;
			done = 1;
			goto out;
		}
		optnext++;
		if (p[0] == '-' && p[1] == '\0')        /* check for "--" */
			goto atend;
	}

	c = *p++;
	for (q = optstr; *q != c; ) {
		if (*q == '\0') {
			if (optstr[0] == ':') {
				s[0] = c;
				s[1] = '\0';
				err |= setvarsafe("OPTARG", s, 0);
			} else {
				fprintf(stderr, "Illegal option -%c\n", c);
				(void) unsetvar("OPTARG");
			}
			c = '?';
			goto out;
		}
		if (*++q == ':')
			q++;
	}

	if (*++q == ':') {
		if (*p == '\0' && (p = *optnext) == NULL) {
			if (optstr[0] == ':') {
				s[0] = c;
				s[1] = '\0';
				err |= setvarsafe("OPTARG", s, 0);
				c = ':';
			} else {
				fprintf(stderr, "No arg for -%c option\n", c);
				(void) unsetvar("OPTARG");
				c = '?';
			}
			goto out;
		}

		if (p == *optnext)
			optnext++;
		err |= setvarsafe("OPTARG", p, 0);
		p = NULL;
	} else
		err |= setvarsafe("OPTARG", nullstr, 0);

out:
	*optoff = p ? p - *(optnext - 1) : -1;
	*param_optind = optnext - optfirst + 1;
	fmtstr(s, sizeof(s), "%d", *param_optind);
	err |= setvarsafe("OPTIND", s, VNOFUNC);
	s[0] = c;
	s[1] = '\0';
	err |= setvarsafe(optvar, s, 0);
	if (err) {
		*param_optind = 1;
		*optoff = -1;
		flushall();
		exraise(EXERROR);
	}
	return done;
}

/*
 * The getopts builtin.  Shellparam.optnext points to the next argument
 * to be processed.  Shellparam.optptr points to the next character to
 * be processed in the current argument.  If shellparam.optnext is NULL,
 * then it's the first time getopts has been called.
 */

int
getoptscmd(int argc, char **argv)
{
	char **optbase;

	if (argc < 3)
		error("Usage: getopts optstring var [arg]");
	else if (argc == 3) {
		optbase = shellparam.p;
		if (shellparam.optind > shellparam.nparam + 1) {
			shellparam.optind = 1;
			shellparam.optoff = -1;
		}
	}
	else {
		optbase = &argv[3];
		if (shellparam.optind > argc - 2) {
			shellparam.optind = 1;
			shellparam.optoff = -1;
		}
	}

	return getopts(argv[1], argv[2], optbase, &shellparam.optind,
		       &shellparam.optoff);
}
#endif /* CONFIG_ASH_GETOPTS */

/*
 * XXX - should get rid of.  have all builtins use getopt(3).  the
 * library getopt must have the BSD extension static variable "optreset"
 * otherwise it can't be used within the shell safely.
 *
 * Standard option processing (a la getopt) for builtin routines.  The
 * only argument that is passed to nextopt is the option string; the
 * other arguments are unnecessary.  It return the character, or '\0' on
 * end of input.
 */

static int
nextopt(const char *optstring)
{
	char *p;
	const char *q;
	char c;

	if ((p = optptr) == NULL || *p == '\0') {
		p = *argptr;
		if (p == NULL || *p != '-' || *++p == '\0')
			return '\0';
		argptr++;
		if (p[0] == '-' && p[1] == '\0')        /* check for "--" */
			return '\0';
	}
	c = *p++;
	for (q = optstring ; *q != c ; ) {
		if (*q == '\0')
			error("Illegal option -%c", c);
		if (*++q == ':')
			q++;
	}
	if (*++q == ':') {
		if (*p == '\0' && (p = *argptr++) == NULL)
			error("No arg for -%c option", c);
		optionarg = p;
		p = NULL;
	}
	optptr = p;
	return c;
}

/*      $NetBSD: output.c,v 1.27 2002/11/24 22:35:42 christos Exp $     */



void
outstr(const char *p, FILE *file)
{
	INTOFF;
	fputs(p, file);
	INTON;
}

void
flushall(void)
{
	INTOFF;
	fflush(stdout);
	fflush(stderr);
	INTON;
}


void
flushout(FILE *dest)
{
	INTOFF;
	fflush(dest);
	INTON;
}

static void
outcslow(int c, FILE *dest)
{
	INTOFF;
	putc(c, dest);
	fflush(dest);
	INTON;
}


static int
out1fmt(const char *fmt, ...)
{
	va_list ap;
	int r;

	INTOFF;
	va_start(ap, fmt);
	r = vprintf(fmt, ap);
	va_end(ap);
	INTON;
	return r;
}


int
fmtstr(char *outbuf, size_t length, const char *fmt, ...)
{
	va_list ap;
	int ret;

	va_start(ap, fmt);
	INTOFF;
	ret = vsnprintf(outbuf, length, fmt, ap);
	va_end(ap);
	INTON;
	return ret;
}


/*
 * Version of write which resumes after a signal is caught.
 */

static void
xwrite(int fd, const void *p, size_t n)
{
	ssize_t i;

	do {
		i = bb_full_write(fd, p, n);
	} while (i < 0 && errno == EINTR);
}


/*      $NetBSD: parser.c,v 1.54 2002/11/24 22:35:42 christos Exp $     */


/*
 * Shell command parser.
 */

#define EOFMARKLEN 79


struct heredoc {
	struct heredoc *next;   /* next here document in list */
	union node *here;               /* redirection node */
	char *eofmark;          /* string indicating end of input */
	int striptabs;          /* if set, strip leading tabs */
};



static struct heredoc *heredoclist;    /* list of here documents to read */


static union node *list(int);
static union node *andor(void);
static union node *pipeline(void);
static union node *command(void);
static union node *simplecmd(void);
static union node *makename(void);
static void parsefname(void);
static void parseheredoc(void);
static char peektoken(void);
static int readtoken(void);
static int xxreadtoken(void);
static int readtoken1(int firstc, int syntax, char *eofmark, int striptabs);
static int noexpand(char *);
static void synexpect(int) __attribute__((__noreturn__));
static void synerror(const char *) __attribute__((__noreturn__));
static void setprompt(int);


static inline int
goodname(const char *p)
{
	return !*endofname(p);
}

static inline int
isassignment(const char *p)
{
	const char *q = endofname(p);
	if (p == q)
		return 0;
	return *q == '=';
}


/*
 * Read and parse a command.  Returns NEOF on end of file.  (NULL is a
 * valid parse tree indicating a blank line.)
 */

union node *
parsecmd(int interact)
{
	int t;

	tokpushback = 0;
	doprompt = interact;
	if (doprompt)
		setprompt(doprompt);
	needprompt = 0;
	t = readtoken();
	if (t == TEOF)
		return NEOF;
	if (t == TNL)
		return NULL;
	tokpushback++;
	return list(1);
}


static union node *
list(int nlflag)
{
	union node *n1, *n2, *n3;
	int tok;

	checkkwd = CHKNL | CHKKWD | CHKALIAS;
	if (nlflag == 2 && peektoken())
		return NULL;
	n1 = NULL;
	for (;;) {
		n2 = andor();
		tok = readtoken();
		if (tok == TBACKGND) {
			if (n2->type == NPIPE) {
				n2->npipe.backgnd = 1;
			} else {
				if (n2->type != NREDIR) {
					n3 = stalloc(sizeof(struct nredir));
					n3->nredir.n = n2;
					n3->nredir.redirect = NULL;
					n2 = n3;
				}
				n2->type = NBACKGND;
			}
		}
		if (n1 == NULL) {
			n1 = n2;
		}
		else {
			n3 = (union node *)stalloc(sizeof (struct nbinary));
			n3->type = NSEMI;
			n3->nbinary.ch1 = n1;
			n3->nbinary.ch2 = n2;
			n1 = n3;
		}
		switch (tok) {
		case TBACKGND:
		case TSEMI:
			tok = readtoken();
			/* fall through */
		case TNL:
			if (tok == TNL) {
				parseheredoc();
				if (nlflag == 1)
					return n1;
			} else {
				tokpushback++;
			}
			checkkwd = CHKNL | CHKKWD | CHKALIAS;
			if (peektoken())
				return n1;
			break;
		case TEOF:
			if (heredoclist)
				parseheredoc();
			else
				pungetc();              /* push back EOF on input */
			return n1;
		default:
			if (nlflag == 1)
				synexpect(-1);
			tokpushback++;
			return n1;
		}
	}
}



static union node *
andor(void)
{
	union node *n1, *n2, *n3;
	int t;

	n1 = pipeline();
	for (;;) {
		if ((t = readtoken()) == TAND) {
			t = NAND;
		} else if (t == TOR) {
			t = NOR;
		} else {
			tokpushback++;
			return n1;
		}
		checkkwd = CHKNL | CHKKWD | CHKALIAS;
		n2 = pipeline();
		n3 = (union node *)stalloc(sizeof (struct nbinary));
		n3->type = t;
		n3->nbinary.ch1 = n1;
		n3->nbinary.ch2 = n2;
		n1 = n3;
	}
}



static union node *
pipeline(void)
{
	union node *n1, *n2, *pipenode;
	struct nodelist *lp, *prev;
	int negate;

	negate = 0;
	TRACE(("pipeline: entered\n"));
	if (readtoken() == TNOT) {
		negate = !negate;
		checkkwd = CHKKWD | CHKALIAS;
	} else
		tokpushback++;
	n1 = command();
	if (readtoken() == TPIPE) {
		pipenode = (union node *)stalloc(sizeof (struct npipe));
		pipenode->type = NPIPE;
		pipenode->npipe.backgnd = 0;
		lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
		pipenode->npipe.cmdlist = lp;
		lp->n = n1;
		do {
			prev = lp;
			lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
			checkkwd = CHKNL | CHKKWD | CHKALIAS;
			lp->n = command();
			prev->next = lp;
		} while (readtoken() == TPIPE);
		lp->next = NULL;
		n1 = pipenode;
	}
	tokpushback++;
	if (negate) {
		n2 = (union node *)stalloc(sizeof (struct nnot));
		n2->type = NNOT;
		n2->nnot.com = n1;
		return n2;
	} else
		return n1;
}



static union node *
command(void)
{
	union node *n1, *n2;
	union node *ap, **app;
	union node *cp, **cpp;
	union node *redir, **rpp;
	union node **rpp2;
	int t;

	redir = NULL;
	rpp2 = &redir;

	switch (readtoken()) {
	default:
		synexpect(-1);
		/* NOTREACHED */
	case TIF:
		n1 = (union node *)stalloc(sizeof (struct nif));
		n1->type = NIF;
		n1->nif.test = list(0);
		if (readtoken() != TTHEN)
			synexpect(TTHEN);
		n1->nif.ifpart = list(0);
		n2 = n1;
		while (readtoken() == TELIF) {
			n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
			n2 = n2->nif.elsepart;
			n2->type = NIF;
			n2->nif.test = list(0);
			if (readtoken() != TTHEN)
				synexpect(TTHEN);
			n2->nif.ifpart = list(0);
		}
		if (lasttoken == TELSE)
			n2->nif.elsepart = list(0);
		else {
			n2->nif.elsepart = NULL;
			tokpushback++;
		}
		t = TFI;
		break;
	case TWHILE:
	case TUNTIL: {
		int got;
		n1 = (union node *)stalloc(sizeof (struct nbinary));
		n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
		n1->nbinary.ch1 = list(0);
		if ((got=readtoken()) != TDO) {
TRACE(("expecting DO got %s %s\n", tokname(got), got == TWORD ? wordtext : ""));
			synexpect(TDO);
		}
		n1->nbinary.ch2 = list(0);
		t = TDONE;
		break;
	}
	case TFOR:
		if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
			synerror("Bad for loop variable");
		n1 = (union node *)stalloc(sizeof (struct nfor));
		n1->type = NFOR;
		n1->nfor.var = wordtext;
		checkkwd = CHKKWD | CHKALIAS;
		if (readtoken() == TIN) {
			app = &ap;
			while (readtoken() == TWORD) {
				n2 = (union node *)stalloc(sizeof (struct narg));
				n2->type = NARG;
				n2->narg.text = wordtext;
				n2->narg.backquote = backquotelist;
				*app = n2;
				app = &n2->narg.next;
			}
			*app = NULL;
			n1->nfor.args = ap;
			if (lasttoken != TNL && lasttoken != TSEMI)
				synexpect(-1);
		} else {
			n2 = (union node *)stalloc(sizeof (struct narg));
			n2->type = NARG;
			n2->narg.text = (char *)dolatstr;
			n2->narg.backquote = NULL;
			n2->narg.next = NULL;
			n1->nfor.args = n2;
			/*
			 * Newline or semicolon here is optional (but note
			 * that the original Bourne shell only allowed NL).
			 */
			if (lasttoken != TNL && lasttoken != TSEMI)
				tokpushback++;
		}
		checkkwd = CHKNL | CHKKWD | CHKALIAS;
		if (readtoken() != TDO)
			synexpect(TDO);
		n1->nfor.body = list(0);
		t = TDONE;
		break;
	case TCASE:
		n1 = (union node *)stalloc(sizeof (struct ncase));
		n1->type = NCASE;
		if (readtoken() != TWORD)
			synexpect(TWORD);
		n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
		n2->type = NARG;
		n2->narg.text = wordtext;
		n2->narg.backquote = backquotelist;
		n2->narg.next = NULL;
		do {
			checkkwd = CHKKWD | CHKALIAS;
		} while (readtoken() == TNL);
		if (lasttoken != TIN)
			synexpect(TIN);
		cpp = &n1->ncase.cases;
next_case:
		checkkwd = CHKNL | CHKKWD;
		t = readtoken();
		while(t != TESAC) {
			if (lasttoken == TLP)
				readtoken();
			*cpp = cp = (union node *)stalloc(sizeof (struct nclist));
			cp->type = NCLIST;
			app = &cp->nclist.pattern;
			for (;;) {
				*app = ap = (union node *)stalloc(sizeof (struct narg));
				ap->type = NARG;
				ap->narg.text = wordtext;
				ap->narg.backquote = backquotelist;
				if (readtoken() != TPIPE)
					break;
				app = &ap->narg.next;
				readtoken();
			}
			ap->narg.next = NULL;
			if (lasttoken != TRP)
				synexpect(TRP);
			cp->nclist.body = list(2);

			cpp = &cp->nclist.next;

			checkkwd = CHKNL | CHKKWD;
			if ((t = readtoken()) != TESAC) {
				if (t != TENDCASE)
					synexpect(TENDCASE);
				else
					goto next_case;
			}
		}
		*cpp = NULL;
		goto redir;
	case TLP:
		n1 = (union node *)stalloc(sizeof (struct nredir));
		n1->type = NSUBSHELL;
		n1->nredir.n = list(0);
		n1->nredir.redirect = NULL;
		t = TRP;
		break;
	case TBEGIN:
		n1 = list(0);
		t = TEND;
		break;
	case TWORD:
	case TREDIR:
		tokpushback++;
		return simplecmd();
	}

	if (readtoken() != t)
		synexpect(t);

redir:
	/* Now check for redirection which may follow command */
	checkkwd = CHKKWD | CHKALIAS;
	rpp = rpp2;
	while (readtoken() == TREDIR) {
		*rpp = n2 = redirnode;
		rpp = &n2->nfile.next;
		parsefname();
	}
	tokpushback++;
	*rpp = NULL;
	if (redir) {
		if (n1->type != NSUBSHELL) {
			n2 = (union node *)stalloc(sizeof (struct nredir));
			n2->type = NREDIR;
			n2->nredir.n = n1;
			n1 = n2;
		}
		n1->nredir.redirect = redir;
	}

	return n1;
}


static union node *
simplecmd(void) {
	union node *args, **app;
	union node *n = NULL;
	union node *vars, **vpp;
	union node **rpp, *redir;
	int savecheckkwd;

	args = NULL;
	app = &args;
	vars = NULL;
	vpp = &vars;
	redir = NULL;
	rpp = &redir;

	savecheckkwd = CHKALIAS;
	for (;;) {
		checkkwd = savecheckkwd;
		switch (readtoken()) {
		case TWORD:
			n = (union node *)stalloc(sizeof (struct narg));
			n->type = NARG;
			n->narg.text = wordtext;
			n->narg.backquote = backquotelist;
			if (savecheckkwd && isassignment(wordtext)) {
				*vpp = n;
				vpp = &n->narg.next;
			} else {
				*app = n;
				app = &n->narg.next;
				savecheckkwd = 0;
			}
			break;
		case TREDIR:
			*rpp = n = redirnode;
			rpp = &n->nfile.next;
			parsefname();   /* read name of redirection file */
			break;
		case TLP:
			if (
				args && app == &args->narg.next &&
				!vars && !redir
			) {
				struct builtincmd *bcmd;
				const char *name;

				/* We have a function */
				if (readtoken() != TRP)
					synexpect(TRP);
				name = n->narg.text;
				if (
					!goodname(name) || (
						(bcmd = find_builtin(name)) &&
						IS_BUILTIN_SPECIAL(bcmd)
					)
				)
					synerror("Bad function name");
				n->type = NDEFUN;
				checkkwd = CHKNL | CHKKWD | CHKALIAS;
				n->narg.next = command();
				return n;
			}
			/* fall through */
		default:
			tokpushback++;
			goto out;
		}
	}
out:
	*app = NULL;
	*vpp = NULL;
	*rpp = NULL;
	n = (union node *)stalloc(sizeof (struct ncmd));
	n->type = NCMD;
	n->ncmd.args = args;
	n->ncmd.assign = vars;
	n->ncmd.redirect = redir;
	return n;
}

static union node *
makename(void)
{
	union node *n;

	n = (union node *)stalloc(sizeof (struct narg));
	n->type = NARG;
	n->narg.next = NULL;
	n->narg.text = wordtext;
	n->narg.backquote = backquotelist;
	return n;
}

void fixredir(union node *n, const char *text, int err)
{
	TRACE(("Fix redir %s %d\n", text, err));
	if (!err)
		n->ndup.vname = NULL;

	if (is_digit(text[0]) && text[1] == '\0')
		n->ndup.dupfd = digit_val(text[0]);
	else if (text[0] == '-' && text[1] == '\0')
		n->ndup.dupfd = -1;
	else {

		if (err)
			synerror("Bad fd number");
		else
			n->ndup.vname = makename();
	}
}


static void
parsefname(void)
{
	union node *n = redirnode;

	if (readtoken() != TWORD)
		synexpect(-1);
	if (n->type == NHERE) {
		struct heredoc *here = heredoc;
		struct heredoc *p;
		int i;

		if (quoteflag == 0)
			n->type = NXHERE;
		TRACE(("Here document %d\n", n->type));
		if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
			synerror("Illegal eof marker for << redirection");
		rmescapes(wordtext);
		here->eofmark = wordtext;
		here->next = NULL;
		if (heredoclist == NULL)
			heredoclist = here;
		else {
			for (p = heredoclist ; p->next ; p = p->next);
			p->next = here;
		}
	} else if (n->type == NTOFD || n->type == NFROMFD) {
		fixredir(n, wordtext, 0);
	} else {
		n->nfile.fname = makename();
	}
}


/*
 * Input any here documents.
 */

static void
parseheredoc(void)
{
	struct heredoc *here;
	union node *n;

	here = heredoclist;
	heredoclist = 0;

	while (here) {
		if (needprompt) {
			setprompt(2);
			needprompt = 0;
		}
		readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
				here->eofmark, here->striptabs);
		n = (union node *)stalloc(sizeof (struct narg));
		n->narg.type = NARG;
		n->narg.next = NULL;
		n->narg.text = wordtext;
		n->narg.backquote = backquotelist;
		here->here->nhere.doc = n;
		here = here->next;
	}
}

static char peektoken(void)
{
	int t;

	t = readtoken();
	tokpushback++;
	return tokname_array[t][0];
}

static int
readtoken(void)
{
	int t;
#ifdef DEBUG
	int alreadyseen = tokpushback;
#endif

#ifdef CONFIG_ASH_ALIAS
top:
#endif

	t = xxreadtoken();

	/*
	 * eat newlines
	 */
	if (checkkwd & CHKNL) {
		while (t == TNL) {
			parseheredoc();
			t = xxreadtoken();
		}
	}

	if (t != TWORD || quoteflag) {
		goto out;
	}

	/*
	 * check for keywords
	 */
	if (checkkwd & CHKKWD) {
		const char *const *pp;

		if ((pp = findkwd(wordtext))) {
			lasttoken = t = pp - tokname_array;
			TRACE(("keyword %s recognized\n", tokname(t)));
			goto out;
		}
	}

	if (checkkwd & CHKALIAS) {
#ifdef CONFIG_ASH_ALIAS
		struct alias *ap;
		if ((ap = lookupalias(wordtext, 1)) != NULL) {
			if (*ap->val) {
				pushstring(ap->val, ap);
			}
			goto top;
		}
#endif
	}
out:
	checkkwd = 0;
#ifdef DEBUG
	if (!alreadyseen)
	    TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
	else
	    TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
#endif
	return (t);
}


/*
 * Read the next input token.
 * If the token is a word, we set backquotelist to the list of cmds in
 *      backquotes.  We set quoteflag to true if any part of the word was
 *      quoted.
 * If the token is TREDIR, then we set redirnode to a structure containing
 *      the redirection.
 * In all cases, the variable startlinno is set to the number of the line
 *      on which the token starts.
 *
 * [Change comment:  here documents and internal procedures]
 * [Readtoken shouldn't have any arguments.  Perhaps we should make the
 *  word parsing code into a separate routine.  In this case, readtoken
 *  doesn't need to have any internal procedures, but parseword does.
 *  We could also make parseoperator in essence the main routine, and
 *  have parseword (readtoken1?) handle both words and redirection.]
 */

#define NEW_xxreadtoken
#ifdef NEW_xxreadtoken

/* singles must be first! */
static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };

static const char xxreadtoken_tokens[] = {
	TNL, TLP, TRP,          /* only single occurrence allowed */
	TBACKGND, TPIPE, TSEMI, /* if single occurrence */
	TEOF,                   /* corresponds to trailing nul */
	TAND, TOR, TENDCASE,    /* if double occurrence */
};

#define xxreadtoken_doubles \
	(sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
#define xxreadtoken_singles \
	(sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)

static int xxreadtoken()
{
	int c;

	if (tokpushback) {
		tokpushback = 0;
		return lasttoken;
	}
	if (needprompt) {
		setprompt(2);
		needprompt = 0;
	}
	startlinno = plinno;
	for (;;) {                      /* until token or start of word found */
		c = pgetc_macro();

		if ((c != ' ') && (c != '\t')
#ifdef CONFIG_ASH_ALIAS
			&& (c != PEOA)
#endif
			) {
			if (c == '#') {
				while ((c = pgetc()) != '\n' && c != PEOF);
				pungetc();
			} else if (c == '\\') {
				if (pgetc() != '\n') {
					pungetc();
					goto READTOKEN1;
				}
				startlinno = ++plinno;
				if (doprompt)
					setprompt(2);
			} else {
				const char *p
					= xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;

				if (c != PEOF) {
					if (c == '\n') {
						plinno++;
						needprompt = doprompt;
					}

					p = strchr(xxreadtoken_chars, c);
					if (p == NULL) {
					  READTOKEN1:
						return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
					}

					if (p - xxreadtoken_chars >= xxreadtoken_singles) {
						if (pgetc() == *p) {    /* double occurrence? */
							p += xxreadtoken_doubles + 1;
						} else {
							pungetc();
						}
					}
				}

				return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
			}
		}
	}
}


#else
#define RETURN(token)   return lasttoken = token

static int
xxreadtoken(void)
{
	int c;

	if (tokpushback) {
		tokpushback = 0;
		return lasttoken;
	}
	if (needprompt) {
		setprompt(2);
		needprompt = 0;
	}
	startlinno = plinno;
	for (;;) {      /* until token or start of word found */
		c = pgetc_macro();
		switch (c) {
		case ' ': case '\t':
#ifdef CONFIG_ASH_ALIAS
		case PEOA:
#endif
			continue;
		case '#':
			while ((c = pgetc()) != '\n' && c != PEOF);
			pungetc();
			continue;
		case '\\':
			if (pgetc() == '\n') {
				startlinno = ++plinno;
				if (doprompt)
					setprompt(2);
				continue;
			}
			pungetc();
			goto breakloop;
		case '\n':
			plinno++;
			needprompt = doprompt;
			RETURN(TNL);
		case PEOF:
			RETURN(TEOF);
		case '&':
			if (pgetc() == '&')
				RETURN(TAND);
			pungetc();
			RETURN(TBACKGND);
		case '|':
			if (pgetc() == '|')
				RETURN(TOR);
			pungetc();
			RETURN(TPIPE);
		case ';':
			if (pgetc() == ';')
				RETURN(TENDCASE);
			pungetc();
			RETURN(TSEMI);
		case '(':
			RETURN(TLP);
		case ')':
			RETURN(TRP);
		default:
			goto breakloop;
		}
	}
breakloop:
	return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
#undef RETURN
}
#endif /* NEW_xxreadtoken */


/*
 * If eofmark is NULL, read a word or a redirection symbol.  If eofmark
 * is not NULL, read a here document.  In the latter case, eofmark is the
 * word which marks the end of the document and striptabs is true if
 * leading tabs should be stripped from the document.  The argument firstc
 * is the first character of the input token or document.
 *
 * Because C does not have internal subroutines, I have simulated them
 * using goto's to implement the subroutine linkage.  The following macros
 * will run code that appears at the end of readtoken1.
 */

#define CHECKEND()      {goto checkend; checkend_return:;}
#define PARSEREDIR()    {goto parseredir; parseredir_return:;}
#define PARSESUB()      {goto parsesub; parsesub_return:;}
#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
#define PARSEARITH()    {goto parsearith; parsearith_return:;}

static int
readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
{
	int c = firstc;
	char *out;
	int len;
	char line[EOFMARKLEN + 1];
	struct nodelist *bqlist;
	int quotef;
	int dblquote;
	int varnest;    /* levels of variables expansion */
	int arinest;    /* levels of arithmetic expansion */
	int parenlevel; /* levels of parens in arithmetic */
	int dqvarnest;  /* levels of variables expansion within double quotes */
	int oldstyle;
	int prevsyntax; /* syntax before arithmetic */
#if __GNUC__
	/* Avoid longjmp clobbering */
	(void) &out;
	(void) &quotef;
	(void) &dblquote;
	(void) &varnest;
	(void) &arinest;
	(void) &parenlevel;
	(void) &dqvarnest;
	(void) &oldstyle;
	(void) &prevsyntax;
	(void) &syntax;
#endif

	startlinno = plinno;
	dblquote = 0;
	if (syntax == DQSYNTAX)
		dblquote = 1;
	quotef = 0;
	bqlist = NULL;
	varnest = 0;
	arinest = 0;
	parenlevel = 0;
	dqvarnest = 0;

	STARTSTACKSTR(out);
	loop: { /* for each line, until end of word */
		CHECKEND();     /* set c to PEOF if at end of here document */
		for (;;) {      /* until end of line or end of word */
			CHECKSTRSPACE(4, out);  /* permit 4 calls to USTPUTC */
			switch(SIT(c, syntax)) {
			case CNL:       /* '\n' */
				if (syntax == BASESYNTAX)
					goto endword;   /* exit outer loop */
				USTPUTC(c, out);
				plinno++;
				if (doprompt)
					setprompt(2);
				c = pgetc();
				goto loop;              /* continue outer loop */
			case CWORD:
				USTPUTC(c, out);
				break;
			case CCTL:
				if (eofmark == NULL || dblquote)
					USTPUTC(CTLESC, out);
				USTPUTC(c, out);
				break;
			case CBACK:     /* backslash */
				c = pgetc2();
				if (c == PEOF) {
					USTPUTC(CTLESC, out);
					USTPUTC('\\', out);
					pungetc();
				} else if (c == '\n') {
					if (doprompt)
						setprompt(2);
				} else {
					if (
						dblquote &&
						c != '\\' && c != '`' &&
						c != '$' && (
							c != '"' ||
							eofmark != NULL
						)
					) {
						USTPUTC(CTLESC, out);
						USTPUTC('\\', out);
					}
					if (SIT(c, SQSYNTAX) == CCTL)
						USTPUTC(CTLESC, out);
					USTPUTC(c, out);
					quotef++;
				}
				break;
			case CSQUOTE:
				syntax = SQSYNTAX;
quotemark:
				if (eofmark == NULL) {
					USTPUTC(CTLQUOTEMARK, out);
				}
				break;
			case CDQUOTE:
				syntax = DQSYNTAX;
				dblquote = 1;
				goto quotemark;
			case CENDQUOTE:
				if (eofmark != NULL && arinest == 0 &&
				    varnest == 0) {
					USTPUTC(c, out);
				} else {
					if (dqvarnest == 0) {
						syntax = BASESYNTAX;
						dblquote = 0;
					}
					quotef++;
					goto quotemark;
				}
				break;
			case CVAR:      /* '$' */
				PARSESUB();             /* parse substitution */
				break;
			case CENDVAR:   /* '}' */
				if (varnest > 0) {
					varnest--;
					if (dqvarnest > 0) {
						dqvarnest--;
					}
					USTPUTC(CTLENDVAR, out);
				} else {
					USTPUTC(c, out);
				}
				break;
#ifdef CONFIG_ASH_MATH_SUPPORT
			case CLP:       /* '(' in arithmetic */
				parenlevel++;
				USTPUTC(c, out);
				break;
			case CRP:       /* ')' in arithmetic */
				if (parenlevel > 0) {
					USTPUTC(c, out);
					--parenlevel;
				} else {
					if (pgetc() == ')') {
						if (--arinest == 0) {
							USTPUTC(CTLENDARI, out);
							syntax = prevsyntax;
							if (syntax == DQSYNTAX)
								dblquote = 1;
							else
								dblquote = 0;
						} else
							USTPUTC(')', out);
					} else {
						/*
						 * unbalanced parens
						 *  (don't 2nd guess - no error)
						 */
						pungetc();
						USTPUTC(')', out);
					}
				}
				break;
#endif
			case CBQUOTE:   /* '`' */
				PARSEBACKQOLD();
				break;
			case CENDFILE:
				goto endword;           /* exit outer loop */
			case CIGN:
				break;
			default:
				if (varnest == 0)
					goto endword;   /* exit outer loop */
#ifdef CONFIG_ASH_ALIAS
				if (c != PEOA)
#endif
					USTPUTC(c, out);

			}
			c = pgetc_macro();
		}
	}
endword:
#ifdef CONFIG_ASH_MATH_SUPPORT
	if (syntax == ARISYNTAX)
		synerror("Missing '))'");
#endif
	if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
		synerror("Unterminated quoted string");
	if (varnest != 0) {
		startlinno = plinno;
		/* { */
		synerror("Missing '}'");
	}
	USTPUTC('\0', out);
	len = out - (char *)stackblock();
	out = stackblock();
	if (eofmark == NULL) {
		if ((c == '>' || c == '<')
		 && quotef == 0
		 && len <= 2
		 && (*out == '\0' || is_digit(*out))) {
			PARSEREDIR();
			return lasttoken = TREDIR;
		} else {
			pungetc();
		}
	}
	quoteflag = quotef;
	backquotelist = bqlist;
	grabstackblock(len);
	wordtext = out;
	return lasttoken = TWORD;
/* end of readtoken routine */



/*
 * Check to see whether we are at the end of the here document.  When this
 * is called, c is set to the first character of the next input line.  If
 * we are at the end of the here document, this routine sets the c to PEOF.
 */

checkend: {
	if (eofmark) {
#ifdef CONFIG_ASH_ALIAS
		if (c == PEOA) {
			c = pgetc2();
		}
#endif
		if (striptabs) {
			while (c == '\t') {
				c = pgetc2();
			}
		}
		if (c == *eofmark) {
			if (pfgets(line, sizeof line) != NULL) {
				char *p, *q;

				p = line;
				for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
				if (*p == '\n' && *q == '\0') {
					c = PEOF;
					plinno++;
					needprompt = doprompt;
				} else {
					pushstring(line, NULL);
				}
			}
		}
	}
	goto checkend_return;
}


/*
 * Parse a redirection operator.  The variable "out" points to a string
 * specifying the fd to be redirected.  The variable "c" contains the
 * first character of the redirection operator.
 */

parseredir: {
	char fd = *out;
	union node *np;

	np = (union node *)stalloc(sizeof (struct nfile));
	if (c == '>') {
		np->nfile.fd = 1;
		c = pgetc();
		if (c == '>')
			np->type = NAPPEND;
		else if (c == '|')
			np->type = NCLOBBER;
		else if (c == '&')
			np->type = NTOFD;
		else {
			np->type = NTO;
			pungetc();
		}
	} else {        /* c == '<' */
		np->nfile.fd = 0;
		switch (c = pgetc()) {
		case '<':
			if (sizeof (struct nfile) != sizeof (struct nhere)) {
				np = (union node *)stalloc(sizeof (struct nhere));
				np->nfile.fd = 0;
			}
			np->type = NHERE;
			heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
			heredoc->here = np;
			if ((c = pgetc()) == '-') {
				heredoc->striptabs = 1;
			} else {
				heredoc->striptabs = 0;
				pungetc();
			}
			break;

		case '&':
			np->type = NFROMFD;
			break;

		case '>':
			np->type = NFROMTO;
			break;

		default:
			np->type = NFROM;
			pungetc();
			break;
		}
	}
	if (fd != '\0')
		np->nfile.fd = digit_val(fd);
	redirnode = np;
	goto parseredir_return;
}


/*
 * Parse a substitution.  At this point, we have read the dollar sign
 * and nothing else.
 */

parsesub: {
	int subtype;
	int typeloc;
	int flags;
	char *p;
	static const char types[] = "}-+?=";

	c = pgetc();
	if (
		c <= PEOA_OR_PEOF  ||
		(c != '(' && c != '{' && !is_name(c) && !is_special(c))
	) {
		USTPUTC('$', out);
		pungetc();
	} else if (c == '(') {  /* $(command) or $((arith)) */
		if (pgetc() == '(') {
#ifdef CONFIG_ASH_MATH_SUPPORT
			PARSEARITH();
#else
			synerror("We unsupport $((arith))");
#endif
		} else {
			pungetc();
			PARSEBACKQNEW();
		}
	} else {
		USTPUTC(CTLVAR, out);
		typeloc = out - (char *)stackblock();
		USTPUTC(VSNORMAL, out);
		subtype = VSNORMAL;
		if (c == '{') {
			c = pgetc();
			if (c == '#') {
				if ((c = pgetc()) == '}')
					c = '#';
				else
					subtype = VSLENGTH;
			}
			else
				subtype = 0;
		}
		if (c > PEOA_OR_PEOF && is_name(c)) {
			do {
				STPUTC(c, out);
				c = pgetc();
			} while (c > PEOA_OR_PEOF && is_in_name(c));
		} else if (is_digit(c)) {
			do {
				STPUTC(c, out);
				c = pgetc();
			} while (is_digit(c));
		}
		else if (is_special(c)) {
			USTPUTC(c, out);
			c = pgetc();
		}
		else
badsub:                 synerror("Bad substitution");

		STPUTC('=', out);
		flags = 0;
		if (subtype == 0) {
			switch (c) {
			case ':':
				flags = VSNUL;
				c = pgetc();
				/*FALLTHROUGH*/
			default:
				p = strchr(types, c);
				if (p == NULL)
					goto badsub;
				subtype = p - types + VSNORMAL;
				break;
			case '%':
			case '#':
				{
					int cc = c;
					subtype = c == '#' ? VSTRIMLEFT :
							     VSTRIMRIGHT;
					c = pgetc();
					if (c == cc)
						subtype++;
					else
						pungetc();
					break;
				}
			}
		} else {
			pungetc();
		}
		if (dblquote || arinest)
			flags |= VSQUOTE;
		*((char *)stackblock() + typeloc) = subtype | flags;
		if (subtype != VSNORMAL) {
			varnest++;
			if (dblquote || arinest) {
				dqvarnest++;
			}
		}
	}
	goto parsesub_return;
}


/*
 * Called to parse command substitutions.  Newstyle is set if the command
 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
 * list of commands (passed by reference), and savelen is the number of
 * characters on the top of the stack which must be preserved.
 */

parsebackq: {
	struct nodelist **nlpp;
	int savepbq;
	union node *n;
	char *volatile str;
	struct jmploc jmploc;
	struct jmploc *volatile savehandler;
	size_t savelen;
	int saveprompt;
#ifdef __GNUC__
	(void) &saveprompt;
#endif

	savepbq = parsebackquote;
	if (setjmp(jmploc.loc)) {
		if (str)
			ckfree(str);
		parsebackquote = 0;
		handler = savehandler;
		longjmp(handler->loc, 1);
	}
	INTOFF;
	str = NULL;
	savelen = out - (char *)stackblock();
	if (savelen > 0) {
		str = ckmalloc(savelen);
		memcpy(str, stackblock(), savelen);
	}
	savehandler = handler;
	handler = &jmploc;
	INTON;
	if (oldstyle) {
		/* We must read until the closing backquote, giving special
		   treatment to some slashes, and then push the string and
		   reread it as input, interpreting it normally.  */
		char *pout;
		int pc;
		size_t psavelen;
		char *pstr;


		STARTSTACKSTR(pout);
		for (;;) {
			if (needprompt) {
				setprompt(2);
				needprompt = 0;
			}
			switch (pc = pgetc()) {
			case '`':
				goto done;

			case '\\':
				if ((pc = pgetc()) == '\n') {
					plinno++;
					if (doprompt)
						setprompt(2);
					/*
					 * If eating a newline, avoid putting
					 * the newline into the new character
					 * stream (via the STPUTC after the
					 * switch).
					 */
					continue;
				}
				if (pc != '\\' && pc != '`' && pc != '$'
				    && (!dblquote || pc != '"'))
					STPUTC('\\', pout);
				if (pc > PEOA_OR_PEOF) {
					break;
				}
				/* fall through */

			case PEOF:
#ifdef CONFIG_ASH_ALIAS
			case PEOA:
#endif
				startlinno = plinno;
				synerror("EOF in backquote substitution");

			case '\n':
				plinno++;
				needprompt = doprompt;
				break;

			default:
				break;
			}
			STPUTC(pc, pout);
		}
done:
		STPUTC('\0', pout);
		psavelen = pout - (char *)stackblock();
		if (psavelen > 0) {
			pstr = grabstackstr(pout);
			setinputstring(pstr);
		}
	}
	nlpp = &bqlist;
	while (*nlpp)
		nlpp = &(*nlpp)->next;
	*nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
	(*nlpp)->next = NULL;
	parsebackquote = oldstyle;

	if (oldstyle) {
		saveprompt = doprompt;
		doprompt = 0;
	}

	n = list(2);

	if (oldstyle)
		doprompt = saveprompt;
	else {
		if (readtoken() != TRP)
			synexpect(TRP);
	}

	(*nlpp)->n = n;
	if (oldstyle) {
		/*
		 * Start reading from old file again, ignoring any pushed back
		 * tokens left from the backquote parsing
		 */
		popfile();
		tokpushback = 0;
	}
	while (stackblocksize() <= savelen)
		growstackblock();
	STARTSTACKSTR(out);
	if (str) {
		memcpy(out, str, savelen);
		STADJUST(savelen, out);
		INTOFF;
		ckfree(str);
		str = NULL;
		INTON;
	}
	parsebackquote = savepbq;
	handler = savehandler;
	if (arinest || dblquote)
		USTPUTC(CTLBACKQ | CTLQUOTE, out);
	else
		USTPUTC(CTLBACKQ, out);
	if (oldstyle)
		goto parsebackq_oldreturn;
	else
		goto parsebackq_newreturn;
}

#ifdef CONFIG_ASH_MATH_SUPPORT
/*
 * Parse an arithmetic expansion (indicate start of one and set state)
 */
parsearith: {

	if (++arinest == 1) {
		prevsyntax = syntax;
		syntax = ARISYNTAX;
		USTPUTC(CTLARI, out);
		if (dblquote)
			USTPUTC('"',out);
		else
			USTPUTC(' ',out);
	} else {
		/*
		 * we collapse embedded arithmetic expansion to
		 * parenthesis, which should be equivalent
		 */
		USTPUTC('(', out);
	}
	goto parsearith_return;
}
#endif

} /* end of readtoken */



/*
 * Returns true if the text contains nothing to expand (no dollar signs
 * or backquotes).
 */

static int
noexpand(char *text)
{
	char *p;
	char c;

	p = text;
	while ((c = *p++) != '\0') {
		if (c == CTLQUOTEMARK)
			continue;
		if (c == CTLESC)
			p++;
		else if (SIT(c, BASESYNTAX) == CCTL)
			return 0;
	}
	return 1;
}


/*
 * Return of a legal variable name (a letter or underscore followed by zero or
 * more letters, underscores, and digits).
 */

char *
endofname(const char *name)
	{
	char *p;

	p = (char *) name;
	if (! is_name(*p))
		return p;
	while (*++p) {
		if (! is_in_name(*p))
			break;
	}
	return p;
}


/*
 * Called when an unexpected token is read during the parse.  The argument
 * is the token that is expected, or -1 if more than one type of token can
 * occur at this point.
 */

static void synexpect(int token)
{
	char msg[64];
	int l;

	l = sprintf(msg, "%s unexpected", tokname(lasttoken));
	if (token >= 0)
		sprintf(msg + l, " (expecting %s)", tokname(token));
	synerror(msg);
	/* NOTREACHED */
}

static void
synerror(const char *msg)
{
	error("Syntax error: %s", msg);
	/* NOTREACHED */
}


/*
 * called by editline -- any expansions to the prompt
 *    should be added here.
 */

static void setprompt(int whichprompt)
{
	const char *prompt;

	switch (whichprompt) {
	case 1:
		prompt = ps1val();
		break;
	case 2:
		prompt = ps2val();
		break;
	default:                        /* 0 */
		prompt = nullstr;
	}
	putprompt(prompt);
}


static const char *const *findkwd(const char *s)
{
	return bsearch(s, tokname_array + KWDOFFSET,
				   (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
				   sizeof(const char *), pstrcmp);
}

/*      $NetBSD: redir.c,v 1.27 2002/11/24 22:35:42 christos Exp $      */

/*
 * Code for dealing with input/output redirection.
 */

#define EMPTY -2                /* marks an unused slot in redirtab */
#ifndef PIPE_BUF
# define PIPESIZE 4096          /* amount of buffering in a pipe */
#else
# define PIPESIZE PIPE_BUF
#endif

/*
 * Open a file in noclobber mode.
 * The code was copied from bash.
 */
static inline int
noclobberopen(const char *fname)
{
	int r, fd;
	struct stat finfo, finfo2;

	/*
	 * If the file exists and is a regular file, return an error
	 * immediately.
	 */
	r = stat(fname, &finfo);
	if (r == 0 && S_ISREG(finfo.st_mode)) {
		errno = EEXIST;
		return -1;
	}

	/*
	 * If the file was not present (r != 0), make sure we open it
	 * exclusively so that if it is created before we open it, our open
	 * will fail.  Make sure that we do not truncate an existing file.
	 * Note that we don't turn on O_EXCL unless the stat failed -- if the
	 * file was not a regular file, we leave O_EXCL off.
	 */
	if (r != 0)
		return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
	fd = open(fname, O_WRONLY|O_CREAT, 0666);

	/* If the open failed, return the file descriptor right away. */
	if (fd < 0)
		return fd;

	/*
	 * OK, the open succeeded, but the file may have been changed from a
	 * non-regular file to a regular file between the stat and the open.
	 * We are assuming that the O_EXCL open handles the case where FILENAME
	 * did not exist and is symlinked to an existing file between the stat
	 * and open.
	 */

	/*
	 * If we can open it and fstat the file descriptor, and neither check
	 * revealed that it was a regular file, and the file has not been
	 * replaced, return the file descriptor.
	 */
	 if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
	     finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
		return fd;

	/* The file has been replaced.  badness. */
	close(fd);
	errno = EEXIST;
	return -1;
}

/*
 * Handle here documents.  Normally we fork off a process to write the
 * data to a pipe.  If the document is short, we can stuff the data in
 * the pipe without forking.
 */

static inline int
openhere(union node *redir)
{
	int pip[2];
	size_t len = 0;

	if (pipe(pip) < 0)
		error("Pipe call failed");
	if (redir->type == NHERE) {
		len = strlen(redir->nhere.doc->narg.text);
		if (len <= PIPESIZE) {
			xwrite(pip[1], redir->nhere.doc->narg.text, len);
			goto out;
		}
	}
	if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
		close(pip[0]);
		signal(SIGINT, SIG_IGN);
		signal(SIGQUIT, SIG_IGN);
		signal(SIGHUP, SIG_IGN);
#ifdef SIGTSTP
		signal(SIGTSTP, SIG_IGN);
#endif
		signal(SIGPIPE, SIG_DFL);
		if (redir->type == NHERE)
			xwrite(pip[1], redir->nhere.doc->narg.text, len);
		else
			expandhere(redir->nhere.doc, pip[1]);
		_exit(0);
	}
out:
	close(pip[1]);
	return pip[0];
}

static int
openredirect(union node *redir)
{
	char *fname;
	int f;

	switch (redir->nfile.type) {
	case NFROM:
		fname = redir->nfile.expfname;
		if ((f = open(fname, O_RDONLY)) < 0)
			goto eopen;
		break;
	case NFROMTO:
		fname = redir->nfile.expfname;
		if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
			goto ecreate;
		break;
	case NTO:
		/* Take care of noclobber mode. */
		if (Cflag) {
			fname = redir->nfile.expfname;
			if ((f = noclobberopen(fname)) < 0)
				goto ecreate;
			break;
		}
		/* FALLTHROUGH */
	case NCLOBBER:
		fname = redir->nfile.expfname;
		if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
			goto ecreate;
		break;
	case NAPPEND:
		fname = redir->nfile.expfname;
		if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
			goto ecreate;
		break;
	default:
#ifdef DEBUG
		abort();
#endif
		/* Fall through to eliminate warning. */
	case NTOFD:
	case NFROMFD:
		f = -1;
		break;
	case NHERE:
	case NXHERE:
		f = openhere(redir);
		break;
	}

	return f;
ecreate:
	error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
eopen:
	error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
}

static inline void
dupredirect(union node *redir, int f)
{
	int fd = redir->nfile.fd;

	if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
		if (redir->ndup.dupfd >= 0) {   /* if not ">&-" */
				copyfd(redir->ndup.dupfd, fd);
		}
		return;
	}

	if (f != fd) {
		copyfd(f, fd);
		close(f);
	}
	return;
}

/*
 * Process a list of redirection commands.  If the REDIR_PUSH flag is set,
 * old file descriptors are stashed away so that the redirection can be
 * undone by calling popredir.  If the REDIR_BACKQ flag is set, then the
 * standard output, and the standard error if it becomes a duplicate of
 * stdout, is saved in memory.
 */

static void
redirect(union node *redir, int flags)
{
	union node *n;
	struct redirtab *sv;
	int i;
	int fd;
	int newfd;
	int *p;
	nullredirs++;
	if (!redir) {
		return;
	}
	sv = NULL;
	INTOFF;
	if (flags & REDIR_PUSH) {
		struct redirtab *q;
		q = ckmalloc(sizeof (struct redirtab));
		q->next = redirlist;
		redirlist = q;
		q->nullredirs = nullredirs - 1;
		for (i = 0 ; i < 10 ; i++)
			q->renamed[i] = EMPTY;
		nullredirs = 0;
		sv = q;
	}
	n = redir;
	do {
		fd = n->nfile.fd;
		if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
		    n->ndup.dupfd == fd)
			continue; /* redirect from/to same file descriptor */

		newfd = openredirect(n);
		if (fd == newfd)
			continue;
		if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
			i = fcntl(fd, F_DUPFD, 10);

			if (i == -1) {
				i = errno;
				if (i != EBADF) {
					close(newfd);
					errno = i;
					error("%d: %m", fd);
					/* NOTREACHED */
				}
			} else {
				*p = i;
				close(fd);
			}
		} else {
			close(fd);
		}
		dupredirect(n, newfd);
	} while ((n = n->nfile.next));
	INTON;
}


/*
 * Undo the effects of the last redirection.
 */

void
popredir(int drop)
{
	struct redirtab *rp;
	int i;

	if (--nullredirs >= 0)
		return;
	INTOFF;
	rp = redirlist;
	for (i = 0 ; i < 10 ; i++) {
		if (rp->renamed[i] != EMPTY) {
			if (!drop) {
				close(i);
				copyfd(rp->renamed[i], i);
			}
			close(rp->renamed[i]);
		}
	}
	redirlist = rp->next;
	nullredirs = rp->nullredirs;
	ckfree(rp);
	INTON;
}

/*
 * Undo all redirections.  Called on error or interrupt.
 */

/*
 * Discard all saved file descriptors.
 */

void
clearredir(int drop)
{
	for (;;) {
		nullredirs = 0;
		if (!redirlist)
			break;
		popredir(drop);
	}
}


/*
 * Copy a file descriptor to be >= to.  Returns -1
 * if the source file descriptor is closed, EMPTY if there are no unused
 * file descriptors left.
 */

int
copyfd(int from, int to)
{
	int newfd;

	newfd = fcntl(from, F_DUPFD, to);
	if (newfd < 0) {
		if (errno == EMFILE)
			return EMPTY;
		else
			error("%d: %m", from);
	}
	return newfd;
}


int
redirectsafe(union node *redir, int flags)
{
	int err;
	volatile int saveint;
	struct jmploc *volatile savehandler = handler;
	struct jmploc jmploc;

	SAVEINT(saveint);
	if (!(err = setjmp(jmploc.loc) * 2)) {
		handler = &jmploc;
		redirect(redir, flags);
	}
	handler = savehandler;
	if (err && exception != EXERROR)
		longjmp(handler->loc, 1);
	RESTOREINT(saveint);
	return err;
}

/*      $NetBSD: show.c,v 1.24 2003/01/22 20:36:04 dsl Exp $    */

#ifdef DEBUG
static void shtree(union node *, int, char *, FILE*);
static void shcmd(union node *, FILE *);
static void sharg(union node *, FILE *);
static void indent(int, char *, FILE *);
static void trstring(char *);


void
showtree(union node *n)
{
	trputs("showtree called\n");
	shtree(n, 1, NULL, stdout);
}


static void
shtree(union node *n, int ind, char *pfx, FILE *fp)
{
	struct nodelist *lp;
	const char *s;

	if (n == NULL)
		return;

	indent(ind, pfx, fp);
	switch(n->type) {
	case NSEMI:
		s = "; ";
		goto binop;
	case NAND:
		s = " && ";
		goto binop;
	case NOR:
		s = " || ";
binop:
		shtree(n->nbinary.ch1, ind, NULL, fp);
	   /*    if (ind < 0) */
			fputs(s, fp);
		shtree(n->nbinary.ch2, ind, NULL, fp);
		break;
	case NCMD:
		shcmd(n, fp);
		if (ind >= 0)
			putc('\n', fp);
		break;
	case NPIPE:
		for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
			shcmd(lp->n, fp);
			if (lp->next)
				fputs(" | ", fp);
		}
		if (n->npipe.backgnd)
			fputs(" &", fp);
		if (ind >= 0)
			putc('\n', fp);
		break;
	default:
		fprintf(fp, "<node type %d>", n->type);
		if (ind >= 0)
			putc('\n', fp);
		break;
	}
}


static void
shcmd(union node *cmd, FILE *fp)
{
	union node *np;
	int first;
	const char *s;
	int dftfd;

	first = 1;
	for (np = cmd->ncmd.args ; np ; np = np->narg.next) {
		if (! first)
			putchar(' ');
		sharg(np, fp);
		first = 0;
	}
	for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) {
		if (! first)
			putchar(' ');
		switch (np->nfile.type) {
			case NTO:       s = ">";  dftfd = 1; break;
			case NCLOBBER:  s = ">|"; dftfd = 1; break;
			case NAPPEND:   s = ">>"; dftfd = 1; break;
			case NTOFD:     s = ">&"; dftfd = 1; break;
			case NFROM:     s = "<";  dftfd = 0; break;
			case NFROMFD:   s = "<&"; dftfd = 0; break;
			case NFROMTO:   s = "<>"; dftfd = 0; break;
			default:        s = "*error*"; dftfd = 0; break;
		}
		if (np->nfile.fd != dftfd)
			fprintf(fp, "%d", np->nfile.fd);
		fputs(s, fp);
		if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
			fprintf(fp, "%d", np->ndup.dupfd);
		} else {
			sharg(np->nfile.fname, fp);
		}
		first = 0;
	}
}



static void
sharg(union node *arg, FILE *fp)
{
	char *p;
	struct nodelist *bqlist;
	int subtype;

	if (arg->type != NARG) {
		out1fmt("<node type %d>\n", arg->type);
		abort();
	}
	bqlist = arg->narg.backquote;
	for (p = arg->narg.text ; *p ; p++) {
		switch (*p) {
		case CTLESC:
			putc(*++p, fp);
			break;
		case CTLVAR:
			putc('$', fp);
			putc('{', fp);
			subtype = *++p;
			if (subtype == VSLENGTH)
				putc('#', fp);

			while (*p != '=')
				putc(*p++, fp);

			if (subtype & VSNUL)
				putc(':', fp);

			switch (subtype & VSTYPE) {
			case VSNORMAL:
				putc('}', fp);
				break;
			case VSMINUS:
				putc('-', fp);
				break;
			case VSPLUS:
				putc('+', fp);
				break;
			case VSQUESTION:
				putc('?', fp);
				break;
			case VSASSIGN:
				putc('=', fp);
				break;
			case VSTRIMLEFT:
				putc('#', fp);
				break;
			case VSTRIMLEFTMAX:
				putc('#', fp);
				putc('#', fp);
				break;
			case VSTRIMRIGHT:
				putc('%', fp);
				break;
			case VSTRIMRIGHTMAX:
				putc('%', fp);
				putc('%', fp);
				break;
			case VSLENGTH:
				break;
			default:
				out1fmt("<subtype %d>", subtype);
			}
			break;
		case CTLENDVAR:
		     putc('}', fp);
		     break;
		case CTLBACKQ:
		case CTLBACKQ|CTLQUOTE:
			putc('$', fp);
			putc('(', fp);
			shtree(bqlist->n, -1, NULL, fp);
			putc(')', fp);
			break;
		default:
			putc(*p, fp);
			break;
		}
	}
}


static void
indent(int amount, char *pfx, FILE *fp)
{
	int i;

	for (i = 0 ; i < amount ; i++) {
		if (pfx && i == amount - 1)
			fputs(pfx, fp);
		putc('\t', fp);
	}
}



/*
 * Debugging stuff.
 */


FILE *tracefile;


void
trputc(int c)
{
	if (debug != 1)
		return;
	putc(c, tracefile);
}

void
trace(const char *fmt, ...)
{
	va_list va;

	if (debug != 1)
		return;
	va_start(va, fmt);
	(void) vfprintf(tracefile, fmt, va);
	va_end(va);
}

void
tracev(const char *fmt, va_list va)
{
	if (debug != 1)
		return;
	(void) vfprintf(tracefile, fmt, va);
}


void
trputs(const char *s)
{
	if (debug != 1)
		return;
	fputs(s, tracefile);
}


static void
trstring(char *s)
{
	char *p;
	char c;

	if (debug != 1)
		return;
	putc('"', tracefile);
	for (p = s ; *p ; p++) {
		switch (*p) {
		case '\n':  c = 'n';  goto backslash;
		case '\t':  c = 't';  goto backslash;
		case '\r':  c = 'r';  goto backslash;
		case '"':  c = '"';  goto backslash;
		case '\\':  c = '\\';  goto backslash;
		case CTLESC:  c = 'e';  goto backslash;
		case CTLVAR:  c = 'v';  goto backslash;
		case CTLVAR+CTLQUOTE:  c = 'V';  goto backslash;
		case CTLBACKQ:  c = 'q';  goto backslash;
		case CTLBACKQ+CTLQUOTE:  c = 'Q';  goto backslash;
backslash:        putc('\\', tracefile);
			putc(c, tracefile);
			break;
		default:
			if (*p >= ' ' && *p <= '~')
				putc(*p, tracefile);
			else {
				putc('\\', tracefile);
				putc(*p >> 6 & 03, tracefile);
				putc(*p >> 3 & 07, tracefile);
				putc(*p & 07, tracefile);
			}
			break;
		}
	}
	putc('"', tracefile);
}


void
trargs(char **ap)
{
	if (debug != 1)
		return;
	while (*ap) {
		trstring(*ap++);
		if (*ap)
			putc(' ', tracefile);
		else
			putc('\n', tracefile);
	}
}


void
opentrace(void)
{
	char s[100];
#ifdef O_APPEND
	int flags;
#endif

	if (debug != 1) {
		if (tracefile)
			fflush(tracefile);
		/* leave open because libedit might be using it */
		return;
	}
	scopy("./trace", s);
	if (tracefile) {
		if (!freopen(s, "a", tracefile)) {
			fprintf(stderr, "Can't re-open %s\n", s);
			debug = 0;
			return;
		}
	} else {
		if ((tracefile = fopen(s, "a")) == NULL) {
			fprintf(stderr, "Can't open %s\n", s);
			debug = 0;
			return;
		}
	}
#ifdef O_APPEND
	if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0)
		fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
#endif
	setlinebuf(tracefile);
	fputs("\nTracing started.\n", tracefile);
}
#endif /* DEBUG */


/*      $NetBSD: trap.c,v 1.28 2002/11/24 22:35:43 christos Exp $       */

/*
 * Sigmode records the current value of the signal handlers for the various
 * modes.  A value of zero means that the current handler is not known.
 * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
 */

#define S_DFL 1                 /* default signal handling (SIG_DFL) */
#define S_CATCH 2               /* signal is caught */
#define S_IGN 3                 /* signal is ignored (SIG_IGN) */
#define S_HARD_IGN 4            /* signal is ignored permenantly */
#define S_RESET 5               /* temporary - to reset a hard ignored sig */



/*
 * The trap builtin.
 */

int
trapcmd(int argc, char **argv)
{
	char *action;
	char **ap;
	int signo;

	nextopt(nullstr);
	ap = argptr;
	if (!*ap) {
		for (signo = 0 ; signo < NSIG ; signo++) {
			if (trap[signo] != NULL) {
				const char *sn;

				sn = u_signal_names(0, &signo, 0);
				if (sn == NULL)
					sn = "???";
				out1fmt("trap -- %s %s\n",
					single_quote(trap[signo]), sn);
			}
		}
		return 0;
	}
	if (!ap[1])
		action = NULL;
	else
		action = *ap++;
	while (*ap) {
		if ((signo = decode_signal(*ap, 0)) < 0)
			error("%s: bad trap", *ap);
		INTOFF;
		if (action) {
			if (action[0] == '-' && action[1] == '\0')
				action = NULL;
			else
				action = savestr(action);
		}
		if (trap[signo])
			ckfree(trap[signo]);
		trap[signo] = action;
		if (signo != 0)
			setsignal(signo);
		INTON;
		ap++;
	}
	return 0;
}


/*
 * Clear traps on a fork.
 */

void
clear_traps(void)
{
	char **tp;

	for (tp = trap ; tp < &trap[NSIG] ; tp++) {
		if (*tp && **tp) {      /* trap not NULL or SIG_IGN */
			INTOFF;
			ckfree(*tp);
			*tp = NULL;
			if (tp != &trap[0])
				setsignal(tp - trap);
			INTON;
		}
	}
}


/*
 * Set the signal handler for the specified signal.  The routine figures
 * out what it should be set to.
 */

void
setsignal(int signo)
{
	int action;
	char *t, tsig;
	struct sigaction act;

	if ((t = trap[signo]) == NULL)
		action = S_DFL;
	else if (*t != '\0')
		action = S_CATCH;
	else
		action = S_IGN;
	if (rootshell && action == S_DFL) {
		switch (signo) {
		case SIGINT:
			if (iflag || minusc || sflag == 0)
				action = S_CATCH;
			break;
		case SIGQUIT:
#ifdef DEBUG
			if (debug)
				break;
#endif
			/* FALLTHROUGH */
		case SIGTERM:
			if (iflag)
				action = S_IGN;
			break;
#if JOBS
		case SIGTSTP:
		case SIGTTOU:
			if (mflag)
				action = S_IGN;
			break;
#endif
		}
	}

	t = &sigmode[signo - 1];
	tsig = *t;
	if (tsig == 0) {
		/*
		 * current setting unknown
		 */
		if (sigaction(signo, 0, &act) == -1) {
			/*
			 * Pretend it worked; maybe we should give a warning
			 * here, but other shells don't. We don't alter
			 * sigmode, so that we retry every time.
			 */
			return;
		}
		if (act.sa_handler == SIG_IGN) {
			if (mflag && (signo == SIGTSTP ||
			     signo == SIGTTIN || signo == SIGTTOU)) {
				tsig = S_IGN;   /* don't hard ignore these */
			} else
				tsig = S_HARD_IGN;
		} else {
			tsig = S_RESET; /* force to be set */
		}
	}
	if (tsig == S_HARD_IGN || tsig == action)
		return;
	switch (action) {
	case S_CATCH:
		act.sa_handler = onsig;
		break;
	case S_IGN:
		act.sa_handler = SIG_IGN;
		break;
	default:
		act.sa_handler = SIG_DFL;
	}
	*t = action;
	act.sa_flags = 0;
	sigfillset(&act.sa_mask);
	sigaction(signo, &act, 0);
}

/*
 * Ignore a signal.
 */

void
ignoresig(int signo)
{
	if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
		signal(signo, SIG_IGN);
	}
	sigmode[signo - 1] = S_HARD_IGN;
}


/*
 * Signal handler.
 */

void
onsig(int signo)
{
	gotsig[signo - 1] = 1;
	pendingsigs = signo;

	if (exsig || (signo == SIGINT && !trap[SIGINT])) {
		if (!suppressint)
			onint();
		intpending = 1;
	}
}


/*
 * Called to execute a trap.  Perhaps we should avoid entering new trap
 * handlers while we are executing a trap handler.
 */

void
dotrap(void)
{
	char *p;
	char *q;
	int savestatus;

	savestatus = exitstatus;
	q = gotsig;
	while (pendingsigs = 0, barrier(), (p = memchr(q, 1, NSIG - 1))) {
		*p = 0;
		p = trap[p - q + 1];
		if (!p)
			continue;
		evalstring(p, 0);
		exitstatus = savestatus;
	}
}


/*
 * Controls whether the shell is interactive or not.
 */

void
setinteractive(int on)
{
	static int is_interactive;

	if (++on == is_interactive)
		return;
	is_interactive = on;
	setsignal(SIGINT);
	setsignal(SIGQUIT);
	setsignal(SIGTERM);
#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
		if(is_interactive > 1) {
			/* Looks like they want an interactive shell */
			static int do_banner;

				if(!do_banner) {
					out1fmt(
			"\n\n" BB_BANNER " Built-in shell (ash)\n"
			"Enter 'help' for a list of built-in commands.\n\n");
					do_banner++;
				}
		}
#endif
}


#ifndef CONFIG_FEATURE_SH_EXTRA_QUIET
/*** List the available builtins ***/

static int helpcmd(int argc, char **argv)
{
	int col, i;

	out1fmt("\nBuilt-in commands:\n-------------------\n");
	for (col = 0, i = 0; i < NUMBUILTINS; i++) {
		col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
					  builtincmd[i].name + 1);
		if (col > 60) {
			out1fmt("\n");
			col = 0;
		}
	}
#ifdef CONFIG_FEATURE_SH_STANDALONE_SHELL
	{
		extern const struct BB_applet applets[];
		extern const size_t NUM_APPLETS;

		for (i = 0; i < NUM_APPLETS; i++) {

			col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
			if (col > 60) {
				out1fmt("\n");
				col = 0;
			}
		}
	}
#endif
	out1fmt("\n\n");
	return EXIT_SUCCESS;
}
#endif /* CONFIG_FEATURE_SH_EXTRA_QUIET */

/*
 * Called to exit the shell.
 */

void
exitshell(void)
{
	struct jmploc loc;
	char *p;
	int status;

	status = exitstatus;
	TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
	if (setjmp(loc.loc)) {
		goto out;
	}
	handler = &loc;
	if ((p = trap[0]) != NULL && *p != '\0') {
		trap[0] = NULL;
		evalstring(p, 0);
	}
	flushall();
#ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
	if (iflag && rootshell) {
		const char *hp = lookupvar("HISTFILE");

		if(hp != NULL )
			save_history ( hp );
	}
#endif
out:
	_exit(status);
	/* NOTREACHED */
}

static int decode_signal(const char *string, int minsig)
{
	int signo;
	const char *name = u_signal_names(string, &signo, minsig);

	return name ? signo : -1;
}

/*      $NetBSD: var.c,v 1.32 2003/01/22 20:36:04 dsl Exp $     */

static struct var *vartab[VTABSIZE];

static int vpcmp(const void *, const void *);
static struct var **findvar(struct var **, const char *);

/*
 * Initialize the varable symbol tables and import the environment
 */


#ifdef CONFIG_ASH_GETOPTS
/*
 * Safe version of setvar, returns 1 on success 0 on failure.
 */

int
setvarsafe(const char *name, const char *val, int flags)
{
	int err;
	volatile int saveint;
	struct jmploc *volatile savehandler = handler;
	struct jmploc jmploc;

	SAVEINT(saveint);
	if (setjmp(jmploc.loc))
		err = 1;
	else {
		handler = &jmploc;
		setvar(name, val, flags);
		err = 0;
	}
	handler = savehandler;
	RESTOREINT(saveint);
	return err;
}
#endif

/*
 * Set the value of a variable.  The flags argument is ored with the
 * flags of the variable.  If val is NULL, the variable is unset.
 */

static void
setvar(const char *name, const char *val, int flags)
{
	char *p, *q;
	size_t namelen;
	char *nameeq;
	size_t vallen;

	q = endofname(name);
	p = strchrnul(q, '=');
	namelen = p - name;
	if (!namelen || p != q)
		error("%.*s: bad variable name", namelen, name);
	vallen = 0;
	if (val == NULL) {
		flags |= VUNSET;
	} else {
		vallen = strlen(val);
	}
	INTOFF;
	p = mempcpy(nameeq = ckmalloc(namelen + vallen + 2), name, namelen);
	*p++ = '\0';
	if (vallen) {
		p[-1] = '=';
		p = mempcpy(p, val, vallen);
	}
	*p = '\0';
	setvareq(nameeq, flags | VNOSAVE);
	INTON;
}


/*
 * Same as setvar except that the variable and value are passed in
 * the first argument as name=value.  Since the first argument will
 * be actually stored in the table, it should not be a string that
 * will go away.
 * Called with interrupts off.
 */

void
setvareq(char *s, int flags)
{
	struct var *vp, **vpp;

	vpp = hashvar(s);
	flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
	vp = *findvar(vpp, s);
	if (vp) {
		if (vp->flags & VREADONLY) {
			if (flags & VNOSAVE)
				free(s);
			error("%.*s: is read only", strchrnul(s, '=') - s, s);
		}

		if (flags & VNOSET)
			return;

		if (vp->func && (flags & VNOFUNC) == 0)
			(*vp->func)(strchrnul(s, '=') + 1);

		if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
			ckfree(vp->text);

		flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
	} else {
		if (flags & VNOSET)
			return;
		/* not found */
		vp = ckmalloc(sizeof (*vp));
		vp->next = *vpp;
		vp->func = NULL;
		*vpp = vp;
	}
	if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
		s = savestr(s);
	vp->text = s;
	vp->flags = flags;
}


/*
 * Process a linked list of variable assignments.
 */

static void
listsetvar(struct strlist *list_set_var, int flags)
{
	struct strlist *lp = list_set_var;

	if (!lp)
		return;
	INTOFF;
	do {
		setvareq(lp->text, flags);
	} while ((lp = lp->next));
	INTON;
}


/*
 * Find the value of a variable.  Returns NULL if not set.
 */

static char *
lookupvar(const char *name)
{
	struct var *v;

	if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) {
		return strchrnul(v->text, '=') + 1;
	}
	return NULL;
}


/*
 * Search the environment of a builtin command.
 */

static char *
bltinlookup(const char *name)
{
	struct strlist *sp;

	for (sp = cmdenviron ; sp ; sp = sp->next) {
		if (varequal(sp->text, name))
			return strchrnul(sp->text, '=') + 1;
	}
	return lookupvar(name);
}


/*
 * Generate a list of variables satisfying the given conditions.
 */

static char **
listvars(int on, int off, char ***end)
{
	struct var **vpp;
	struct var *vp;
	char **ep;
	int mask;

	STARTSTACKSTR(ep);
	vpp = vartab;
	mask = on | off;
	do {
		for (vp = *vpp ; vp ; vp = vp->next)
			if ((vp->flags & mask) == on) {
				if (ep == stackstrend())
					ep = growstackstr();
				*ep++ = (char *) vp->text;
			}
	} while (++vpp < vartab + VTABSIZE);
	if (ep == stackstrend())
		ep = growstackstr();
	if (end)
		*end = ep;
	*ep++ = NULL;
	return grabstackstr(ep);
}


/*
 * POSIX requires that 'set' (but not export or readonly) output the
 * variables in lexicographic order - by the locale's collating order (sigh).
 * Maybe we could keep them in an ordered balanced binary tree
 * instead of hashed lists.
 * For now just roll 'em through qsort for printing...
 */

static int
showvars(const char *sep_prefix, int on, int off)
{
	const char *sep;
	char **ep, **epend;

	ep = listvars(on, off, &epend);
	qsort(ep, epend - ep, sizeof(char *), vpcmp);

	sep = *sep_prefix ? spcstr : sep_prefix;

	for (; ep < epend; ep++) {
		const char *p;
		const char *q;

		p = strchrnul(*ep, '=');
		q = nullstr;
		if (*p)
			q = single_quote(++p);

		out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
	}

	return 0;
}



/*
 * The export and readonly commands.
 */

static int
exportcmd(int argc, char **argv)
{
	struct var *vp;
	char *name;
	const char *p;
	char **aptr;
	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
	int notp;

	notp = nextopt("p") - 'p';
	if (notp && ((name = *(aptr = argptr)))) {
		do {
			if ((p = strchr(name, '=')) != NULL) {
				p++;
			} else {
				if ((vp = *findvar(hashvar(name), name))) {
					vp->flags |= flag;
					continue;
				}
			}
			setvar(name, p, flag);
		} while ((name = *++aptr) != NULL);
	} else {
		showvars(argv[0], flag, 0);
	}
	return 0;
}


/*
 * Make a variable a local variable.  When a variable is made local, it's
 * value and flags are saved in a localvar structure.  The saved values
 * will be restored when the shell function returns.  We handle the name
 * "-" as a special case.
 */

static inline void
mklocal(char *name)
{
	struct localvar *lvp;
	struct var **vpp;
	struct var *vp;

	INTOFF;
	lvp = ckmalloc(sizeof (struct localvar));
	if (name[0] == '-' && name[1] == '\0') {
		char *p;
		p = ckmalloc(sizeof(optlist));
		lvp->text = memcpy(p, optlist, sizeof(optlist));
		vp = NULL;
	} else {
		char *eq;

		vpp = hashvar(name);
		vp = *findvar(vpp, name);
		eq = strchr(name, '=');
		if (vp == NULL) {
			if (eq)
				setvareq(name, VSTRFIXED);
			else
				setvar(name, NULL, VSTRFIXED);
			vp = *vpp;      /* the new variable */
			lvp->flags = VUNSET;
		} else {
			lvp->text = vp->text;
			lvp->flags = vp->flags;
			vp->flags |= VSTRFIXED|VTEXTFIXED;
			if (eq)
				setvareq(name, 0);
		}
	}
	lvp->vp = vp;
	lvp->next = localvars;
	localvars = lvp;
	INTON;
}

/*
 * The "local" command.
 */

static int
localcmd(int argc, char **argv)
{
	char *name;

	argv = argptr;
	while ((name = *argv++) != NULL) {
		mklocal(name);
	}
	return 0;
}



/*
 * Called after a function returns.
 * Interrupts must be off.
 */

static void
poplocalvars(void)
{
	struct localvar *lvp;
	struct var *vp;

	while ((lvp = localvars) != NULL) {
		localvars = lvp->next;
		vp = lvp->vp;
		TRACE(("poplocalvar %s", vp ? vp->text : "-"));
		if (vp == NULL) {       /* $- saved */
			memcpy(optlist, lvp->text, sizeof(optlist));
			ckfree(lvp->text);
			optschanged();
		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
			unsetvar(vp->text);
		} else {
			if (vp->func)
				(*vp->func)(strchrnul(lvp->text, '=') + 1);
			if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
				ckfree(vp->text);
			vp->flags = lvp->flags;
			vp->text = lvp->text;
		}
		ckfree(lvp);
	}
}


/*
 * The unset builtin command.  We unset the function before we unset the
 * variable to allow a function to be unset when there is a readonly variable
 * with the same name.
 */

int
unsetcmd(int argc, char **argv)
{
	char **ap;
	int i;
	int flag = 0;
	int ret = 0;

	while ((i = nextopt("vf")) != '\0') {
		flag = i;
	}

	for (ap = argptr; *ap ; ap++) {
		if (flag != 'f') {
			i = unsetvar(*ap);
			ret |= i;
			if (!(i & 2))
				continue;
		}
		if (flag != 'v')
			unsetfunc(*ap);
	}
	return ret & 1;
}


/*
 * Unset the specified variable.
 */

int
unsetvar(const char *s)
{
	struct var **vpp;
	struct var *vp;
	int retval;

	vpp = findvar(hashvar(s), s);
	vp = *vpp;
	retval = 2;
	if (vp) {
		int flags = vp->flags;

		retval = 1;
		if (flags & VREADONLY)
			goto out;
		if (flags & VUNSET)
			goto ok;
		if ((flags & VSTRFIXED) == 0) {
			INTOFF;
			if ((flags & (VTEXTFIXED|VSTACK)) == 0)
				ckfree(vp->text);
			*vpp = vp->next;
			ckfree(vp);
			INTON;
		} else {
			setvar(s, 0, 0);
			vp->flags &= ~VEXPORT;
		}
ok:
		retval = 0;
	}

out:
	return retval;
}



/*
 * Find the appropriate entry in the hash table from the name.
 */

static struct var **
hashvar(const char *p)
{
	unsigned int hashval;

	hashval = ((unsigned char) *p) << 4;
	while (*p && *p != '=')
		hashval += (unsigned char) *p++;
	return &vartab[hashval % VTABSIZE];
}



/*
 * Compares two strings up to the first = or '\0'.  The first
 * string must be terminated by '='; the second may be terminated by
 * either '=' or '\0'.
 */

int
varcmp(const char *p, const char *q)
{
	int c, d;

	while ((c = *p) == (d = *q)) {
		if (!c || c == '=')
			goto out;
		p++;
		q++;
	}
	if (c == '=')
		c = 0;
	if (d == '=')
		d = 0;
out:
	return c - d;
}

static int
vpcmp(const void *a, const void *b)
{
	return varcmp(*(const char **)a, *(const char **)b);
}

static struct var **
findvar(struct var **vpp, const char *name)
{
	for (; *vpp; vpp = &(*vpp)->next) {
		if (varequal((*vpp)->text, name)) {
			break;
		}
	}
	return vpp;
}
/*      $NetBSD: setmode.c,v 1.29 2003/01/15 23:58:03 kleink Exp $      */

/*
 * Copyright (c) 1999 Herbert Xu <herbert@debian.org>
 * This code for the times builtin.
 */

#include <sys/times.h>

int timescmd(int ac, char **av) {
	struct tms buf;
	long int clk_tck = sysconf(_SC_CLK_TCK);

	times(&buf);
	out1fmt("%dm%fs %dm%fs\n%dm%fs %dm%fs\n",
	       (int) (buf.tms_utime / clk_tck / 60),
	       ((double) buf.tms_utime) / clk_tck,
	       (int) (buf.tms_stime / clk_tck / 60),
	       ((double) buf.tms_stime) / clk_tck,
	       (int) (buf.tms_cutime / clk_tck / 60),
	       ((double) buf.tms_cutime) / clk_tck,
	       (int) (buf.tms_cstime / clk_tck / 60),
	       ((double) buf.tms_cstime) / clk_tck);
	return 0;
}

#ifdef CONFIG_ASH_MATH_SUPPORT
static int
dash_arith(const char *s)
{
	long result = 0;
	int errcode = 0;

	INTOFF;
	result = arith(s, &errcode);
	if (errcode < 0) {
		if (errcode == -2)
			error("divide by zero");
		else
			synerror(s);
	}
	INTON;

	return (result);
}


/*
 *  The exp(1) builtin.
 */
static int
expcmd(int argc, char **argv)
{
	const char *p;
	char *concat;
	char **ap;
	long i;

	if (argc > 1) {
		p = argv[1];
		if (argc > 2) {
			/*
			 * concatenate arguments
			 */
			STARTSTACKSTR(concat);
			ap = argv + 2;
			for (;;) {
				while (*p)
					STPUTC(*p++, concat);
				if ((p = *ap++) == NULL)
					break;
				STPUTC(' ', concat);
			}
			STPUTC('\0', concat);
			p = grabstackstr(concat);
		}
	} else
		p = nullstr;

	i = dash_arith(p);

	out1fmt("%ld\n", i);
	return (! i);
}
#endif /* CONFIG_ASH_MATH_SUPPORT */

/*      $NetBSD: miscbltin.c,v 1.31 2002/11/24 22:35:41 christos Exp $  */

/*
 * Miscelaneous builtins.
 */

#undef rflag

#ifdef __GLIBC__
#if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
typedef enum __rlimit_resource rlim_t;
#endif
#endif


/*
 * The read builtin.  The -e option causes backslashes to escape the
 * following character.
 *
 * This uses unbuffered input, which may be avoidable in some cases.
 */

static int
readcmd(int argc, char **argv)
{
	char **ap;
	int backslash;
	char c;
	int rflag;
	char *prompt;
	const char *ifs;
	char *p;
	int startword;
	int status;
	int i;

	rflag = 0;
	prompt = NULL;
	while ((i = nextopt("p:r")) != '\0') {
		if (i == 'p')
			prompt = optionarg;
		else
			rflag = 1;
	}
	if (prompt && isatty(0)) {
		out2str(prompt);
		flushall();
	}
	if (*(ap = argptr) == NULL)
		error("arg count");
	if ((ifs = bltinlookup("IFS")) == NULL)
		ifs = defifs;
	status = 0;
	startword = 1;
	backslash = 0;
	STARTSTACKSTR(p);
	for (;;) {
		if (read(0, &c, 1) != 1) {
			status = 1;
			break;
		}
		if (c == '\0')
			continue;
		if (backslash) {
			backslash = 0;
			if (c != '\n')
				goto put;
			continue;
		}
		if (!rflag && c == '\\') {
			backslash++;
			continue;
		}
		if (c == '\n')
			break;
		if (startword && *ifs == ' ' && strchr(ifs, c)) {
			continue;
		}
		startword = 0;
		if (ap[1] != NULL && strchr(ifs, c) != NULL) {
			STACKSTRNUL(p);
			setvar(*ap, stackblock(), 0);
			ap++;
			startword = 1;
			STARTSTACKSTR(p);
		} else {
put:
			STPUTC(c, p);
		}
	}
	STACKSTRNUL(p);
	/* Remove trailing blanks */
	while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
		*p = '\0';
	setvar(*ap, stackblock(), 0);
	while (*++ap != NULL)
		setvar(*ap, nullstr, 0);
	return status;
}


static int umaskcmd(int argc, char **argv)
{
	static const char permuser[3] = "ugo";
	static const char permmode[3] = "rwx";
	static const short int permmask[] = {
		S_IRUSR, S_IWUSR, S_IXUSR,
		S_IRGRP, S_IWGRP, S_IXGRP,
		S_IROTH, S_IWOTH, S_IXOTH
	};

	char *ap;
	mode_t mask;
	int i;
	int symbolic_mode = 0;

	while (nextopt("S") != '\0') {
		symbolic_mode = 1;
	}

	INTOFF;
	mask = umask(0);
	umask(mask);
	INTON;

	if ((ap = *argptr) == NULL) {
		if (symbolic_mode) {
			char buf[18];
			char *p = buf;

			for (i = 0; i < 3; i++) {
				int j;

				*p++ = permuser[i];
				*p++ = '=';
				for (j = 0; j < 3; j++) {
					if ((mask & permmask[3 * i + j]) == 0) {
						*p++ = permmode[j];
					}
				}
				*p++ = ',';
			}
			*--p = 0;
			puts(buf);
		} else {
			out1fmt("%.4o\n", mask);
		}
	} else {
		if (is_digit((unsigned char) *ap)) {
			mask = 0;
			do {
				if (*ap >= '8' || *ap < '0')
					error(illnum, argv[1]);
				mask = (mask << 3) + (*ap - '0');
			} while (*++ap != '\0');
			umask(mask);
		} else {
			mask = ~mask & 0777;
			if (!bb_parse_mode(ap, &mask)) {
				error("Illegal mode: %s", ap);
			}
			umask(~mask & 0777);
		}
	}
	return 0;
}

/*
 * ulimit builtin
 *
 * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and
 * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with
 * ash by J.T. Conklin.
 *
 * Public domain.
 */

struct limits {
	const char *name;
	int     cmd;
	int     factor; /* multiply by to get rlim_{cur,max} values */
	char    option;
};

static const struct limits limits[] = {
#ifdef RLIMIT_CPU
	{ "time(seconds)",              RLIMIT_CPU,        1, 't' },
#endif
#ifdef RLIMIT_FSIZE
	{ "file(blocks)",               RLIMIT_FSIZE,    512, 'f' },
#endif
#ifdef RLIMIT_DATA
	{ "data(kbytes)",               RLIMIT_DATA,    1024, 'd' },
#endif
#ifdef RLIMIT_STACK
	{ "stack(kbytes)",              RLIMIT_STACK,   1024, 's' },
#endif
#ifdef  RLIMIT_CORE
	{ "coredump(blocks)",           RLIMIT_CORE,     512, 'c' },
#endif
#ifdef RLIMIT_RSS
	{ "memory(kbytes)",             RLIMIT_RSS,     1024, 'm' },
#endif
#ifdef RLIMIT_MEMLOCK
	{ "locked memory(kbytes)",      RLIMIT_MEMLOCK, 1024, 'l' },
#endif
#ifdef RLIMIT_NPROC
	{ "process(processes)",         RLIMIT_NPROC,      1, 'p' },
#endif
#ifdef RLIMIT_NOFILE
	{ "nofiles(descriptors)",       RLIMIT_NOFILE,     1, 'n' },
#endif
#ifdef RLIMIT_VMEM
	{ "vmemory(kbytes)",            RLIMIT_VMEM,    1024, 'v' },
#endif
#ifdef RLIMIT_SWAP
	{ "swap(kbytes)",               RLIMIT_SWAP,    1024, 'w' },
#endif
	{ (char *) 0,                   0,                 0,  '\0' }
};

int
ulimitcmd(int argc, char **argv)
{
	int     c;
	rlim_t val = 0;
	enum { SOFT = 0x1, HARD = 0x2 }
			how = SOFT | HARD;
	const struct limits     *l;
	int             set, all = 0;
	int             optc, what;
	struct rlimit   limit;

	what = 'f';
	while ((optc = nextopt("HSatfdsmcnpl")) != '\0')
		switch (optc) {
		case 'H':
			how = HARD;
			break;
		case 'S':
			how = SOFT;
			break;
		case 'a':
			all = 1;
			break;
		default:
			what = optc;
		}

	for (l = limits; l->name && l->option != what; l++)
		;
	if (!l->name)
		error("internal error (%c)", what);

	set = *argptr ? 1 : 0;
	if (set) {
		char *p = *argptr;

		if (all || argptr[1])
			error("too many arguments");
		if (strncmp(p, "unlimited\n", 9) == 0)
			val = RLIM_INFINITY;
		else {
			val = (rlim_t) 0;

			while ((c = *p++) >= '0' && c <= '9')
			{
				val = (val * 10) + (long)(c - '0');
				if (val < (rlim_t) 0)
					break;
			}
			if (c)
				error("bad number");
			val *= l->factor;
		}
	}
	if (all) {
		for (l = limits; l->name; l++) {
			getrlimit(l->cmd, &limit);
			if (how & SOFT)
				val = limit.rlim_cur;
			else if (how & HARD)
				val = limit.rlim_max;

			out1fmt("%-20s ", l->name);
			if (val == RLIM_INFINITY)
				out1fmt("unlimited\n");
			else
			{
				val /= l->factor;
				out1fmt("%lld\n", (long long) val);
			}
		}
		return 0;
	}

	getrlimit(l->cmd, &limit);
	if (set) {
		if (how & HARD)
			limit.rlim_max = val;
		if (how & SOFT)
			limit.rlim_cur = val;
		if (setrlimit(l->cmd, &limit) < 0)
			error("error setting limit (%m)");
	} else {
		if (how & SOFT)
			val = limit.rlim_cur;
		else if (how & HARD)
			val = limit.rlim_max;

		if (val == RLIM_INFINITY)
			out1fmt("unlimited\n");
		else
		{
			val /= l->factor;
			out1fmt("%lld\n", (long long) val);
		}
	}
	return 0;
}

#ifdef DEBUG
const char *bb_applet_name = "debug stuff usage";
int main(int argc, char **argv)
{
	return ash_main(argc, argv);
}
#endif

/*-
 * Copyright (c) 1989, 1991, 1993, 1994
 *      The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Kenneth Almquist.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
 *              ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
 *
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */