Code Search for Developers
 
 
  

ash.c from openap at Krugle


Show ash.c syntax highlighted

/* 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.
 *
 * 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
 *
 * This version of ash is adapted from the source in Debian's ash 0.3.8-5
 * package.
 *
 * Modified by Erik Andersen <andersee@debian.org> and
 * Vladimir Oleynik <dzo@simtreas.ru> to be used in busybox
 *
 *
 * Original copyright notice is retained at the end of this file.
 */


/* These defines allow you to adjust the feature set to be compiled
 * into the ash shell.   As a rule, enabling these options will make
 * ash get bigger...   With all of these options off, ash adds about
 * 60k to busybox on an x86 system.*/


/* Enable job control.  This allows you to run jobs in the background,
 * which is great when ash is being  used as an interactive shell, but
 * it completely useless for is all you are doing is running scripts.
 * This adds about 2.5k on an x86 system. */
#undef JOBS

/* This enables alias support in ash.  If you want to support things
 * like "alias ls='ls -l'" with ash, enable this.  This is only useful
 * when ash is used as an intractive shell.   This adds about 1.5k */
#define ASH_ALIAS

/* If you need ash to act as a full Posix shell, with full math
 * support, enable this.   This adds a bit over 2k an x86 system. */
//#undef ASH_MATH_SUPPORT
#define ASH_MATH_SUPPORT

/* Getopts is used by shell procedures to parse positional parameters.
 * You probably want to leave this disabled, and use the busybox getopt
 * applet if you want to do this sort of thing.  There are some scripts
 * out there that use it, so if you need it, enable it.  Most people will
 * leave this disabled.  This adds 1k on an x86 system. */
#undef ASH_GETOPTS

/* This allows you to override shell builtins and use whatever is on
 * the filesystem.  This is most useful when ash is acting as a
 * standalone shell.   Adds about 272 bytes. */
#undef ASH_CMDCMD


/* Optimize size vs speed as size */
#define ASH_OPTIMIZE_FOR_SIZE

/* Enable this to compile in extra debugging noise.  When debugging is
 * on, debugging info will be written to $HOME/trace and a quit signal
 * will generate a core dump. */
#undef DEBUG

/* These are here to work with glibc -- Don't change these... */
#undef FNMATCH_BROKEN
#undef GLOB_BROKEN

#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <paths.h>
#include <pwd.h>
#include <setjmp.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/types.h>
#include <sys/wait.h>


#if !defined(FNMATCH_BROKEN)
#include <fnmatch.h>
#endif
#if !defined(GLOB_BROKEN)
#include <glob.h>
#endif

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

#include "busybox.h"
#include "cmdedit.h"

/*
 * 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 */

/* Syntax classes for is_ functions */
#define ISDIGIT 01              /* a digit */
#define ISUPPER 02              /* an upper case letter */
#define ISLOWER 04              /* a lower case letter */
#define ISUNDER 010             /* an underscore */
#define ISSPECL 020             /* the name of a special parameter */

#define SYNBASE 130
#define PEOF -130

#define PEOA -129

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


#define BASESYNTAX (basesyntax + SYNBASE)
#define DQSYNTAX (dqsyntax + SYNBASE)
#define SQSYNTAX (sqsyntax + SYNBASE)
#define ARISYNTAX (arisyntax + SYNBASE)

/* control characters in argument strings */
#define CTLESC '\201'
#define CTLVAR '\202'
#define CTLENDVAR '\203'
#define CTLBACKQ '\204'
#define CTLQUOTE 01             /* ored with CTLBACKQ code if in quotes */
/*      CTLBACKQ | CTLQUOTE == '\205' */
#define CTLARI  '\206'
#define CTLENDARI '\207'
#define CTLQUOTEMARK '\210'

#define is_digit(c)     ((c)>='0' && (c)<='9')
#define is_alpha(c)     (((c) < CTLESC || (c) > CTLENDARI) && isalpha((unsigned char) (c)))
#define is_name(c)      (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalpha((unsigned char) (c))))
#define is_in_name(c)   (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalnum((unsigned char) (c))))
#define is_special(c)   ((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT))
#define digit_val(c)    ((c) - '0')


#define _DIAGASSERT(x)



#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 */


/* 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 VSTRIMLEFT      0x6             /* ${var#pattern} */
#define VSTRIMLEFTMAX   0x7             /* ${var##pattern} */
#define VSTRIMRIGHT     0x8             /* ${var%pattern} */
#define VSTRIMRIGHTMAX  0x9             /* ${var%%pattern} */
#define VSLENGTH        0xa             /* ${#var} */

/* flags passed to redirect */
#define REDIR_PUSH 01           /* save previous values of file descriptors */
#define REDIR_BACKQ 02          /* save the command output to pipe */

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

#if defined(BSD)
#define setjmp(jmploc)  _setjmp(jmploc)
#define longjmp(jmploc, val)    _longjmp(jmploc, val)
#endif

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

#ifndef ALIGN
union align {
	int i;
	char *cp;
};

#define ALIGN(nbytes)   (((nbytes) + sizeof(union align) - 1) & ~(sizeof(union align) - 1))
#endif

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

/*
 * 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. :-))
 */

static void onint (void);
static volatile int suppressint;
static volatile int intpending;

#define INTOFF suppressint++
#ifndef ASH_OPTIMIZE_FOR_SIZE
#define INTON { if (--suppressint == 0 && intpending) onint(); }
#define FORCEINTON {suppressint = 0; if (intpending) onint();}
#else
static void __inton (void);
static void forceinton (void);
#define INTON __inton()
#define FORCEINTON forceinton()
#endif

#define CLEAR_PENDING_INT intpending = 0
#define int_pending() intpending


typedef void *pointer;
#ifndef NULL
#define NULL (void *)0
#endif

static inline pointer  ckmalloc (int sz)          { return xmalloc(sz);     }
static inline pointer  ckrealloc(void *p, int sz) { return xrealloc(p, sz); }
static inline char *   savestr  (const char *s)   { return xstrdup(s);      }

static pointer stalloc (int);
static void stunalloc (pointer);
static void ungrabstackstr (char *, char *);
static char * growstackstr(void);
static char * makestrspace(size_t newlen);
static char *sstrdup (const char *);

/*
 * 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.
 */

#define MINSIZE 504             /* minimum size of a block */


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 int stacknleft = MINSIZE;


#define equal(s1, s2)   (strcmp(s1, s2) == 0)

#define stackblock() stacknxt
#define stackblocksize() stacknleft
#define STARTSTACKSTR(p)        p = stackblock(), sstrnleft = stackblocksize()

#define STPUTC(c, p)    (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c)))
#define CHECKSTRSPACE(n, p)     { if (sstrnleft < n) p = makestrspace(n); }
#define STACKSTRNUL(p)  (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0'))


#define USTPUTC(c, p)   (--sstrnleft, *p++ = (c))
#define STUNPUTC(p)     (++sstrnleft, --p)
#define STTOPC(p)       p[-1]
#define STADJUST(amount, p)     (p += (amount), sstrnleft -= (amount))
#define grabstackstr(p) stalloc(stackblocksize() - sstrnleft)

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


#ifdef DEBUG
#define TRACE(param)    trace param
static void trace (const char *, ...);
static void trargs (char **);
static void showtree (union node *);
static void trputc (int);
static void trputs (const char *);
static void opentrace (void);
#else
#define TRACE(param)
#endif

#define NSEMI 0
#define NCMD 1
#define NPIPE 2
#define NREDIR 3
#define NBACKGND 4
#define NSUBSHELL 5
#define NAND 6
#define NOR 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 NFROM 17
#define NFROMTO 18
#define NAPPEND 19
#define NTOOV 20
#define NTOFD 21
#define NFROMFD 22
#define NHERE 23
#define NXHERE 24
#define NNOT 25

/*
 * 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 NOPTS   16

static char optet_vals[NOPTS];

static const char * const optlist[NOPTS] = {
	"e" "errexit",
	"f" "noglob",
	"I" "ignoreeof",
	"i" "interactive",
	"m" "monitor",
	"n" "noexec",
	"s" "stdin",
	"x" "xtrace",
	"v" "verbose",
	"V" "vi",
	"E" "emacs",
	"C" "noclobber",
	"a" "allexport",
	"b" "notify",
	"u" "nounset",
	"q" "quietprofile"
};

#define optent_name(optent) (optent+1)
#define optent_letter(optent) optent[0]
#define optent_val(optent) optet_vals[optent]

#define eflag optent_val(0)
#define fflag optent_val(1)
#define Iflag optent_val(2)
#define iflag optent_val(3)
#define mflag optent_val(4)
#define nflag optent_val(5)
#define sflag optent_val(6)
#define xflag optent_val(7)
#define vflag optent_val(8)
#define Vflag optent_val(9)
#define Eflag optent_val(10)
#define Cflag optent_val(11)
#define aflag optent_val(12)
#define bflag optent_val(13)
#define uflag optent_val(14)
#define qflag optent_val(15)


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


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


struct ncmd {
      int type;
      int backgnd;
      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 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 nbinary nbinary;
      struct ncmd ncmd;
      struct npipe npipe;
      struct nredir nredir;
      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 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 */
};

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

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


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

struct strpush {
	struct strpush *prev;   /* preceding string on stack */
	char *prevstring;
	int prevnleft;
#ifdef 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 */
};

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

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

/*
 * 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 int exerrno = 0;                 /* Last exec error */


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


static void flushall (void);
static void out2fmt (const char *, ...)
    __attribute__((__format__(__printf__,1,2)));
static int xwrite (int, const char *, int);

static void outstr (const char *p, FILE *file) { fputs(p, file); }
static void out1str(const char *p) { outstr(p, stdout); }
static void out2str(const char *p) { outstr(p, stderr); }

#ifndef ASH_OPTIMIZE_FOR_SIZE
#define out2c(c)        putc((c), stderr)
#else
static void out2c(int c)           { putc(c, stderr); }
#endif

/* syntax table used when not in quotes */
static const char basesyntax[257] = {
      CENDFILE,   CSPCL,   CWORD,   CCTL,
      CCTL,    CCTL,    CCTL,    CCTL,
      CCTL,    CCTL,    CCTL,    CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CSPCL,
      CNL,     CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CSPCL,   CWORD,
      CDQUOTE, CWORD,   CVAR,    CWORD,
      CSPCL,   CSQUOTE, CSPCL,   CSPCL,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CSPCL,   CSPCL,   CWORD,
      CSPCL,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CBACK,   CWORD,
      CWORD,   CWORD,   CBQUOTE, CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CSPCL,   CENDVAR,
      CWORD
};

/* syntax table used when in double quotes */
static const char dqsyntax[257] = {
      CENDFILE,   CIGN,    CWORD,   CCTL,
      CCTL,    CCTL,    CCTL,    CCTL,
      CCTL,    CCTL,    CCTL,    CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CNL,     CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CCTL,
      CENDQUOTE,CWORD,  CVAR,    CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CCTL,    CWORD,   CWORD,   CCTL,
      CWORD,   CCTL,    CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CCTL,    CWORD,   CWORD,   CCTL,
      CWORD,   CCTL,    CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CCTL,    CBACK,   CCTL,
      CWORD,   CWORD,   CBQUOTE, CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CENDVAR,
      CCTL
};

/* syntax table used when in single quotes */
static const char sqsyntax[257] = {
      CENDFILE,   CIGN,    CWORD,   CCTL,
      CCTL,    CCTL,    CCTL,    CCTL,
      CCTL,    CCTL,    CCTL,    CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CNL,     CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CCTL,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CENDQUOTE,CWORD,  CWORD,
      CCTL,    CWORD,   CWORD,   CCTL,
      CWORD,   CCTL,    CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CCTL,    CWORD,   CWORD,   CCTL,
      CWORD,   CCTL,    CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CCTL,    CCTL,    CCTL,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CCTL
};

/* syntax table used when in arithmetic */
static const char arisyntax[257] = {
      CENDFILE,   CIGN,    CWORD,   CCTL,
      CCTL,    CCTL,    CCTL,    CCTL,
      CCTL,    CCTL,    CCTL,    CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CNL,     CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CDQUOTE, CWORD,   CVAR,    CWORD,
      CWORD,   CSQUOTE, CLP,     CRP,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CBACK,   CWORD,
      CWORD,   CWORD,   CBQUOTE, CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CWORD,
      CWORD,   CWORD,   CWORD,   CENDVAR,
      CWORD
};

/* character classification table */
static const char is_type[257] = {
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       0,
      0,       0,       0,       ISSPECL,
      0,       ISSPECL, ISSPECL, 0,
      0,       0,       0,       0,
      ISSPECL, 0,       0,       ISSPECL,
      0,       0,       ISDIGIT, ISDIGIT,
      ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT,
      ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT,
      0,       0,       0,       0,
      0,       ISSPECL, ISSPECL, ISUPPER,
      ISUPPER, ISUPPER, ISUPPER, ISUPPER,
      ISUPPER, ISUPPER, ISUPPER, ISUPPER,
      ISUPPER, ISUPPER, ISUPPER, ISUPPER,
      ISUPPER, ISUPPER, ISUPPER, ISUPPER,
      ISUPPER, ISUPPER, ISUPPER, ISUPPER,
      ISUPPER, ISUPPER, ISUPPER, ISUPPER,
      ISUPPER, 0,       0,       0,
      0,       ISUNDER, 0,       ISLOWER,
      ISLOWER, ISLOWER, ISLOWER, ISLOWER,
      ISLOWER, ISLOWER, ISLOWER, ISLOWER,
      ISLOWER, ISLOWER, ISLOWER, ISLOWER,
      ISLOWER, ISLOWER, ISLOWER, ISLOWER,
      ISLOWER, ISLOWER, ISLOWER, ISLOWER,
      ISLOWER, ISLOWER, ISLOWER, ISLOWER,
      ISLOWER, 0,       0,       0,
      0
};

/* Array indicating which tokens mark the end of a list */
static const char tokendlist[] = {
	1,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	1,
	1,
	1,
	0,
	0,
	0,
	0,
	0,
	1,
	1,
	1,
	1,
	1,
	1,
	0,
	0,
	0,
	1,
	0,
	0,
	0,
	1,
};

static const char *const tokname[] = {
	"end of file",
	"newline",
	"\";\"",
	"\"&\"",
	"\"&&\"",
	"\"||\"",
	"\"|\"",
	"\"(\"",
	"\")\"",
	"\";;\"",
	"\"`\"",
	"redirection",
	"word",
	"assignment",
	"\"!\"",
	"\"case\"",
	"\"do\"",
	"\"done\"",
	"\"elif\"",
	"\"else\"",
	"\"esac\"",
	"\"fi\"",
	"\"for\"",
	"\"if\"",
	"\"in\"",
	"\"then\"",
	"\"until\"",
	"\"while\"",
	"\"{\"",
	"\"}\"",
};

#define KWDOFFSET 14

static const char *const parsekwd[] = {
	"!",
	"case",
	"do",
	"done",
	"elif",
	"else",
	"esac",
	"fi",
	"for",
	"if",
	"in",
	"then",
	"until",
	"while",
	"{",
	"}"
};


static int plinno = 1;          /* input line number */

static int parselleft;          /* copy of parsefile->lleft */

static struct parsefile basepf; /* top level input file */
static char basebuf[BUFSIZ];    /* buffer for top level input file */
static struct parsefile *parsefile = &basepf;  /* current input file */

/*
 * 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 tokpushback;         /* last token pushed back */
#define NEOF ((union node *)&tokpushback)
static int checkkwd;            /* 1 == check for kwds, 2 == also eat newlines */


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

static int  goodname(const char *);
static void ignoresig (int);
static void onsig (int);
static void dotrap (void);
static int  decode_signal (const char *, int);

static void shprocvar(void);
static void deletefuncs(void);
static void setparam (char **);
static void freeparam (volatile struct shparam *);

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

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

#define DO_ERR  1               /* find_command prints errors */
#define DO_ABS  2               /* find_command checks absolute paths */
#define DO_NOFUN        4       /* find_command ignores functions */
#define DO_BRUTE        8       /* find_command ignores hash table */

/*
 * Shell variables.
 */

/* flags */
#define VEXPORT         0x01    /* variable is exported */
#define VREADONLY       0x02    /* variable cannot be modified */
#define VSTRFIXED       0x04    /* variable struct is staticly allocated */
#define VTEXTFIXED      0x08    /* text is staticly 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 */


struct var {
	struct var *next;               /* next entry in hash list */
	int flags;                      /* flags are defined above */
	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 */
	char *text;                     /* saved text */
};


#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
#define rmescapes(p) _rmescapes((p), 0)
static char *_rmescapes (char *, int);
#else
static void rmescapes (char *);
#endif

static int  casematch (union node *, const char *);
static void clearredir(void);
static void popstring(void);
static void readcmdfile (const char *);

static int number (const char *);
static int is_number (const char *, int *num);
static char *single_quote (const char *);
static int nextopt (const char *);

static void redirect (union node *, int);
static void popredir (void);
static int dup_as_newfd (int, int);

static void changepath(const char *newval);
static void getoptsreset(const char *value);


static int parsenleft;                  /* copy of parsefile->nleft */
static char *parsenextc;                /* copy of parsefile->nextc */
static int rootpid;     /* pid of main shell */
static int rootshell;   /* true if we aren't a child of the main shell */

static const char spcstr[] = " ";
static const char snlfmt[] = "%s\n";

static int sstrnleft;
static int herefd = -1;

static struct localvar *localvars;

static struct var vifs;
static struct var vmail;
static struct var vmpath;
static struct var vpath;
static struct var vps1;
static struct var vps2;
static struct var voptind;
#ifdef BB_LOCALE_SUPPORT
static struct var vlc_all;
static struct var vlc_ctype;
#endif

struct varinit {
	struct var *var;
	int flags;
	const char *text;
	void (*func) (const char *);
};

static const char defpathvar[] =
	"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
#define defpath (defpathvar + 5)

#ifdef IFS_BROKEN
static const char defifsvar[] = "IFS= \t\n";
#define defifs (defifsvar + 4)
#else
static const char defifs[] = " \t\n";
#endif

static const struct varinit varinit[] = {
#ifdef IFS_BROKEN
	{ &vifs,        VSTRFIXED|VTEXTFIXED,           defifsvar,
#else
	{ &vifs,        VSTRFIXED|VTEXTFIXED|VUNSET,    "IFS=",
#endif
	  NULL },
	{ &vmail,       VSTRFIXED|VTEXTFIXED|VUNSET,    "MAIL=",
	  NULL },
	{ &vmpath,      VSTRFIXED|VTEXTFIXED|VUNSET,    "MAILPATH=",
	  NULL },
	{ &vpath,       VSTRFIXED|VTEXTFIXED,           defpathvar,
	  changepath },
	/*
	 * vps1 depends on uid
	 */
	{ &vps2,        VSTRFIXED|VTEXTFIXED,           "PS2=> ",
	  NULL },
	{ &voptind,     VSTRFIXED|VTEXTFIXED,           "OPTIND=1",
	  getoptsreset },
#ifdef BB_LOCALE_SUPPORT
	{ &vlc_all,     VSTRFIXED|VTEXTFIXED|VUNSET,    "LC_ALL=",
	  change_lc_all },
	{ &vlc_ctype,   VSTRFIXED|VTEXTFIXED|VUNSET,    "LC_CTYPE=",
	  change_lc_ctype },
#endif
	{ NULL, 0,                              NULL,
	  NULL }
};

#define VTABSIZE 39

static struct var *vartab[VTABSIZE];

/*
 * 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 optindval()     (voptind.text + 7)

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

static void initvar (void);
static void setvar (const char *, const char *, int);
static void setvareq (char *, int);
static void listsetvar (struct strlist *);
static const char *lookupvar (const char *);
static const char *bltinlookup (const char *);
static char **environment (void);
static int showvarscmd (int, char **);
static void mklocal (char *);
static void poplocalvars (void);
static int unsetvar (const char *);
static int varequal (const char *, const char *);


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 */


#ifdef ASH_ALIAS

#define ALIASINUSE      1
#define ALIASDEAD       2

#define ATABSIZE 39

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

static struct alias *atab[ATABSIZE];

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

static void
setalias(name, val)
	char *name, *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(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);
}

static void
printalias(const struct alias *ap) {
	char *p;

	p = single_quote(ap->val);
	printf("alias %s=%s\n", ap->name, p);
	stunalloc(p);
}


/*
 * 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) {
				out2fmt("%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)) {
			out2fmt("%s: %s not found\n", "unalias", *argptr);
			i = 1;
		}
	}

	return (i);
}

static struct alias **
hashalias(p)
	const char *p;
	{
	unsigned int hashval;

	hashval = *p << 4;
	while (*p)
		hashval+= *p++;
	return &atab[hashval % ATABSIZE];
}

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 struct alias **
__lookupalias(const char *name) {
	struct alias **app = hashalias(name);

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

	return app;
}
#endif

#ifdef ASH_MATH_SUPPORT
/* The generated file arith.c has been replaced with a custom hand
 * written implementation written by Aaron Lehmann <aaronl@vitelus.com>.  
 * This is now part of libbb, so that it can be used by all the shells 
 * in busybox. */
#define ARITH_NUM 257
#define ARITH_LPAREN 258
#define ARITH_RPAREN 259
#define ARITH_OR 260
#define ARITH_AND 261
#define ARITH_BOR 262
#define ARITH_BXOR 263
#define ARITH_BAND 264
#define ARITH_EQ 265
#define ARITH_NE 266
#define ARITH_LT 267
#define ARITH_GT 268
#define ARITH_GE 269
#define ARITH_LE 270
#define ARITH_LSHIFT 271
#define ARITH_RSHIFT 272
#define ARITH_ADD 273
#define ARITH_SUB 274
#define ARITH_MUL 275
#define ARITH_DIV 276
#define ARITH_REM 277
#define ARITH_UNARYMINUS 278
#define ARITH_UNARYPLUS 279
#define ARITH_NOT 280
#define ARITH_BNOT 281

static void expari (int);
#endif

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

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

#ifdef JOBS
static int bgcmd (int, char **);
static int fgcmd (int, char **);
static int killcmd (int, char **);
#endif
static int bltincmd (int, char **);
static int cdcmd (int, char **);
static int breakcmd (int, char **);
#ifdef 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 **);
static int exportcmd (int, char **);
static int histcmd (int, char **);
static int hashcmd (int, char **);
static int helpcmd (int, char **);
static int jobscmd (int, char **);
static int localcmd (int, char **);
#ifndef BB_PWD
static int pwdcmd (int, char **);
#endif
static int readcmd (int, char **);
static int returncmd (int, char **);
static int setcmd (int, char **);
static int setvarcmd (int, char **);
static int shiftcmd (int, char **);
static int trapcmd (int, char **);
static int umaskcmd (int, char **);
#ifdef ASH_ALIAS
static int aliascmd (int, char **);
static int unaliascmd (int, char **);
#endif
static int unsetcmd (int, char **);
static int waitcmd (int, char **);
static int ulimitcmd (int, char **);
static int timescmd (int, char **);
#ifdef ASH_MATH_SUPPORT
static int letcmd (int, char **);
#endif
static int typecmd (int, char **);
#ifdef ASH_GETOPTS
static int getoptscmd (int, char **);
#endif

#ifndef BB_TRUE_FALSE
static int true_main (int, char **);
static int false_main (int, char **);
#endif

static void     setpwd (const char *, int);


#define BUILTIN_NOSPEC  "0"
#define BUILTIN_SPECIAL "1"
#define BUILTIN_REGULAR "2"
#define BUILTIN_ASSIGN  "4"
#define BUILTIN_SPEC_ASSG  "5"
#define BUILTIN_REG_ASSG   "6"

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

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


/* It is CRUCIAL that this listing be kept in ascii order, otherwise
 * the binary search in find_builtin() will stop working. If you value
 * your kneecaps, you'll be sure to *make sure* that any changes made
 * to this array result in the listing remaining in ascii order. You
 * have been warned.
 */
static const struct builtincmd builtincmds[] = {
	{ BUILTIN_SPECIAL   ".", dotcmd },    /* first, see declare DOTCMD */
	{ BUILTIN_SPECIAL   ":", true_main },
#ifdef ASH_ALIAS
	{ BUILTIN_REG_ASSG  "alias", aliascmd },
#endif
#ifdef JOBS
	{ BUILTIN_REGULAR   "bg", bgcmd },
#endif
	{ BUILTIN_SPECIAL   "break", breakcmd },
	{ BUILTIN_SPECIAL   "builtin", bltincmd },
	{ BUILTIN_REGULAR   "cd", cdcmd },
	{ BUILTIN_NOSPEC    "chdir", cdcmd },
#ifdef ASH_CMDCMD
	{ BUILTIN_REGULAR   "command", commandcmd },
#endif
	{ BUILTIN_SPECIAL   "continue", breakcmd },
	{ BUILTIN_SPECIAL   "eval", evalcmd },
	{ BUILTIN_SPECIAL   "exec", execcmd },
	{ BUILTIN_SPECIAL   "exit", exitcmd },
	{ BUILTIN_SPEC_ASSG "export", exportcmd },
	{ BUILTIN_REGULAR   "false", false_main },
	{ BUILTIN_REGULAR   "fc", histcmd },
#ifdef JOBS
	{ BUILTIN_REGULAR   "fg", fgcmd },
#endif
#ifdef ASH_GETOPTS
	{ BUILTIN_REGULAR   "getopts", getoptscmd },
#endif
	{ BUILTIN_NOSPEC    "hash", hashcmd },
	{ BUILTIN_NOSPEC    "help", helpcmd },
	{ BUILTIN_REGULAR   "jobs", jobscmd },
#ifdef JOBS
	{ BUILTIN_REGULAR   "kill", killcmd },
#endif
#ifdef ASH_MATH_SUPPORT
	{ BUILTIN_REGULAR    "let", letcmd },
#endif
	{ BUILTIN_ASSIGN    "local", localcmd },
#ifndef BB_PWD
	{ BUILTIN_NOSPEC    "pwd", pwdcmd },
#endif
	{ BUILTIN_REGULAR   "read", readcmd },
	{ BUILTIN_SPEC_ASSG "readonly", exportcmd },
	{ BUILTIN_SPECIAL   "return", returncmd },
	{ BUILTIN_SPECIAL   "set", setcmd },
	{ BUILTIN_NOSPEC    "setvar", setvarcmd },
	{ BUILTIN_SPECIAL   "shift", shiftcmd },
	{ BUILTIN_SPECIAL   "times", timescmd },
	{ BUILTIN_SPECIAL   "trap", trapcmd },
	{ BUILTIN_REGULAR   "true", true_main },
	{ BUILTIN_NOSPEC    "type", typecmd },
	{ BUILTIN_NOSPEC    "ulimit", ulimitcmd },
	{ BUILTIN_REGULAR   "umask", umaskcmd },
#ifdef ASH_ALIAS
	{ BUILTIN_REGULAR   "unalias", unaliascmd },
#endif
	{ BUILTIN_SPECIAL   "unset", unsetcmd },
	{ BUILTIN_REGULAR   "wait", waitcmd },
};
#define NUMBUILTINS  (sizeof (builtincmds) / sizeof (struct builtincmd) )

static const struct builtincmd *DOTCMD = &builtincmds[0];
static struct builtincmd *BLTINCMD;
static struct builtincmd *EXECCMD;
static struct builtincmd *EVALCMD;

/* states */
#define JOBSTOPPED 1            /* all procs are stopped */
#define JOBDONE 2               /* all procs are completed */

/*
 * 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;             /* status flags (defined above) */
	char *cmd;              /* text of command being run */
};


static int job_warning;         /* user was warned about stopped jobs */

#ifdef JOBS
static void setjobctl(int enable);
#else
#define setjobctl(on)   /* do nothing */
#endif


struct job {
	struct procstat ps0;    /* status of process */
	struct procstat *ps;    /* status or processes when more than one */
	short nprocs;           /* number of processes */
	short pgrp;             /* process group of this job */
	char state;             /* true if job is finished */
	char used;              /* true if this entry is in used */
	char changed;           /* true if status has changed */
#ifdef JOBS
	char jobctl;            /* job running under job control */
#endif
};

static struct job *jobtab;      /* array of jobs */
static int njobs;               /* size of array */
static int backgndpid = -1;     /* pid of last background process */
#ifdef JOBS
static int initialpgrp;         /* pgrp of shell on invocation */
static int curjob;              /* current job */
static int jobctl;
#endif
static int intreceived;

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

static int docd (char *, int);
static char *getcomponent (void);
static void updatepwd (const char *);
static void getpwd (void);

static char *padvance (const char **, const char *);

static char nullstr[1];         /* zero length string */
static char *curdir = nullstr;          /* current working directory */
static char *cdcomppath;

static int
cdcmd(argc, argv)
	int argc;
	char **argv;
{
	const char *dest;
	const char *path;
	char *p;
	struct stat statb;
	int print = 0;

	nextopt(nullstr);
	if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME")) == NULL)
		error("HOME not set");
	if (*dest == '\0')
		dest = ".";
	if (dest[0] == '-' && dest[1] == '\0') {
		dest = bltinlookup("OLDPWD");
		if (!dest || !*dest) {
			dest = curdir;
		}
		print = 1;
		if (dest)
			print = 1;
		else
			dest = ".";
	}
	if (*dest == '/' || (path = bltinlookup("CDPATH")) == NULL)
		path = nullstr;
	while ((p = padvance(&path, dest)) != NULL) {
		if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
			if (!print) {
				/*
				 * XXX - rethink
				 */
				if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
					p += 2;
				print = strcmp(p, dest);
			}
			if (docd(p, print) >= 0)
				return 0;

		}
	}
	error("can't cd to %s", dest);
	/* NOTREACHED */
}


/*
 * Actually do the chdir.  In an interactive shell, print the
 * directory name if "print" is nonzero.
 */

static int
docd(dest, print)
	char *dest;
	int print;
{
	char *p;
	char *q;
	char *component;
	struct stat statb;
	int first;
	int badstat;

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

	/*
	 *  Check each component of the path. If we find a symlink or
	 *  something we can't stat, clear curdir to force a getcwd()
	 *  next time we get the value of the current directory.
	 */
	badstat = 0;
	cdcomppath = sstrdup(dest);
	STARTSTACKSTR(p);
	if (*dest == '/') {
		STPUTC('/', p);
		cdcomppath++;
	}
	first = 1;
	while ((q = getcomponent()) != NULL) {
		if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
			continue;
		if (! first)
			STPUTC('/', p);
		first = 0;
		component = q;
		while (*q)
			STPUTC(*q++, p);
		if (equal(component, ".."))
			continue;
		STACKSTRNUL(p);
		if ((lstat(stackblock(), &statb) < 0)
		    || (S_ISLNK(statb.st_mode)))  {
			/* print = 1; */
			badstat = 1;
			break;
		}
	}

	INTOFF;
	if (chdir(dest) < 0) {
		INTON;
		return -1;
	}
	updatepwd(badstat ? NULL : dest);
	INTON;
	if (print && iflag)
		printf(snlfmt, curdir);
	return 0;
}


/*
 * Get the next component of the path name pointed to by cdcomppath.
 * This routine overwrites the string pointed to by cdcomppath.
 */

static char *
getcomponent() {
	char *p;
	char *start;

	if ((p = cdcomppath) == NULL)
		return NULL;
	start = cdcomppath;
	while (*p != '/' && *p != '\0')
		p++;
	if (*p == '\0') {
		cdcomppath = NULL;
	} else {
		*p++ = '\0';
		cdcomppath = p;
	}
	return start;
}



/*
 * Update curdir (the name of the current directory) in response to a
 * cd command.  We also call hashcd to let the routines in exec.c know
 * that the current directory has changed.
 */

static void hashcd (void);

static void
updatepwd(const char *dir)
{
	char *new;
	char *p;
	size_t len;

	hashcd();                               /* update command hash table */

	/*
	 * If our argument is NULL, we don't know the current directory
	 * any more because we traversed a symbolic link or something
	 * we couldn't stat().
	 */
	if (dir == NULL || curdir == nullstr)  {
		setpwd(0, 1);
		return;
	}
	len = strlen(dir);
	cdcomppath = sstrdup(dir);
	STARTSTACKSTR(new);
	if (*dir != '/') {
		p = curdir;
		while (*p)
			STPUTC(*p++, new);
		if (p[-1] == '/')
			STUNPUTC(new);
	}
	while ((p = getcomponent()) != NULL) {
		if (equal(p, "..")) {
			while (new > stackblock() && (STUNPUTC(new), *new) != '/');
		} else if (*p != '\0' && ! equal(p, ".")) {
			STPUTC('/', new);
			while (*p)
				STPUTC(*p++, new);
		}
	}
	if (new == stackblock())
		STPUTC('/', new);
	STACKSTRNUL(new);
	setpwd(stackblock(), 1);
}


#ifndef BB_PWD
static int
pwdcmd(argc, argv)
	int argc;
	char **argv;
{
	printf(snlfmt, curdir);
	return 0;
}
#endif

/*
 * Find out what the current directory is. If we already know the current
 * directory, this routine returns immediately.
 */
static void
getpwd(void)
{
	curdir = xgetcwd(0);
	if(curdir==0)
		curdir = nullstr;
}

static void
setpwd(const char *val, int setold)
{
	if (setold) {
		setvar("OLDPWD", curdir, VEXPORT);
	}
	INTOFF;
	if (curdir != nullstr) {
		free(curdir);
		curdir = nullstr;
	}
	if (!val) {
		getpwd();
	} else {
		curdir = savestr(val);
	}
	INTON;
	setvar("PWD", curdir, VEXPORT);
}

/*
 * Errors and exceptions.
 */

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

/*
 * 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;
};

/* 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 */

static struct jmploc *handler;
static int exception;

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) __attribute__((__noreturn__));

static void
exraise(int e)
{
#ifdef DEBUG
	if (handler == NULL)
		abort();
#endif
	flushall();
	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 call to _exit is necessary because
 * there is a short period after a fork before the signal handlers are
 * set to the appropriate value for the child.  (The test for iflag is
 * just defensive programming.)
 */

static void
onint(void) {
	sigset_t mysigset;

	if (suppressint) {
		intpending++;
		return;
	}
	intpending = 0;
	sigemptyset(&mysigset);
	sigprocmask(SIG_SETMASK, &mysigset, NULL);
	if (rootshell && iflag)
		exraise(EXINT);
	else {
		signal(SIGINT, SIG_DFL);
		raise(SIGINT);
	}
	/* NOTREACHED */
}


static char *commandname;       /* currently executing command */

/*
 * Exverror is called to raise the error exception.  If the first 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)
{
	CLEAR_PENDING_INT;
	INTOFF;

#ifdef DEBUG
	if (msg)
		TRACE(("exverror(%d, \"%s\") pid=%d\n", cond, msg, getpid()));
	else
		TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
#endif
	if (msg) {
		if (commandname)
			out2fmt("%s: ", commandname);
		vfprintf(stderr, msg, ap);
		out2c('\n');
	}
	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);
}



/*
 * Table of error messages.
 */

struct errname {
	short errcode;          /* error number */
	char  action;           /* operation which encountered the error */
};

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

#define E_OPEN 01       /* opening a file */
#define E_CREAT 02      /* creating a file */
#define E_EXEC 04       /* executing a program */

#define ALL (E_OPEN|E_CREAT|E_EXEC)

static const struct errname errormsg[] = {
	{ EINTR,        ALL     },
	{ EACCES,       ALL     },
	{ EIO,          ALL     },
	{ ENOENT,       E_OPEN  },
	{ ENOENT,       E_CREAT },
	{ ENOENT,       E_EXEC  },
	{ ENOTDIR,      E_OPEN  },
	{ ENOTDIR,      E_CREAT },
	{ ENOTDIR,      E_EXEC  },
	{ EISDIR,       ALL     },
	{ EEXIST,       E_CREAT },
#ifdef EMFILE
	{ EMFILE,       ALL     },
#endif
	{ ENFILE,       ALL     },
	{ ENOSPC,       ALL     },
#ifdef EDQUOT
	{ EDQUOT,       ALL     },
#endif
#ifdef ENOSR
	{ ENOSR,        ALL     },
#endif
	{ ENXIO,        ALL     },
	{ EROFS,        ALL     },
	{ ETXTBSY,      ALL     },
#ifdef EAGAIN
	{ EAGAIN,       E_EXEC  },
#endif
	{ ENOMEM,       ALL     },
#ifdef ENOLINK
	{ ENOLINK,      ALL     },
#endif
#ifdef EMULTIHOP
	{ EMULTIHOP,    ALL     },
#endif
#ifdef ECOMM
	{ ECOMM,        ALL     },
#endif
#ifdef ESTALE
	{ ESTALE,       ALL     },
#endif
#ifdef ETIMEDOUT
	{ ETIMEDOUT,    ALL     },
#endif
#ifdef ELOOP
	{ ELOOP,        ALL     },
#endif
	{ E2BIG,        E_EXEC  },
#ifdef ELIBACC
	{ ELIBACC,      E_EXEC  },
#endif
};

#define ERRNAME_SIZE (sizeof(errormsg)/sizeof(struct errname))

/*
 * 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, int action)
{
	struct errname const *ep;
	static char buf[12];

	for (ep = errormsg ; ep < errormsg+ERRNAME_SIZE; ep++) {
		if (ep->errcode == e && (ep->action & action) != 0)
			return strerror(e);
	}

	snprintf(buf, sizeof buf, "error %d", e);
	return buf;
}


#ifdef ASH_OPTIMIZE_FOR_SIZE
static void
__inton() {
	if (--suppressint == 0 && intpending) {
		onint();
	}
}
static void forceinton (void) {
	suppressint = 0;
	if (intpending)
		onint();
}
#endif

/* 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 int evalskip;                    /* set if we are skipping commands */
static int skipcount;           /* number of levels to skip */
static int loopnest;            /* current loop nesting level */
static int funcnest;                    /* depth of function calls */


static struct strlist *cmdenviron;      /* environment for builtin command */
static int exitstatus;                  /* exit status of last command */
static int oexitstatus;         /* saved exit status */

static void evalsubshell (const union node *, int);
static void expredir (union node *);
static void prehash (union node *);
static void eprintlist (struct strlist *);

static union node *parsecmd(int);
/*
 * Called to reset things after an exception.
 */

/*
 * The eval commmand.
 */
static void evalstring (char *, int);

static int
evalcmd(argc, argv)
	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 (;;) {
				while (*p)
					STPUTC(*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 evaltree (union node *, int);
static void setinputstring (char *);
static void popfile (void);
static void setstackmark(struct stackmark *mark);
static void popstackmark(struct stackmark *mark);


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);
	}
	popfile();
	popstackmark(&smark);
}

static struct builtincmd *find_builtin (const char *);
static void expandarg (union node *, struct arglist *, int);
static void calcsize (const union node *);
static union node *copynode (const union node *);

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

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 inline union node *
copyfunc(union node *n)
{
	if (n == NULL)
		return NULL;
	funcblocksize = 0;
	funcstringsize = 0;
	calcsize(n);
	funcblock = ckmalloc(funcblocksize + funcstringsize);
	funcstring = (char *) funcblock + funcblocksize;
	return copynode(n);
}

/*
 * Free a parse tree.
 */

static void
freefunc(union node *n)
{
	if (n)
		ckfree(n);
}


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

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

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

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

	loopnest++;
	status = 0;
	for (;;) {
		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;
		}
		if (n->type == NWHILE) {
			if (exitstatus != 0)
				break;
		} else {
			if (exitstatus == 0)
				break;
		}
		evaltree(n->nbinary.ch2, flags & EV_TESTED);
		status = exitstatus;
		if (evalskip)
			goto skipping;
	}
	loopnest--;
	exitstatus = status;
}

static void
evalfor(const 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) {
		oexitstatus = exitstatus;
		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
		if (evalskip)
			goto out;
	}
	*arglist.lastp = NULL;

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

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

	setstackmark(&smark);
	arglist.lastp = &arglist.list;
	oexitstatus = exitstatus;
	expandarg(n->ncase.expr, &arglist, EXP_TILDE);
	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);
}

/*
 * 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 inline void
evalpipe(n)
	union node *n;
{
	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++;
	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 (prevfd > 0) {
				close(0);
				dup_as_newfd(prevfd, 0);
				close(prevfd);
				if (pip[0] == 0) {
					pip[0] = -1;
				}
			}
			if (pip[1] >= 0) {
				if (pip[0] >= 0) {
					close(pip[0]);
				}
				if (pip[1] != 1) {
					close(1);
					dup_as_newfd(pip[1], 1);
					close(pip[1]);
				}
			}
			evaltree(lp->n, EV_EXIT);
		}
		if (prevfd >= 0)
			close(prevfd);
		prevfd = pip[0];
		close(pip[1]);
	}
	INTON;
	if (n->npipe.backgnd == 0) {
		INTOFF;
		exitstatus = waitforjob(jp);
		TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
		INTON;
	}
}

static void find_command (const char *, struct cmdentry *, int, const char *);

static int
isassignment(const char *word) {
	if (!is_name(*word)) {
		return 0;
	}
	do {
		word++;
	} while (is_in_name(*word));
	return *word == '=';
}


static void
evalcommand(union node *cmd, int flags)
{
	struct stackmark smark;
	union node *argp;
	struct arglist arglist;
	struct arglist varlist;
	char **argv;
	int argc;
	char **envp;
	struct strlist *sp;
	int mode;
	struct cmdentry cmdentry;
	struct job *jp;
	char *volatile savecmdname;
	volatile struct shparam saveparam;
	struct localvar *volatile savelocalvars;
	volatile int e;
	char *lastarg;
	const char *path;
	const struct builtincmd *firstbltin;
	struct jmploc *volatile savehandler;
	struct jmploc jmploc;
#if __GNUC__
	/* Avoid longjmp clobbering */
	(void) &argv;
	(void) &argc;
	(void) &lastarg;
	(void) &flags;
#endif

	/* First expand the arguments. */
	TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
	setstackmark(&smark);
	arglist.lastp = &arglist.list;
	varlist.lastp = &varlist.list;
	arglist.list = 0;
	oexitstatus = exitstatus;
	exitstatus = 0;
	path = pathval();
	for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
		expandarg(argp, &varlist, EXP_VARTILDE);
	}
	for (
		argp = cmd->ncmd.args; argp && !arglist.list;
		argp = argp->narg.next
	) {
		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
	}
	if (argp) {
		struct builtincmd *bcmd;
		int pseudovarflag;
		bcmd = find_builtin(arglist.list->text);
		pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
		for (; argp; argp = argp->narg.next) {
			if (pseudovarflag && isassignment(argp->narg.text)) {
				expandarg(argp, &arglist, EXP_VARTILDE);
				continue;
			}
			expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
		}
	}
	*arglist.lastp = NULL;
	*varlist.lastp = NULL;
	expredir(cmd->ncmd.redirect);
	argc = 0;
	for (sp = arglist.list ; sp ; sp = sp->next)
		argc++;
	argv = stalloc(sizeof (char *) * (argc + 1));

	for (sp = arglist.list ; sp ; sp = sp->next) {
		TRACE(("evalcommand arg: %s\n", sp->text));
		*argv++ = sp->text;
	}
	*argv = NULL;
	lastarg = NULL;
	if (iflag && funcnest == 0 && argc > 0)
		lastarg = argv[-1];
	argv -= argc;

	/* Print the command if xflag is set. */
	if (xflag) {
		out2c('+');
		eprintlist(varlist.list);
		eprintlist(arglist.list);
		out2c('\n');
	}

	/* Now locate the command. */
	if (argc == 0) {
		cmdentry.cmdtype = CMDBUILTIN;
		firstbltin = cmdentry.u.cmd = BLTINCMD;
	} else {
		const char *oldpath;
		int findflag = DO_ERR;
		int oldfindflag;

		/*
		 * Modify the command lookup path, if a PATH= assignment
		 * is present
		 */
		for (sp = varlist.list ; sp ; sp = sp->next)
			if (varequal(sp->text, defpathvar)) {
				path = sp->text + 5;
				findflag |= DO_BRUTE;
			}
		oldpath = path;
		oldfindflag = findflag;
		firstbltin = 0;
		for(;;) {
			find_command(argv[0], &cmdentry, findflag, path);
			if (cmdentry.cmdtype == CMDUNKNOWN) {   /* command not found */
				exitstatus = 127;
				goto out;
			}
			/* implement bltin and command here */
			if (cmdentry.cmdtype != CMDBUILTIN) {
				break;
			}
			if (!firstbltin) {
				firstbltin = cmdentry.u.cmd;
			}
			if (cmdentry.u.cmd == BLTINCMD) {
				for(;;) {
					struct builtincmd *bcmd;

					argv++;
					if (--argc == 0)
						goto found;
					if (!(bcmd = find_builtin(*argv))) {
						out2fmt("%s: not found\n", *argv);
						exitstatus = 127;
						goto out;
					}
					cmdentry.u.cmd = bcmd;
					if (bcmd != BLTINCMD)
						break;
				}
			}
			if (cmdentry.u.cmd == find_builtin("command")) {
				argv++;
				if (--argc == 0) {
					goto found;
				}
				if (*argv[0] == '-') {
					if (!equal(argv[0], "-p")) {
						argv--;
						argc++;
						break;
					}
					argv++;
					if (--argc == 0) {
						goto found;
					}
					path = defpath;
					findflag |= DO_BRUTE;
				} else {
					path = oldpath;
					findflag = oldfindflag;
				}
				findflag |= DO_NOFUN;
				continue;
			}
found:
			break;
		}
	}

	/* Fork off a child process if necessary. */
	if (cmd->ncmd.backgnd
	 || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
	) {
		jp = makejob(cmd, 1);
		mode = cmd->ncmd.backgnd;
		if (forkshell(jp, cmd, mode) != 0)
			goto parent;    /* at end of routine */
		flags |= EV_EXIT;
	}

	/* This is the child process if a fork occurred. */
	/* Execute the command. */
	if (cmdentry.cmdtype == CMDFUNCTION) {
#ifdef DEBUG
		trputs("Shell function:  ");  trargs(argv);
#endif
		exitstatus = oexitstatus;
		redirect(cmd->ncmd.redirect, REDIR_PUSH);
		saveparam = shellparam;
		shellparam.malloc = 0;
		shellparam.nparam = argc - 1;
		shellparam.p = argv + 1;
		INTOFF;
		savelocalvars = localvars;
		localvars = NULL;
		INTON;
		if (setjmp(jmploc.loc)) {
			if (exception == EXSHELLPROC) {
				freeparam((volatile struct shparam *)
				    &saveparam);
			} else {
				saveparam.optind = shellparam.optind;
				saveparam.optoff = shellparam.optoff;
				freeparam(&shellparam);
				shellparam = saveparam;
			}
			poplocalvars();
			localvars = savelocalvars;
			handler = savehandler;
			longjmp(handler->loc, 1);
		}
		savehandler = handler;
		handler = &jmploc;
		for (sp = varlist.list ; sp ; sp = sp->next)
			mklocal(sp->text);
		funcnest++;
		evaltree(cmdentry.u.func, flags & EV_TESTED);
		funcnest--;
		INTOFF;
		poplocalvars();
		localvars = savelocalvars;
		saveparam.optind = shellparam.optind;
		saveparam.optoff = shellparam.optoff;
		freeparam(&shellparam);
		shellparam = saveparam;
		handler = savehandler;
		popredir();
		INTON;
		if (evalskip == SKIPFUNC) {
			evalskip = 0;
			skipcount = 0;
		}
		if (flags & EV_EXIT)
			exitshell(exitstatus);
	} else if (cmdentry.cmdtype == CMDBUILTIN) {
#ifdef DEBUG
		trputs("builtin command:  ");  trargs(argv);
#endif
		mode = (cmdentry.u.cmd == EXECCMD)? 0 : REDIR_PUSH;
		redirect(cmd->ncmd.redirect, mode);
		savecmdname = commandname;
		if (IS_BUILTIN_SPECIAL(firstbltin)) {
			listsetvar(varlist.list);
		} else {
			cmdenviron = varlist.list;
		}
		e = -1;
		if (setjmp(jmploc.loc)) {
			e = exception;
			exitstatus = (e == EXINT)? SIGINT+128 : 2;
			goto cmddone;
		}
		savehandler = handler;
		handler = &jmploc;
		commandname = argv[0];
		argptr = argv + 1;
		optptr = NULL;                  /* initialize nextopt */
		exitstatus = (*cmdentry.u.cmd->builtinfunc)(argc, argv);
		flushall();
cmddone:
		cmdenviron = NULL;
		if (e != EXSHELLPROC) {
			commandname = savecmdname;
			if (flags & EV_EXIT)
				exitshell(exitstatus);
		}
		handler = savehandler;
		if (e != -1) {
			if ((e != EXERROR && e != EXEXEC)
			   || cmdentry.u.cmd == BLTINCMD
			   || cmdentry.u.cmd == DOTCMD
			   || cmdentry.u.cmd == EVALCMD
			   || cmdentry.u.cmd == EXECCMD)
				exraise(e);
			FORCEINTON;
		}
		if (cmdentry.u.cmd != EXECCMD)
			popredir();
	} else {
#ifdef DEBUG
		trputs("normal command:  ");  trargs(argv);
#endif
		redirect(cmd->ncmd.redirect, 0);
		clearredir();
		for (sp = varlist.list ; sp ; sp = sp->next)
			setvareq(sp->text, VEXPORT|VSTACK);
		envp = environment();
		shellexec(argv, envp, path, cmdentry.u.index);
	}
	goto out;

parent: /* parent process gets here (if we forked) */
	if (mode == 0) {        /* argument to fork */
		INTOFF;
		exitstatus = waitforjob(jp);
		INTON;
	}

out:
	if (lastarg)
		setvar("_", lastarg, 0);
	popstackmark(&smark);
}

/*
 * Evaluate a parse tree.  The value is left in the global variable
 * exitstatus.
 */
static void
evaltree(n, flags)
	union node *n;
	int flags;
{
	int checkexit = 0;
	if (n == NULL) {
		TRACE(("evaltree(NULL) called\n"));
		goto out;
	}
	TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type));
	switch (n->type) {
	case NSEMI:
		evaltree(n->nbinary.ch1, flags & EV_TESTED);
		if (evalskip)
			goto out;
		evaltree(n->nbinary.ch2, flags);
		break;
	case NAND:
		evaltree(n->nbinary.ch1, EV_TESTED);
		if (evalskip || exitstatus != 0)
			goto out;
		evaltree(n->nbinary.ch2, flags);
		break;
	case NOR:
		evaltree(n->nbinary.ch1, EV_TESTED);
		if (evalskip || exitstatus == 0)
			goto out;
		evaltree(n->nbinary.ch2, flags);
		break;
	case NREDIR:
		expredir(n->nredir.redirect);
		redirect(n->nredir.redirect, REDIR_PUSH);
		evaltree(n->nredir.n, flags);
		popredir();
		break;
	case NSUBSHELL:
		evalsubshell(n, flags);
		break;
	case NBACKGND:
		evalsubshell(n, flags);
		break;
	case NIF: {
		evaltree(n->nif.test, EV_TESTED);
		if (evalskip)
			goto out;
		if (exitstatus == 0)
			evaltree(n->nif.ifpart, flags);
		else if (n->nif.elsepart)
			evaltree(n->nif.elsepart, flags);
		else
			exitstatus = 0;
		break;
	}
	case NWHILE:
	case NUNTIL:
		evalloop(n, flags);
		break;
	case NFOR:
		evalfor(n, flags);
		break;
	case NCASE:
		evalcase(n, flags);
		break;
	case NDEFUN: {
		struct builtincmd *bcmd;
		struct cmdentry entry;
		if (
			(bcmd = find_builtin(n->narg.text)) &&
			IS_BUILTIN_SPECIAL(bcmd)
		) {
			out2fmt("%s is a special built-in\n", n->narg.text);
			exitstatus = 1;
			break;
		}
		entry.cmdtype = CMDFUNCTION;
		entry.u.func = copyfunc(n->narg.next);
		addcmdentry(n->narg.text, &entry);
		exitstatus = 0;
		break;
	}
	case NNOT:
		evaltree(n->nnot.com, EV_TESTED);
		exitstatus = !exitstatus;
		break;

	case NPIPE:
		evalpipe(n);
		checkexit = 1;
		break;
	case NCMD:
		evalcommand(n, flags);
		checkexit = 1;
		break;
#ifdef DEBUG
	default:
		printf("Node type = %d\n", n->type);
		break;
#endif
	}
out:
	if (pendingsigs)
		dotrap();
	if (
		flags & EV_EXIT ||
		(checkexit && eflag && exitstatus && !(flags & EV_TESTED))
	)
		exitshell(exitstatus);
}

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

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

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

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

static void fixredir(union node *n, const char *text, int err);

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

	for (redir = n ; redir ; redir = redir->nfile.next) {
		struct arglist fn;
		fn.lastp = &fn.list;
		oexitstatus = exitstatus;
		switch (redir->type) {
		case NFROMTO:
		case NFROM:
		case NTO:
		case NAPPEND:
		case NTOOV:
			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;
		}
	}
}


/*
 * 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 pip[2];
	struct job *jp;
	struct stackmark smark;         /* unnecessary */

	setstackmark(&smark);
	result->fd = -1;
	result->buf = NULL;
	result->nleft = 0;
	result->jp = NULL;
	if (n == NULL) {
		exitstatus = 0;
		goto out;
	}
	exitstatus = 0;
	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);
			dup_as_newfd(pip[1], 1);
			close(pip[1]);
		}
		eflag = 0;
		evaltree(n, EV_EXIT);
	}
	close(pip[1]);
	result->fd = pip[0];
	result->jp = jp;
out:
	popstackmark(&smark);
	TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
		result->fd, result->buf, result->nleft, result->jp));
}


/*
 * Execute a simple command.
 */

/*
 * 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.  The check for "goodname" is an overly conservative
 * check that the name will not be subject to expansion.
 */

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

	if (n->type == NCMD && n->ncmd.args)
		if (goodname(n->ncmd.args->narg.text))
			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, or a bltin command with no arguments.  Set the
 * specified variables.
 */

int
bltincmd(argc, argv)
	int argc;
	char **argv;
{
	/*
	 * Preserve exitstatus of a previous possible redirection
	 * as POSIX mandates
	 */
	return 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(argc, argv)
	int argc;
	char **argv;
{
	int n = argc > 1 ? number(argv[1]) : 1;

	if (n > loopnest)
		n = loopnest;
	if (n > 0) {
		evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
		skipcount = n;
	}
	return 0;
}


/*
 * The return command.
 */

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

	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;
	}
}


#ifndef BB_TRUE_FALSE
static int
false_main(argc, argv)
	int argc;
	char **argv;
{
	return 1;
}


static int
true_main(argc, argv)
	int argc;
	char **argv;
{
	return 0;
}
#endif

/*
 * Controls whether the shell is interactive or not.
 */

static void setsignal(int signo);
static void chkmail(int silent);


static void
setinteractive(int on)
{
	static int is_interactive;
	static int do_banner=0;

	if (on == is_interactive)
		return;
	setsignal(SIGINT);
	setsignal(SIGQUIT);
	setsignal(SIGTERM);
	chkmail(1);
	is_interactive = on;
	if (do_banner==0 && is_interactive) {
		/* Looks like they want an interactive shell */
		printf( "\n\n" BB_BANNER " Built-in shell (ash)\n");
		printf( "Enter 'help' for a list of built-in commands.\n\n");
		do_banner=1;
	}
}

static void
optschanged(void)
{
	setinteractive(iflag);
	setjobctl(mflag);
}


static int
execcmd(argc, argv)
	int argc;
	char **argv;
{
	if (argc > 1) {
		struct strlist *sp;

		iflag = 0;              /* exit on error */
		mflag = 0;
		optschanged();
		for (sp = cmdenviron; sp ; sp = sp->next)
			setvareq(sp->text, VEXPORT|VSTACK);
		shellexec(argv + 1, environment(), pathval(), 0);
	}
	return 0;
}

static void
eprintlist(struct strlist *sp)
{
	for (; sp; sp = sp->next) {
		out2fmt(" %s",sp->text);
	}
}

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

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

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

	if (strchr(argv[0], '/') != NULL) {
		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;
	}
	exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
	/* NOTREACHED */
}

/*
 * Clear traps on a fork.
 */
static 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;
		}
	}
}


static void
initshellproc(void) {

#ifdef ASH_ALIAS
      /* from alias.c: */
      {
	      rmaliases();
      }
#endif
      /* from eval.c: */
      {
	      exitstatus = 0;
      }

      /* from exec.c: */
      {
	      deletefuncs();
      }

      /* from jobs.c: */
      {
	      backgndpid = -1;
#ifdef JOBS
	      jobctl = 0;
#endif
      }

      /* from options.c: */
      {
	      int i;

	      for (i = 0; i < NOPTS; i++)
		      optent_val(i) = 0;
	      optschanged();

      }

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

      /* from trap.c: */
      {
	      char *sm;

	      clear_traps();
	      for (sm = sigmode ; sm < sigmode + NSIG - 1; sm++) {
		      if (*sm == S_IGN)
			      *sm = S_HARD_IGN;
	      }
      }

      /* from var.c: */
      {
	      shprocvar();
      }
}

static int preadbuffer(void);
static void pushfile (void);

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

#ifndef ASH_OPTIMIZE_FOR_SIZE
#define pgetc_macro()   (--parsenleft >= 0? *parsenextc++ : preadbuffer())
static int
pgetc(void)
{
	return pgetc_macro();
}
#else
static int
pgetc_macro(void)
{
	return --parsenleft >= 0? *parsenextc++ : preadbuffer();
}

static inline int
pgetc(void)
{
	return pgetc_macro();
}
#endif


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

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


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() {
	popallfiles();
	if (parsefile->fd > 0) {
		close(parsefile->fd);
		parsefile->fd = 0;
	}
}


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

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


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

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

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


static void
tryexec(char *cmd, char **argv, char **envp)
{
	int e;

#ifdef BB_FEATURE_SH_STANDALONE_SHELL
	char *name = cmd;
	char** argv_l=argv;
	int argc_l;
#ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN
	name = get_last_path_component(name);
#endif
	argv_l=envp;
	for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++)
		putenv(*argv_l);
	argv_l=argv;
	for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++)
	optind = 1;
	run_applet_by_name(name, argc_l, argv);
#endif
	execve(cmd, argv, envp);
	e = errno;
	if (e == ENOEXEC) {
		INTOFF;
		initshellproc();
		setinputfile(cmd, 0);
		commandname = arg0 = savestr(argv[0]);
		setparam(argv + 1);
		exraise(EXSHELLPROC);
	}
	errno = e;
}

static char *commandtext (const union node *);

/*
 * 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 const char *pathopt;

static void growstackblock(void);


static char *
padvance(const char **path, const char *name)
{
	const char *p;
	char *q;
	const char *start;
	int 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);
}

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

/*
 * Find a keyword is in a sorted array.
 */

static const char *const *
findkwd(const char *s)
{
	return  bsearch(s, parsekwd, sizeof(parsekwd) / sizeof(const char *),
			sizeof(const char *), pstrcmp);
}


/*** Command hashing code ***/


static int
hashcmd(argc, argv)
	int argc;
	char **argv;
{
	struct tblentry **pp;
	struct tblentry *cmdp;
	int c;
	int verbose;
	struct cmdentry entry;
	char *name;
#ifdef ASH_ALIAS
	const struct alias *ap;
#endif

	verbose = 0;
	while ((c = nextopt("rvV")) != '\0') {
		if (c == 'r') {
			clearcmdentry(0);
			return 0;
		} else if (c == 'v' || c == 'V') {
			verbose = c;
		}
	}
	if (*argptr == NULL) {
		for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
			for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
				if (cmdp->cmdtype != CMDBUILTIN) {
					printentry(cmdp, verbose);
				}
			}
		}
		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();
#ifdef ASH_ALIAS
	/* Then look at the aliases */
		if ((ap = lookupalias(name, 0)) != NULL) {
			if (verbose=='v')
				printf("%s is an alias for %s\n", name, ap->val);
			else
				printalias(ap);
			continue;
		}
#endif
			/* First look at the keywords */
		if (findkwd(name)!=0) {
			if (verbose=='v')
				printf("%s is a shell keyword\n", name);
			else
				printf(snlfmt, name);
			continue;
		}

		find_command(name, &entry, DO_ERR, pathval());
		if (entry.cmdtype == CMDUNKNOWN) c = 1;
		else if (verbose) {
			cmdp = cmdlookup(name, 0);
			if (cmdp) printentry(cmdp, verbose=='v');
			flushall();
		}
	}
	return c;
}

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

	printf("%s%s", cmdp->cmdname, (verbose ? " is " : ""));
	if (cmdp->cmdtype == CMDNORMAL) {
		idx = cmdp->param.index;
		path = pathval();
		do {
			name = padvance(&path, cmdp->cmdname);
			stunalloc(name);
		} while (--idx >= 0);
		if(verbose)
			out1str(name);
	} else if (cmdp->cmdtype == CMDBUILTIN) {
		if(verbose)
			out1str("a shell builtin");
	} else if (cmdp->cmdtype == CMDFUNCTION) {
		if (verbose) {
			INTOFF;
			out1str("a function\n");
			name = commandtext(cmdp->param.func);
			printf("%s() {\n %s\n}", cmdp->cmdname, name);
			ckfree(name);
			INTON;
		}
#ifdef DEBUG
	} else {
		error("internal error: cmdtype %d", cmdp->cmdtype);
#endif
	}
	printf(snlfmt, cmdp->rehash ? "*" : nullstr);
}



/*** List the available builtins ***/


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

	printf("\nBuilt-in commands:\n-------------------\n");
	for (col=0, i=0; i < NUMBUILTINS; i++) {
		col += printf("%c%s", ((col == 0) ? '\t' : ' '),
				builtincmds[i].name+1);
		if (col > 60) {
			printf("\n");
			col = 0;
		}
	}
#ifdef BB_FEATURE_SH_STANDALONE_SHELL
	{
		extern const struct BB_applet applets[];
		extern const size_t NUM_APPLETS;

		for (i=0; i < NUM_APPLETS; i++) {

			col += printf("%c%s", ((col == 0) ? '\t' : ' '),
					applets[i].name);
			if (col > 60) {
				printf("\n");
				col = 0;
			}
		}
	}
#endif
	printf("\n\n");
	return EXIT_SUCCESS;
}

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

static int prefix (const char *, const char *);

static void
find_command(const 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 bltin;
	int firstchange;
	int updatetbl;
	int regular;
	struct builtincmd *bcmd;

	/* If name contains a slash, don't use the hash table */
	if (strchr(name, '/') != NULL) {
		if (act & DO_ABS) {
			while (stat(name, &statb) < 0) {
				if (errno != ENOENT && errno != ENOTDIR)
					e = errno;
				entry->cmdtype = CMDUNKNOWN;
				entry->u.index = -1;
				return;
			}
			entry->cmdtype = CMDNORMAL;
			entry->u.index = -1;
			return;
		}
		entry->cmdtype = CMDNORMAL;
		entry->u.index = 0;
		return;
	}

	updatetbl = 1;
	if (act & DO_BRUTE) {
		firstchange = path_change(path, &bltin);
	} else {
		bltin = builtinloc;
		firstchange = 9999;
	}

	/* If name is in the table, and not invalidated by cd, we're done */
	if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) {
		if (cmdp->cmdtype == CMDFUNCTION) {
			if (act & DO_NOFUN) {
				updatetbl = 0;
			} else {
				goto success;
			}
		} else if (act & DO_BRUTE) {
			if ((cmdp->cmdtype == CMDNORMAL &&
			     cmdp->param.index >= firstchange) ||
			    (cmdp->cmdtype == CMDBUILTIN &&
			     ((builtinloc < 0 && bltin >= 0) ?
			      bltin : builtinloc) >= firstchange)) {
				/* need to recompute the entry */
			} else {
				goto success;
			}
		} else {
			goto success;
		}
	}

	bcmd = find_builtin(name);
	regular = bcmd && IS_BUILTIN_REGULAR(bcmd);

	if (regular) {
		if (cmdp && (cmdp->cmdtype == CMDBUILTIN)) {
			goto success;
		}
	} else if (act & DO_BRUTE) {
		if (firstchange == 0) {
			updatetbl = 0;
		}
	}

	/* If %builtin not in path, check for builtin next */
	if (regular || (bltin < 0 && bcmd)) {
builtin:
		if (!updatetbl) {
			entry->cmdtype = CMDBUILTIN;
			entry->u.cmd = bcmd;
			return;
		}
		INTOFF;
		cmdp = cmdlookup(name, 1);
		cmdp->cmdtype = CMDBUILTIN;
		cmdp->param.cmd = bcmd;
		INTON;
		goto 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 (idx >= firstchange) {
			updatetbl = 0;
		}
		if (pathopt) {
			if (prefix("builtin", pathopt)) {
				if ((bcmd = find_builtin(name))) {
					goto builtin;
				}
				continue;
			} else if (!(act & DO_NOFUN) &&
				   prefix("func", pathopt)) {
				/* handled below */
			} else {
				continue;       /* ignore unimplemented options */
			}
		}
		/* if rehash, don't redo absolute path names */
		if (fullname[0] == '/' && idx <= prev &&
		    idx < firstchange) {
			if (idx < prev)
				continue;
			TRACE(("searchexec \"%s\": no change\n", name));
			goto success;
		}
		while (stat(fullname, &statb) < 0) {
			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 we aren't called with DO_BRUTE and cmdp is set, it must
		   be a function and we're being called with DO_NOFUN */
		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)
		out2fmt("%s: %s\n", name, errmsg(e, E_EXEC));
	entry->cmdtype = CMDUNKNOWN;
	return;

success:
	cmdp->rehash = 0;
	entry->cmdtype = cmdp->cmdtype;
	entry->u = cmdp->param;
}



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

static int
bstrcmp(const void *name, const void *b)
{
	return strcmp((const char *)name, (*(const char *const *) b)+1);
}

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

	bp = bsearch(name, builtincmds, NUMBUILTINS, sizeof(struct builtincmd),
		bstrcmp
	);
	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 && builtinloc >= 0))
				cmdp->rehash = 1;
		}
	}
}



/*
 * 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)
{
	int firstchange;
	int bltin;

	firstchange = path_change(newval, &bltin);
	if (builtinloc < 0 && bltin >= 0)
		builtinloc = bltin;             /* zap builtins */
	clearcmdentry(firstchange);
	builtinloc = bltin;
}


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

static void
clearcmdentry(firstchange)
	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;
}


/*
 * Delete all functions.
 */

static void
deletefuncs(void) {
	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 == CMDFUNCTION) {
				*pp = cmdp->next;
				freefunc(cmdp->param.func);
				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.
 */

static struct tblentry **lastcmdentry;

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

	p = name;
	hashval = *p << 4;
	while (*p)
		hashval += *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) {
		INTOFF;
		cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
					+ strlen(name) + 1);
		cmdp->next = NULL;
		cmdp->cmdtype = CMDUNKNOWN;
		cmdp->rehash = 0;
		strcpy(cmdp->cmdname, name);
		INTON;
	}
	lastcmdentry = pp;
	return cmdp;
}

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

static void
delete_cmd_entry() {
	struct tblentry *cmdp;

	INTOFF;
	cmdp = *lastcmdentry;
	*lastcmdentry = cmdp->next;
	ckfree(cmdp);
	INTON;
}





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



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

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

	if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
		freefunc(cmdp->param.func);
		delete_cmd_entry();
	}
}


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

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

	argv_a[1] = 0;

	for (i = 1; i < argc; i++) {
		argv_a[0] = argv[i];
		argptr = argv_a;
		optptr = "v";
		err |= hashcmd(argc, argv);
	}
	return err;
}

#ifdef ASH_CMDCMD
static int
commandcmd(argc, argv)
	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) {
		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) {
			out2str(
				"command [-p] command [arg ...]\n"
				"command {-v|-V} command\n");
			return EX_USAGE;
	}

	if (verify_only || verbose_verify_only) {
		char *argv_a[2];

		argv_a[1] = 0;
		argv_a[0] = *argptr;
		argptr = argv_a;
		optptr = verbose_verify_only ? "v" : "V"; /* reverse special */
		return hashcmd(argc, argv);
	}

	return 0;
}
#endif

static int
path_change(newval, bltin)
	const char *newval;
	int *bltin;
{
	const char *old, *new;
	int idx;
	int firstchange;

	old = pathval();
	new = newval;
	firstchange = 9999;     /* assume no change */
	idx = 0;
	*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 == '%' && *bltin < 0 && prefix("builtin", new + 1))
			*bltin = idx;
		if (*new == ':') {
			idx++;
		}
		new++, old++;
	}
	if (builtinloc >= 0 && *bltin < 0)
		firstchange = 0;
	return firstchange;
}
/*
 * 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 */

/*
 * 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 */
};


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

static void argstr (char *, int);
static char *exptilde (char *, int);
static void expbackq (union node *, int, int);
static int subevalvar (char *, char *, int, int, int, int, int);
static int varisset (char *, int);
static void strtodest (const char *, const char *, int);
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);
#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
#define preglob(p) _rmescapes((p), RMESCAPE_ALLOC | RMESCAPE_GLOB)
#if !defined(GLOB_BROKEN)
static void addglob (const glob_t *);
#endif
#endif
#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
static void expmeta (char *, char *);
#endif
#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
static struct strlist *expsort (struct strlist *);
static struct strlist *msort (struct strlist *, int);
#endif
static int patmatch (char *, char *, int);
#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
static int patmatch2 (char *, char *, int);
#else
static int pmatch (char *, char *, int);
#define patmatch2 patmatch
#endif
static char *cvtnum (int, char *);

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

/* arg: the document, fd: where to write the expanded version */
static inline void
expandhere(union node *arg, int fd)
{
	herefd = fd;
	expandarg(arg, (struct arglist *)NULL, 0);
	xwrite(fd, stackblock(), expdest - 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.
 */

static void
expandarg(arg, arglist, flag)
	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;
	}
	ifsfree();
	*exparg.lastp = NULL;
	if (exparg.list) {
		*arglist->lastp = exparg.list;
		arglist->lastp = exparg.lastp;
	}
}


/*
 * Expand a variable, and return a pointer to the next character in the
 * input string.
 */

static inline char *
evalvar(p, flag)
	char *p;
	int flag;
{
	int subtype;
	int varflags;
	char *var;
	const char *val;
	int patloc;
	int c;
	int set;
	int special;
	int startloc;
	int varlen;
	int easy;
	int quotes = flag & (EXP_FULL | EXP_CASE);

	varflags = *p++;
	subtype = varflags & VSTYPE;
	var = p;
	special = 0;
	if (! is_name(*p))
		special = 1;
	p = strchr(p, '=') + 1;
again: /* jump here after setting a variable with ${var=text} */
	if (special) {
		set = varisset(var, varflags & VSNUL);
		val = NULL;
	} else {
		val = lookupvar(var);
		if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
			val = NULL;
			set = 0;
		} else
			set = 1;
	}
	varlen = 0;
	startloc = expdest - stackblock();
	if (set && subtype != VSPLUS) {
		/* insert the value of the variable */
		if (special) {
			varvalue(var, varflags & VSQUOTE, flag);
			if (subtype == VSLENGTH) {
				varlen = expdest - stackblock() - startloc;
				STADJUST(-varlen, expdest);
			}
		} else {
			if (subtype == VSLENGTH) {
				varlen = strlen(val);
			} else {
				strtodest(
					val,
					varflags & VSQUOTE ?
						DQSYNTAX : BASESYNTAX,
					quotes
				);
			}
		}
	}

	if (subtype == VSPLUS)
		set = ! set;

	easy = ((varflags & VSQUOTE) == 0 ||
		(*var == '@' && shellparam.nparam != 1));


	switch (subtype) {
	case VSLENGTH:
		expdest = cvtnum(varlen, expdest);
		goto record;

	case VSNORMAL:
		if (!easy)
			break;
record:
		recordregion(startloc, expdest - stackblock(),
			     varflags & VSQUOTE);
		break;

	case VSPLUS:
	case VSMINUS:
		if (!set) {
			argstr(p, flag);
			break;
		}
		if (easy)
			goto record;
		break;

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

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

#ifdef DEBUG
	default:
		abort();
#endif
	}

	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;
}


/*
 * 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(p, flag)
	char *p;
	int flag;
{
	char c;
	int quotes = flag & (EXP_FULL | EXP_CASE);      /* do CTLESC */
	int firsteq = 1;

	if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
		p = exptilde(p, flag);
	for (;;) {
		switch (c = *p++) {
		case '\0':
		case CTLENDVAR: /* ??? */
			goto breakloop;
		case CTLQUOTEMARK:
			/* "$@" syntax adherence hack */
			if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
				break;
			if ((flag & EXP_FULL) != 0)
				STPUTC(c, expdest);
			break;
		case CTLESC:
			if (quotes)
				STPUTC(c, expdest);
			c = *p++;
			STPUTC(c, expdest);
			break;
		case CTLVAR:
			p = evalvar(p, flag);
			break;
		case CTLBACKQ:
		case CTLBACKQ|CTLQUOTE:
			expbackq(argbackq->n, c & CTLQUOTE, flag);
			argbackq = argbackq->next;
			break;
#ifdef ASH_MATH_SUPPORT
		case CTLENDARI:
			expari(flag);
			break;
#endif
		case ':':
		case '=':
			/*
			 * sort of a hack - expand tildes in variable
			 * assignments (after the first '=' and after ':'s).
			 */
			STPUTC(c, expdest);
			if (flag & EXP_VARTILDE && *p == '~') {
				if (c == '=') {
					if (firsteq)
						firsteq = 0;
					else
						break;
				}
				p = exptilde(p, flag);
			}
			break;
		default:
			STPUTC(c, expdest);
		}
	}
breakloop:;
	return;
}

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

	while ((c = *p) != '\0') {
		switch(c) {
		case CTLESC:
			return (startp);
		case CTLQUOTEMARK:
			return (startp);
		case ':':
			if (flag & EXP_VARTILDE)
				goto done;
			break;
		case '/':
			goto done;
		}
		p++;
	}
done:
	*p = '\0';
	if (*(startp+1) == '\0') {
		if ((home = lookupvar("HOME")) == NULL)
			goto lose;
	} else {
		if ((pw = getpwnam(startp+1)) == NULL)
			goto lose;
		home = pw->pw_dir;
	}
	if (*home == '\0')
		goto lose;
	*p = c;
	strtodest(home, SQSYNTAX, quotes);
	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 ASH_MATH_SUPPORT
/*
 * Expand arithmetic expression.  Backup to start of expression,
 * evaluate, place result in (backed up) result, adjust string position.
 */
static void
expari(int flag)
{
	char *p, *start;
	int errcode;
	int result;
	int begoff;
	int quotes = flag & (EXP_FULL | EXP_CASE);
	int quoted;

	/*      ifsfree(); */

	/*
	 * This routine is slightly over-complicated for
	 * efficiency.  First we make sure there is
	 * enough space for the result, which may be bigger
	 * than the expression if we add exponentation.  Next we
	 * scan backwards looking for the start of arithmetic.  If the
	 * next previous character is a CTLESC character, then we
	 * have to rescan starting from the beginning since CTLESC
	 * characters have to be processed left to right.
	 */
	CHECKSTRSPACE(10, expdest);
	USTPUTC('\0', expdest);
	start = stackblock();
	p = expdest - 1;
	while (*p != CTLARI && p >= start)
		--p;
	if (*p != CTLARI)
		error("missing CTLARI (shouldn't happen)");
	if (p > start && *(p-1) == CTLESC)
		for (p = start; *p != CTLARI; p++)
			if (*p == CTLESC)
				p++;

	if (p[1] == '"')
		quoted=1;
	else
		quoted=0;
	begoff = p - start;
	removerecordregions(begoff);
	if (quotes)
		rmescapes(p+2);
	result = arith(p+2, &errcode);
	if (errcode < 0) {
		if(errcode == -2)
			error("divide by zero");
		else
			error("syntax error: \"%s\"\n", p+2);
	}
	snprintf(p, 12, "%d", result);

	while (*p++)
		;

	if (quoted == 0)
		recordregion(begoff, p - 1 - start, 0);
	result = expdest - p + 1;
	STADJUST(-result, expdest);
}
#endif

/*
 * Expand stuff in backwards quotes.
 */

static void
expbackq(cmd, quoted, flag)
	union node *cmd;
	int quoted;
	int flag;
{
	volatile struct backcmd in;
	int i;
	char buf[128];
	char *p;
	char *dest = expdest;
	volatile struct ifsregion saveifs;
	struct ifsregion *volatile savelastp;
	struct nodelist *volatile saveargbackq;
	char lastc;
	int startloc = dest - stackblock();
	char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
	volatile int saveherefd;
	int quotes = flag & (EXP_FULL | EXP_CASE);
	struct jmploc jmploc;
	struct jmploc *volatile savehandler;
	int ex;

#if __GNUC__
	/* Avoid longjmp clobbering */
	(void) &dest;
	(void) &syntax;
#endif

	in.fd = -1;
	in.buf = 0;
	in.jp = 0;

	INTOFF;
	saveifs = ifsfirst;
	savelastp = ifslastp;
	saveargbackq = argbackq;
	saveherefd = herefd;
	herefd = -1;
	if ((ex = setjmp(jmploc.loc))) {
		goto err1;
	}
	savehandler = handler;
	handler = &jmploc;
	INTON;
	p = grabstackstr(dest);
	evalbackcmd(cmd, (struct backcmd *) &in);
	ungrabstackstr(p, dest);
err1:
	INTOFF;
	ifsfirst = saveifs;
	ifslastp = savelastp;
	argbackq = saveargbackq;
	herefd = saveherefd;
	if (ex) {
		goto err2;
	}

	p = in.buf;
	lastc = '\0';
	for (;;) {
		if (--in.nleft < 0) {
			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;
			in.nleft = i - 1;
		}
		lastc = *p++;
		if (lastc != '\0') {
			if (quotes && syntax[(int)lastc] == CCTL)
				STPUTC(CTLESC, dest);
			STPUTC(lastc, dest);
		}
	}

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

err2:
	if (in.fd >= 0)
		close(in.fd);
	if (in.buf)
		ckfree(in.buf);
	if (in.jp)
		exitstatus = waitforjob(in.jp);
	handler = savehandler;
	if (ex) {
		longjmp(handler->loc, 1);
	}
	if (quoted == 0)
		recordregion(startloc, dest - stackblock(), 0);
	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
		(dest - stackblock()) - startloc,
		(dest - stackblock()) - startloc,
		stackblock() + startloc));
	expdest = dest;
	INTON;
}

static int
subevalvar(p, str, strloc, subtype, startloc, varflags, quotes)
	char *p;
	char *str;
	int strloc;
	int subtype;
	int startloc;
	int varflags;
	int quotes;
{
	char *startp;
	char *loc = NULL;
	char *q;
	int c = 0;
	int saveherefd = herefd;
	struct nodelist *saveargbackq = argbackq;
	int amount;

	herefd = -1;
	argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0);
	STACKSTRNUL(expdest);
	herefd = saveherefd;
	argbackq = saveargbackq;
	startp = stackblock() + startloc;
	if (str == NULL)
	    str = stackblock() + strloc;

	switch (subtype) {
	case VSASSIGN:
		setvar(str, startp, 0);
		amount = startp - expdest;
		STADJUST(amount, expdest);
		varflags &= ~VSNUL;
		if (c != 0)
			*loc = c;
		return 1;

	case VSQUESTION:
		if (*p != CTLENDVAR) {
			out2fmt(snlfmt, startp);
			error((char *)NULL);
		}
		error("%.*s: parameter %snot set", p - str - 1,
		      str, (varflags & VSNUL) ? "null or "
					      : nullstr);
		/* NOTREACHED */

	case VSTRIMLEFT:
		for (loc = startp; loc < str; loc++) {
			c = *loc;
			*loc = '\0';
			if (patmatch2(str, startp, quotes))
				goto recordleft;
			*loc = c;
			if (quotes && *loc == CTLESC)
				loc++;
		}
		return 0;

	case VSTRIMLEFTMAX:
		for (loc = str - 1; loc >= startp;) {
			c = *loc;
			*loc = '\0';
			if (patmatch2(str, startp, quotes))
				goto recordleft;
			*loc = c;
			loc--;
			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
				for (q = startp; q < loc; q++)
					if (*q == CTLESC)
						q++;
				if (q > loc)
					loc--;
			}
		}
		return 0;

	case VSTRIMRIGHT:
		for (loc = str - 1; loc >= startp;) {
			if (patmatch2(str, loc, quotes))
				goto recordright;
			loc--;
			if (quotes && loc > startp && *(loc - 1) == CTLESC) {
				for (q = startp; q < loc; q++)
					if (*q == CTLESC)
						q++;
				if (q > loc)
					loc--;
			}
		}
		return 0;

	case VSTRIMRIGHTMAX:
		for (loc = startp; loc < str - 1; loc++) {
			if (patmatch2(str, loc, quotes))
				goto recordright;
			if (quotes && *loc == CTLESC)
				loc++;
		}
		return 0;

#ifdef DEBUG
	default:
		abort();
#endif
	}

recordleft:
	*loc = c;
	amount = ((str - 1) - (loc - startp)) - expdest;
	STADJUST(amount, expdest);
	while (loc != str - 1)
		*startp++ = *loc++;
	return 1;

recordright:
	amount = loc - expdest;
	STADJUST(amount, expdest);
	STPUTC('\0', expdest);
	STADJUST(-1, expdest);
	return 1;
}


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

static int
varisset(name, nulok)
	char *name;
	int nulok;
{
	if (*name == '!')
		return backgndpid != -1;
	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
strtodest(p, syntax, quotes)
	const char *p;
	const char *syntax;
	int quotes;
{
	while (*p) {
		if (quotes && syntax[(int) *p] == CCTL)
			STPUTC(CTLESC, expdest);
		STPUTC(*p++, expdest);
	}
}

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

static void
varvalue(name, quoted, flags)
	char *name;
	int quoted;
	int flags;
{
	int num;
	char *p;
	int i;
	int sep;
	int sepq = 0;
	char **ap;
	char const *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 = oexitstatus;
		goto numvar;
	case '#':
		num = shellparam.nparam;
		goto numvar;
	case '!':
		num = backgndpid;
numvar:
		expdest = cvtnum(num, expdest);
		break;
	case '-':
		for (i = 0 ; i < NOPTS ; i++) {
			if (optent_val(i))
				STPUTC(optent_letter(optlist[i]), expdest);
		}
		break;
	case '@':
		if (allow_split && quoted) {
			sep = 1 << CHAR_BIT;
			goto param;
		}
		/* fall through */
	case '*':
		sep = ifsset() ? ifsval()[0] : ' ';
		if (quotes) {
			sepq = syntax[(int) sep] == CCTL;
		}
param:
		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
			strtodest(p, syntax, quotes);
			if (*ap && sep) {
				if (sepq)
					STPUTC(CTLESC, expdest);
				STPUTC(sep, expdest);
			}
		}
		break;
	case '0':
		strtodest(arg0, syntax, quotes);
		break;
	default:
		num = atoi(name);
		if (num > 0 && num <= shellparam.nparam) {
			strtodest(shellpar