Remove the now unused ARITH_* defines
[oweals/busybox.git] / shell / ash.c
index 7d394b61714fa7f46a3d21abc9333cd8fd2c412a..5933b151834db179e0db6b7f9455355530ec94b8 100644 (file)
@@ -3,7 +3,7 @@
  * ash shell port for busybox
  *
  * Copyright (c) 1989, 1991, 1993, 1994
- *     The Regents of the University of California.  All rights reserved.
+ *      The Regents of the University of California.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
  * 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.  
+ * 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
  *
- * Modified by Erik Andersen <andersee@debian.org> to be used in busybox.
  *
  * Original copyright notice is retained at the end of this file.
  */
 
-#undef _GNU_SOURCE
-#undef ASH_TYPE
+
+/* 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 it you need it, enable.  Most people will
+ * leave this disabled.  This adds 1k on an x86 system. */
 #undef ASH_GETOPTS
-#undef ASH_MATH_SUPPORT
+
+/* 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
+#define IFS_BROKEN
 
 #include <assert.h>
+#include <stddef.h>
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
@@ -48,7 +93,6 @@
 #include <setjmp.h>
 #include <signal.h>
 #include <stdarg.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <glob.h>
 #endif
 
-#if JOBS
+#ifdef JOBS
 #include <termios.h>
-#undef CEOF                    /* syntax.h redefines this */
 #endif
 
-#include "cmdedit.h"
 #include "busybox.h"
-#include "ash.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 */
+
+#define SYNBASE 130
+#define PEOF -130
+
+#define PEOA -129
+
+#define TEOF 0
+#define TNL 1
+#define TREDIR 2
+#define TWORD 3
+#define TASSIGN 4
+#define TSEMI 5
+#define TBACKGND 6
+#define TAND 7
+#define TOR 8
+#define TPIPE 9
+#define TLP 10
+#define TRP 11
+#define TENDCASE 12
+#define TENDBQUOTE 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
+
+
+
+/* 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_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))))
+
+/*
+ * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
+ * (assuming ascii char codes, as the original implementation did)
+ */
+#define is_special(c) \
+    ( (((unsigned int)c) - 33 < 32) \
+                        && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
+
+#define digit_val(c)    ((c) - '0')
 
 
 #define _DIAGASSERT(x)
 
-#define ATABSIZE 39
 
-#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 */
 
+#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 */
 
 
-struct alias *atab[ATABSIZE];
+/* 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 */
 
-static void setalias __P((char *, char *));
-static struct alias **hashalias __P((const char *));
-static struct alias *freealias __P((struct alias *));
-static struct alias **__lookupalias __P((const char *));
-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 */
+/* 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 */
 
-static void
-setalias(name, val)
-       char *name, *val;
-{
-       struct alias *ap, **app;
+/*
+ * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
+ * so we use _setjmp instead.
+ */
 
-       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;
-}
+#if defined(BSD)
+#define setjmp(jmploc)  _setjmp(jmploc)
+#define longjmp(jmploc, val)    _longjmp(jmploc, val)
+#endif
 
-static int
-unalias(name)
-       char *name;
-       {
-       struct alias **app;
+/*
+ * Most machines require the value returned from malloc to be aligned
+ * in some way.  The following macro will get this right on many machines.
+ */
 
-       app = __lookupalias(name);
+#ifndef ALIGN
+union align {
+       int i;
+       char *cp;
+};
 
-       if (*app) {
-               INTOFF;
-               *app = freealias(*app);
-               INTON;
-               return (0);
-       }
+#define ALIGN(nbytes)   (((nbytes) + sizeof(union align) - 1) & ~(sizeof(union align) - 1))
+#endif
 
-       return (1);
-}
+#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. :-))
+ */
 
-#ifdef mkinit
-static void rmaliases __P((void));
+static void onint (void);
+static volatile int suppressint;
+static volatile int intpending;
 
-SHELLPROC {
-       rmaliases();
-}
+#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
 
-static void
-rmaliases() {
-       struct alias *ap, **app;
-       int i;
+#define CLEAR_PENDING_INT intpending = 0
+#define int_pending() intpending
 
-       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;
-}
 
-struct alias *
-lookupalias(name, check)
-       const char *name;
-       int check;
-{
-       struct alias *ap = *__lookupalias(name);
+typedef void *pointer;
+#ifndef NULL
+#define NULL (void *)0
+#endif
 
-       if (check && ap && (ap->flag & ALIASINUSE))
-               return (NULL);
-       return (ap);
-}
+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 *);
 
 /*
- * TODO - sort output
+ * Parse trees for commands are allocated in lifo order, so we use a stack
+ * to make this more efficient, and also to avoid all sorts of exception
+ * handling code to handle interrupts in the middle of a parse.
+ *
+ * The size 504 was chosen because the Ultrix malloc handles that size
+ * well.
  */
-static int
-aliascmd(argc, argv)
-       int argc;
-       char **argv;
-{
-       char *n, *v;
-       int ret = 0;
-       struct alias *ap;
 
-       if (argc == 1) {
-               int i;
+#define MINSIZE 504             /* minimum size of a block */
 
-               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) {
-                               outfmt(out2, "%s: %s not found\n", "alias", n);
-                               ret = 1;
-                       } else
-                               printalias(ap);
-               }
-               else {
-                       *v++ = '\0';
-                       setalias(n, v);
-               }
-       }
 
-       return (ret);
-}
+struct stack_block {
+       struct stack_block *prev;
+       char space[MINSIZE];
+};
 
-static int
-unaliascmd(argc, argv)
-       int argc;
-       char **argv;
-{
-       int i;
+static struct stack_block stackbase;
+static struct stack_block *stackp = &stackbase;
+static struct stackmark *markp;
+static char *stacknxt = stackbase.space;
+static int stacknleft = MINSIZE;
 
-       while ((i = nextopt("a")) != '\0') {
-               if (i == 'a') {
-                       rmaliases();
-                       return (0);
-               }
-       }
-       for (i = 0; *argptr; argptr++) {
-               if (unalias(*argptr)) {
-                       outfmt(out2, "%s: %s not found\n", "unalias", *argptr);
-                       i = 1;
-               }
-       }
 
-       return (i);
-}
+#define equal(s1, s2)   (strcmp(s1, s2) == 0)
 
-static struct alias **
-hashalias(p)
-       const char *p;
-       {
-       unsigned int hashval;
+#define stackblock() stacknxt
+#define stackblocksize() stacknleft
+#define STARTSTACKSTR(p)        p = stackblock(), sstrnleft = stackblocksize()
 
-       hashval = *p << 4;
-       while (*p)
-               hashval+= *p++;
-       return &atab[hashval % ATABSIZE];
-}
+#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'))
 
-static struct alias *
-freealias(struct alias *ap) {
-       struct alias *next;
 
-       if (ap->flag & ALIASINUSE) {
-               ap->flag |= ALIASDEAD;
-               return ap;
-       }
+#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)
 
-       next = ap->next;
-       ckfree(ap->name);
-       ckfree(ap->val);
-       ckfree(ap);
-       return next;
-}
+#define ckfree(p)       free((pointer)(p))
 
-static void
-printalias(const struct alias *ap) {
-       char *p;
 
-       p = single_quote(ap->val);
-       out1fmt("alias %s=%s\n", ap->name, p);
-       stunalloc(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"
+};
 
-static struct alias **
-__lookupalias(const char *name) {
-       struct alias **app = hashalias(name);
+#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;
+};
 
-       for (; *app; app = &(*app)->next) {
-               if (equal(name, (*app)->name)) {
-                       break;
-               }
-       }
 
-       return app;
-}
+struct ncmd {
+      int type;
+      int backgnd;
+      union node *assign;
+      union node *args;
+      union node *redirect;
+};
 
-#ifdef ASH_MATH_SUPPORT
-/* The generated file arith.c has been snipped.  If you want this
- * stuff back in, feel free to add it to your own copy.  */
-#endif
 
-/*
- * This file was generated by the mkbuiltins program.
- */
+struct npipe {
+      int type;
+      int backgnd;
+      struct nodelist *cmdlist;
+};
 
-static int bgcmd __P((int, char **));
-static int breakcmd __P((int, char **));
-static int cdcmd __P((int, char **));
-static int commandcmd __P((int, char **));
-static int dotcmd __P((int, char **));
-static int evalcmd __P((int, char **));
-static int execcmd __P((int, char **));
-static int exitcmd __P((int, char **));
-static int exportcmd __P((int, char **));
-static int histcmd __P((int, char **));
-static int fgcmd __P((int, char **));
-static int hashcmd __P((int, char **));
-static int jobscmd __P((int, char **));
-static int killcmd __P((int, char **));
-static int localcmd __P((int, char **));
-static int pwdcmd __P((int, char **));
-static int readcmd __P((int, char **));
-static int returncmd __P((int, char **));
-static int setcmd __P((int, char **));
-static int setvarcmd __P((int, char **));
-static int shiftcmd __P((int, char **));
-static int trapcmd __P((int, char **));
-static int umaskcmd __P((int, char **));
-static int unaliascmd __P((int, char **));
-static int unsetcmd __P((int, char **));
-static int waitcmd __P((int, char **));
-static int aliascmd __P((int, char **));
-static int ulimitcmd __P((int, char **));
-static int timescmd __P((int, char **));
-#ifdef ASH_MATH_SUPPORT
-static int expcmd __P((int, char **));
-#endif
-#ifdef ASH_TYPE
-static int typecmd __P((int, char **));
-#endif
-#ifdef ASH_GETOPTS
-static int getoptscmd __P((int, char **));
-#endif
-#ifndef BB_TRUE_FALSE
-static int true_main __P((int, char **));
-static int false_main __P((int, char **));
-#endif
 
-static struct builtincmd *DOTCMD;
-static struct builtincmd *BLTINCMD;
-static struct builtincmd *COMMANDCMD;
-static struct builtincmd *EXECCMD;
-static struct builtincmd *EVALCMD;
+struct nredir {
+      int type;
+      union node *n;
+      union node *redirect;
+};
 
-/* 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[] = {
-       { ".", dotcmd, 1 },
-       { ":", true_main, 1 },
-       { "alias", aliascmd, 6 },
-       { "bg", bgcmd, 2 },
-       { "break", breakcmd, 1 },
-       { "builtin", bltincmd, 1 },
-       { "cd", cdcmd, 2 },
-       { "chdir", cdcmd, 0 },
-       { "command", commandcmd, 2 },
-       { "continue", breakcmd, 1 },
-       { "eval", evalcmd, 1 },
-       { "exec", execcmd, 1 },
-       { "exit", exitcmd, 1 },
-#ifdef ASH_MATH_SUPPORT
-       { "exp", expcmd, 0 },
-#endif
-       { "export", exportcmd, 5 },
-       { "false", false_main, 2 },
-       { "fc", histcmd, 2 },
-       { "fg", fgcmd, 2 },
-#ifdef ASH_GETOPTS
-       { "getopts", getoptscmd, 2 },
-#endif 
-       { "hash", hashcmd, 0 },
-       { "jobs", jobscmd, 2 },
-       { "kill", killcmd, 2 },
-#ifdef ASH_MATH_SUPPORT
-       { "let", expcmd, 0 },
-#endif
-       { "local", localcmd, 4 },
-       { "pwd", pwdcmd, 0 },
-       { "read", readcmd, 2 },
-       { "readonly", exportcmd, 5 },
-       { "return", returncmd, 1 },
-       { "set", setcmd, 1 },
-       { "setvar", setvarcmd, 0 },
-       { "shift", shiftcmd, 1 },
-       { "times", timescmd, 1 },
-       { "trap", trapcmd, 1 },
-       { "true", true_main, 2 },
-#ifdef ASH_TYPE
-       { "type", typecmd, 0 },
-#endif
-       { "ulimit", ulimitcmd, 0 },
-       { "umask", umaskcmd, 2 },
-       { "unalias", unaliascmd, 2 },
-       { "unset", unsetcmd, 1 },
-       { "wait", waitcmd, 2 },
+
+struct nif {
+      int type;
+      union node *test;
+      union node *ifpart;
+      union node *elsepart;
 };
-#define NUMBUILTINS  (sizeof (builtincmds) / sizeof (struct builtincmd) )
 
 
-/*     $NetBSD: cd.c,v 1.27 1999/07/09 03:05:49 christos Exp $ */
+struct nfor {
+      int type;
+      union node *args;
+      union node *body;
+      char *var;
+};
 
-static int docd __P((char *, int));
-static char *getcomponent __P((void));
-static void updatepwd __P((char *));
-static void getpwd __P((void));
 
-static char *curdir = nullstr;         /* current working directory */
-static char *cdcomppath;
+struct ncase {
+      int type;
+      union node *expr;
+      union node *cases;
+};
 
-#ifdef mkinit
-INCLUDE "cd.h"
-INIT {
-       setpwd(0, 0);
-}
-#endif
 
-static int
-cdcmd(argc, argv)
-       int argc;
-       char **argv;
-{
-       const char *dest;
-       const char *path;
-       char *p;
-       struct stat statb;
-       int print = 0;
+struct nclist {
+      int type;
+      union node *next;
+      union node *pattern;
+      union node *body;
+};
 
-       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 */
-}
+struct narg {
+      int type;
+      union node *next;
+      char *text;
+      struct nodelist *backquote;
+};
 
 
-/*
- * Actually do the chdir.  In an interactive shell, print the
- * directory name if "print" is nonzero.
- */
+struct nfile {
+      int type;
+      union node *next;
+      int fd;
+      union node *fname;
+      char *expfname;
+};
 
-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));
+struct ndup {
+      int type;
+      union node *next;
+      int fd;
+      int dupfd;
+      union node *vname;
+};
 
-       /*
-        *  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)
-               out1fmt(snlfmt, curdir);
-       return 0;
-}
+struct nhere {
+      int type;
+      union node *next;
+      int fd;
+      union node *doc;
+};
 
 
-/*
- * Get the next component of the path name pointed to by cdcomppath.
- * This routine overwrites the string pointed to by cdcomppath.
- */
+struct nnot {
+      int type;
+      union node *com;
+};
 
-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;
-}
+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 */
+};
 
 /*
- * 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.
+ * 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 */
 
-static void
-updatepwd(dir)
-       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);
-}
 
+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 int
-pwdcmd(argc, argv)
-       int argc;
-       char **argv;
-{
-       out1fmt(snlfmt, curdir);
-       return 0;
-}
+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 *);
 
 
-#define MAXPWD 256
+static void flushall (void);
+static void out2fmt (const char *, ...)
+    __attribute__((__format__(__printf__,1,2)));
+static int xwrite (int, const char *, int);
 
-/*
- * Find out what the current directory is. If we already know the current
- * directory, this routine returns immediately.
- */
-static void
-getpwd()
-{
-       char buf[MAXPWD];
+static inline 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); }
 
-       /*
-        * Things are a bit complicated here; we could have just used
-        * getcwd, but traditionally getcwd is implemented using popen
-        * to /bin/pwd. This creates a problem for us, since we cannot
-        * keep track of the job if it is being ran behind our backs.
-        * So we re-implement getcwd(), and we suppress interrupts
-        * throughout the process. This is not completely safe, since
-        * the user can still break out of it by killing the pwd program.
-        * We still try to use getcwd for systems that we know have a
-        * c implementation of getcwd, that does not open a pipe to
-        * /bin/pwd.
-        */
-#if defined(__NetBSD__) || defined(__SVR4) || defined(__GLIBC__)
-               
-       if (getcwd(buf, sizeof(buf)) == NULL) {
-               char *pwd = getenv("PWD");
-               struct stat stdot, stpwd;
-
-               if (pwd && *pwd == '/' && stat(".", &stdot) != -1 &&
-                   stat(pwd, &stpwd) != -1 &&
-                   stdot.st_dev == stpwd.st_dev &&
-                   stdot.st_ino == stpwd.st_ino) {
-                       curdir = savestr(pwd);
-                       return;
-               }
-               error("getcwd() failed: %s", strerror(errno));
-       }
-       curdir = savestr(buf);
+#ifndef ASH_OPTIMIZE_FOR_SIZE
+#define out2c(c)        putc((c), stderr)
 #else
-       {
-               char *p;
-               int i;
-               int status;
-               struct job *jp;
-               int pip[2];
-
-               if (pipe(pip) < 0)
-                       error("Pipe call failed");
-               jp = makejob((union node *)NULL, 1);
-               if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
-                       (void) close(pip[0]);
-                       if (pip[1] != 1) {
-                               close(1);
-                               dup_as_newfd(pip[1], 1);
-                               close(pip[1]);
-                       }
-                       (void) execl("/bin/pwd", "pwd", (char *)0);
-                       error("Cannot exec /bin/pwd");
-               }
-               (void) close(pip[1]);
-               pip[1] = -1;
-               p = buf;
-               while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
-                    || (i == -1 && errno == EINTR)) {
-                       if (i > 0)
-                               p += i;
-               }
-               (void) close(pip[0]);
-               pip[0] = -1;
-               status = waitforjob(jp);
-               if (status != 0)
-                       error((char *)0);
-               if (i < 0 || p == buf || p[-1] != '\n')
-                       error("pwd command failed");
-               p[-1] = '\0';
-       }
-       curdir = savestr(buf);
+static void out2c(int c)           { putc(c, stderr); }
+#endif
+
+
+#ifdef ASH_OPTIMIZE_FOR_SIZE
+#define USE_SIT_FUNCTION
+#endif
+
+/* number syntax index */
+#define  BASESYNTAX  0                  /* not in quotes */
+#define  DQSYNTAX    1                  /* in double quotes */
+#define  SQSYNTAX    2                  /* in single quotes */
+#define  ARISYNTAX   3                  /* in arithmetic */
+
+static const char S_I_T[][4] = {
+  /*  0 */  { CSPCL,    CIGN,      CIGN,      CIGN     },   /* PEOA */
+  /*  1 */  { CSPCL,    CWORD,     CWORD,     CWORD    },   /* ' ' */
+  /*  2 */  { CNL,      CNL,       CNL,       CNL      },   /* \n */
+  /*  3 */  { CWORD,    CCTL,      CCTL,      CWORD    },   /* !*-/:=?[]~ */
+  /*  4 */  { CDQUOTE,  CENDQUOTE, CWORD,     CDQUOTE  },   /* '"' */
+  /*  5 */  { CVAR,     CVAR,      CWORD,     CVAR     },   /* $ */
+  /*  6 */  { CSQUOTE,  CWORD,     CENDQUOTE, CSQUOTE  },   /* "'" */
+  /*  7 */  { CSPCL,    CWORD,     CWORD,     CLP      },   /* ( */
+  /*  8 */  { CSPCL,    CWORD,     CWORD,     CRP      },   /* ) */
+  /*  9 */  { CBACK,    CBACK,     CCTL,      CBACK    },   /* \ */
+  /* 10 */  { CBQUOTE,  CBQUOTE,   CWORD,     CBQUOTE  },   /* ` */
+  /* 11 */  { CENDVAR,  CENDVAR,   CWORD,     CENDVAR  },   /* } */
+#ifndef USE_SIT_FUNCTION
+  /* 12 */  { CENDFILE, CENDFILE,  CENDFILE,  CENDFILE },   /* PEOF */
+  /* 13 */  { CWORD,    CWORD,     CWORD,     CWORD    },   /* 0-9A-Za-z */
+  /* 14 */  { CCTL,     CCTL,      CCTL,      CCTL     }    /* CTLESC ... */
 #endif
-}
+};
 
-static void
-setpwd(const char *val, int setold)
+#ifdef USE_SIT_FUNCTION
+
+#define U_C(c) ((unsigned char)(c))
+
+static int SIT(int c, int syntax)
 {
-       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);
+       static const char spec_symbls[]="\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
+       static const char syntax_index_table [] = {
+                               1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
+                               7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */
+                               3, 1, 3, 3, 9, 3,10, 1, /* "=>?[\\]`|" */
+                               11,3 }; /* "}~" */
+       const char *s;
+       int indx;
+
+       if(c==PEOF)             /* 2^8+2 */
+               return CENDFILE;
+       if(c==PEOA)             /* 2^8+1 */
+               indx = 0;
+        else if(U_C(c)>=U_C(CTLESC) && U_C(c)<=U_C(CTLQUOTEMARK))
+               return CCTL;
+        else {
+               s = strchr(spec_symbls, c);
+               if(s==0)
+                       return CWORD;
+               indx = syntax_index_table[(s-spec_symbls)];
+       }
+       return S_I_T[indx][syntax];
+}
+
+#else  /* USE_SIT_FUNCTION */
+
+#define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]
+
+#define CSPCL_CIGN_CIGN_CIGN                           0
+#define CSPCL_CWORD_CWORD_CWORD                        1
+#define CNL_CNL_CNL_CNL                                2
+#define CWORD_CCTL_CCTL_CWORD                          3
+#define CDQUOTE_CENDQUOTE_CWORD_CDQUOTE                4
+#define CVAR_CVAR_CWORD_CVAR                           5
+#define CSQUOTE_CWORD_CENDQUOTE_CSQUOTE                6
+#define CSPCL_CWORD_CWORD_CLP                          7
+#define CSPCL_CWORD_CWORD_CRP                          8
+#define CBACK_CBACK_CCTL_CBACK                         9
+#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE                 10
+#define CENDVAR_CENDVAR_CWORD_CENDVAR                 11
+#define CENDFILE_CENDFILE_CENDFILE_CENDFILE           12
+#define CWORD_CWORD_CWORD_CWORD                       13
+#define CCTL_CCTL_CCTL_CCTL                           14
+
+static const char syntax_index_table[258] = {
+                /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
+  /*   0  -130 PEOF */  CENDFILE_CENDFILE_CENDFILE_CENDFILE,
+  /*   1  -129 PEOA */  CSPCL_CIGN_CIGN_CIGN,
+  /*   2  -128 0xff */  CWORD_CWORD_CWORD_CWORD,
+  /*   3  -127      */  CCTL_CCTL_CCTL_CCTL,    /* CTLQUOTEMARK */
+  /*   4  -126      */  CCTL_CCTL_CCTL_CCTL,
+  /*   5  -125      */  CCTL_CCTL_CCTL_CCTL,
+  /*   6  -124      */  CCTL_CCTL_CCTL_CCTL,
+  /*   7  -123      */  CCTL_CCTL_CCTL_CCTL,
+  /*   8  -122      */  CCTL_CCTL_CCTL_CCTL,
+  /*   9  -121      */  CCTL_CCTL_CCTL_CCTL,
+  /*  10  -120      */  CCTL_CCTL_CCTL_CCTL,    /* CTLESC */
+  /*  11  -119      */  CWORD_CWORD_CWORD_CWORD,
+  /*  12  -118      */  CWORD_CWORD_CWORD_CWORD,
+  /*  13  -117      */  CWORD_CWORD_CWORD_CWORD,
+  /*  14  -116      */  CWORD_CWORD_CWORD_CWORD,
+  /*  15  -115      */  CWORD_CWORD_CWORD_CWORD,
+  /*  16  -114      */  CWORD_CWORD_CWORD_CWORD,
+  /*  17  -113      */  CWORD_CWORD_CWORD_CWORD,
+  /*  18  -112      */  CWORD_CWORD_CWORD_CWORD,
+  /*  19  -111      */  CWORD_CWORD_CWORD_CWORD,
+  /*  20  -110      */  CWORD_CWORD_CWORD_CWORD,
+  /*  21  -109      */  CWORD_CWORD_CWORD_CWORD,
+  /*  22  -108      */  CWORD_CWORD_CWORD_CWORD,
+  /*  23  -107      */  CWORD_CWORD_CWORD_CWORD,
+  /*  24  -106      */  CWORD_CWORD_CWORD_CWORD,
+  /*  25  -105      */  CWORD_CWORD_CWORD_CWORD,
+  /*  26  -104      */  CWORD_CWORD_CWORD_CWORD,
+  /*  27  -103      */  CWORD_CWORD_CWORD_CWORD,
+  /*  28  -102      */  CWORD_CWORD_CWORD_CWORD,
+  /*  29  -101      */  CWORD_CWORD_CWORD_CWORD,
+  /*  30  -100      */  CWORD_CWORD_CWORD_CWORD,
+  /*  31   -99      */  CWORD_CWORD_CWORD_CWORD,
+  /*  32   -98      */  CWORD_CWORD_CWORD_CWORD,
+  /*  33   -97      */  CWORD_CWORD_CWORD_CWORD,
+  /*  34   -96      */  CWORD_CWORD_CWORD_CWORD,
+  /*  35   -95      */  CWORD_CWORD_CWORD_CWORD,
+  /*  36   -94      */  CWORD_CWORD_CWORD_CWORD,
+  /*  37   -93      */  CWORD_CWORD_CWORD_CWORD,
+  /*  38   -92      */  CWORD_CWORD_CWORD_CWORD,
+  /*  39   -91      */  CWORD_CWORD_CWORD_CWORD,
+  /*  40   -90      */  CWORD_CWORD_CWORD_CWORD,
+  /*  41   -89      */  CWORD_CWORD_CWORD_CWORD,
+  /*  42   -88      */  CWORD_CWORD_CWORD_CWORD,
+  /*  43   -87      */  CWORD_CWORD_CWORD_CWORD,
+  /*  44   -86      */  CWORD_CWORD_CWORD_CWORD,
+  /*  45   -85      */  CWORD_CWORD_CWORD_CWORD,
+  /*  46   -84      */  CWORD_CWORD_CWORD_CWORD,
+  /*  47   -83      */  CWORD_CWORD_CWORD_CWORD,
+  /*  48   -82      */  CWORD_CWORD_CWORD_CWORD,
+  /*  49   -81      */  CWORD_CWORD_CWORD_CWORD,
+  /*  50   -80      */  CWORD_CWORD_CWORD_CWORD,
+  /*  51   -79      */  CWORD_CWORD_CWORD_CWORD,
+  /*  52   -78      */  CWORD_CWORD_CWORD_CWORD,
+  /*  53   -77      */  CWORD_CWORD_CWORD_CWORD,
+  /*  54   -76      */  CWORD_CWORD_CWORD_CWORD,
+  /*  55   -75      */  CWORD_CWORD_CWORD_CWORD,
+  /*  56   -74      */  CWORD_CWORD_CWORD_CWORD,
+  /*  57   -73      */  CWORD_CWORD_CWORD_CWORD,
+  /*  58   -72      */  CWORD_CWORD_CWORD_CWORD,
+  /*  59   -71      */  CWORD_CWORD_CWORD_CWORD,
+  /*  60   -70      */  CWORD_CWORD_CWORD_CWORD,
+  /*  61   -69      */  CWORD_CWORD_CWORD_CWORD,
+  /*  62   -68      */  CWORD_CWORD_CWORD_CWORD,
+  /*  63   -67      */  CWORD_CWORD_CWORD_CWORD,
+  /*  64   -66      */  CWORD_CWORD_CWORD_CWORD,
+  /*  65   -65      */  CWORD_CWORD_CWORD_CWORD,
+  /*  66   -64      */  CWORD_CWORD_CWORD_CWORD,
+  /*  67   -63      */  CWORD_CWORD_CWORD_CWORD,
+  /*  68   -62      */  CWORD_CWORD_CWORD_CWORD,
+  /*  69   -61      */  CWORD_CWORD_CWORD_CWORD,
+  /*  70   -60      */  CWORD_CWORD_CWORD_CWORD,
+  /*  71   -59      */  CWORD_CWORD_CWORD_CWORD,
+  /*  72   -58      */  CWORD_CWORD_CWORD_CWORD,
+  /*  73   -57      */  CWORD_CWORD_CWORD_CWORD,
+  /*  74   -56      */  CWORD_CWORD_CWORD_CWORD,
+  /*  75   -55      */  CWORD_CWORD_CWORD_CWORD,
+  /*  76   -54      */  CWORD_CWORD_CWORD_CWORD,
+  /*  77   -53      */  CWORD_CWORD_CWORD_CWORD,
+  /*  78   -52      */  CWORD_CWORD_CWORD_CWORD,
+  /*  79   -51      */  CWORD_CWORD_CWORD_CWORD,
+  /*  80   -50      */  CWORD_CWORD_CWORD_CWORD,
+  /*  81   -49      */  CWORD_CWORD_CWORD_CWORD,
+  /*  82   -48      */  CWORD_CWORD_CWORD_CWORD,
+  /*  83   -47      */  CWORD_CWORD_CWORD_CWORD,
+  /*  84   -46      */  CWORD_CWORD_CWORD_CWORD,
+  /*  85   -45      */  CWORD_CWORD_CWORD_CWORD,
+  /*  86   -44      */  CWORD_CWORD_CWORD_CWORD,
+  /*  87   -43      */  CWORD_CWORD_CWORD_CWORD,
+  /*  88   -42      */  CWORD_CWORD_CWORD_CWORD,
+  /*  89   -41      */  CWORD_CWORD_CWORD_CWORD,
+  /*  90   -40      */  CWORD_CWORD_CWORD_CWORD,
+  /*  91   -39      */  CWORD_CWORD_CWORD_CWORD,
+  /*  92   -38      */  CWORD_CWORD_CWORD_CWORD,
+  /*  93   -37      */  CWORD_CWORD_CWORD_CWORD,
+  /*  94   -36      */  CWORD_CWORD_CWORD_CWORD,
+  /*  95   -35      */  CWORD_CWORD_CWORD_CWORD,
+  /*  96   -34      */  CWORD_CWORD_CWORD_CWORD,
+  /*  97   -33      */  CWORD_CWORD_CWORD_CWORD,
+  /*  98   -32      */  CWORD_CWORD_CWORD_CWORD,
+  /*  99   -31      */  CWORD_CWORD_CWORD_CWORD,
+  /* 100   -30      */  CWORD_CWORD_CWORD_CWORD,
+  /* 101   -29      */  CWORD_CWORD_CWORD_CWORD,
+  /* 102   -28      */  CWORD_CWORD_CWORD_CWORD,
+  /* 103   -27      */  CWORD_CWORD_CWORD_CWORD,
+  /* 104   -26      */  CWORD_CWORD_CWORD_CWORD,
+  /* 105   -25      */  CWORD_CWORD_CWORD_CWORD,
+  /* 106   -24      */  CWORD_CWORD_CWORD_CWORD,
+  /* 107   -23      */  CWORD_CWORD_CWORD_CWORD,
+  /* 108   -22      */  CWORD_CWORD_CWORD_CWORD,
+  /* 109   -21      */  CWORD_CWORD_CWORD_CWORD,
+  /* 110   -20      */  CWORD_CWORD_CWORD_CWORD,
+  /* 111   -19      */  CWORD_CWORD_CWORD_CWORD,
+  /* 112   -18      */  CWORD_CWORD_CWORD_CWORD,
+  /* 113   -17      */  CWORD_CWORD_CWORD_CWORD,
+  /* 114   -16      */  CWORD_CWORD_CWORD_CWORD,
+  /* 115   -15      */  CWORD_CWORD_CWORD_CWORD,
+  /* 116   -14      */  CWORD_CWORD_CWORD_CWORD,
+  /* 117   -13      */  CWORD_CWORD_CWORD_CWORD,
+  /* 118   -12      */  CWORD_CWORD_CWORD_CWORD,
+  /* 119   -11      */  CWORD_CWORD_CWORD_CWORD,
+  /* 120   -10      */  CWORD_CWORD_CWORD_CWORD,
+  /* 121    -9      */  CWORD_CWORD_CWORD_CWORD,
+  /* 122    -8      */  CWORD_CWORD_CWORD_CWORD,
+  /* 123    -7      */  CWORD_CWORD_CWORD_CWORD,
+  /* 124    -6      */  CWORD_CWORD_CWORD_CWORD,
+  /* 125    -5      */  CWORD_CWORD_CWORD_CWORD,
+  /* 126    -4      */  CWORD_CWORD_CWORD_CWORD,
+  /* 127    -3      */  CWORD_CWORD_CWORD_CWORD,
+  /* 128    -2      */  CWORD_CWORD_CWORD_CWORD,
+  /* 129    -1      */  CWORD_CWORD_CWORD_CWORD,
+  /* 130     0      */  CWORD_CWORD_CWORD_CWORD,
+  /* 131     1      */  CWORD_CWORD_CWORD_CWORD,
+  /* 132     2      */  CWORD_CWORD_CWORD_CWORD,
+  /* 133     3      */  CWORD_CWORD_CWORD_CWORD,
+  /* 134     4      */  CWORD_CWORD_CWORD_CWORD,
+  /* 135     5      */  CWORD_CWORD_CWORD_CWORD,
+  /* 136     6      */  CWORD_CWORD_CWORD_CWORD,
+  /* 137     7      */  CWORD_CWORD_CWORD_CWORD,
+  /* 138     8      */  CWORD_CWORD_CWORD_CWORD,
+  /* 139     9 "\t" */  CSPCL_CWORD_CWORD_CWORD,
+  /* 140    10 "\n" */  CNL_CNL_CNL_CNL,
+  /* 141    11      */  CWORD_CWORD_CWORD_CWORD,
+  /* 142    12      */  CWORD_CWORD_CWORD_CWORD,
+  /* 143    13      */  CWORD_CWORD_CWORD_CWORD,
+  /* 144    14      */  CWORD_CWORD_CWORD_CWORD,
+  /* 145    15      */  CWORD_CWORD_CWORD_CWORD,
+  /* 146    16      */  CWORD_CWORD_CWORD_CWORD,
+  /* 147    17      */  CWORD_CWORD_CWORD_CWORD,
+  /* 148    18      */  CWORD_CWORD_CWORD_CWORD,
+  /* 149    19      */  CWORD_CWORD_CWORD_CWORD,
+  /* 150    20      */  CWORD_CWORD_CWORD_CWORD,
+  /* 151    21      */  CWORD_CWORD_CWORD_CWORD,
+  /* 152    22      */  CWORD_CWORD_CWORD_CWORD,
+  /* 153    23      */  CWORD_CWORD_CWORD_CWORD,
+  /* 154    24      */  CWORD_CWORD_CWORD_CWORD,
+  /* 155    25      */  CWORD_CWORD_CWORD_CWORD,
+  /* 156    26      */  CWORD_CWORD_CWORD_CWORD,
+  /* 157    27      */  CWORD_CWORD_CWORD_CWORD,
+  /* 158    28      */  CWORD_CWORD_CWORD_CWORD,
+  /* 159    29      */  CWORD_CWORD_CWORD_CWORD,
+  /* 160    30      */  CWORD_CWORD_CWORD_CWORD,
+  /* 161    31      */  CWORD_CWORD_CWORD_CWORD,
+  /* 162    32  " " */  CSPCL_CWORD_CWORD_CWORD,
+  /* 163    33  "!" */  CWORD_CCTL_CCTL_CWORD,
+  /* 164    34  """ */  CDQUOTE_CENDQUOTE_CWORD_CDQUOTE,
+  /* 165    35  "#" */  CWORD_CWORD_CWORD_CWORD,
+  /* 166    36  "$" */  CVAR_CVAR_CWORD_CVAR,
+  /* 167    37  "%" */  CWORD_CWORD_CWORD_CWORD,
+  /* 168    38  "&" */  CSPCL_CWORD_CWORD_CWORD,
+  /* 169    39  "'" */  CSQUOTE_CWORD_CENDQUOTE_CSQUOTE,
+  /* 170    40  "(" */  CSPCL_CWORD_CWORD_CLP,
+  /* 171    41  ")" */  CSPCL_CWORD_CWORD_CRP,
+  /* 172    42  "*" */  CWORD_CCTL_CCTL_CWORD,
+  /* 173    43  "+" */  CWORD_CWORD_CWORD_CWORD,
+  /* 174    44  "," */  CWORD_CWORD_CWORD_CWORD,
+  /* 175    45  "-" */  CWORD_CCTL_CCTL_CWORD,
+  /* 176    46  "." */  CWORD_CWORD_CWORD_CWORD,
+  /* 177    47  "/" */  CWORD_CCTL_CCTL_CWORD,
+  /* 178    48  "0" */  CWORD_CWORD_CWORD_CWORD,
+  /* 179    49  "1" */  CWORD_CWORD_CWORD_CWORD,
+  /* 180    50  "2" */  CWORD_CWORD_CWORD_CWORD,
+  /* 181    51  "3" */  CWORD_CWORD_CWORD_CWORD,
+  /* 182    52  "4" */  CWORD_CWORD_CWORD_CWORD,
+  /* 183    53  "5" */  CWORD_CWORD_CWORD_CWORD,
+  /* 184    54  "6" */  CWORD_CWORD_CWORD_CWORD,
+  /* 185    55  "7" */  CWORD_CWORD_CWORD_CWORD,
+  /* 186    56  "8" */  CWORD_CWORD_CWORD_CWORD,
+  /* 187    57  "9" */  CWORD_CWORD_CWORD_CWORD,
+  /* 188    58  ":" */  CWORD_CCTL_CCTL_CWORD,
+  /* 189    59  ";" */  CSPCL_CWORD_CWORD_CWORD,
+  /* 190    60  "<" */  CSPCL_CWORD_CWORD_CWORD,
+  /* 191    61  "=" */  CWORD_CCTL_CCTL_CWORD,
+  /* 192    62  ">" */  CSPCL_CWORD_CWORD_CWORD,
+  /* 193    63  "?" */  CWORD_CCTL_CCTL_CWORD,
+  /* 194    64  "@" */  CWORD_CWORD_CWORD_CWORD,
+  /* 195    65  "A" */  CWORD_CWORD_CWORD_CWORD,
+  /* 196    66  "B" */  CWORD_CWORD_CWORD_CWORD,
+  /* 197    67  "C" */  CWORD_CWORD_CWORD_CWORD,
+  /* 198    68  "D" */  CWORD_CWORD_CWORD_CWORD,
+  /* 199    69  "E" */  CWORD_CWORD_CWORD_CWORD,
+  /* 200    70  "F" */  CWORD_CWORD_CWORD_CWORD,
+  /* 201    71  "G" */  CWORD_CWORD_CWORD_CWORD,
+  /* 202    72  "H" */  CWORD_CWORD_CWORD_CWORD,
+  /* 203    73  "I" */  CWORD_CWORD_CWORD_CWORD,
+  /* 204    74  "J" */  CWORD_CWORD_CWORD_CWORD,
+  /* 205    75  "K" */  CWORD_CWORD_CWORD_CWORD,
+  /* 206    76  "L" */  CWORD_CWORD_CWORD_CWORD,
+  /* 207    77  "M" */  CWORD_CWORD_CWORD_CWORD,
+  /* 208    78  "N" */  CWORD_CWORD_CWORD_CWORD,
+  /* 209    79  "O" */  CWORD_CWORD_CWORD_CWORD,
+  /* 210    80  "P" */  CWORD_CWORD_CWORD_CWORD,
+  /* 211    81  "Q" */  CWORD_CWORD_CWORD_CWORD,
+  /* 212    82  "R" */  CWORD_CWORD_CWORD_CWORD,
+  /* 213    83  "S" */  CWORD_CWORD_CWORD_CWORD,
+  /* 214    84  "T" */  CWORD_CWORD_CWORD_CWORD,
+  /* 215    85  "U" */  CWORD_CWORD_CWORD_CWORD,
+  /* 216    86  "V" */  CWORD_CWORD_CWORD_CWORD,
+  /* 217    87  "W" */  CWORD_CWORD_CWORD_CWORD,
+  /* 218    88  "X" */  CWORD_CWORD_CWORD_CWORD,
+  /* 219    89  "Y" */  CWORD_CWORD_CWORD_CWORD,
+  /* 220    90  "Z" */  CWORD_CWORD_CWORD_CWORD,
+  /* 221    91  "[" */  CWORD_CCTL_CCTL_CWORD,
+  /* 222    92  "\" */  CBACK_CBACK_CCTL_CBACK,
+  /* 223    93  "]" */  CWORD_CCTL_CCTL_CWORD,
+  /* 224    94  "^" */  CWORD_CWORD_CWORD_CWORD,
+  /* 225    95  "_" */  CWORD_CWORD_CWORD_CWORD,
+  /* 226    96  "`" */  CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
+  /* 227    97  "a" */  CWORD_CWORD_CWORD_CWORD,
+  /* 228    98  "b" */  CWORD_CWORD_CWORD_CWORD,
+  /* 229    99  "c" */  CWORD_CWORD_CWORD_CWORD,
+  /* 230   100  "d" */  CWORD_CWORD_CWORD_CWORD,
+  /* 231   101  "e" */  CWORD_CWORD_CWORD_CWORD,
+  /* 232   102  "f" */  CWORD_CWORD_CWORD_CWORD,
+  /* 233   103  "g" */  CWORD_CWORD_CWORD_CWORD,
+  /* 234   104  "h" */  CWORD_CWORD_CWORD_CWORD,
+  /* 235   105  "i" */  CWORD_CWORD_CWORD_CWORD,
+  /* 236   106  "j" */  CWORD_CWORD_CWORD_CWORD,
+  /* 237   107  "k" */  CWORD_CWORD_CWORD_CWORD,
+  /* 238   108  "l" */  CWORD_CWORD_CWORD_CWORD,
+  /* 239   109  "m" */  CWORD_CWORD_CWORD_CWORD,
+  /* 240   110  "n" */  CWORD_CWORD_CWORD_CWORD,
+  /* 241   111  "o" */  CWORD_CWORD_CWORD_CWORD,
+  /* 242   112  "p" */  CWORD_CWORD_CWORD_CWORD,
+  /* 243   113  "q" */  CWORD_CWORD_CWORD_CWORD,
+  /* 244   114  "r" */  CWORD_CWORD_CWORD_CWORD,
+  /* 245   115  "s" */  CWORD_CWORD_CWORD_CWORD,
+  /* 246   116  "t" */  CWORD_CWORD_CWORD_CWORD,
+  /* 247   117  "u" */  CWORD_CWORD_CWORD_CWORD,
+  /* 248   118  "v" */  CWORD_CWORD_CWORD_CWORD,
+  /* 249   119  "w" */  CWORD_CWORD_CWORD_CWORD,
+  /* 250   120  "x" */  CWORD_CWORD_CWORD_CWORD,
+  /* 251   121  "y" */  CWORD_CWORD_CWORD_CWORD,
+  /* 252   122  "z" */  CWORD_CWORD_CWORD_CWORD,
+  /* 253   123  "{" */  CWORD_CWORD_CWORD_CWORD,
+  /* 254   124  "|" */  CSPCL_CWORD_CWORD_CWORD,
+  /* 255   125  "}" */  CENDVAR_CENDVAR_CWORD_CENDVAR,
+  /* 256   126  "~" */  CWORD_CCTL_CCTL_CWORD,
+  /* 257   127      */  CWORD_CWORD_CWORD_CWORD,
+};
+
+#endif  /* USE_SIT_FUNCTION */
+
+
+/* first char is indicating which tokens mark the end of a list */
+static const char *const tokname_array[] = {
+       "\1end of file",
+       "\0newline",
+       "\0redirection",
+       "\0word",
+       "\0assignment",
+       "\0;",
+       "\0&",
+       "\0&&",
+       "\0||",
+       "\0|",
+       "\0(",
+       "\1)",
+       "\1;;",
+       "\1`",
+#define KWDOFFSET 14
+       /* the following are keywords */
+       "\0!",
+       "\0case",
+       "\1do",
+       "\1done",
+       "\1elif",
+       "\1else",
+       "\1esac",
+       "\1fi",
+       "\0for",
+       "\0if",
+       "\0in",
+       "\1then",
+       "\0until",
+       "\0while",
+       "\0{",
+       "\1}",
+};
+
+static const char *tokname(int tok)
+{
+       static char buf[16];
+
+       if(tok>=TSEMI)
+               buf[0] = '"';
+       sprintf(buf+(tok>=TSEMI), "%s%c",
+                       tokname_array[tok]+1, (tok>=TSEMI ? '"' : 0));
+       return buf;
 }
 
-/*     $NetBSD: error.c,v 1.23 2000/07/03 03:26:19 matt Exp $  */
+static int plinno = 1;          /* input line number */
 
-/*
- * Errors and exceptions.
- */
+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 */
 
 /*
- * Code to handle exceptions in C.
+ * 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.
  */
 
-struct jmploc *handler;
-static int exception;
-volatile int suppressint;
-volatile int intpending;
+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 exverror __P((int, const char *, va_list))
-    __attribute__((__noreturn__));
+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 */
+};
 
-/*
- * 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".
- */
+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 */
+};
 
-static void
-exraise(e)
-       int e;
-{
-#ifdef DEBUG
-       if (handler == NULL)
-               abort();
+
+#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
-       exception = e;
-       longjmp(handler->loc, 1);
-}
 
+static int  casematch (union node *, const char *);
+static void clearredir(void);
+static void popstring(void);
+static void readcmdfile (const char *);
 
-/*
- * 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 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
-onint() {
-       sigset_t mysigset;
+static void redirect (union node *, int);
+static void popredir (void);
+static int dup_as_newfd (int, int);
 
-       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 void changepath(const char *newval);
+static void getoptsreset(const char *value);
 
 
-/*
- * 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(cond, msg, ap)
-       int cond;
-       const char *msg;
-       va_list ap;
-{
-       CLEAR_PENDING_INT;
-       INTOFF;
+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 */
 
-#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)
-                       outfmt(&errout, "%s: ", commandname);
-               doformat(&errout, msg, ap);
-#if FLUSHERR
-               outc('\n', &errout);
-#else
-               outcslow('\n', &errout);
-#endif
-       }
-       flushall();
-       exraise(cond);
-       /* NOTREACHED */
-}
+static const char spcstr[] = " ";
+static const char snlfmt[] = "%s\n";
+
+static int sstrnleft;
+static int herefd = -1;
 
+static struct localvar *localvars;
 
-#ifdef __STDC__
-static void
-error(const char *msg, ...)
-#else
-static void
-error(va_alist)
-       va_dcl
-#endif
-{
-#ifndef __STDC__
-       const char *msg;
-#endif
-       va_list ap;
-#ifdef __STDC__
-       va_start(ap, msg);
-#else
-       va_start(ap);
-       msg = va_arg(ap, const char *);
+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
-       exverror(EXERROR, msg, ap);
-       /* NOTREACHED */
-       va_end(ap);
-}
 
+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 __STDC__
-static void
-exerror(int cond, const char *msg, ...)
+#ifdef IFS_BROKEN
+static const char defifsvar[] = "IFS= \t\n";
+#define defifs (defifsvar + 4)
 #else
-static void
-exerror(va_alist)
-       va_dcl
-#endif
-{
-#ifndef __STDC__
-       int cond;
-       const char *msg;
+static const char defifs[] = " \t\n";
 #endif
-       va_list ap;
-#ifdef __STDC__
-       va_start(ap, msg);
+
+static const struct varinit varinit[] = {
+#ifdef IFS_BROKEN
+       { &vifs,        VSTRFIXED|VTEXTFIXED,           defifsvar,
 #else
-       va_start(ap);
-       cond = va_arg(ap, int);
-       msg = va_arg(ap, const char *);
+       { &vifs,        VSTRFIXED|VTEXTFIXED|VUNSET,    "IFS=",
 #endif
-       exverror(cond, msg, ap);
-       /* NOTREACHED */
-       va_end(ap);
-}
+         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];
 
 /*
- * Table of error messages.
+ * 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.
  */
 
-struct errname {
-       short errcode;          /* error number */
-       short action;           /* operation which encountered the error */
-       const char *msg;        /* text describing the error */
-};
+#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)
 
-#define ALL (E_OPEN|E_CREAT|E_EXEC)
+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 const struct errname errormsg[] = {
-       { EINTR,        ALL,    "interrupted" },
-       { EACCES,       ALL,    "permission denied" },
-       { EIO,          ALL,    "I/O error" },
-       { ENOENT,       E_OPEN, "no such file" },
-       { ENOENT,       E_CREAT,"directory nonexistent" },
-       { ENOENT,       E_EXEC, "not found" },
-       { ENOTDIR,      E_OPEN, "no such file" },
-       { ENOTDIR,      E_CREAT,"directory nonexistent" },
-       { ENOTDIR,      E_EXEC, "not found" },
-       { EISDIR,       ALL,    "is a directory" },
-       { EEXIST,       E_CREAT,"file exists" },
-#ifdef notdef
-       { EMFILE,       ALL,    "too many open files" },
-#endif
-       { ENFILE,       ALL,    "file table overflow" },
-       { ENOSPC,       ALL,    "file system full" },
-#ifdef EDQUOT
-       { EDQUOT,       ALL,    "disk quota exceeded" },
-#endif
-#ifdef ENOSR
-       { ENOSR,        ALL,    "no streams resources" },
-#endif
-       { ENXIO,        ALL,    "no such device or address" },
-       { EROFS,        ALL,    "read-only file system" },
-       { ETXTBSY,      ALL,    "text busy" },
-#ifdef SYSV
-       { EAGAIN,       E_EXEC, "not enough memory" },
-#endif
-       { ENOMEM,       ALL,    "not enough memory" },
-#ifdef ENOLINK
-       { ENOLINK,      ALL,    "remote access failed" },
-#endif
-#ifdef EMULTIHOP
-       { EMULTIHOP,    ALL,    "remote access failed" },
-#endif
-#ifdef ECOMM
-       { ECOMM,        ALL,    "remote access failed" },
-#endif
-#ifdef ESTALE
-       { ESTALE,       ALL,    "remote access failed" },
-#endif
-#ifdef ETIMEDOUT
-       { ETIMEDOUT,    ALL,    "remote access failed" },
-#endif
-#ifdef ELOOP
-       { ELOOP,        ALL,    "symbolic link loop" },
-#endif
-       { E2BIG,        E_EXEC, "argument list too long" },
-#ifdef ELIBACC
-       { ELIBACC,      E_EXEC, "shared library missing" },
-#endif
-       { 0,            0,      NULL },
-};
 
+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 */
 
-/*
- * 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(e, action)
-       int e;
-       int action;
-{
-       struct errname const *ep;
-       static char buf[12];
+#ifdef ASH_ALIAS
 
-       for (ep = errormsg ; ep->errcode ; ep++) {
-               if (ep->errcode == e && (ep->action & action) != 0)
-                       return ep->msg;
-       }
-       fmtstr(buf, sizeof buf, "error %d", e);
-       return buf;
-}
+#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 *);
 
-#ifdef REALLY_SMALL
 static void
-__inton() {
-       if (--suppressint == 0 && intpending) {
-               onint();
+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;
 }
-#endif
-/*     $NetBSD: eval.c,v 1.57 2001/02/04 19:52:06 christos Exp $       */
 
+static int
+unalias(char *name)
+{
+       struct alias **app;
 
-/* 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 char *commandname;
-struct strlist *cmdenviron;
-static int exitstatus;                 /* exit status of last command */
-static int oexitstatus;                /* saved exit status */
-
-
-static void evalloop __P((union node *, int));
-static void evalfor __P((union node *, int));
-static void evalcase __P((union node *, int));
-static void evalsubshell __P((union node *, int));
-static void expredir __P((union node *));
-static void evalpipe __P((union node *));
-#ifdef notyet
-static void evalcommand __P((union node *, int, struct backcmd *));
-#else
-static void evalcommand __P((union node *, int));
-#endif
-static void prehash __P((union node *));
-static void eprintlist __P((struct strlist *));
+       app = __lookupalias(name);
 
+       if (*app) {
+               INTOFF;
+               *app = freealias(*app);
+               INTON;
+               return (0);
+       }
 
-/*
- * Called to reset things after an exception.
- */
+       return (1);
+}
 
-#ifdef mkinit
-INCLUDE "eval.h"
+static void
+rmaliases(void)
+{
+       struct alias *ap, **app;
+       int i;
 
-RESET {
-       evalskip = 0;
-       loopnest = 0;
-       funcnest = 0;
+       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;
 }
 
-SHELLPROC {
-       exitstatus = 0;
+static struct alias *
+lookupalias(const char *name, int check)
+{
+       struct alias *ap = *__lookupalias(name);
+
+       if (check && ap && (ap->flag & ALIASINUSE))
+               return (NULL);
+       return (ap);
 }
-#endif
 
+static void
+printalias(const struct alias *ap) {
+       char *p;
+
+       p = single_quote(ap->val);
+       printf("alias %s=%s\n", ap->name, p);
+       stunalloc(p);
+}
 
 
 /*
- * The eval commmand.
+ * TODO - sort output
  */
-
 static int
-evalcmd(argc, argv)
-       int argc;
-       char **argv;
+aliascmd(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;
-}
+       char *n, *v;
+       int ret = 0;
+       struct alias *ap;
 
+       if (argc == 1) {
+               int i;
 
-/*
- * Execute a command or commands contained in a string.
- */
-
-static void
-evalstring(s, flag)
-       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);
-}
-
-
-
-/*
- * 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;
+               for (i = 0; i < ATABSIZE; i++)
+                       for (ap = atab[i]; ap; ap = ap->next) {
+                               printalias(ap);
+                       }
+               return (0);
        }
-       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;
-               if (
-                       (bcmd = find_builtin(n->narg.text)) &&
-                       bcmd->flags & BUILTIN_SPECIAL
-               ) {
-                       outfmt(out2, "%s is a special built-in\n", n->narg.text);
-                       exitstatus = 1;
-                       break;
+       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);
                }
-               defun(n->narg.text, n->narg.next);
-               exitstatus = 0;
-               break;
        }
-       case NNOT:
-               evaltree(n->nnot.com, EV_TESTED);
-               exitstatus = !exitstatus;
-               break;
 
-       case NPIPE:
-               evalpipe(n);
-               checkexit = 1;
-               break;
-       case NCMD:
-#ifdef notyet
-               evalcommand(n, flags, (struct backcmd *)NULL);
-#else
-               evalcommand(n, flags);
-#endif
-               checkexit = 1;
-               break;
-#ifdef DEBUG
-       default:
-               out1fmt("Node type = %d\n", n->type);
-#ifndef USE_GLIBC_STDIO
-               flushout(out1);
-#endif
-               break;
-#endif
-       }
-out:
-       if (pendingsigs)
-               dotrap();
-       if (
-               flags & EV_EXIT ||
-               (checkexit && eflag && exitstatus && !(flags & EV_TESTED))
-       )
-               exitshell(exitstatus);
+       return (ret);
 }
 
-
-static void
-evalloop(n, flags)
-       union node *n;
-       int flags;
+static int
+unaliascmd(int argc, char **argv)
 {
-       int status;
+       int i;
 
-       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;
+       while ((i = nextopt("a")) != '\0') {
+               if (i == 'a') {
+                       rmaliases();
+                       return (0);
                }
-               if (n->type == NWHILE) {
-                       if (exitstatus != 0)
-                               break;
-               } else {
-                       if (exitstatus == 0)
-                               break;
+       }
+       for (i = 0; *argptr; argptr++) {
+               if (unalias(*argptr)) {
+                       out2fmt("%s: %s not found\n", "unalias", *argptr);
+                       i = 1;
                }
-               evaltree(n->nbinary.ch2, flags & EV_TESTED);
-               status = exitstatus;
-               if (evalskip)
-                       goto skipping;
        }
-       loopnest--;
-       exitstatus = status;
+
+       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 void
-evalfor(n, flags)
-    union node *n;
-    int flags;
-{
-       struct arglist arglist;
-       union node *argp;
-       struct strlist *sp;
-       struct stackmark smark;
+static struct alias *
+freealias(struct alias *ap) {
+       struct alias *next;
 
-       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;
+       if (ap->flag & ALIASINUSE) {
+               ap->flag |= ALIASDEAD;
+               return ap;
        }
-       *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;
+       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;
                }
        }
-       loopnest--;
-out:
-       popstackmark(&smark);
-}
 
+       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. */
+static void expari (int);
+#endif
 
-static void
-evalcase(n, flags)
-       union node *n;
-       int flags;
-{
-       union node *cp;
-       union node *patp;
-       struct arglist arglist;
-       struct stackmark smark;
+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 */
 
-       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);
-}
+/*
+ * 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
 
-/*
- * Kick off a subshell to evaluate a tree.
- */
+static void     setpwd (const char *, int);
 
-static void
-evalsubshell(n, flags)
-       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;
-       }
-}
+#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) )
 
+#define 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 */
 
 /*
- * Compute the names of the files in a redirection list.
+ * 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.
  */
 
-static void
-expredir(n)
-       union node *n;
-{
-       union node *redir;
+struct procstat {
+       pid_t pid;              /* process id */
+       int status;             /* status flags (defined above) */
+       char *cmd;              /* text of command being run */
+};
 
-       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;
-               }
-       }
-}
 
+static int job_warning;         /* user was warned about stopped jobs */
 
+#ifdef JOBS
+static void setjobctl(int enable);
+#else
+#define setjobctl(on)   /* do nothing */
+#endif
 
-/*
- * Evaluate a pipeline.  All the processes in the pipeline are children
- * of the process creating the pipeline.  (This differs from some versions
- * of the shell, which make the last process in a pipeline the parent
- * of all the rest.)
- */
 
-static void
-evalpipe(n)
-       union node *n;
-{
-       struct job *jp;
-       struct nodelist *lp;
-       int pipelen;
-       int prevfd;
-       int pip[2];
+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
+};
 
-       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 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);
 
-/*
- * 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 char *padvance (const char **, const char *);
 
-static void
-evalbackcmd(n, result)
-       union node *n;
-       struct backcmd *result;
+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;
 {
-       int pip[2];
-       struct job *jp;
-       struct stackmark smark;         /* unnecessary */
+       const char *dest;
+       const char *path;
+       char *p;
+       struct stat statb;
+       int print = 0;
 
-       setstackmark(&smark);
-       result->fd = -1;
-       result->buf = NULL;
-       result->nleft = 0;
-       result->jp = NULL;
-       if (n == NULL) {
-               exitstatus = 0;
-               goto out;
+       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 = ".";
        }
-#ifdef notyet
-       /*
-        * For now we disable executing builtins in the same
-        * context as the shell, because we are not keeping
-        * enough state to recover from changes that are
-        * supposed only to affect subshells. eg. echo "`cd /`"
-        */
-       if (n->type == NCMD) {
-               exitstatus = oexitstatus;
-               evalcommand(n, EV_BACKCMD, result);
-       } else
-#endif
-       {
-               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]);
+       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);
                        }
-                       eflag = 0;
-                       evaltree(n, EV_EXIT);
+                       if (docd(p, print) >= 0)
+                               return 0;
+
                }
-               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));
+       error("can't cd to %s", dest);
+       /* NOTREACHED */
 }
 
 
-
 /*
- * Execute a simple command.
+ * Actually do the chdir.  In an interactive shell, print the
+ * directory name if "print" is nonzero.
  */
 
-static void
-#ifdef notyet
-evalcommand(cmd, flags, backcmd)
-       union node *cmd;
-       int flags;
-       struct backcmd *backcmd;
-#else
-evalcommand(cmd, flags)
-       union node *cmd;
-       int flags;
-#endif
-{
-       struct stackmark smark;
-       union node *argp;
+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(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;
@@ -1567,9 +2568,6 @@ evalcommand(cmd, flags)
        char **envp;
        struct strlist *sp;
        int mode;
-#ifdef notyet
-       int pip[2];
-#endif
        struct cmdentry cmdentry;
        struct job *jp;
        char *volatile savecmdname;
@@ -1609,9 +2607,9 @@ evalcommand(cmd, flags)
        }
        if (argp) {
                struct builtincmd *bcmd;
-               bool pseudovarflag;
+               int pseudovarflag;
                bcmd = find_builtin(arglist.list->text);
-               pseudovarflag = bcmd && bcmd->flags & BUILTIN_ASSIGN;
+               pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
                for (; argp; argp = argp->narg.next) {
                        if (pseudovarflag && isassignment(argp->narg.text)) {
                                expandarg(argp, &arglist, EXP_VARTILDE);
@@ -1640,19 +2638,10 @@ evalcommand(cmd, flags)
 
        /* Print the command if xflag is set. */
        if (xflag) {
-#ifdef FLUSHERR
-               outc('+', &errout);
-#else
-               outcslow('+', &errout);
-#endif
+               out2c('+');
                eprintlist(varlist.list);
                eprintlist(arglist.list);
-#ifdef FLUSHERR
-               outc('\n', &errout);
-               flushout(&errout);
-#else
-               outcslow('\n', &errout);
-#endif
+               out2c('\n');
        }
 
        /* Now locate the command. */
@@ -1678,11 +2667,8 @@ evalcommand(cmd, flags)
                firstbltin = 0;
                for(;;) {
                        find_command(argv[0], &cmdentry, findflag, path);
-                       if (cmdentry.cmdtype == CMDUNKNOWN) {   /* command not found */
+                       if (cmdentry.cmdtype == CMDUNKNOWN) {   /* command not found */
                                exitstatus = 127;
-#ifdef FLUSHERR
-                               flushout(&errout);
-#endif
                                goto out;
                        }
                        /* implement bltin and command here */
@@ -1700,11 +2686,8 @@ evalcommand(cmd, flags)
                                        if (--argc == 0)
                                                goto found;
                                        if (!(bcmd = find_builtin(*argv))) {
-                                               outfmt(&errout, "%s: not found\n", *argv);
+                                               out2fmt("%s: not found\n", *argv);
                                                exitstatus = 127;
-#ifdef FLUSHERR
-                                               flushout(&errout);
-#endif
                                                goto out;
                                        }
                                        cmdentry.u.cmd = bcmd;
@@ -1712,7 +2695,7 @@ evalcommand(cmd, flags)
                                                break;
                                }
                        }
-                       if (cmdentry.u.cmd == COMMANDCMD) {
+                       if (cmdentry.u.cmd == find_builtin("command")) {
                                argv++;
                                if (--argc == 0) {
                                        goto found;
@@ -1744,35 +2727,11 @@ found:
        /* Fork off a child process if necessary. */
        if (cmd->ncmd.backgnd
         || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
-#ifdef notyet
-        || ((flags & EV_BACKCMD) != 0
-           && (cmdentry.cmdtype != CMDBUILTIN
-                || cmdentry.u.bcmd == DOTCMD
-                || cmdentry.u.bcmd == EVALCMD))
-#endif
        ) {
                jp = makejob(cmd, 1);
                mode = cmd->ncmd.backgnd;
-#ifdef notyet
-               if (flags & EV_BACKCMD) {
-                       mode = FORK_NOJOB;
-                       if (pipe(pip) < 0)
-                               error("Pipe call failed");
-               }
-#endif
                if (forkshell(jp, cmd, mode) != 0)
-                       goto parent;    /* at end of routine */
-#ifdef notyet
-               if (flags & EV_BACKCMD) {
-                       FORCEINTON;
-                       close(pip[0]);
-                       if (pip[1] != 1) {
-                               close(1);
-                               dup_as_newfd(pip[1], 1);
-                               close(pip[1]);
-                       }
-               }
-#endif
+                       goto parent;    /* at end of routine */
                flags |= EV_EXIT;
        }
 
@@ -1835,21 +2794,9 @@ found:
                trputs("builtin command:  ");  trargs(argv);
 #endif
                mode = (cmdentry.u.cmd == EXECCMD)? 0 : REDIR_PUSH;
-#ifdef notyet
-               if (flags == EV_BACKCMD) {
-#ifdef USE_GLIBC_STDIO
-                       openmemout();
-#else
-                       memout.nleft = 0;
-                       memout.nextc = memout.buf;
-                       memout.bufsize = 64;
-#endif
-                       mode |= REDIR_BACKQ;
-               }
-#endif
                redirect(cmd->ncmd.redirect, mode);
                savecmdname = commandname;
-               if (firstbltin->flags & BUILTIN_SPECIAL) {
+               if (IS_BUILTIN_SPECIAL(firstbltin)) {
                        listsetvar(varlist.list);
                } else {
                        cmdenviron = varlist.list;
@@ -1864,14 +2811,10 @@ found:
                handler = &jmploc;
                commandname = argv[0];
                argptr = argv + 1;
-               optptr = NULL;                  /* initialize nextopt */
+               optptr = NULL;                  /* initialize nextopt */
                exitstatus = (*cmdentry.u.cmd->builtinfunc)(argc, argv);
                flushall();
 cmddone:
-               exitstatus |= outerr(out1);
-               out1 = &output;
-               out2 = &errout;
-               freestdout();
                cmdenviron = NULL;
                if (e != EXSHELLPROC) {
                        commandname = savecmdname;
@@ -1890,27 +2833,6 @@ cmddone:
                }
                if (cmdentry.u.cmd != EXECCMD)
                        popredir();
-#ifdef notyet
-               if (flags == EV_BACKCMD) {
-                       INTOFF;
-#ifdef USE_GLIBC_STDIO
-                       if (__closememout()) {
-                               error(
-                                       "__closememout() failed: %s",
-                                       strerror(errno)
-                               );
-                       }
-#endif
-                       backcmd->buf = memout.buf;
-#ifdef USE_GLIBC_STDIO
-                       backcmd->nleft = memout.bufsize;
-#else
-                       backcmd->nleft = memout.nextc - memout.buf;
-#endif
-                       memout.buf = NULL;
-                       INTON;
-               }
-#endif
        } else {
 #ifdef DEBUG
                trputs("normal command:  ");  trargs(argv);
@@ -1922,28 +2844,247 @@ cmddone:
                envp = environment();
                shellexec(argv, envp, path, cmdentry.u.index);
        }
-       goto out;
+       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;
+               }
+       }
+}
 
-parent:        /* parent process gets here (if we forked) */
-       if (mode == 0) {        /* argument to fork */
-               INTOFF;
-               exitstatus = waitforjob(jp);
-               INTON;
-#ifdef notyet
-       } else if (mode == 2) {
-               backcmd->fd = pip[0];
-               close(pip[1]);
-               backcmd->jp = jp;
-#endif
-       }
 
+/*
+ * 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:
-       if (lastarg)
-               setvar("_", lastarg, 0);
        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
@@ -1965,7 +3106,6 @@ prehash(n)
 }
 
 
-
 /*
  * Builtin commands.  Builtin commands whose functions are closely
  * tied to evaluation are implemented here.
@@ -2061,6 +3201,43 @@ true_main(argc, argv)
 }
 #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;
@@ -2069,7 +3246,7 @@ execcmd(argc, argv)
        if (argc > 1) {
                struct strlist *sp;
 
-               iflag = 0;              /* exit on error */
+               iflag = 0;              /* exit on error */
                mflag = 0;
                optschanged();
                for (sp = cmdenviron; sp ; sp = sp->next)
@@ -2079,222 +3256,312 @@ execcmd(argc, argv)
        return 0;
 }
 
-static void
-eprintlist(struct strlist *sp)
-{
-       for (; sp; sp = sp->next) {
-               outfmt(&errout, " %s",sp->text);
-       }
-}
-/*     $NetBSD: exec.c,v 1.32 2001/02/04 19:52:06 christos Exp $       */
+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(void) 
+{
+       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;
+}
+
 
 /*
- * 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.
+ * Return to top level.
  */
-#define CMDTABLESIZE 31                /* should be prime */
-#define ARB 1                  /* actual size determined at run time */
-
 
+static void
+popallfiles(void) {
+       while (parsefile != &basepf)
+               popfile();
+}
 
-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 */
-};
+/*
+ * Close the file(s) that the shell is reading commands from.  Called
+ * after a fork is done.
+ */
 
+static void closescript(void) 
+{
+       popallfiles();
+       if (parsefile->fd > 0) {
+               close(parsefile->fd);
+               parsefile->fd = 0;
+       }
+}
 
-static struct tblentry *cmdtable[CMDTABLESIZE];
-static int builtinloc = -1;            /* index in path of %builtin, or -1 */
-static int exerrno = 0;                        /* Last exec error */
 
+/*
+ * Like setinputfile, but takes an open file descriptor.  Call this with
+ * interrupts off.
+ */
 
-static void tryexec __P((char *, char **, char **));
-#if !defined(BSD) && !defined(linux)
-static void execinterp __P((char **, char **));
-#endif
-static void printentry __P((struct tblentry *, int));
-static void clearcmdentry __P((int));
-static struct tblentry *cmdlookup __P((char *, int));
-static void delete_cmd_entry __P((void));
-#ifdef ASH_TYPE
-static int describe_command __P((char *, int));
-#endif
-static int path_change __P((const char *, int *));
+static void setinputfd(int fd, int 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;
+}
 
 
 /*
- * Exec a program.  Never returns.  If you change this routine, you may
- * have to change the find_command routine as well.
+ * Set the input to take input from a file.  If push is set, push the
+ * old input onto the stack first.
  */
 
 static void
-shellexec(argv, envp, path, idx)
-       char **argv, **envp;
-       const char *path;
-       int idx;
+setinputfile(const char *fname, int push)
 {
-       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);
-               }
-       }
+       int fd;
+       int myfileno2;
 
-       /* Map to POSIX errors */
-       switch (e) {
-       case EACCES:
-               exerrno = 126;
-               break;
-       case ENOENT:
-               exerrno = 127;
-               break;
-       default:
-               exerrno = 2;
-               break;
+       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;
        }
-       exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC));
-       /* NOTREACHED */
+       setinputfd(fd, push);
+       INTON;
 }
 
 
 static void
-tryexec(cmd, argv, envp)
-       char *cmd;
-       char **argv;
-       char **envp;
-       {
+tryexec(char *cmd, char **argv, char **envp)
+{
        int e;
-#if !defined(BSD) && !defined(linux)
-       char *p;
-#endif
 
-#ifdef SYSV
-       do {
-               execve(cmd, argv, envp);
-       } while (errno == EINTR);
-#else
-       execve(cmd, argv, envp);
+#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]);
-#if !defined(BSD) && !defined(linux)
-               INTON;
-               pgetc(); pungetc();             /* fill up input buffer */
-               p = parsenextc;
-               if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
-                       argv[0] = cmd;
-                       execinterp(argv, envp);
-               }
-               INTOFF;
-#endif
                setparam(argv + 1);
                exraise(EXSHELLPROC);
        }
        errno = e;
 }
 
-
-#if !defined(BSD) && !defined(linux)
-/*
- * Execute an interpreter introduced by "#!", for systems where this
- * feature has not been built into the kernel.  If the interpreter is
- * the shell, return (effectively ignoring the "#!").  If the execution
- * of the interpreter fails, exit.
- *
- * This code peeks inside the input buffer in order to avoid actually
- * reading any input.  It would benefit from a rewrite.
- */
-
-#define NEWARGS 5
-
-static void
-execinterp(argv, envp)
-       char **argv, **envp;
-       {
-       int n;
-       char *inp;
-       char *outp;
-       char c;
-       char *p;
-       char **ap;
-       char *newargs[NEWARGS];
-       int i;
-       char **ap2;
-       char **new;
-
-       n = parsenleft - 2;
-       inp = parsenextc + 2;
-       ap = newargs;
-       for (;;) {
-               while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
-                       inp++;
-               if (n < 0)
-                       goto bad;
-               if ((c = *inp++) == '\n')
-                       break;
-               if (ap == &newargs[NEWARGS])
-bad:             error("Bad #! line");
-               STARTSTACKSTR(outp);
-               do {
-                       STPUTC(c, outp);
-               } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
-               STPUTC('\0', outp);
-               n++, inp--;
-               *ap++ = grabstackstr(outp);
-       }
-       if (ap == newargs + 1) {        /* if no args, maybe no exec is needed */
-               p = newargs[0];
-               for (;;) {
-                       if (equal(p, "sh") || equal(p, "ash")) {
-                               return;
-                       }
-                       while (*p != '/') {
-                               if (*p == '\0')
-                                       goto break2;
-                               p++;
-                       }
-                       p++;
-               }
-break2:;
-       }
-       i = (char *)ap - (char *)newargs;               /* size in bytes */
-       if (i == 0)
-               error("Bad #! line");
-       for (ap2 = argv ; *ap2++ != NULL ; );
-       new = ckmalloc(i + ((char *)ap2 - (char *)argv));
-       ap = newargs, ap2 = new;
-       while ((i -= sizeof (char **)) >= 0)
-               *ap2++ = *ap++;
-       ap = argv;
-       while (*ap2++ = *ap++);
-       shellexec(new, envp, pathval(), 0);
-       /* NOTREACHED */
-}
-#endif
-
-
+static char *commandtext (const union node *);
 
 /*
  * Do a path search.  The variable path (passed by reference) should be
@@ -2308,11 +3575,12 @@ break2:;
 
 static const char *pathopt;
 
+static void growstackblock(void);
+
+
 static char *
-padvance(path, name)
-       const char **path;
-       const char *name;
-       {
+padvance(const char **path, const char *name)
+{
        const char *p;
        char *q;
        const char *start;
@@ -2322,7 +3590,7 @@ padvance(path, name)
                return NULL;
        start = *path;
        for (p = start ; *p && *p != ':' && *p != '%' ; p++);
-       len = p - start + strlen(name) + 2;     /* "2" is for '/' and '\0' */
+       len = p - start + strlen(name) + 2;     /* "2" is for '/' and '\0' */
        while (stackblocksize() < len)
                growstackblock();
        q = stackblock();
@@ -2344,6 +3612,26 @@ padvance(path, name)
        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) + 1);
+}
+
+/*
+ * Find a keyword is in a sorted array.
+ */
+
+static const char *const *
+findkwd(const char *s)
+{
+       return  bsearch(s, tokname_array + KWDOFFSET,
+                                       (sizeof(tokname_array)/sizeof(const char *)) - KWDOFFSET,
+                                       sizeof(const char *), pstrcmp);
+}
 
 
 /*** Command hashing code ***/
@@ -2360,14 +3648,17 @@ hashcmd(argc, argv)
        int verbose;
        struct cmdentry entry;
        char *name;
+#ifdef ASH_ALIAS
+       const struct alias *ap;
+#endif
 
        verbose = 0;
-       while ((c = nextopt("rv")) != '\0') {
+       while ((c = nextopt("rvV")) != '\0') {
                if (c == 'r') {
                        clearcmdentry(0);
                        return 0;
-               } else if (c == 'v') {
-                       verbose++;
+               } else if (c == 'v' || c == 'V') {
+                       verbose = c;
                }
        }
        if (*argptr == NULL) {
@@ -2381,24 +3672,41 @@ hashcmd(argc, argv)
                return 0;
        }
        c = 0;
-       while ((name = *argptr) != NULL) {
+       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);
+                       if (cmdp) printentry(cmdp, verbose=='v');
                        flushall();
                }
-               argptr++;
        }
        return c;
 }
 
-
 static void
 printentry(cmdp, verbose)
        struct tblentry *cmdp;
@@ -2408,6 +3716,7 @@ printentry(cmdp, verbose)
        const char *path;
        char *name;
 
+       printf("%s%s", cmdp->cmdname, (verbose ? " is " : ""));
        if (cmdp->cmdtype == CMDNORMAL) {
                idx = cmdp->param.index;
                path = pathval();
@@ -2415,15 +3724,17 @@ printentry(cmdp, verbose)
                        name = padvance(&path, cmdp->cmdname);
                        stunalloc(name);
                } while (--idx >= 0);
-               out1str(name);
+               if(verbose)
+                       out1str(name);
        } else if (cmdp->cmdtype == CMDBUILTIN) {
-               out1fmt("builtin %s", cmdp->cmdname);
+               if(verbose)
+                       out1str("a shell builtin");
        } else if (cmdp->cmdtype == CMDFUNCTION) {
-               out1fmt("function %s", cmdp->cmdname);
                if (verbose) {
                        INTOFF;
+                       out1str("a function\n");
                        name = commandtext(cmdp->param.func);
-                       out1fmt(" %s", name);
+                       printf("%s() {\n %s\n}", cmdp->cmdname, name);
                        ckfree(name);
                        INTON;
                }
@@ -2432,22 +3743,56 @@ printentry(cmdp, verbose)
                error("internal error: cmdtype %d", cmdp->cmdtype);
 #endif
        }
-       out1fmt(snlfmt, cmdp->rehash ? "*" : nullstr);
+       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(name, entry, act, path)
-       char *name;
-       struct cmdentry *entry;
-       int act;
-       const char *path;
+find_command(const char *name, struct cmdentry *entry, int act, const char *path)
 {
        struct tblentry *cmdp;
        int idx;
@@ -2458,17 +3803,13 @@ find_command(name, entry, act, path)
        int bltin;
        int firstchange;
        int updatetbl;
-       bool regular;
+       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) {
-       #ifdef SYSV
-                               if (errno == EINTR)
-                                       continue;
-       #endif
                                if (errno != ENOENT && errno != ENOTDIR)
                                        e = errno;
                                entry->cmdtype = CMDUNKNOWN;
@@ -2516,11 +3857,11 @@ find_command(name, entry, act, path)
        }
 
        bcmd = find_builtin(name);
-       regular = bcmd && bcmd->flags & BUILTIN_REGULAR;
+       regular = bcmd && IS_BUILTIN_REGULAR(bcmd);
 
        if (regular) {
                if (cmdp && (cmdp->cmdtype == CMDBUILTIN)) {
-                       goto success;
+                       goto success;
                }
        } else if (act & DO_BRUTE) {
                if (firstchange == 0) {
@@ -2545,8 +3886,8 @@ builtin:
        }
 
        /* We have to search path. */
-       prev = -1;              /* where to start */
-       if (cmdp && cmdp->rehash) {     /* doing a rehash */
+       prev = -1;              /* where to start */
+       if (cmdp && cmdp->rehash) {     /* doing a rehash */
                if (cmdp->cmdtype == CMDBUILTIN)
                        prev = builtinloc;
                else
@@ -2572,7 +3913,7 @@ loop:
                                   prefix("func", pathopt)) {
                                /* handled below */
                        } else {
-                               continue;       /* ignore unimplemented options */
+                               continue;       /* ignore unimplemented options */
                        }
                }
                /* if rehash, don't redo absolute path names */
@@ -2584,18 +3925,14 @@ loop:
                        goto success;
                }
                while (stat(fullname, &statb) < 0) {
-#ifdef SYSV
-                       if (errno == EINTR)
-                               continue;
-#endif
                        if (errno != ENOENT && errno != ENOTDIR)
                                e = errno;
                        goto loop;
                }
-               e = EACCES;     /* if we fail, this will be the error */
+               e = EACCES;     /* if we fail, this will be the error */
                if (!S_ISREG(statb.st_mode))
                        continue;
-               if (pathopt) {          /* this is a %func directory */
+               if (pathopt) {          /* this is a %func directory */
                        stalloc(strlen(fullname) + 1);
                        readcmdfile(fullname);
                        if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
@@ -2603,18 +3940,6 @@ loop:
                        stunalloc(fullname);
                        goto success;
                }
-#ifdef notdef
-               if (statb.st_uid == geteuid()) {
-                       if ((statb.st_mode & 0100) == 0)
-                               goto loop;
-               } else if (statb.st_gid == getegid()) {
-                       if ((statb.st_mode & 010) == 0)
-                               goto loop;
-               } else {
-                       if ((statb.st_mode & 01) == 0)
-                               goto loop;
-               }
-#endif
                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 */
@@ -2635,7 +3960,7 @@ loop:
        if (cmdp && updatetbl)
                delete_cmd_entry();
        if (act & DO_ERR)
-               outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
+               out2fmt("%s: %s\n", name, errmsg(e, E_EXEC));
        entry->cmdtype = CMDUNKNOWN;
        return;
 
@@ -2651,14 +3976,19 @@ success:
  * Search the table of builtin commands.
  */
 
-struct builtincmd *
-find_builtin(name)
-       char *name;
+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),
-               pstrcmp
+       bp = bsearch(name, builtincmds, NUMBUILTINS, sizeof(struct builtincmd),
+               bstrcmp
        );
        return bp;
 }
@@ -2670,7 +4000,7 @@ find_builtin(name)
  */
 
 static void
-hashcd() {
+hashcd(void) {
        struct tblentry **pp;
        struct tblentry *cmdp;
 
@@ -2692,15 +4022,14 @@ hashcd() {
  */
 
 static void
-changepath(newval)
-       const char *newval;
+changepath(const char *newval)
 {
        int firstchange;
        int bltin;
 
        firstchange = path_change(newval, &bltin);
        if (builtinloc < 0 && bltin >= 0)
-               builtinloc = bltin;             /* zap builtins */
+               builtinloc = bltin;             /* zap builtins */
        clearcmdentry(firstchange);
        builtinloc = bltin;
 }
@@ -2742,16 +4071,8 @@ clearcmdentry(firstchange)
  * Delete all functions.
  */
 
-#ifdef mkinit
-static void deletefuncs __P((void));
-
-SHELLPROC {
-       deletefuncs();
-}
-#endif
-
 static void
-deletefuncs() {
+deletefuncs(void) {
        struct tblentry **tblp;
        struct tblentry **pp;
        struct tblentry *cmdp;
@@ -2782,16 +4103,13 @@ deletefuncs() {
  * entry.
  */
 
-struct tblentry **lastcmdentry;
-
+static struct tblentry **lastcmdentry;
 
 static struct tblentry *
-cmdlookup(name, add)
-       char *name;
-       int add;
+cmdlookup(const char *name, int add)
 {
        int hashval;
-       char *p;
+       const char *p;
        struct tblentry *cmdp;
        struct tblentry **pp;
 
@@ -2825,75 +4143,49 @@ cmdlookup(name, add)
  */
 
 static void
-delete_cmd_entry() {
-       struct tblentry *cmdp;
-
-       INTOFF;
-       cmdp = *lastcmdentry;
-       *lastcmdentry = cmdp->next;
-       ckfree(cmdp);
-       INTON;
-}
-
-
-
-#ifdef notdef
-static void
-getcmdentry(name, entry)
-       char *name;
-       struct cmdentry *entry;
-       {
-       struct tblentry *cmdp = cmdlookup(name, 0);
-
-       if (cmdp) {
-               entry->u = cmdp->param;
-               entry->cmdtype = cmdp->cmdtype;
-       } else {
-               entry->cmdtype = CMDUNKNOWN;
-               entry->u.index = 0;
-       }
-}
-#endif
-
-
-/*
- * Add a new command entry, replacing any existing command entry for
- * the same name.
- */
-
-static void
-addcmdentry(name, entry)
-       char *name;
-       struct cmdentry *entry;
-       {
+delete_cmd_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;
+       cmdp = *lastcmdentry;
+       *lastcmdentry = cmdp->next;
+       ckfree(cmdp);
        INTON;
 }
 
 
-/*
- * Define a shell function.
- */
 
-static void
-defun(name, func)
-       char *name;
-       union node *func;
-       {
-       struct cmdentry entry;
 
-       entry.cmdtype = CMDFUNCTION;
-       entry.u.func = copyfunc(func);
-       addcmdentry(name, &entry);
-}
+
+static const unsigned char 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)),
+};
+
 
 
 /*
@@ -2901,9 +4193,8 @@ defun(name, func)
  */
 
 static void
-unsetfunc(name)
-       char *name;
-       {
+unsetfunc(char *name)
+{
        struct tblentry *cmdp;
 
        if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
@@ -2912,120 +4203,30 @@ unsetfunc(name)
        }
 }
 
-#ifdef ASH_TYPE
+
 /*
  * Locate and print what a word is...
  */
 
 static int
-typecmd(argc, argv)
-       int argc;
-       char **argv;
+typecmd(int argc, char **argv)
 {
        int i;
        int err = 0;
+       char *argv_a[2];
+
+       argv_a[1] = 0;
 
        for (i = 1; i < argc; i++) {
-               err |= describe_command(argv[i], 1);
+               argv_a[0] = argv[i];
+               argptr = argv_a;
+               optptr = "v";
+               err |= hashcmd(argc, argv);
        }
        return err;
 }
 
-static int
-describe_command(command, verbose)
-       char *command;
-       int verbose;
-{
-       struct cmdentry entry;
-       struct tblentry *cmdp;
-       const struct alias *ap;
-       const char *path = pathval();
-
-       if (verbose) {
-               out1str(command);
-       }
-
-       /* First look at the keywords */
-       if (findkwd(command)) {
-               out1str(verbose ? " is a shell keyword" : command);
-               goto out;
-       }
-
-       /* Then look at the aliases */
-       if ((ap = lookupalias(command, 0)) != NULL) {
-               if (verbose) {
-                       out1fmt(" is an alias for %s", ap->val);
-               } else {
-                       printalias(ap);
-               }
-               goto out;
-       }
-
-       /* Then check if it is a tracked alias */
-       if ((cmdp = cmdlookup(command, 0)) != NULL) {
-               entry.cmdtype = cmdp->cmdtype;
-               entry.u = cmdp->param;
-       } else {
-               /* Finally use brute force */
-               find_command(command, &entry, DO_ABS, path);
-       }
-
-       switch (entry.cmdtype) {
-       case CMDNORMAL: {
-               int j = entry.u.index;
-               char *p;
-               if (j == -1) {
-                       p = command;
-               } else {
-                       do {
-                               p = padvance(&path, command);
-                               stunalloc(p);
-                       } while (--j >= 0);
-               }
-               if (verbose) {
-                       out1fmt(
-                               " is%s %s",
-                               cmdp ? " a tracked alias for" : nullstr, p
-                       );
-               } else {
-                       out1str(p);
-               }
-               break;
-       }
-
-       case CMDFUNCTION:
-               if (verbose) {
-                       out1str(" is a shell function");
-               } else {
-                       out1str(command);
-               }
-               break;
-
-       case CMDBUILTIN:
-               if (verbose) {
-                       out1fmt(
-                               " is a %sshell builtin",
-                               entry.u.cmd->flags & BUILTIN_SPECIAL ?
-                                       "special " : nullstr
-                       );
-               } else {
-                       out1str(command);
-               }
-               break;
-
-       default:
-               if (verbose) {
-                       out1str(": not found\n");
-               }
-               return 127;
-       }
-
-out:
-       out1c('\n');
-       return 0;
-}
-#endif 
-
+#ifdef ASH_CMDCMD
 static int
 commandcmd(argc, argv)
        int argc;
@@ -3047,29 +4248,29 @@ commandcmd(argc, argv)
                case 'V':
                        verbose_verify_only = 1;
                        break;
-               default:
-                       outfmt(out2,
-"command: nextopt returned character code 0%o\n", c);
-                       return EX_SOFTWARE;
                }
 
        if (default_path + verify_only + verbose_verify_only > 1 ||
            !*argptr) {
-                       outfmt(out2,
-"command [-p] command [arg ...]\n");
-                       outfmt(out2,
-"command {-v|-V} command\n");
+                       out2str(
+                               "command [-p] command [arg ...]\n"
+                               "command {-v|-V} command\n");
                        return EX_USAGE;
        }
 
-#ifdef ASH_TYPE
        if (verify_only || verbose_verify_only) {
-               return describe_command(*argptr, 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);
        }
-#endif 
 
        return 0;
 }
+#endif
 
 static int
 path_change(newval, bltin)
@@ -3082,7 +4283,7 @@ path_change(newval, bltin)
 
        old = pathval();
        new = newval;
-       firstchange = 9999;     /* assume no change */
+       firstchange = 9999;     /* assume no change */
        idx = 0;
        *bltin = -1;
        for (;;) {
@@ -3091,7 +4292,7 @@ path_change(newval, bltin)
                        if ((*old == '\0' && *new == ':')
                         || (*old == ':' && *new == '\0'))
                                firstchange++;
-                       old = new;      /* ignore subsequent differences */
+                       old = new;      /* ignore subsequent differences */
                }
                if (*new == '\0')
                        break;
@@ -3106,8 +4307,6 @@ path_change(newval, bltin)
                firstchange = 0;
        return firstchange;
 }
-/*     $NetBSD: expand.c,v 1.50 2001/02/04 19:52:06 christos Exp $     */
-
 /*
  * Routines to expand arguments to commands.  We have to deal with
  * backquotes, shell variables, and file metacharacters.
@@ -3115,8 +4314,8 @@ path_change(newval, bltin)
 /*
  * _rmescape() flags
  */
-#define RMESCAPE_ALLOC 0x1     /* Allocate a new string */
-#define RMESCAPE_GLOB  0x2     /* Add backslashes for glob */
+#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
@@ -3124,125 +4323,279 @@ path_change(newval, bltin)
  */
 
 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 */
+       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 */
-struct nodelist *argbackq;     /* list of back quote expressions */
-struct ifsregion ifsfirst;     /* first struct in list of ifs regions */
-struct ifsregion *ifslastp;    /* last struct in list */
-struct arglist exparg;         /* holds expanded arg list */
-
-static void argstr __P((char *, int));
-static char *exptilde __P((char *, int));
-static void expbackq __P((union node *, int, int));
-static int subevalvar __P((char *, char *, int, int, int, int, int));
-static char *evalvar __P((char *, int));
-static int varisset __P((char *, int));
-static void strtodest __P((const char *, const char *, int));
-static void varvalue __P((char *, int, int));
-static void recordregion __P((int, int, int));
-static void removerecordregions __P((int)); 
-static void ifsbreakup __P((char *, struct arglist *));
-static void ifsfree __P((void));
-static void expandmeta __P((struct strlist *, int));
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
+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 *, int, 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 __P((const glob_t *));
+static void addglob (const glob_t *);
 #endif
 #endif
-#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
-static void expmeta __P((char *, char *));
+#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
+static void expmeta (char *, char *);
 #endif
-static void addfname __P((char *));
-#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
-static struct strlist *expsort __P((struct strlist *));
-static struct strlist *msort __P((struct strlist *, int));
+#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
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
-static int patmatch __P((char *, char *, int));
-static int patmatch2 __P((char *, char *, int));
+static int patmatch (char *, char *, int);
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
+static int patmatch2 (char *, char *, int);
 #else
-static int pmatch __P((char *, char *, int));
+static int pmatch (char *, char *, int);
 #define patmatch2 patmatch
 #endif
-static char *cvtnum __P((int, char *));
-
-extern int oexitstatus;
+static char *cvtnum (int, char *);
 
 /*
  * Expand shell variables and backquotes inside a here document.
  */
 
-static void
-expandhere(arg, fd)
-       union node *arg;        /* the document */
-       int fd;                 /* where to write the expanded version */
-       {
+/* 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.
- */
+/*
+ * 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(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;
 
-static void
-expandarg(arg, arglist, flag)
-       union node *arg;
-       struct arglist *arglist;
-       int flag;
-{
-       struct strlist *sp;
-       char *p;
+       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;
 
-       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;
+#ifdef DEBUG
+       default:
+               abort();
+#endif
        }
-       ifsfree();
-       *exparg.lastp = NULL;
-       if (exparg.list) {
-               *arglist->lastp = exparg.list;
-               arglist->lastp = exparg.lastp;
+
+       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
@@ -3255,7 +4608,7 @@ argstr(p, flag)
        int flag;
 {
        char c;
-       int quotes = flag & (EXP_FULL | EXP_CASE);      /* do CTLESC */
+       int quotes = flag & (EXP_FULL | EXP_CASE);      /* do CTLESC */
        int firsteq = 1;
 
        if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
@@ -3290,7 +4643,7 @@ argstr(p, flag)
                case CTLENDARI:
                        expari(flag);
                        break;
-#endif 
+#endif
                case ':':
                case '=':
                        /*
@@ -3362,9 +4715,8 @@ lose:
 }
 
 
-static void 
-removerecordregions(endoff)
-       int endoff;
+static void
+removerecordregions(int endoff)
 {
        if (ifslastp == NULL)
                return;
@@ -3386,7 +4738,7 @@ removerecordregions(endoff)
                }
                return;
        }
-       
+
        ifslastp = &ifsfirst;
        while (ifslastp->next && ifslastp->next->begoff < endoff)
                ifslastp=ifslastp->next;
@@ -3409,16 +4761,16 @@ removerecordregions(endoff)
  * evaluate, place result in (backed up) result, adjust string position.
  */
 static void
-expari(flag)
-       int flag;
+expari(int flag)
 {
        char *p, *start;
+       int errcode;
        int result;
        int begoff;
        int quotes = flag & (EXP_FULL | EXP_CASE);
        int quoted;
 
-       /*      ifsfree(); */
+       /*      ifsfree(); */
 
        /*
         * This routine is slightly over-complicated for
@@ -3451,8 +4803,14 @@ expari(flag)
        removerecordregions(begoff);
        if (quotes)
                rmescapes(p+2);
-       result = arith(p+2);
-       fmtstr(p, 12, "%d", result);
+       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++)
                ;
@@ -3462,8 +4820,7 @@ expari(flag)
        result = expdest - p + 1;
        STADJUST(-result, expdest);
 }
-#endif 
-
+#endif
 
 /*
  * Expand stuff in backwards quotes.
@@ -3485,7 +4842,7 @@ expbackq(cmd, quoted, flag)
        struct nodelist *volatile saveargbackq;
        char lastc;
        int startloc = dest - stackblock();
-       char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
+       int syntax = quoted ? DQSYNTAX : BASESYNTAX;
        volatile int saveherefd;
        int quotes = flag & (EXP_FULL | EXP_CASE);
        struct jmploc jmploc;
@@ -3533,7 +4890,7 @@ err1:
                if (--in.nleft < 0) {
                        if (in.fd < 0)
                                break;
-                       while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
+                       i = safe_read(in.fd, buf, sizeof buf);
                        TRACE(("expbackq: read returns %d\n", i));
                        if (i <= 0)
                                break;
@@ -3542,7 +4899,7 @@ err1:
                }
                lastc = *p++;
                if (lastc != '\0') {
-                       if (quotes && syntax[(int)lastc] == CCTL)
+                       if (quotes && SIT(lastc, syntax) == CCTL)
                                STPUTC(CTLESC, dest);
                        STPUTC(lastc, dest);
                }
@@ -3573,8 +4930,6 @@ err2:
        INTON;
 }
 
-
-
 static int
 subevalvar(p, str, strloc, subtype, startloc, varflags, quotes)
        char *p;
@@ -3614,7 +4969,7 @@ subevalvar(p, str, strloc, subtype, startloc, varflags, quotes)
 
        case VSQUESTION:
                if (*p != CTLENDVAR) {
-                       outfmt(&errout, snlfmt, startp);
+                       out2fmt(snlfmt, startp);
                        error((char *)NULL);
                }
                error("%.*s: parameter %snot set", p - str - 1,
@@ -3630,7 +4985,7 @@ subevalvar(p, str, strloc, subtype, startloc, varflags, quotes)
                                goto recordleft;
                        *loc = c;
                        if (quotes && *loc == CTLESC)
-                               loc++;
+                               loc++;
                }
                return 0;
 
@@ -3642,198 +4997,39 @@ subevalvar(p, str, strloc, subtype, startloc, varflags, 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;
-}
-
-
-/*
- * Expand a variable, and return a pointer to the next character in the
- * input string.
- */
-
-static char *
-evalvar(p, flag)
-       char *p;
-       int flag;
-{
-       int subtype;
-       int varflags;
-       char *var;
-       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 (quotes && loc > startp && *(loc - 1) == CTLESC) {
+                               for (q = startp; q < loc; q++)
+                                       if (*q == CTLESC)
+                                               q++;
+                               if (q > loc)
+                                       loc--;
+                       }
                }
-               if (easy)
-                       goto record;
-               break;
+               return 0;
 
-       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);
+               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--;
+                       }
                }
-               /* Remove any recorded regions beyond start of variable */
-               removerecordregions(startloc);
-               goto record;
+               return 0;
 
-       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;
+       case VSTRIMRIGHTMAX:
+               for (loc = startp; loc < str - 1; loc++) {
+                       if (patmatch2(str, loc, quotes))
+                               goto recordright;
+                       if (quotes && *loc == CTLESC)
+                               loc++;
                }
-               if (easy)
-                       goto record;
-               break;
+               return 0;
 
 #ifdef DEBUG
        default:
@@ -3841,26 +5037,21 @@ record:
 #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;
-}
+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;
+}
 
 
 /*
@@ -3904,36 +5095,26 @@ varisset(name, nulok)
        return 1;
 }
 
-
-
 /*
  * Put a string on the stack.
  */
 
 static void
-strtodest(p, syntax, quotes)
-       const char *p;
-       const char *syntax;
-       int quotes;
+strtodest(const char *p, int syntax, int quotes)
 {
        while (*p) {
-               if (quotes && syntax[(int) *p] == CCTL)
+               if (quotes && SIT(*p,syntax) == 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;
+varvalue(char *name, int quoted, int flags)
 {
        int num;
        char *p;
@@ -3941,7 +5122,7 @@ varvalue(name, quoted, flags)
        int sep;
        int sepq = 0;
        char **ap;
-       char const *syntax;
+       int syntax;
        int allow_split = flags & EXP_FULL;
        int quotes = flags & (EXP_FULL | EXP_CASE);
 
@@ -3963,8 +5144,8 @@ numvar:
                break;
        case '-':
                for (i = 0 ; i < NOPTS ; i++) {
-                       if (optlist[i].val)
-                               STPUTC(optlist[i].letter, expdest);
+                       if (optent_val(i))
+                               STPUTC(optent_letter(optlist[i]), expdest);
                }
                break;
        case '@':
@@ -3976,7 +5157,7 @@ numvar:
        case '*':
                sep = ifsset() ? ifsval()[0] : ' ';
                if (quotes) {
-                       sepq = syntax[(int) sep] == CCTL;
+                       sepq = SIT(sep,syntax) == CCTL;
                }
 param:
                for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
@@ -4001,7 +5182,6 @@ param:
 }
 
 
-
 /*
  * Record the fact that we have to scan this region of the
  * string for IFS characters.
@@ -4136,14 +5316,29 @@ ifsfree()
        ifsfirst.next = NULL;
 }
 
+/*
+ * Add a file name to the list.
+ */
+
+static void
+addfname(const char *name)
+{
+       char *p;
+       struct strlist *sp;
 
+       p = sstrdup(name);
+       sp = (struct strlist *)stalloc(sizeof *sp);
+       sp->text = p;
+       *exparg.lastp = sp;
+       exparg.lastp = &sp->next;
+}
 
 /*
  * Expand shell metacharacters.  At this point, the only control characters
  * should be escapes.  The results are stored in the list exparg.
  */
 
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)
 static void
 expandmeta(str, flag)
        struct strlist *str;
@@ -4158,9 +5353,9 @@ expandmeta(str, flag)
                        goto nometa;
                p = preglob(str->text);
                INTOFF;
-               switch (glob(p, GLOB_NOMAGIC, 0, &pglob)) {
+               switch (glob(p, 0, 0, &pglob)) {
                case 0:
-                       if (!(pglob.gl_flags & GLOB_MAGCHAR))
+                       if(pglob.gl_pathv[1]==0 && !strcmp(p, pglob.gl_pathv[0]))
                                goto nometa2;
                        addglob(&pglob);
                        globfree(&pglob);
@@ -4175,7 +5370,7 @@ nometa:
                        rmescapes(str->text);
                        exparg.lastp = &str->next;
                        break;
-               default:        /* GLOB_NOSPACE */
+               default:        /* GLOB_NOSPACE */
                        error("Out of space");
                }
                str = str->next;
@@ -4199,7 +5394,7 @@ addglob(pglob)
 }
 
 
-#else  /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */
+#else   /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */
 static char *expdir;
 
 
@@ -4218,7 +5413,7 @@ expandmeta(str, flag)
                if (fflag)
                        goto nometa;
                p = str->text;
-               for (;;) {                      /* fast check for meta chars */
+               for (;;) {                      /* fast check for meta chars */
                        if ((c = *p++) == '\0')
                                goto nometa;
                        if (c == '*' || c == '?' || c == '[' || c == '!')
@@ -4297,7 +5492,7 @@ expmeta(enddir, name)
                                        break;
                                }
                        }
-               } else if (*p == '!' && p[1] == '!'     && (p == name || p[-1] == '/')) {
+               } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) {
                        metaflag = 1;
                } else if (*p == '\0')
                        break;
@@ -4311,7 +5506,7 @@ expmeta(enddir, name)
                        start = p + 1;
                }
        }
-       if (metaflag == 0) {    /* we've reached the end of the file name */
+       if (metaflag == 0) {    /* we've reached the end of the file name */
                if (enddir != expdir)
                        metaflag++;
                for (p = name ; ; p++) {
@@ -4369,7 +5564,7 @@ expmeta(enddir, name)
                        continue;
                if (patmatch(start, dp->d_name, 0)) {
                        if (atend) {
-                               scopy(dp->d_name, enddir);
+                               strcpy(enddir, dp->d_name);
                                addfname(expdir);
                        } else {
                                for (p = enddir, cp = dp->d_name;
@@ -4384,29 +5579,11 @@ expmeta(enddir, name)
        if (! atend)
                endname[-1] = '/';
 }
-#endif /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */
-
-
-/*
- * Add a file name to the list.
- */
-
-static void
-addfname(name)
-       char *name;
-       {
-       char *p;
-       struct strlist *sp;
+#endif  /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */
 
-       p = sstrdup(name);
-       sp = (struct strlist *)stalloc(sizeof *sp);
-       sp->text = p;
-       *exparg.lastp = sp;
-       exparg.lastp = &sp->next;
-}
 
 
-#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
+#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
 /*
  * Sort the results of file name expansion.  It calculates the number of
  * strings to sort and then calls msort (short for merge sort) to do the
@@ -4445,9 +5622,9 @@ msort(list, len)
                q = p;
                p = p->next;
        }
-       q->next = NULL;                 /* terminate first half of list */
-       q = msort(list, half);          /* sort first half of list */
-       p = msort(p, len - half);               /* sort second half */
+       q->next = NULL;                 /* terminate first half of list */
+       q = msort(list, half);          /* sort first half of list */
+       p = msort(p, len - half);               /* sort second half */
        lpp = &list;
        for (;;) {
                if (strcmp(p->text, q->text) < 0) {
@@ -4476,13 +5653,11 @@ msort(list, len)
  * Returns true if the pattern matches the string.
  */
 
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
+/* squoted: string might have quote chars */
 static int
-patmatch(pattern, string, squoted)
-       char *pattern;
-       char *string;
-       int squoted;    /* string might have quote chars */
-       {
+patmatch(char *pattern, char *string, int squoted)
+{
        const char *p;
        char *q;
 
@@ -4494,11 +5669,8 @@ patmatch(pattern, string, squoted)
 
 
 static int
-patmatch2(pattern, string, squoted)
-       char *pattern;
-       char *string;
-       int squoted;    /* string might have quote chars */
-       {
+patmatch2(char *pattern, char *string, int squoted)
+{
        char *p;
        int res;
 
@@ -4510,26 +5682,14 @@ patmatch2(pattern, string, squoted)
 }
 #else
 static int
-patmatch(pattern, string, squoted)
-       char *pattern;
-       char *string;
-       int squoted;    /* string might have quote chars */
-       {
-#ifdef notdef
-       if (pattern[0] == '!' && pattern[1] == '!')
-               return 1 - pmatch(pattern + 2, string);
-       else
-#endif
-               return pmatch(pattern, string, squoted);
+patmatch(char *pattern, char *string, int squoted) {
+       return pmatch(pattern, string, squoted);
 }
 
 
 static int
-pmatch(pattern, string, squoted)
-       char *pattern;
-       char *string;
-       int squoted;
-       {
+pmatch(char *pattern, char *string, int squoted)
+{
        char *p, *q;
        char c;
 
@@ -4589,7 +5749,7 @@ pmatch(pattern, string, squoted)
                                while (*endp == CTLQUOTEMARK)
                                        endp++;
                                if (*endp == '\0')
-                                       goto dft;               /* no matching ] */
+                                       goto dft;               /* no matching ] */
                                if (*endp == CTLESC)
                                        endp++;
                                if (*++endp == ']')
@@ -4630,7 +5790,7 @@ pmatch(pattern, string, squoted)
                                return 0;
                        break;
                }
-dft:           default:
+dft:            default:
                        if (squoted && *q == CTLESC)
                                q++;
                        if (*q++ != c)
@@ -4651,11 +5811,9 @@ breakloop:
  * Remove any CTLESC characters from a string.
  */
 
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
 static char *
-_rmescapes(str, flag)
-       char *str;
-       int flag;
+_rmescapes(char *str, int flag)
 {
        char *p, *q, *r;
        static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
@@ -4670,12 +5828,8 @@ _rmescapes(str, flag)
                size_t len = p - str;
                q = r = stalloc(strlen(p) + len + 1);
                if (len > 0) {
-#ifdef _GNU_SOURCE
-                       q = mempcpy(q, str, len);
-#else
                        memcpy(q, str, len);
                        q += len;
-#endif
                }
        }
        while (*p) {
@@ -4727,10 +5881,8 @@ rmescapes(str)
  */
 
 static int
-casematch(pattern, val)
-       union node *pattern;
-       char *val;
-       {
+casematch(union node *pattern, const char *val)
+{
        struct stackmark smark;
        int result;
        char *p;
@@ -4742,7 +5894,7 @@ casematch(pattern, val)
        argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
        STPUTC('\0', expdest);
        p = grabstackstr(expdest);
-       result = patmatch(p, val, 0);
+       result = patmatch(p, (char *)val, 0);
        popstackmark(&smark);
        return result;
 }
@@ -4763,8 +5915,6 @@ cvtnum(num, buf)
        STADJUST(len, buf);
        return buf;
 }
-/*     $NetBSD: histedit.c,v 1.25 2001/02/04 19:52:06 christos Exp $   */
-
 /*
  * Editline and history functions (and glue).
  */
@@ -4777,54 +5927,13 @@ static int histcmd(argc, argv)
 }
 
 
-/*
- * This file was generated by the mkinit program.
- */
-
-extern void rmaliases __P((void));
-
-extern int loopnest;           /* current loop nesting level */
-
-extern void deletefuncs __P((void));
-
-struct strpush {
-       struct strpush *prev;   /* preceding string on stack */
-       char *prevstring;
-       int prevnleft;
-       struct alias *ap;       /* if push was associated with an alias */
-       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 */
-};
-
-extern int parselleft;         /* copy of parsefile->lleft */
-extern struct parsefile basepf;        /* top level input file */
-extern char basebuf[BUFSIZ];   /* buffer for top level input file */
-
-extern short backgndpid;       /* pid of last background process */
-extern int jobctl;
-
-extern int tokpushback;                /* last token pushed back */
-extern int checkkwd;            /* 1 == check for kwds, 2 == also eat newlines */
-
 struct redirtab {
        struct redirtab *next;
-       short renamed[10];
+       short renamed[10]; /* Current ash support only 0-9 descriptors */
+       /* char on arm (and others) can't be negative */
 };
 
-extern struct redirtab *redirlist;
-
-extern char sigmode[NSIG - 1]; /* current value of signal */
+static struct redirtab *redirlist;
 
 extern char **environ;
 
@@ -4835,7 +5944,7 @@ extern char **environ;
  */
 
 static void
-init() {
+init(void) {
 
       /* from cd.c: */
       {
@@ -4847,13 +5956,6 @@ init() {
              basepf.nextc = basepf.buf = basebuf;
       }
 
-      /* from output.c: */
-      {
-#ifdef USE_GLIBC_STDIO
-             initstreams();
-#endif
-      }
-
       /* from var.c: */
       {
              char **envp;
@@ -4866,7 +5968,7 @@ init() {
                      }
              }
 
-             fmtstr(ppid, sizeof(ppid), "%d", (int) getppid());
+             snprintf(ppid, sizeof(ppid), "%d", (int) getppid());
              setvar("PPID", ppid, 0);
       }
 }
@@ -4878,8 +5980,11 @@ init() {
  * interactive shell and control is returned to the main command loop.
  */
 
+/* 1 == check for aliases, 2 == also check for assignments */
+static int checkalias;  /* also used in no alias mode for check assignments */
+
 static void
-reset() {
+reset(void) {
 
       /* from eval.c: */
       {
@@ -4891,7 +5996,7 @@ reset() {
       /* from input.c: */
       {
              if (exception != EXSHELLPROC)
-                     parselleft = parsenleft = 0;      /* clear input buffer */
+                     parselleft = parsenleft = 0;      /* clear input buffer */
              popallfiles();
       }
 
@@ -4908,92 +6013,15 @@ reset() {
                      popredir();
       }
 
-      /* from output.c: */
-      {
-             out1 = &output;
-             out2 = &errout;
-#ifdef USE_GLIBC_STDIO
-             if (memout.stream != NULL)
-                     __closememout();
-#endif
-             if (memout.buf != NULL) {
-                     ckfree(memout.buf);
-                     memout.buf = NULL;
-             }
-      }
 }
 
 
 
-/*
- * This routine is called to initialize the shell to run a shell procedure.
- */
-
-static void
-initshellproc() {
-
-      /* from alias.c: */
-      {
-             rmaliases();
-      }
-
-      /* from eval.c: */
-      {
-             exitstatus = 0;
-      }
-
-      /* from exec.c: */
-      {
-             deletefuncs();
-      }
-
-      /* from jobs.c: */
-      {
-             backgndpid = -1;
-#if JOBS
-             jobctl = 0;
-#endif
-      }
-
-      /* from options.c: */
-      {
-             int i;
-
-             for (i = 0; i < NOPTS; i++)
-                     optlist[i].val = 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();
-      }
-}
-/*     $NetBSD: input.c,v 1.35 2001/02/04 19:52:06 christos Exp $      */
-
 /*
  * This file implements the input routines used by the parser.
  */
 
 #ifdef BB_FEATURE_COMMAND_EDITING
-unsigned int shell_context;
 static const char * cmdedit_prompt;
 static inline void putprompt(const char *s) {
     cmdedit_prompt = s;
@@ -5004,45 +6032,34 @@ static inline void putprompt(const char *s) {
 }
 #endif
 
-#define EOF_NLEFT -99          /* value of parsenleft when EOF pushed back */
+#define EOF_NLEFT -99           /* value of parsenleft when EOF pushed back */
 
-static int plinno = 1;                 /* input line number */
-static int parsenleft;                 /* copy of parsefile->nleft */
-static int parselleft;         /* copy of parsefile->lleft */
-static char *parsenextc;               /* copy of parsefile->nextc */
-struct parsefile basepf;       /* top level input file */
-static char basebuf[BUFSIZ];   /* buffer for top level input file */
-struct parsefile *parsefile = &basepf; /* current input file */
-static int whichprompt;                /* 1 == PS1, 2 == PS2 */
 
-static void pushfile __P((void));
-static int preadfd __P((void));
 
-#ifdef mkinit
-INCLUDE <stdio.h>
-INCLUDE "input.h"
-INCLUDE "error.h"
-
-INIT {
-       basepf.nextc = basepf.buf = basebuf;
-}
+/*
+ * Same as pgetc(), but ignores PEOA.
+ */
 
-RESET {
-       if (exception != EXSHELLPROC)
-               parselleft = parsenleft = 0;    /* clear input buffer */
-       popallfiles();
+#ifdef ASH_ALIAS
+static int
+pgetc2(void)
+{
+       int c;
+       do {
+               c = pgetc_macro();
+       } while (c == PEOA);
+       return c;
 }
+#else
+static inline int pgetc2() { return pgetc_macro(); }
 #endif
 
-
 /*
  * Read a line from the script.
  */
 
-static char *
-pfgets(line, len)
-       char *line;
-       int len;
+static inline char *
+pfgets(char *line, int len)
 {
        char *p = line;
        int nleft = len;
@@ -5063,36 +6080,8 @@ pfgets(line, len)
        return line;
 }
 
-
-/*
- * Read a character from the script, returning PEOF on end of file.
- * Nul characters in the input are silently discarded.
- */
-
-static int
-pgetc()
-{
-       return pgetc_macro();
-}
-
-
-/*
- * Same as pgetc(), but ignores PEOA.
- */
-
-static int
-pgetc2()
-{
-       int c;
-       do {
-               c = pgetc_macro();
-       } while (c == PEOA);
-       return c;
-}
-
-
-static int
-preadfd()
+static inline int
+preadfd(void)
 {
     int nr;
     char *buf =  parsefile->buf;
@@ -5101,23 +6090,17 @@ preadfd()
 retry:
 #ifdef BB_FEATURE_COMMAND_EDITING
        {
-           if (parsefile->fd)
-               nr = read(parsefile->fd, buf, BUFSIZ - 1);
-           else { 
-               do {
-                   cmdedit_read_input((char*)cmdedit_prompt, buf);
-                   nr = strlen(buf);
-               } while (nr <=0 || shell_context);
-               cmdedit_terminate();
+           if (!iflag || parsefile->fd)
+                   nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
+           else {
+                   nr = cmdedit_read_input((char*)cmdedit_prompt, buf);
            }
        }
 #else
-       nr = read(parsefile->fd, buf, BUFSIZ - 1);
+       nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
 #endif
 
        if (nr < 0) {
-               if (errno == EINTR)
-                       goto retry;
                if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
                        int flags = fcntl(0, F_GETFL, 0);
                        if (flags >= 0 && flags & O_NONBLOCK) {
@@ -5132,6 +6115,39 @@ retry:
        return nr;
 }
 
+static void
+popstring(void)
+{
+       struct strpush *sp = parsefile->strpush;
+
+       INTOFF;
+#ifdef ASH_ALIAS
+       if (sp->ap) {
+               if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
+                       if (!checkalias) {
+                               checkalias = 1;
+                       }
+               }
+               if (sp->string != sp->ap->val) {
+                       ckfree(sp->string);
+               }
+
+               sp->ap->flag &= ~ALIASINUSE;
+               if (sp->ap->flag & ALIASDEAD) {
+                       unalias(sp->ap->name);
+               }
+       }
+#endif
+       parsenextc = sp->prevstring;
+       parsenleft = sp->prevnleft;
+/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
+       parsefile->strpush = sp->prev;
+       if (sp != &(parsefile->basestrpush))
+               ckfree(sp);
+       INTON;
+}
+
+
 /*
  * Refill the input buffer and return the next input character:
  *
@@ -5143,29 +6159,26 @@ retry:
  */
 
 static int
-preadbuffer()
+preadbuffer(void)
 {
        char *p, *q;
        int more;
        char savec;
 
        while (parsefile->strpush) {
-               if (
-                       parsenleft == -1 && parsefile->strpush->ap &&
-                       parsenextc[-1] != ' ' && parsenextc[-1] != '\t'
-               ) {
+#ifdef ASH_ALIAS
+               if (parsenleft == -1 && parsefile->strpush->ap &&
+                       parsenextc[-1] != ' ' && parsenextc[-1] != '\t') {
                        return PEOA;
                }
+#endif
                popstring();
                if (--parsenleft >= 0)
                        return (*parsenextc++);
        }
        if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
                return PEOF;
-       flushout(&output);
-#ifdef FLUSHERR
-       flushout(&errout);
-#endif
+       flushall();
 
 again:
        if (parselleft <= 0) {
@@ -5181,7 +6194,7 @@ again:
        for (more = 1; more;) {
                switch (*p) {
                case '\0':
-                       p++;    /* Skip nul */
+                       p++;    /* Skip nul */
                        goto check;
 
 
@@ -5206,9 +6219,6 @@ check:
 
        if (vflag) {
                out2str(parsenextc);
-#ifdef FLUSHERR
-               flushout(out2);
-#endif
        }
 
        *q = savec;
@@ -5216,27 +6226,14 @@ check:
        return *parsenextc++;
 }
 
-/*
- * Undo the last call to pgetc.  Only one character may be pushed back.
- * PEOF may be pushed back.
- */
-
-static void
-pungetc() {
-       parsenleft++;
-       parsenextc--;
-}
 
 /*
  * Push a string back onto the input at this current parsefile level.
  * We handle aliases this way.
  */
 static void
-pushstring(s, len, ap)
-       char *s;
-       int len;
-       void *ap;
-       {
+pushstring(char *s, int len, void *ap)
+{
        struct strpush *sp;
 
        INTOFF;
@@ -5249,107 +6246,26 @@ pushstring(s, len, ap)
                sp = parsefile->strpush = &(parsefile->basestrpush);
        sp->prevstring = parsenextc;
        sp->prevnleft = parsenleft;
+#ifdef ASH_ALIAS
        sp->ap = (struct alias *)ap;
        if (ap) {
                ((struct alias *)ap)->flag |= ALIASINUSE;
                sp->string = s;
        }
+#endif
        parsenextc = s;
        parsenleft = len;
        INTON;
 }
 
-static void
-popstring()
-{
-       struct strpush *sp = parsefile->strpush;
-
-       INTOFF;
-       if (sp->ap) {
-               if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') {
-                       if (!checkalias) {
-                               checkalias = 1;
-                       }
-               }
-               if (sp->string != sp->ap->val) {
-                       ckfree(sp->string);
-               }
-               sp->ap->flag &= ~ALIASINUSE;
-               if (sp->ap->flag & ALIASDEAD) {
-                       unalias(sp->ap->name);
-               }
-       }
-       parsenextc = sp->prevstring;
-       parsenleft = sp->prevnleft;
-/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
-       parsefile->strpush = sp->prev;
-       if (sp != &(parsefile->basestrpush))
-               ckfree(sp);
-       INTON;
-}
-
-/*
- * Set the input to take input from a file.  If push is set, push the
- * old input onto the stack first.
- */
-
-static void
-setinputfile(fname, push)
-       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;
-}
-
-
-/*
- * 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;
-}
-
 
 /*
  * Like setinputfile, but takes input from a string.
  */
 
 static void
-setinputstring(string)
-       char *string;
-       {
+setinputstring(char *string)
+{
        INTOFF;
        pushfile();
        parsenextc = string;
@@ -5367,7 +6283,7 @@ setinputstring(string)
  */
 
 static void
-pushfile() {
+pushfile(void) {
        struct parsefile *pf;
 
        parsefile->nleft = parsenleft;
@@ -5382,79 +6298,32 @@ pushfile() {
        parsefile = pf;
 }
 
-
-static void
-popfile() {
-       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() {
-       while (parsefile != &basepf)
-               popfile();
-}
-
+#ifdef JOBS
+static void restartjob (struct job *);
+#endif
+static void freejob (struct job *);
+static struct job *getjob (const char *);
+static int dowait (int, struct job *);
+static void waitonint(int);
 
 
 /*
- * Close the file(s) that the shell is reading commands from.  Called
- * after a fork is done.
- */
+ * We keep track of whether or not fd0 has been redirected.  This is for
+ * background commands, where we want to redirect fd0 to /dev/null only
+ * if it hasn't already been redirected.
+*/
+static int fd0_redirected = 0;
 
-static void
-closescript() {
-       popallfiles();
-       if (parsefile->fd > 0) {
-               close(parsefile->fd);
-               parsefile->fd = 0;
-       }
+/* Return true if fd 0 has already been redirected at least once.  */
+static inline int
+fd0_redirected_p (void) 
+{
+       return fd0_redirected != 0;
 }
-/*     $NetBSD: jobs.c,v 1.36 2000/05/22 10:18:47 elric Exp $  */
-
-
-struct job *jobtab;            /* array of jobs */
-static int njobs;                      /* size of array */
-short backgndpid = -1; /* pid of last background process */
-#if JOBS
-static int initialpgrp;                /* pgrp of shell on invocation */
-short curjob;                  /* current job */
-#endif
-static int intreceived;
-
-static void restartjob __P((struct job *));
-static void freejob __P((struct job *));
-static struct job *getjob __P((char *));
-static int dowait __P((int, struct job *));
-#ifdef SYSV
-static int onsigchild __P((void));
-#endif
-static int waitproc __P((int, int *));
-static void cmdtxt __P((union node *));
-static void cmdputs __P((const char *));
-static void waitonint(int);
 
+static void dupredirect (const union node *, int, int fd1dup);
 
-#if JOBS
+#ifdef JOBS
 /*
  * Turn job control on and off.
  *
@@ -5463,7 +6332,7 @@ static void waitonint(int);
  * System V doesn't have job control yet, this isn't a problem now.
  */
 
-static int jobctl;
+
 
 static void setjobctl(int enable)
 {
@@ -5476,12 +6345,12 @@ static void setjobctl(int enable)
        if (enable) {
                do { /* while we are in the background */
 #ifdef OLD_TTY_DRIVER
-                       if (ioctl(fileno2, TIOCGPGRP, (char *)&initialpgrp) < 0) {
+                       if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) {
 #else
-                       initialpgrp = tcgetpgrp(fileno2);
+                       initialpgrp = tcgetpgrp(2);
                        if (initialpgrp < 0) {
 #endif
-                               out2str("sh: can't access tty; job cenabletrol turned off\n");
+                               out2str("sh: can't access tty; job control turned off\n");
                                mflag = 0;
                                return;
                        }
@@ -5493,8 +6362,8 @@ static void setjobctl(int enable)
                        }
                } while (0);
 #ifdef OLD_TTY_DRIVER
-               if (ioctl(fileno2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
-                       out2str("sh: need new tty driver to run job cenabletrol; job cenabletrol turned off\n");
+               if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
+                       out2str("sh: need new tty driver to run job control; job control turned off\n");
                        mflag = 0;
                        return;
                }
@@ -5504,16 +6373,16 @@ static void setjobctl(int enable)
                setsignal(SIGTTIN);
                setpgid(0, rootpid);
 #ifdef OLD_TTY_DRIVER
-               ioctl(fileno2, TIOCSPGRP, (char *)&rootpid);
+               ioctl(2, TIOCSPGRP, (char *)&rootpid);
 #else
-               tcsetpgrp(fileno2, rootpid);
+               tcsetpgrp(2, rootpid);
 #endif
-       } else { /* turning job cenabletrol off */
+       } else { /* turning job control off */
                setpgid(0, initialpgrp);
 #ifdef OLD_TTY_DRIVER
-               ioctl(fileno2, TIOCSPGRP, (char *)&initialpgrp);
+               ioctl(2, TIOCSPGRP, (char *)&initialpgrp);
 #else
-               tcsetpgrp(fileno2, initialpgrp);
+               tcsetpgrp(2, initialpgrp);
 #endif
                setsignal(SIGTSTP);
                setsignal(SIGTTOU);
@@ -5524,95 +6393,7 @@ static void setjobctl(int enable)
 #endif
 
 
-#ifdef mkinit
-INCLUDE <stdlib.h>
-
-SHELLPROC {
-       backgndpid = -1;
-#if JOBS
-       jobctl = 0;
-#endif
-}
-
-#endif
-
-
-/* This file was automatically created by ./mksignames.
-   Do not edit.  Edit support/mksignames.c instead. */
-
-/* A translation list so we can be polite to our users. */
-static char *signal_names[NSIG + 2] = {
-    "EXIT",
-    "SIGHUP",
-    "SIGINT",
-    "SIGQUIT",
-    "SIGILL",
-    "SIGTRAP",
-    "SIGABRT",
-    "SIGBUS",
-    "SIGFPE",
-    "SIGKILL",
-    "SIGUSR1",
-    "SIGSEGV",
-    "SIGUSR2",
-    "SIGPIPE",
-    "SIGALRM",
-    "SIGTERM",
-    "SIGJUNK(16)",
-    "SIGCHLD",
-    "SIGCONT",
-    "SIGSTOP",
-    "SIGTSTP",
-    "SIGTTIN",
-    "SIGTTOU",
-    "SIGURG",
-    "SIGXCPU",
-    "SIGXFSZ",
-    "SIGVTALRM",
-    "SIGPROF",
-    "SIGWINCH",
-    "SIGIO",
-    "SIGPWR",
-    "SIGSYS",
-    "SIGRTMIN",
-    "SIGRTMIN+1",
-    "SIGRTMIN+2",
-    "SIGRTMIN+3",
-    "SIGRTMIN+4",
-    "SIGRTMIN+5",
-    "SIGRTMIN+6",
-    "SIGRTMIN+7",
-    "SIGRTMIN+8",
-    "SIGRTMIN+9",
-    "SIGRTMIN+10",
-    "SIGRTMIN+11",
-    "SIGRTMIN+12",
-    "SIGRTMIN+13",
-    "SIGRTMIN+14",
-    "SIGRTMIN+15",
-    "SIGRTMAX-15",
-    "SIGRTMAX-14",
-    "SIGRTMAX-13",
-    "SIGRTMAX-12",
-    "SIGRTMAX-11",
-    "SIGRTMAX-10",
-    "SIGRTMAX-9",
-    "SIGRTMAX-8",
-    "SIGRTMAX-7",
-    "SIGRTMAX-6",
-    "SIGRTMAX-5",
-    "SIGRTMAX-4",
-    "SIGRTMAX-3",
-    "SIGRTMAX-2",
-    "SIGRTMAX-1",
-    "SIGRTMAX",
-    "DEBUG",
-    (char *)0x0,
-};
-
-
-
-#if JOBS
+#ifdef JOBS
 static int
 killcmd(argc, argv)
        int argc;
@@ -5650,7 +6431,7 @@ usage:
                                                        optionarg
                                                );
                                        }
-                                       break;
+                                       break;
 #ifdef DEBUG
                                default:
                                        error(
@@ -5669,18 +6450,20 @@ usage:
        }
 
        if (list) {
+               const char *name;
+
                if (!*argptr) {
                        out1str("0\n");
                        for (i = 1; i < NSIG; i++) {
-                               out1fmt(snlfmt, signal_names[i] + 3);
+                               name = u_signal_names(0, &i, 1);
+                               if(name)
+                                       printf(snlfmt, name);
                        }
                        return 0;
                }
-               signo = atoi(*argptr);
-               if (signo > 128)
-                       signo -= 128;
-               if (0 < signo && signo < NSIG)
-                               out1fmt(snlfmt, signal_names[signo] + 3);
+               name = u_signal_names(*argptr, &signo, -1);
+               if (name)
+                       printf(snlfmt, name);
                else
                        error("invalid signal number or exit status: %s",
                              *argptr);
@@ -5697,7 +6480,7 @@ usage:
                } else
                        pid = atoi(*argptr);
                if (kill(pid, signo) != 0)
-                       error("%s: %s", *argptr, strerror(errno));
+                       error("%s: %m", *argptr);
        } while (*++argptr);
 
        return 0;
@@ -5717,9 +6500,9 @@ fgcmd(argc, argv)
                error("job not created under job control");
        pgrp = jp->ps[0].pid;
 #ifdef OLD_TTY_DRIVER
-       ioctl(fileno2, TIOCSPGRP, (char *)&pgrp);
+       ioctl(2, TIOCSPGRP, (char *)&pgrp);
 #else
-       tcsetpgrp(fileno2, pgrp);
+       tcsetpgrp(2, pgrp);
 #endif
        restartjob(jp);
        INTOFF;
@@ -5767,6 +6550,8 @@ restartjob(jp)
 }
 #endif
 
+static void showjobs(int change);
+
 
 static int
 jobscmd(argc, argv)
@@ -5811,12 +6596,12 @@ showjobs(change)
                if (change && ! jp->changed)
                        continue;
                procno = jp->nprocs;
-               for (ps = jp->ps ; ; ps++) {    /* for each process */
+               for (ps = jp->ps ; ; ps++) {    /* for each process */
                        if (ps == jp->ps)
-                               fmtstr(s, 64, "[%d] %ld ", jobno, 
+                               snprintf(s, 64, "[%d] %ld ", jobno,
                                    (long)ps->pid);
                        else
-                               fmtstr(s, 64, "    %ld ", 
+                               snprintf(s, 64, "    %ld ",
                                    (long)ps->pid);
                        out1str(s);
                        col = strlen(s);
@@ -5824,25 +6609,25 @@ showjobs(change)
                        if (ps->status == -1) {
                                /* don't print anything */
                        } else if (WIFEXITED(ps->status)) {
-                               fmtstr(s, 64, "Exit %d", 
+                               snprintf(s, 64, "Exit %d",
                                       WEXITSTATUS(ps->status));
                        } else {
-#if JOBS
-                               if (WIFSTOPPED(ps->status)) 
+#ifdef JOBS
+                               if (WIFSTOPPED(ps->status))
                                        i = WSTOPSIG(ps->status);
                                else /* WIFSIGNALED(ps->status) */
 #endif
                                        i = WTERMSIG(ps->status);
                                if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F])
-                                       scopy(sys_siglist[i & 0x7F], s);
+                                       strcpy(s, sys_siglist[i & 0x7F]);
                                else
-                                       fmtstr(s, 64, "Signal %d", i & 0x7F);
+                                       snprintf(s, 64, "Signal %d", i & 0x7F);
                                if (WCOREDUMP(ps->status))
                                        strcat(s, " (core dumped)");
                        }
                        out1str(s);
                        col += strlen(s);
-                       out1fmt(
+                       printf(
                                "%*c%s\n", 30 - col >= 0 ? 30 - col : 0, ' ',
                                ps->cmd
                        );
@@ -5862,10 +6647,9 @@ showjobs(change)
  */
 
 static void
-freejob(jp)
-       struct job *jp;
-       {
-       struct procstat *ps;
+freejob(struct job *jp)
+{
+       const struct procstat *ps;
        int i;
 
        INTOFF;
@@ -5876,7 +6660,7 @@ freejob(jp)
        if (jp->ps != &jp->ps0)
                ckfree(jp->ps);
        jp->used = 0;
-#if JOBS
+#ifdef JOBS
        if (curjob == jp - jobtab + 1)
                curjob = 0;
 #endif
@@ -5900,7 +6684,7 @@ start:
        } else {
                job = NULL;
        }
-       for (;;) {      /* loop until process terminated or stopped */
+       for (;;) {      /* loop until process terminated or stopped */
                if (job != NULL) {
                        if (job->state) {
                                status = job->ps[job->nprocs - 1].status;
@@ -5911,7 +6695,7 @@ start:
                                }
                                if (WIFEXITED(status))
                                        retval = WEXITSTATUS(status);
-#if JOBS
+#ifdef JOBS
                                else if (WIFSTOPPED(status))
                                        retval = WSTOPSIG(status) + 128;
 #endif
@@ -5923,7 +6707,7 @@ start:
                        }
                } else {
                        for (jp = jobtab ; ; jp++) {
-                               if (jp >= jobtab + njobs) {     /* no running procs */
+                               if (jp >= jobtab + njobs) {     /* no running procs */
                                        return 0;
                                }
                                if (jp->used && jp->state == 0)
@@ -5943,16 +6727,15 @@ start:
  */
 
 static struct job *
-getjob(name)
-       char *name;
-       {
+getjob(const char *name)
+{
        int jobno;
        struct job *jp;
        int pid;
        int i;
 
        if (name == NULL) {
-#if JOBS
+#ifdef JOBS
 currentjob:
                if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0)
                        error("No current job");
@@ -5966,7 +6749,7 @@ currentjob:
                        if (jobno > 0 && jobno <= njobs
                         && jobtab[jobno - 1].used != 0)
                                return &jobtab[jobno - 1];
-#if JOBS
+#ifdef JOBS
                } else if (name[1] == '%' && name[2] == '\0') {
                        goto currentjob;
 #endif
@@ -5983,8 +6766,7 @@ currentjob:
                        if (found)
                                return found;
                }
-       } else if (is_number(name)) {
-               pid = number(name);
+       } else if (is_number(name, &pid)) {
                for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
                        if (jp->used && jp->nprocs > 0
                         && jp->ps[jp->nprocs - 1].pid == pid)
@@ -6001,10 +6783,8 @@ currentjob:
  * Return a new job structure,
  */
 
-struct job *
-makejob(node, nprocs)
-       union node *node;
-       int nprocs;
+static struct job *
+makejob(const union node *node, int nprocs)
 {
        int i;
        struct job *jp;
@@ -6037,7 +6817,7 @@ makejob(node, nprocs)
        jp->used = 1;
        jp->changed = 0;
        jp->nprocs = 0;
-#if JOBS
+#ifdef JOBS
        jp->jobctl = jobctl;
 #endif
        if (nprocs > 1) {
@@ -6057,24 +6837,25 @@ makejob(node, nprocs)
  * own process group.  Jp is a job structure that the job is to be added to.
  * N is the command that will be evaluated by the child.  Both jp and n may
  * be NULL.  The mode parameter can be one of the following:
- *     FORK_FG - Fork off a foreground process.
- *     FORK_BG - Fork off a background process.
- *     FORK_NOJOB - Like FORK_FG, but don't give the process its own
- *                  process group even if job control is on.
+ *      FORK_FG - Fork off a foreground process.
+ *      FORK_BG - Fork off a background process.
+ *      FORK_NOJOB - Like FORK_FG, but don't give the process its own
+ *                   process group even if job control is on.
  *
  * When job control is turned off, background processes have their standard
  * input redirected to /dev/null (except for the second and later processes
  * in a pipeline).
  */
 
+
+
 static int
-forkshell(jp, n, mode)
-       union node *n;
-       struct job *jp;
-       int mode;
+forkshell(struct job *jp, const union node *n, int mode)
 {
        int pid;
+#ifdef JOBS
        int pgrp;
+#endif
        const char *devnull = _PATH_DEVNULL;
        const char *nullerr = "Can't open %s";
 
@@ -6098,8 +6879,8 @@ forkshell(jp, n, mode)
                closescript();
                INTON;
                clear_traps();
-#if JOBS
-               jobctl = 0;             /* do job control only in root shell */
+#ifdef JOBS
+               jobctl = 0;             /* do job control only in root shell */
                if (wasroot && mode != FORK_NOJOB && mflag) {
                        if (jp == NULL || jp->nprocs == 0)
                                pgrp = getpid();
@@ -6109,10 +6890,10 @@ forkshell(jp, n, mode)
                        if (mode == FORK_FG) {
                                /*** this causes superfluous TIOCSPGRPS ***/
 #ifdef OLD_TTY_DRIVER
-                               if (ioctl(fileno2, TIOCSPGRP, (char *)&pgrp) < 0)
+                               if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0)
                                        error("TIOCSPGRP failed, errno=%d", errno);
 #else
-                               if (tcsetpgrp(fileno2, pgrp) < 0)
+                               if (tcsetpgrp(2, pgrp) < 0)
                                        error("tcsetpgrp failed, errno=%d", errno);
 #endif
                        }
@@ -6150,6 +6931,7 @@ forkshell(jp, n, mode)
                }
                return pid;
        }
+#ifdef JOBS
        if (rootshell && mode != FORK_NOJOB && mflag) {
                if (jp == NULL || jp->nprocs == 0)
                        pgrp = pid;
@@ -6157,8 +6939,9 @@ forkshell(jp, n, mode)
                        pgrp = jp->ps[0].pid;
                setpgid(pid, pgrp);
        }
+#endif
        if (mode == FORK_BG)
-               backgndpid = pid;               /* set $! */
+               backgndpid = pid;               /* set $! */
        if (jp) {
                struct procstat *ps = &jp->ps[jp->nprocs++];
                ps->pid = pid;
@@ -6194,10 +6977,9 @@ forkshell(jp, n, mode)
  */
 
 static int
-waitforjob(jp)
-       struct job *jp;
-       {
-#if JOBS
+waitforjob(struct job *jp)
+{
+#ifdef JOBS
        int mypgrp = getpgrp();
 #endif
        int status;
@@ -6206,7 +6988,7 @@ waitforjob(jp)
 
        INTOFF;
        intreceived = 0;
-#if JOBS
+#ifdef JOBS
        if (!jobctl) {
 #else
        if (!iflag) {
@@ -6219,7 +7001,7 @@ waitforjob(jp)
        while (jp->state == 0) {
                dowait(1, jp);
        }
-#if JOBS
+#ifdef JOBS
        if (!jobctl) {
 #else
        if (!iflag) {
@@ -6227,13 +7009,13 @@ waitforjob(jp)
                sigaction(SIGINT, &oact, 0);
                if (intreceived && trap[SIGINT]) kill(getpid(), SIGINT);
        }
-#if JOBS
+#ifdef JOBS
        if (jp->jobctl) {
 #ifdef OLD_TTY_DRIVER
-               if (ioctl(fileno2, TIOCSPGRP, (char *)&mypgrp) < 0)
+               if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0)
                        error("TIOCSPGRP failed, errno=%d\n", errno);
 #else
-               if (tcsetpgrp(fileno2, mypgrp) < 0)
+               if (tcsetpgrp(2, mypgrp) < 0)
                        error("tcsetpgrp failed, errno=%d\n", errno);
 #endif
        }
@@ -6244,13 +7026,13 @@ waitforjob(jp)
        /* convert to 8 bits */
        if (WIFEXITED(status))
                st = WEXITSTATUS(status);
-#if JOBS
+#ifdef JOBS
        else if (WIFSTOPPED(status))
                st = WSTOPSIG(status) + 128;
 #endif
        else
                st = WTERMSIG(status) + 128;
-#if JOBS
+#ifdef JOBS
        if (jp->jobctl) {
                /*
                 * This is truly gross.
@@ -6263,8 +7045,9 @@ waitforjob(jp)
                if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT)
                        raise(SIGINT);
        }
+       if (jp->state == JOBDONE)
+
 #endif
-       if (! JOBS || jp->state == JOBDONE)
                freejob(jp);
        INTON;
        return st;
@@ -6272,14 +7055,51 @@ waitforjob(jp)
 
 
 
-/*
- * Wait for a process to terminate.
- */
+/*
+ * Wait for a process to terminate.
+ */
+
+/*
+ * Do a wait system call.  If job control is compiled in, we accept
+ * stopped processes.  If block is zero, we return a value of zero
+ * rather than blocking.
+ *
+ * System V doesn't have a non-blocking wait system call.  It does
+ * have a SIGCLD signal that is sent to a process when one of it's
+ * children dies.  The obvious way to use SIGCLD would be to install
+ * a handler for SIGCLD which simply bumped a counter when a SIGCLD
+ * was received, and have waitproc bump another counter when it got
+ * the status of a process.  Waitproc would then know that a wait
+ * system call would not block if the two counters were different.
+ * This approach doesn't work because if a process has children that
+ * have not been waited for, System V will send it a SIGCLD when it
+ * installs a signal handler for SIGCLD.  What this means is that when
+ * a child exits, the shell will be sent SIGCLD signals continuously
+ * until is runs out of stack space, unless it does a wait call before
+ * restoring the signal handler.  The code below takes advantage of
+ * this (mis)feature by installing a signal handler for SIGCLD and
+ * then checking to see whether it was called.  If there are any
+ * children to be waited for, it will be.
+ *
+ */
+
+static inline int
+waitproc(int block, int *status)
+{
+       int flags;
+
+       flags = 0;
+#ifdef JOBS
+       if (jobctl)
+               flags |= WUNTRACED;
+#endif
+       if (block == 0)
+               flags |= WNOHANG;
+       return wait3(status, flags, (struct rusage *)NULL);
+}
 
 static int
-dowait(block, job)
-       int block;
-       struct job *job;
+dowait(int block, struct job *job)
 {
        int pid;
        int status;
@@ -6317,14 +7137,14 @@ dowait(block, job)
                                else if (WIFSTOPPED(sp->status))
                                        done = 0;
                        }
-                       if (stopped) {          /* stopped or done */
+                       if (stopped) {          /* stopped or done */
                                int state = done? JOBDONE : JOBSTOPPED;
                                if (jp->state != state) {
                                        TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
                                        jp->state = state;
-#if JOBS
+#ifdef JOBS
                                        if (done && curjob == jp - jobtab + 1)
-                                               curjob = 0;             /* no current job */
+                                               curjob = 0;             /* no current job */
 #endif
                                }
                        }
@@ -6333,7 +7153,7 @@ dowait(block, job)
        INTON;
        if (! rootshell || ! iflag || (job && thisjob == job)) {
                core = WCOREDUMP(status);
-#if JOBS
+#ifdef JOBS
                if (WIFSTOPPED(status)) sig = WSTOPSIG(status);
                else
 #endif
@@ -6342,24 +7162,21 @@ dowait(block, job)
 
                if (sig != 0 && sig != SIGINT && sig != SIGPIPE) {
                        if (thisjob != job)
-                               outfmt(out2, "%d: ", pid);
-#if JOBS
+                               out2fmt("%d: ", pid);
+#ifdef JOBS
                        if (sig == SIGTSTP && rootshell && iflag)
-                               outfmt(out2, "%%%ld ",
+                               out2fmt("%%%ld ",
                                    (long)(job - jobtab + 1));
 #endif
                        if (sig < NSIG && sys_siglist[sig])
                                out2str(sys_siglist[sig]);
                        else
-                               outfmt(out2, "Signal %d", sig);
+                               out2fmt("Signal %d", sig);
                        if (core)
                                out2str(" - core dumped");
                        out2c('\n');
-#ifdef FLUSHERR
-                       flushout(&errout);
-#endif
                } else {
-                       TRACE(("Not printing status: status=%d, sig=%d\n", 
+                       TRACE(("Not printing status: status=%d, sig=%d\n",
                               status, sig));
                }
        } else {
@@ -6372,86 +7189,12 @@ dowait(block, job)
 
 
 
-/*
- * Do a wait system call.  If job control is compiled in, we accept
- * stopped processes.  If block is zero, we return a value of zero
- * rather than blocking.
- *
- * System V doesn't have a non-blocking wait system call.  It does
- * have a SIGCLD signal that is sent to a process when one of it's
- * children dies.  The obvious way to use SIGCLD would be to install
- * a handler for SIGCLD which simply bumped a counter when a SIGCLD
- * was received, and have waitproc bump another counter when it got
- * the status of a process.  Waitproc would then know that a wait
- * system call would not block if the two counters were different.
- * This approach doesn't work because if a process has children that
- * have not been waited for, System V will send it a SIGCLD when it
- * installs a signal handler for SIGCLD.  What this means is that when
- * a child exits, the shell will be sent SIGCLD signals continuously
- * until is runs out of stack space, unless it does a wait call before
- * restoring the signal handler.  The code below takes advantage of
- * this (mis)feature by installing a signal handler for SIGCLD and
- * then checking to see whether it was called.  If there are any
- * children to be waited for, it will be.
- *
- * If neither SYSV nor BSD is defined, we don't implement nonblocking
- * waits at all.  In this case, the user will not be informed when
- * a background process until the next time she runs a real program
- * (as opposed to running a builtin command or just typing return),
- * and the jobs command may give out of date information.
- */
-
-#ifdef SYSV
-static int gotsigchild;
-
-static int onsigchild() {
-       gotsigchild = 1;
-}
-#endif
-
-
-static int
-waitproc(block, status)
-       int block;
-       int *status;
-{
-#ifdef BSD
-       int flags;
-
-       flags = 0;
-#if JOBS
-       if (jobctl)
-               flags |= WUNTRACED;
-#endif
-       if (block == 0)
-               flags |= WNOHANG;
-       return wait3(status, flags, (struct rusage *)NULL);
-#else
-#ifdef SYSV
-       int (*save)();
-
-       if (block == 0) {
-               gotsigchild = 0;
-               save = signal(SIGCLD, onsigchild);
-               signal(SIGCLD, save);
-               if (gotsigchild == 0)
-                       return 0;
-       }
-       return wait(status);
-#else
-       if (block == 0)
-               return 0;
-       return wait(status);
-#endif
-#endif
-}
 
 /*
  * return 1 if there are stopped jobs, otherwise 0
  */
-static int job_warning = 0;
 static int
-stoppedjobs()
+stoppedjobs(void)
 {
        int jobno;
        struct job *jp;
@@ -6478,26 +7221,263 @@ stoppedjobs()
 
 static char *cmdnextc;
 static int cmdnleft;
-#define MAXCMDTEXT     200
+#define MAXCMDTEXT      200
 
-static char *
-commandtext(n)
-       union node *n;
-       {
-       char *name;
+static void
+cmdputs(const char *s)
+{
+       const char *p;
+       char *q;
+       char c;
+       int subtype = 0;
 
-       cmdnextc = name = ckmalloc(MAXCMDTEXT);
-       cmdnleft = MAXCMDTEXT - 4;
-       cmdtxt(n);
-       *cmdnextc = '\0';
-       return name;
+       if (cmdnleft <= 0)
+               return;
+       p = s;
+       q = cmdnextc;
+       while ((c = *p++) != '\0') {
+               if (c == CTLESC)
+                       *q++ = *p++;
+               else if (c == CTLVAR) {
+                       *q++ = '$';
+                       if (--cmdnleft > 0)
+                               *q++ = '{';
+                       subtype = *p++;
+               } else if (c == '=' && subtype != 0) {
+                       *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL];
+                       subtype = 0;
+               } else if (c == CTLENDVAR) {
+                       *q++ = '}';
+               } else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE)
+                       cmdnleft++;             /* ignore it */
+               else
+                       *q++ = c;
+               if (--cmdnleft <= 0) {
+                       *q++ = '.';
+                       *q++ = '.';
+                       *q++ = '.';
+                       break;
+               }
+       }
+       cmdnextc = q;
 }
 
+#define CMDTXT_TABLE
+#ifdef CMDTXT_TABLE
+/*
+ * To collect a lot of redundant code in cmdtxt() case statements, we
+ * implement a mini language here.  Each type of node struct has an
+ * associated instruction sequence that operates on its members via
+ * their offsets.  The instruction are pack in unsigned chars with
+ * format   IIDDDDDE   where the bits are
+ *   I : part of the instruction opcode, which are
+ *       00 : member is a pointer to another node -- process it recursively
+ *       40 : member is a pointer to a char string -- output it
+ *       80 : output the string whose index is stored in the data field
+ *       CC : flag signaling that this case needs external processing
+ *   D : data - either the (shifted) index of a fixed string to output or
+ *              the actual offset of the member to operate on in the struct
+ *              (since we assume bit 0 is set, the offset is not shifted)
+ *   E : flag signaling end of instruction sequence
+ *
+ * WARNING: In order to handle larger offsets for 64bit archs, this code
+ *          assumes that no offset can be an odd number and stores the
+ *          end-of-instructions flag in bit 0.
+ */
+
+#define CMDTXT_NOMORE      0x01 /* NOTE: no offset should be odd */
+#define CMDTXT_CHARPTR     0x40
+#define CMDTXT_STRING      0x80
+#define CMDTXT_SPECIAL     0xC0
+#define CMDTXT_OFFSETMASK  0x3E
+
+static const char * const cmdtxt_strings[] = {
+ /* 0     1    2    3       4       5      6          7     */
+       "; ", "(", ")", " && ", " || ", "if ", "; then ", "...",
+ /* 8         9        10       11        12      13       */
+    "while ", "; do ", "; done", "until ", "for ", " in ...",
+ /* 14       15     16        17     */
+       "case ", "???", "() ...", "<<..."
+};
+
+static const char * const redir_strings[] = {
+       ">", "<", "<>", ">>", ">|", ">&", "<&"
+};
+
+static const unsigned char cmdtxt_ops[] = {
+#define CMDTXT_NSEMI    0
+       offsetof(union node, nbinary.ch1),
+       0|CMDTXT_STRING,
+       offsetof(union node, nbinary.ch2)|CMDTXT_NOMORE,
+#define CMDTXT_NCMD     (CMDTXT_NSEMI + 3)
+#define CMDTXT_NPIPE    (CMDTXT_NCMD)
+#define  CMDTXT_NCASE    (CMDTXT_NCMD)
+#define  CMDTXT_NTO      (CMDTXT_NCMD)
+#define  CMDTXT_NFROM    (CMDTXT_NCMD)
+#define  CMDTXT_NFROMTO  (CMDTXT_NCMD)
+#define  CMDTXT_NAPPEND  (CMDTXT_NCMD)
+#define  CMDTXT_NTOOV    (CMDTXT_NCMD)
+#define  CMDTXT_NTOFD    (CMDTXT_NCMD)
+#define  CMDTXT_NFROMFD  (CMDTXT_NCMD)
+       CMDTXT_SPECIAL,
+#define CMDTXT_NREDIR   (CMDTXT_NPIPE + 1)
+#define CMDTXT_NBACKGND (CMDTXT_NREDIR)
+       offsetof(union node, nredir.n)|CMDTXT_NOMORE,
+#define CMDTXT_NSUBSHELL (CMDTXT_NBACKGND + 1)
+       (1*2)|CMDTXT_STRING,
+       offsetof(union node, nredir.n),
+       (2*2)|CMDTXT_STRING|CMDTXT_NOMORE,
+#define CMDTXT_NAND     (CMDTXT_NSUBSHELL + 3)
+       offsetof(union node, nbinary.ch1),
+       (3*2)|CMDTXT_STRING,
+       offsetof(union node, nbinary.ch2)|CMDTXT_NOMORE,
+#define CMDTXT_NOR      (CMDTXT_NAND + 3)
+       offsetof(union node, nbinary.ch1),
+       (4*2)|CMDTXT_STRING,
+       offsetof(union node, nbinary.ch2)|CMDTXT_NOMORE,
+#define CMDTXT_NIF      (CMDTXT_NOR + 3)
+       (5*2)|CMDTXT_STRING,
+       offsetof(union node, nif.test),
+       (6*2)|CMDTXT_STRING,
+       offsetof(union node, nif.ifpart),
+       (7*2)|CMDTXT_STRING|CMDTXT_NOMORE,
+#define CMDTXT_NWHILE   (CMDTXT_NIF + 5)
+       (8*2)|CMDTXT_STRING,
+       offsetof(union node, nbinary.ch1),
+       (9*2)|CMDTXT_STRING,
+       offsetof(union node, nbinary.ch2),
+       (10*2)|CMDTXT_STRING|CMDTXT_NOMORE,
+#define CMDTXT_NUNTIL   (CMDTXT_NWHILE + 5)
+       (11*2)|CMDTXT_STRING,
+       offsetof(union node, nbinary.ch1),
+       (9*2)|CMDTXT_STRING,
+       offsetof(union node, nbinary.ch2),
+       (10*2)|CMDTXT_STRING|CMDTXT_NOMORE,
+#define CMDTXT_NFOR     (CMDTXT_NUNTIL + 5)
+       (12*2)|CMDTXT_STRING,
+       offsetof(union node, nfor.var)|CMDTXT_CHARPTR,
+       (13*2)|CMDTXT_STRING|CMDTXT_NOMORE,
+#define CMDTXT_NCLIST   (CMDTXT_NFOR + 3) /* TODO: IS THIS CORRECT??? */
+#define  CMDTXT_NNOT     (CMDTXT_NCLIST)        /* TODO: IS THIS CORRECT??? */
+       (15*2)|CMDTXT_STRING|CMDTXT_NOMORE,
+#define CMDTXT_NDEFUN   (CMDTXT_NCLIST + 1)
+       offsetof(union node, narg.text)|CMDTXT_CHARPTR,
+       (16*2)|CMDTXT_STRING|CMDTXT_NOMORE,
+#define CMDTXT_NARG     (CMDTXT_NDEFUN + 2)
+       offsetof(union node, narg.text)|CMDTXT_CHARPTR|CMDTXT_NOMORE,
+#define CMDTXT_NHERE    (CMDTXT_NARG + 1)
+#define CMDTXT_NXHERE   (CMDTXT_NHERE)
+       (17*2)|CMDTXT_STRING|CMDTXT_NOMORE,
+};
+
+#if CMDTXT_NXHERE != 36
+#error CMDTXT_NXHERE
+#endif
+
+static const unsigned char cmdtxt_ops_index[26] = {
+       CMDTXT_NSEMI,
+       CMDTXT_NCMD,
+       CMDTXT_NPIPE,
+       CMDTXT_NREDIR,
+       CMDTXT_NBACKGND,
+       CMDTXT_NSUBSHELL,
+       CMDTXT_NAND,
+       CMDTXT_NOR,
+       CMDTXT_NIF,
+       CMDTXT_NWHILE,
+       CMDTXT_NUNTIL,
+       CMDTXT_NFOR,
+       CMDTXT_NCASE,
+       CMDTXT_NCLIST,
+       CMDTXT_NDEFUN,
+       CMDTXT_NARG,
+       CMDTXT_NTO,
+       CMDTXT_NFROM,
+       CMDTXT_NFROMTO,
+       CMDTXT_NAPPEND,
+       CMDTXT_NTOOV,
+       CMDTXT_NTOFD,
+       CMDTXT_NFROMFD,
+       CMDTXT_NHERE,
+       CMDTXT_NXHERE,
+       CMDTXT_NNOT,
+};
 
 static void
-cmdtxt(n)
-       union node *n;
-       {
+cmdtxt(const union node *n)
+{
+       const char *p;
+
+       if (n == NULL)
+               return;
+
+       p = cmdtxt_ops + (int) cmdtxt_ops_index[n->type];
+       if ((*p & CMDTXT_SPECIAL) != CMDTXT_SPECIAL) { /* normal case */
+               do {
+                       if (*p & CMDTXT_STRING) { /* output fixed string */
+                               cmdputs(cmdtxt_strings[((int)(*p & CMDTXT_OFFSETMASK) >> 1)]);
+                       } else {
+                               const char *pf = ((const char *) n)
+                                                                 + ((int)(*p & CMDTXT_OFFSETMASK));
+                               if (*p & CMDTXT_CHARPTR) { /* output dynamic string */
+                                       cmdputs(*((const char **) pf));
+                               } else {                /* output field */
+                                       cmdtxt(*((const union node **) pf));
+                               }
+                       }
+               } while (!(*p++ & CMDTXT_NOMORE));
+       } else if (n->type == NCMD) {
+               union node *np;
+               for (np = n->ncmd.args ; np ; np = np->narg.next) {
+                       cmdtxt(np);
+                       if (np->narg.next)
+                               cmdputs(spcstr);
+               }
+               for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
+                       cmdputs(spcstr);
+                       cmdtxt(np);
+               }
+       } else if (n->type == NPIPE) {
+               struct nodelist *lp;
+               for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
+                       cmdtxt(lp->n);
+                       if (lp->next)
+                               cmdputs(" | ");
+               }
+       } else if (n->type == NCASE) {
+               cmdputs(cmdtxt_strings[14]);
+               cmdputs(n->ncase.expr->narg.text);
+               cmdputs(cmdtxt_strings[13]);
+       } else {
+#if (NTO != 16) || (NFROM != 17) || (NFROMTO != 18) || (NAPPEND != 19) || (NTOOV != 20) || (NTOFD != 21) || (NFROMFD != 22)
+#error Assumption violated regarding range and ordering of NTO ... NFROMFD!
+#endif
+               char s[2];
+
+#ifdef DEBUG
+               assert((n->type >= NTO) && (n->type <= NFROMFD));
+#endif
+
+               p = redir_strings[n->type - NTO];
+               if (n->nfile.fd != ('>' == *p)) {
+                       s[0] = n->nfile.fd + '0';
+                       s[1] = '\0';
+                       cmdputs(s);
+               }
+               cmdputs(p);
+               if (n->type >= NTOFD) {
+                       s[0] = n->ndup.dupfd + '0';
+                       s[1] = '\0';
+                       cmdputs(s);
+               } else {
+                       cmdtxt(n->nfile.fname);
+               }
+       }
+}
+#else  /* CMDTXT_TABLE */
+static void
+cmdtxt(const union node *n)
+{
        union node *np;
        struct nodelist *lp;
        const char *p;
@@ -6622,55 +7602,25 @@ redir:
                break;
        }
 }
+#endif /* CMDTXT_TABLE */
 
+static char *
+commandtext(const union node *n)
+{
+       char *name;
 
-
-static void
-cmdputs(s)
-       const char *s;
-       {
-       const char *p;
-       char *q;
-       char c;
-       int subtype = 0;
-
-       if (cmdnleft <= 0)
-               return;
-       p = s;
-       q = cmdnextc;
-       while ((c = *p++) != '\0') {
-               if (c == CTLESC)
-                       *q++ = *p++;
-               else if (c == CTLVAR) {
-                       *q++ = '$';
-                       if (--cmdnleft > 0)
-                               *q++ = '{';
-                       subtype = *p++;
-               } else if (c == '=' && subtype != 0) {
-                       *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL];
-                       subtype = 0;
-               } else if (c == CTLENDVAR) {
-                       *q++ = '}';
-               } else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE)
-                       cmdnleft++;             /* ignore it */
-               else
-                       *q++ = c;
-               if (--cmdnleft <= 0) {
-                       *q++ = '.';
-                       *q++ = '.';
-                       *q++ = '.';
-                       break;
-               }
-       }
-       cmdnextc = q;
+       cmdnextc = name = ckmalloc(MAXCMDTEXT);
+       cmdnleft = MAXCMDTEXT - 4;
+       cmdtxt(n);
+       *cmdnextc = '\0';
+       return name;
 }
 
+
 static void waitonint(int sig) {
        intreceived = 1;
        return;
 }
-/*     $NetBSD: mail.c,v 1.14 2000/07/03 03:26:19 matt Exp $   */
-
 /*
  * Routines to check for mail.  (Perhaps make part of main.c?)
  */
@@ -6679,8 +7629,8 @@ static void waitonint(int sig) {
 #define MAXMBOXES 10
 
 
-static int nmboxes;                    /* number of mailboxes */
-static time_t mailtime[MAXMBOXES];     /* times of mailboxes */
+static int nmboxes;                     /* number of mailboxes */
+static time_t mailtime[MAXMBOXES];      /* times of mailboxes */
 
 
 
@@ -6691,8 +7641,7 @@ static time_t mailtime[MAXMBOXES];        /* times of mailboxes */
  */
 
 static void
-chkmail(silent)
-       int silent;
+chkmail(int silent)
 {
        int i;
        const char *mpath;
@@ -6718,49 +7667,33 @@ chkmail(silent)
                if (q[-1] != '/')
                        abort();
 #endif
-               q[-1] = '\0';                   /* delete trailing '/' */
-#ifdef notdef /* this is what the System V shell claims to do (it lies) */
-               if (stat(p, &statb) < 0)
-                       statb.st_mtime = 0;
-               if (statb.st_mtime > mailtime[i] && ! silent) {
-                       outfmt(
-                               &errout, snlfmt,
-                               pathopt? pathopt : "you have mail"
-                       );
-               }
-               mailtime[i] = statb.st_mtime;
-#else /* this is what it should do */
+               q[-1] = '\0';                   /* delete trailing '/' */
                if (stat(p, &statb) < 0)
                        statb.st_size = 0;
                if (statb.st_size > mailtime[i] && ! silent) {
-                       outfmt(
-                               &errout, snlfmt,
-                               pathopt? pathopt : "you have mail"
-                       );
+                       out2fmt(snlfmt,
+                               pathopt? pathopt : "you have mail");
                }
                mailtime[i] = statb.st_size;
-#endif
        }
        nmboxes = i;
        popstackmark(&smark);
 }
-/*     $NetBSD: main.c,v 1.40 2001/02/04 19:52:06 christos Exp $       */
-
 
 #define PROFILE 0
 
-static int rootpid;
-static int rootshell;
 #if PROFILE
-short profile_buf[16384];
+static short profile_buf[16384];
 extern int etext();
 #endif
 
-static void read_profile __P((const char *));
-static char *find_dot_file __P((char *));
-int shell_main __P((int, char **));
+static void read_profile (const char *);
+static void cmdloop (int);
+static void options (int);
+static void setoption (int, int);
+static void procargs (int, char **);
+
 
-extern int oexitstatus;
 /*
  * Main routine.  We initialize things, parse the arguments, execute
  * profiles if we're a login shell, and then call cmdloop to execute
@@ -6770,21 +7703,24 @@ extern int oexitstatus;
  */
 
 int
-shell_main(argc, argv)
+ash_main(argc, argv)
        int argc;
        char **argv;
 {
        struct jmploc jmploc;
        struct stackmark smark;
        volatile int state;
-       char *shinit;
+       const char *shinit;
 
-       DOTCMD = find_builtin(".");
        BLTINCMD = find_builtin("builtin");
-       COMMANDCMD = find_builtin("command");
        EXECCMD = find_builtin("exec");
        EVALCMD = find_builtin("eval");
 
+#ifndef BB_FEATURE_SH_FANCY_PROMPT
+       unsetenv("PS1");
+       unsetenv("PS2");
+#endif
+
 #if PROFILE
        monitor(4, etext, profile_buf, sizeof profile_buf, 50);
 #endif
@@ -6799,43 +7735,26 @@ shell_main(argc, argv)
                 * exception EXSHELLPROC to clean up before executing
                 * the shell procedure.
                 */
-               switch (exception) {
-               case EXSHELLPROC:
+               if (exception == EXSHELLPROC) {
                        rootpid = getpid();
                        rootshell = 1;
                        minusc = NULL;
                        state = 3;
-                       break;
-
-               case EXEXEC:
-                       exitstatus = exerrno;
-                       break;
-
-               case EXERROR:
-                       exitstatus = 2;
-                       break;
-
-               default:
-                       break;
-               }
-
-               if (exception != EXSHELLPROC) {
+               } else {
+                       if (exception == EXEXEC) {
+                               exitstatus = exerrno;
+                       } else if (exception == EXERROR) {
+                               exitstatus = 2;
+                       }
                    if (state == 0 || iflag == 0 || ! rootshell)
                            exitshell(exitstatus);
                }
                reset();
-               if (exception == EXINT
-#if ATTY
-                && (! attyset() || equal(termval(), "emacs"))
-#endif
-                ) {
+               if (exception == EXINT) {
                        out2c('\n');
-#ifdef FLUSHERR
-                       flushout(out2);
-#endif
                }
                popstackmark(&smark);
-               FORCEINTON;                             /* enable interrupts */
+               FORCEINTON;                             /* enable interrupts */
                if (state == 1)
                        goto state1;
                else if (state == 2)
@@ -6877,14 +7796,14 @@ state2:
 state3:
        state = 4;
        if (sflag == 0 || minusc) {
-               static int sigs[] =  {
-                   SIGINT, SIGQUIT, SIGHUP, 
+               static const char sigs[] =  {
+                   SIGINT, SIGQUIT, SIGHUP,
 #ifdef SIGTSTP
                    SIGTSTP,
 #endif
                    SIGPIPE
                };
-#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0]))
+#define SIGSSIZE ((sizeof(sigs)/sizeof(sigs[0])) - 1) /* trailing nul */
                int i;
 
                for (i = 0; i < SIGSSIZE; i++)
@@ -6895,7 +7814,7 @@ state3:
                evalstring(minusc, 0);
 
        if (sflag || minusc == NULL) {
-state4:        /* XXX ??? - why isn't this before the "if" statement */
+state4: /* XXX ??? - why isn't this before the "if" statement */
                cmdloop(1);
        }
 #if PROFILE
@@ -6912,8 +7831,7 @@ state4:   /* XXX ??? - why isn't this before the "if" statement */
  */
 
 static void
-cmdloop(top)
-       int top;
+cmdloop(int top)
 {
        union node *n;
        struct stackmark smark;
@@ -6930,7 +7848,7 @@ cmdloop(top)
                        inter++;
                        showjobs(1);
                        chkmail(0);
-                       flushout(&output);
+                       flushall();
                }
                n = parsecmd(inter);
                /* showtree(n); DEBUG */
@@ -6969,8 +7887,8 @@ read_profile(name)
        const char *name;
 {
        int fd;
-       int xflag_set = 0;
-       int vflag_set = 0;
+       int xflag_save;
+       int vflag_save;
 
        INTOFF;
        if ((fd = open(name, O_RDONLY)) >= 0)
@@ -6979,19 +7897,15 @@ read_profile(name)
        if (fd < 0)
                return;
        /* -q turns off -x and -v just when executing init files */
+       /* Note: Might do a little redundant work, but reduces code size. */
+       xflag_save = xflag;
+       vflag_save = vflag;
        if (qflag)  {
-           if (xflag)
-                   xflag = 0, xflag_set = 1;
-           if (vflag)
-                   vflag = 0, vflag_set = 1;
+               vflag = xflag = 0;
        }
        cmdloop(0);
-       if (qflag)  {
-           if (xflag_set)
-                   xflag = 1;
-           if (vflag_set)
-                   vflag = 1;
-       }
+       xflag = xflag_save;
+       vflag = vflag_save;
        popfile();
 }
 
@@ -7002,8 +7916,7 @@ read_profile(name)
  */
 
 static void
-readcmdfile(name)
-       char *name;
+readcmdfile(const char *name)
 {
        int fd;
 
@@ -7025,9 +7938,8 @@ readcmdfile(name)
  */
 
 
-static char *
-find_dot_file(mybasename)
-       char *mybasename;
+static inline char *
+find_dot_file(char *mybasename)
 {
        char *fullname;
        const char *path = pathval();
@@ -7064,7 +7976,7 @@ dotcmd(argc, argv)
        for (sp = cmdenviron; sp ; sp = sp->next)
                setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED);
 
-       if (argc >= 2) {                /* That's what SVR2 does */
+       if (argc >= 2) {                /* That's what SVR2 does */
                char *fullname;
                struct stackmark smark;
 
@@ -7094,38 +8006,9 @@ exitcmd(argc, argv)
        exitshell(exitstatus);
        /* NOTREACHED */
 }
-/*     $NetBSD: memalloc.c,v 1.23 2000/11/01 19:56:01 christos Exp $   */
 
-/*
- * 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];
-};
-
-struct stack_block stackbase;
-struct stack_block *stackp = &stackbase;
-struct stackmark *markp;
-static char *stacknxt = stackbase.space;
-static int stacknleft = MINSIZE;
-static int sstrnleft;
-static int herefd = -1;
-
-
-
-pointer
-stalloc(nbytes)
-       int nbytes;
+static pointer
+stalloc(int nbytes)
 {
        char *p;
 
@@ -7153,11 +8036,10 @@ stalloc(nbytes)
 
 
 static void
-stunalloc(p)
-       pointer p;
-       {
+stunalloc(pointer p)
+{
 #ifdef DEBUG
-       if (p == NULL) {                /*DEBUG */
+       if (p == NULL) {                /*DEBUG */
                write(2, "stunalloc\n", 10);
                abort();
        }
@@ -7170,11 +8052,9 @@ stunalloc(p)
 }
 
 
-
 static void
-setstackmark(mark)
-       struct stackmark *mark;
-       {
+setstackmark(struct stackmark *mark)
+{
        mark->stackp = stackp;
        mark->stacknxt = stacknxt;
        mark->stacknleft = stacknleft;
@@ -7184,9 +8064,8 @@ setstackmark(mark)
 
 
 static void
-popstackmark(mark)
-       struct stackmark *mark;
-       {
+popstackmark(struct stackmark *mark)
+{
        struct stack_block *sp;
 
        INTOFF;
@@ -7213,7 +8092,7 @@ popstackmark(mark)
  */
 
 static void
-growstackblock() {
+growstackblock(void) {
        char *p;
        int newlen = ALIGN(stacknleft * 2 + 100);
        char *oldspace = stacknxt;
@@ -7233,7 +8112,7 @@ growstackblock() {
                stacknleft = newlen;
                {
                  /* Stack marks pointing to the start of the old block
-                  * must be relocated to point to the new block 
+                  * must be relocated to point to the new block
                   */
                  struct stackmark *xmark;
                  xmark = markp;
@@ -7248,16 +8127,15 @@ growstackblock() {
        } else {
                p = stalloc(newlen);
                memcpy(p, oldspace, oldlen);
-               stacknxt = p;                   /* free the space */
-               stacknleft += newlen;           /* we just allocated */
+               stacknxt = p;                   /* free the space */
+               stacknleft += newlen;           /* we just allocated */
        }
 }
 
 
 
-static void
-grabstackblock(len)
-       int len;
+static inline void
+grabstackblock(int len)
 {
        len = ALIGN(len);
        stacknxt += len;
@@ -7286,7 +8164,7 @@ grabstackblock(len)
 
 
 static char *
-growstackstr() {
+growstackstr(void) {
        int len = stackblocksize();
        if (herefd >= 0 && len >= 1024) {
                xwrite(herefd, stackblock(), len);
@@ -7316,16 +8194,12 @@ makestrspace(size_t newlen) {
 
 
 static void
-ungrabstackstr(s, p)
-       char *s;
-       char *p;
-       {
+ungrabstackstr(char *s, char *p)
+{
        stacknleft += stacknxt - s;
        stacknxt = s;
        sstrnleft = stacknleft - (p - s);
 }
-/*     $NetBSD: miscbltin.c,v 1.30 2001/02/04 19:52:06 christos Exp $  */
-
 /*
  * Miscelaneous builtins.
  */
@@ -7333,13 +8207,8 @@ ungrabstackstr(s, p)
 
 #undef rflag
 
-#ifdef __GLIBC__
-mode_t getmode(const void *, mode_t);
-static void *setmode(const char *);
-
 #if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
-typedef enum __rlimit_resource rlim_t;
-#endif
+typedef long rlim_t;
 #endif
 
 
@@ -7352,9 +8221,7 @@ typedef enum __rlimit_resource rlim_t;
  */
 
 static int
-readcmd(argc, argv)
-       int argc;
-       char **argv;
+readcmd(int argc, char **argv)
 {
        char **ap;
        int backslash;
@@ -7376,7 +8243,7 @@ readcmd(argc, argv)
                        rflag = 1;
        }
        if (prompt && isatty(0)) {
-               putprompt(prompt);
+               out2str(prompt);     /* read without cmdedit */
                flushall();
        }
        if (*(ap = argptr) == NULL)
@@ -7443,12 +8310,20 @@ umaskcmd(argc, argv)
        int argc;
        char **argv;
 {
+       static const char permuser[3] = "ugo";
+       static const char permmode[3] = "rwx";
+       static const short int permmask[] = {
+               S_IRUSR, S_IWUSR, S_IXUSR,
+               S_IRGRP, S_IWGRP, S_IXGRP,
+               S_IROTH, S_IWOTH, S_IXOTH
+       };
+
        char *ap;
-       int mask;
+       mode_t mask;
        int i;
        int symbolic_mode = 0;
 
-       while ((i = nextopt("S")) != '\0') {
+       while (nextopt("S") != '\0') {
                symbolic_mode = 1;
        }
 
@@ -7459,41 +8334,26 @@ umaskcmd(argc, argv)
 
        if ((ap = *argptr) == NULL) {
                if (symbolic_mode) {
-                       char u[4], g[4], o[4];
-
-                       i = 0;
-                       if ((mask & S_IRUSR) == 0)
-                               u[i++] = 'r';
-                       if ((mask & S_IWUSR) == 0)
-                               u[i++] = 'w';
-                       if ((mask & S_IXUSR) == 0)
-                               u[i++] = 'x';
-                       u[i] = '\0';
-
-                       i = 0;
-                       if ((mask & S_IRGRP) == 0)
-                               g[i++] = 'r';
-                       if ((mask & S_IWGRP) == 0)
-                               g[i++] = 'w';
-                       if ((mask & S_IXGRP) == 0)
-                               g[i++] = 'x';
-                       g[i] = '\0';
-
-                       i = 0;
-                       if ((mask & S_IROTH) == 0)
-                               o[i++] = 'r';
-                       if ((mask & S_IWOTH) == 0)
-                               o[i++] = 'w';
-                       if ((mask & S_IXOTH) == 0)
-                               o[i++] = 'x';
-                       o[i] = '\0';
-
-                       out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
+                       char buf[18];
+                       char *p = buf;
+                       for (i=0 ; i<3 ; i++) {
+                               int j;
+                               *p++ = permuser[i];
+                               *p++ = '=';
+                               for (j=0 ; j<3 ; j++) {
+                                       if ((mask & permmask[3*i+j]) == 0) {
+                                               *p++ = permmode[j];
+                                       }
+                               }
+                               *p++ = ',';
+                       }
+                       *--p = 0;
+                       puts(buf);
                } else {
-                       out1fmt("%.4o\n", mask);
+                       printf("%.4o\n", mask);
                }
        } else {
-               if (isdigit((unsigned char)*ap)) {
+               if (is_digit((unsigned char)*ap)) {
                        mask = 0;
                        do {
                                if (*ap >= '8' || *ap < '0')
@@ -7502,17 +8362,10 @@ umaskcmd(argc, argv)
                        } while (*++ap != '\0');
                        umask(mask);
                } else {
-                       void *set;
-
-                       INTOFF;
-                       if ((set = setmode(ap)) != 0) {
-                               mask = getmode(set, ~mask & 0777);
-                               ckfree(set);
-                       }
-                       INTON;
-                       if (!set)
+                       mask = ~mask & 0777;
+                       if (parse_mode(ap, &mask) == FALSE) {
                                error("Illegal mode: %s", ap);
-
+                       }
                        umask(~mask & 0777);
                }
        }
@@ -7531,46 +8384,45 @@ umaskcmd(argc, argv)
 
 struct limits {
        const char *name;
-       int     cmd;
-       int     factor; /* multiply by to get rlim_{cur,max} values */
-       char    option;
+       short   cmd;
+       short   factor; /* multiply by to get rlim_{cur,max} values */
 };
 
 static const struct limits limits[] = {
 #ifdef RLIMIT_CPU
-       { "time(seconds)",              RLIMIT_CPU,        1, 't' },
+       { "time(seconds)",             RLIMIT_CPU,        1 },
 #endif
 #ifdef RLIMIT_FSIZE
-       { "file(blocks)",               RLIMIT_FSIZE,    512, 'f' },
+       { "file(blocks)",              RLIMIT_FSIZE,    512 },
 #endif
 #ifdef RLIMIT_DATA
-       { "data(kbytes)",               RLIMIT_DATA,    1024, 'd' },
+       { "data(kbytes)",              RLIMIT_DATA,    1024 },
 #endif
 #ifdef RLIMIT_STACK
-       { "stack(kbytes)",              RLIMIT_STACK,   1024, 's' },
+       { "stack(kbytes)",             RLIMIT_STACK,   1024 },
 #endif
 #ifdef  RLIMIT_CORE
-       { "coredump(blocks)",           RLIMIT_CORE,     512, 'c' },
+       { "coredump(blocks)",          RLIMIT_CORE,     512 },
 #endif
 #ifdef RLIMIT_RSS
-       { "memory(kbytes)",             RLIMIT_RSS,     1024, 'm' },
+       { "memory(kbytes)",            RLIMIT_RSS,     1024 },
 #endif
 #ifdef RLIMIT_MEMLOCK
-       { "locked memory(kbytes)",      RLIMIT_MEMLOCK, 1024, 'l' },
+       { "locked memory(kbytes)",     RLIMIT_MEMLOCK, 1024 },
 #endif
 #ifdef RLIMIT_NPROC
-       { "process(processes)",         RLIMIT_NPROC,      1, 'p' },
+       { "process(processes)",        RLIMIT_NPROC,      1 },
 #endif
 #ifdef RLIMIT_NOFILE
-       { "nofiles(descriptors)",       RLIMIT_NOFILE,     1, 'n' },
+       { "nofiles(descriptors)",      RLIMIT_NOFILE,     1 },
 #endif
 #ifdef RLIMIT_VMEM
-       { "vmemory(kbytes)",            RLIMIT_VMEM,    1024, 'v' },
+       { "vmemory(kbytes)",           RLIMIT_VMEM,    1024 },
 #endif
 #ifdef RLIMIT_SWAP
-       { "swap(kbytes)",               RLIMIT_SWAP,    1024, 'w' },
+       { "swap(kbytes)",              RLIMIT_SWAP,    1024 },
 #endif
-       { (char *) 0,                   0,                 0,  '\0' }
+       { NULL,                         0,                 0 }
 };
 
 static int
@@ -7578,35 +8430,70 @@ ulimitcmd(argc, argv)
        int argc;
        char **argv;
 {
-       int     c;
+       static const char unlimited_string[] = "unlimited";
+       int     c;
        rlim_t val = 0;
        enum { SOFT = 0x1, HARD = 0x2 }
                        how = SOFT | HARD;
-       const struct limits     *l;
-       int             set, all = 0;
-       int             optc, what;
-       struct rlimit   limit;
+       const struct limits     *l;
+       int             set, all = 0;
+       int             optc, what;
+       struct rlimit   limit;
 
        what = 'f';
-       while ((optc = nextopt("HSatfdsmcnpl")) != '\0')
-               switch (optc) {
-               case 'H':
+
+       while ((optc = nextopt("HSa"
+#ifdef RLIMIT_CPU
+       "t"
+#endif
+#ifdef RLIMIT_FSIZE
+       "f"
+#endif
+#ifdef RLIMIT_DATA
+       "d"
+#endif
+#ifdef RLIMIT_STACK
+       "s"
+#endif
+#ifdef  RLIMIT_CORE
+       "c"
+#endif
+#ifdef RLIMIT_RSS
+       "m"
+#endif
+#ifdef RLIMIT_MEMLOCK
+       "l"
+#endif
+#ifdef RLIMIT_NPROC
+       "p"
+#endif
+#ifdef RLIMIT_NOFILE
+       "n"
+#endif
+#ifdef RLIMIT_VMEM
+       "v"
+#endif
+#ifdef RLIMIT_SWAP
+       "w"
+#endif
+                                       )) != '\0') {
+               if (optc == 'H') {
                        how = HARD;
-                       break;
-               case 'S':
+               } else if (optc == 'S') {
                        how = SOFT;
-                       break;
-               case 'a':
+               } else if (optc == 'a') {
                        all = 1;
-                       break;
-               default:
+               } else {
                        what = optc;
                }
+       }
 
-       for (l = limits; l->name && l->option != what; l++)
-               ;
-       if (!l->name)
-               error("internal error (%c)", what);
+       for (l = limits; l->name; l++) {
+               if(l->name[0] == what)
+                       break;
+               if(l->name[1]=='w' && what=='w')
+                       break;
+       }
 
        set = *argptr ? 1 : 0;
        if (set) {
@@ -7614,7 +8501,7 @@ ulimitcmd(argc, argv)
 
                if (all || argptr[1])
                        error("too many arguments");
-               if (strcmp(p, "unlimited") == 0)
+               if (strcmp(p, unlimited_string) == 0)
                        val = RLIM_INFINITY;
                else {
                        val = (rlim_t) 0;
@@ -7630,115 +8517,51 @@ ulimitcmd(argc, argv)
                        val *= l->factor;
                }
        }
+
        if (all) {
                for (l = limits; l->name; l++) {
+                       printf("%-20s ", l->name);
                        getrlimit(l->cmd, &limit);
+               OUTPUT_LIMIT:
                        if (how & SOFT)
                                val = limit.rlim_cur;
                        else if (how & HARD)
                                val = limit.rlim_max;
 
-                       out1fmt("%-20s ", l->name);
                        if (val == RLIM_INFINITY)
-                               out1fmt("unlimited\n");
+                               puts(unlimited_string);
                        else
                        {
                                val /= l->factor;
-#ifdef BSD4_4
-                               out1fmt("%lld\n", (long long) val);
-#else
-                               out1fmt("%ld\n", (long) val);
-#endif
+                               printf("%lld\n", (long long) val);
+                       }
+                       if (!all) {
+                               break;
                        }
                }
                return 0;
        }
 
-       getrlimit(l->cmd, &limit);
-       if (set) {
-               if (how & HARD)
-                       limit.rlim_max = val;
-               if (how & SOFT)
-                       limit.rlim_cur = val;
-               if (setrlimit(l->cmd, &limit) < 0)
-                       error("error setting limit (%s)", strerror(errno));
-       } else {
-               if (how & SOFT)
-                       val = limit.rlim_cur;
-               else if (how & HARD)
-                       val = limit.rlim_max;
-
-               if (val == RLIM_INFINITY)
-                       out1fmt("unlimited\n");
-               else
-               {
-                       val /= l->factor;
-#ifdef BSD4_4
-                       out1fmt("%lld\n", (long long) val);
-#else
-                       out1fmt("%ld\n", (long) val);
-#endif
-               }
+       if (!set) {
+               goto OUTPUT_LIMIT;
        }
-       return 0;
-}
-/*     $NetBSD: mystring.c,v 1.14 1999/07/09 03:05:50 christos Exp $   */
-
-/*
- * String functions.
- *
- *     equal(s1, s2)           Return true if strings are equal.
- *     scopy(from, to)         Copy a string.
- *     scopyn(from, to, n)     Like scopy, but checks for overflow.
- *     number(s)               Convert a string of digits to an integer.
- *     is_number(s)            Return true if s is a string of digits.
- */
-
-static char nullstr[1];                /* zero length string */
-static const char spcstr[] = " ";
-static const char snlfmt[] = "%s\n";
-
-/*
- * equal - #defined in mystring.h
- */
-
-/*
- * scopy - #defined in mystring.h
- */
-
-
-#if 0
-/*
- * scopyn - copy a string from "from" to "to", truncating the string
- *             if necessary.  "To" is always nul terminated, even if
- *             truncation is performed.  "Size" is the size of "to".
- */
-
-static void
-scopyn(from, to, size)
-       char const *from;
-       char *to;
-       int size;
-       {
 
-       while (--size > 0) {
-               if ((*to++ = *from++) == '\0')
-                       return;
-       }
-       *to = '\0';
+       getrlimit(l->cmd, &limit);
+       if (how & HARD)
+               limit.rlim_max = val;
+       if (how & SOFT)
+               limit.rlim_cur = val;
+       if (setrlimit(l->cmd, &limit) < 0)
+               error("error setting limit (%m)");
+       return 0;
 }
-#endif
-
-
 /*
  * prefix -- see if pfx is a prefix of string.
  */
 
 static int
-prefix(pfx, string)
-       char const *pfx;
-       char const *string;
-       {
+prefix(char const *pfx, char const *string)
+{
        while (*pfx) {
                if (*pfx++ != *string++)
                        return 0;
@@ -7746,40 +8569,42 @@ prefix(pfx, string)
        return 1;
 }
 
-
 /*
- * Convert a string of digits to an integer, printing an error message on
- * failure.
+ * Return true if s is a string of digits, and save munber in intptr
+ * nagative is bad
  */
 
 static int
-number(s)
-       const char *s;
-       {
-
-       if (! is_number(s))
-               error("Illegal number: %s", s);
-       return atoi(s);
-}
+is_number(const char *p, int *intptr)
+{
+       int ret = 0;
 
+       do {
+               if (! is_digit(*p))
+                       return 0;
+               ret *= 10;
+               ret += digit_val(*p);
+               p++;
+       } while (*p != '\0');
 
+       *intptr = ret;
+       return 1;
+}
 
 /*
- * Check for a valid number.  This should be elsewhere.
+ * Convert a string of digits to an integer, printing an error message on
+ * failure.
  */
 
 static int
-is_number(p)
-       const char *p;
-       {
-       do {
-               if (! is_digit(*p))
-                       return 0;
-       } while (*++p != '\0');
-       return 1;
+number(const char *s)
+{
+       int i;
+       if (! is_number(s, &i))
+               error("Illegal number: %s", s);
+       return i;
 }
 
-
 /*
  * Produce a possibly single quoted string suitable as input to the shell.
  * The return string is allocated on the stack.
@@ -7799,349 +8624,416 @@ single_quote(const char *s) {
                len2 = strspn(s + len1, "'");
 
                len1p = len1 ? len1 + 2 : len1;
-               switch (len2) {
-               case 0:
-                       len2p = 0;
-                       break;
-               case 1:
-                       len2p = 2;
-                       break;
-               default:
-                       len2p = len2 + 2;
-               }
+               len2p = len2 + ((len2 < 2) ? len2 : 2);
 
                CHECKSTRSPACE(len1p + len2p + 1, p);
 
                if (len1) {
                        *p = '\'';
-#ifdef _GNU_SOURCE
-                       q = mempcpy(p + 1, s, len1);
-#else
                        q = p + 1 + len1;
-                       memcpy(p + 1, s, len1);
-#endif
-                       *q++ = '\'';
-                       s += len1;
-               }
-
-               switch (len2) {
-               case 0:
-                       break;
-               case 1:
-                       *q++ = '\\';
-                       *q = '\'';
-                       s++;
-                       break;
-               default:
-                       *q = '"';
-#ifdef _GNU_SOURCE
-                       *(char *) mempcpy(q + 1, s, len2) = '"';
-#else
-                       q += 1 + len2;
-                       memcpy(q + 1, s, len2);
-                       *q = '"';
-#endif
-                       s += len2;
-               }
-
-               STADJUST(len1p + len2p, p);
-       } while (*s);
-
-       USTPUTC(0, p);
-
-       return grabstackstr(p);
-}
-
-/*
- * Like strdup but works with the ash stack.
- */
-
-static char *
-sstrdup(const char *p)
-{
-       size_t len = strlen(p) + 1;
-       return memcpy(stalloc(len), p, len);
-}
-
-/*
- * Wrapper around strcmp for qsort/bsearch/...
- */
-static int
-pstrcmp(const void *a, const void *b)
-{
-       return strcmp(*(const char *const *) a, *(const char *const *) b);
-}
-
-/*
- * Find a string is in a sorted array.
- */
-static const char *const *
-findstring(const char *s, const char *const *array, size_t nmemb)
-{
-       return bsearch(&s, array, nmemb, sizeof(const char *), pstrcmp);
-}
-/*
- * This file was generated by the mknodes program.
- */
-
-/*     $NetBSD: nodes.c.pat,v 1.8 1997/04/11 23:03:09 christos Exp $   */
-
-/*
- * Routine for dealing with parsed shell commands.
- */
-
-
-static int     funcblocksize;          /* size of structures in function */
-static int     funcstringsize;         /* size of strings in node */
-pointer funcblock;             /* block to allocate function from */
-static char   *funcstring;             /* block to allocate strings from */
-
-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)),
-};
+                       memcpy(p + 1, s, len1);
+                       *q++ = '\'';
+                       s += len1;
+               }
 
+               if (len2 > 1) {
+                       *q = '"';
+                       q += 1 + len2;
+                       memcpy(q + 1, s, len2);
+                       *q = '"';
+                       s += len2;
+               } else if (len2 == 1) {
+                       *q++ = '\\';
+                       *q = '\'';
+                       s++;
+               }
 
-static void calcsize __P((union node *));
-static void sizenodelist __P((struct nodelist *));
-static union node *copynode __P((union node *));
-static struct nodelist *copynodelist __P((struct nodelist *));
-static char *nodesavestr __P((char *));
+               STADJUST(len1p + len2p, p);
+       } while (*s);
 
+       USTPUTC(0, p);
 
+       return grabstackstr(p);
+}
 
 /*
- * Make a copy of a parse tree.
+ * Like strdup but works with the ash stack.
  */
 
-union node *
-copyfunc(n)
-       union node *n;
+static char *
+sstrdup(const char *p)
 {
-       if (n == NULL)
-               return NULL;
-       funcblocksize = 0;
-       funcstringsize = 0;
-       calcsize(n);
-       funcblock = ckmalloc(funcblocksize + funcstringsize);
-       funcstring = (char *) funcblock + funcblocksize;
-       return copynode(n);
+       size_t len = strlen(p) + 1;
+       return memcpy(stalloc(len), p, len);
 }
 
 
+/*
+ * Routine for dealing with parsed shell commands.
+ */
+
 
-static void
-calcsize(n)
-       union node *n;
+static void sizenodelist (const struct nodelist *);
+static struct nodelist *copynodelist (const struct nodelist *);
+static char *nodesavestr (const char *);
+
+#define CALCSIZE_TABLE
+#define COPYNODE_TABLE
+#if defined(CALCSIZE_TABLE) || defined(COPYNODE_TABLE)
+/*
+ * To collect a lot of redundant code in case statements for copynode()
+ * and calcsize(), we implement a mini language here.  Each type of node
+ * struct has an associated instruction sequence that operates on its
+ * members via their offsets.  The instruction are pack in unsigned chars
+ * with format   IIDDDDDE   where the bits are
+ *   I : part of the instruction opcode, which are
+ *       00 : member is a pointer to another node
+ *       40 : member is an integer
+ *       80 : member is a pointer to a nodelist
+ *       CC : member is a pointer to a char string
+ *   D : data - the actual offset of the member to operate on in the struct
+ *              (since we assume bit 0 is set, it is not shifted)
+ *   E : flag signaling end of instruction sequence
+ *
+ * WARNING: In order to handle larger offsets for 64bit archs, this code
+ *          assumes that no offset can be an odd number and stores the
+ *          end-of-instructions flag in bit 0.
+ */
+
+#define NODE_INTEGER    0x40
+#define NODE_NODELIST   0x80
+#define NODE_CHARPTR    0xC0
+#define NODE_NOMORE             0x01    /* Note: no offset should be odd (aligned)*/
+#define NODE_MBRMASK    0xC0
+#define NODE_OFFSETMASK 0x3E
+
+static const unsigned char copynode_ops[35] = {
+#define COPYNODE_OPS0   0
+       offsetof(union node, nbinary.ch2),
+       offsetof(union node, nbinary.ch1)|NODE_NOMORE,
+#define COPYNODE_OPS1   (COPYNODE_OPS0 + 2)
+       offsetof(union node, ncmd.redirect),
+       offsetof(union node, ncmd.args),
+       offsetof(union node, ncmd.assign),
+       offsetof(union node, ncmd.backgnd)|NODE_INTEGER|NODE_NOMORE,
+#define COPYNODE_OPS2   (COPYNODE_OPS1 + 4)
+       offsetof(union node, npipe.cmdlist)|NODE_NODELIST,
+       offsetof(union node, npipe.backgnd)|NODE_INTEGER|NODE_NOMORE,
+#define COPYNODE_OPS3   (COPYNODE_OPS2 + 2)
+       offsetof(union node, nredir.redirect),
+       offsetof(union node, nredir.n)|NODE_NOMORE,
+#define COPYNODE_OPS4   (COPYNODE_OPS3 + 2)
+       offsetof(union node, nif.elsepart),
+       offsetof(union node, nif.ifpart),
+       offsetof(union node, nif.test)|NODE_NOMORE,
+#define COPYNODE_OPS5   (COPYNODE_OPS4 + 3)
+       offsetof(union node, nfor.var)|NODE_CHARPTR,
+       offsetof(union node, nfor.body),
+       offsetof(union node, nfor.args)|NODE_NOMORE,
+#define COPYNODE_OPS6   (COPYNODE_OPS5 + 3)
+       offsetof(union node, ncase.cases),
+       offsetof(union node, ncase.expr)|NODE_NOMORE,
+#define COPYNODE_OPS7   (COPYNODE_OPS6 + 2)
+       offsetof(union node, nclist.body),
+       offsetof(union node, nclist.pattern),
+       offsetof(union node, nclist.next)|NODE_NOMORE,
+#define COPYNODE_OPS8   (COPYNODE_OPS7 + 3)
+       offsetof(union node, narg.backquote)|NODE_NODELIST,
+       offsetof(union node, narg.text)|NODE_CHARPTR,
+       offsetof(union node, narg.next)|NODE_NOMORE,
+#define COPYNODE_OPS9   (COPYNODE_OPS8 + 3)
+       offsetof(union node, nfile.fname),
+       offsetof(union node, nfile.fd)|NODE_INTEGER,
+       offsetof(union node, nfile.next)|NODE_NOMORE,
+#define COPYNODE_OPS10   (COPYNODE_OPS9 + 3)
+       offsetof(union node, ndup.vname),
+       offsetof(union node, ndup.dupfd)|NODE_INTEGER,
+       offsetof(union node, ndup.fd)|NODE_INTEGER,
+       offsetof(union node, ndup.next)|NODE_NOMORE,
+#define COPYNODE_OPS11   (COPYNODE_OPS10 + 4)
+       offsetof(union node, nhere.doc),
+       offsetof(union node, nhere.fd)|NODE_INTEGER,
+       offsetof(union node, nhere.next)|NODE_NOMORE,
+#define COPYNODE_OPS12   (COPYNODE_OPS11 + 3)
+       offsetof(union node, nnot.com)|NODE_NOMORE,
+};
+
+#if COPYNODE_OPS12 != 34
+#error COPYNODE_OPS12 is incorrect
+#endif
+
+static const unsigned char copynode_ops_index[26] = {
+       COPYNODE_OPS0, /* NSEMI */
+       COPYNODE_OPS1, /* NCMD */
+       COPYNODE_OPS2, /* NPIPE */
+       COPYNODE_OPS3, /* NREDIR */
+       COPYNODE_OPS3, /* NBACKGND */
+       COPYNODE_OPS3, /* NSUBSHELL */
+       COPYNODE_OPS0, /* NAND */
+       COPYNODE_OPS0, /* NOR */
+       COPYNODE_OPS4, /* NIF */
+       COPYNODE_OPS0, /* NWHILE */
+       COPYNODE_OPS0, /* NUNTIL */
+       COPYNODE_OPS5, /* NFOR */
+       COPYNODE_OPS6, /* NCASE */
+       COPYNODE_OPS7, /* NCLIST */
+       COPYNODE_OPS8, /* NDEFUN */
+       COPYNODE_OPS8, /* NARG */
+       COPYNODE_OPS9, /* NTO */
+       COPYNODE_OPS9, /* NFROM */
+       COPYNODE_OPS9, /* NFROMTO */
+       COPYNODE_OPS9, /* NAPPEND */
+       COPYNODE_OPS9, /* NTOOV */
+       COPYNODE_OPS10, /* NTOFD */
+       COPYNODE_OPS10, /* NFROMFD */
+       COPYNODE_OPS11, /* NHERE */
+       COPYNODE_OPS11, /* NXHERE */
+       COPYNODE_OPS12, /* NNOT */
+};
+
+#if NODE_CHARPTR != NODE_MBRMASK
+#error NODE_CHARPTR != NODE_MBRMASK!!!
+#endif
+#endif /* defined(CALCSIZE_TABLE) || defined(COPYNODE_TABLE) */
+
+#ifdef COPYNODE_TABLE
+static union node *
+copynode(const union node *n)
+{
+      union node *new;
+         const unsigned char *p;
+
+      if (n == NULL) {
+          return NULL;
+         }
+      new = funcblock;
+      new->type = n->type;
+      funcblock = (char *) funcblock + (int) nodesize[n->type];
+         p = copynode_ops + (int) copynode_ops_index[n->type];
+         do {
+                 char *nn = ((char *) new) + ((int)(*p & NODE_OFFSETMASK));
+                 const char *no = ((const char *) n) + ((int)(*p & NODE_OFFSETMASK));
+
+                 if (!(*p & NODE_MBRMASK)) { /* standard node */
+                         *((union node **)nn) = copynode(*((const union node **) no));
+                 } else if ((*p & NODE_MBRMASK) == NODE_CHARPTR) { /* string */
+                         *((const char **)nn) = nodesavestr(*((const char **)no));
+                 } else if (*p & NODE_NODELIST) { /* nodelist */
+                         *((struct nodelist **)nn)
+                                 = copynodelist(*((const struct nodelist **) no));
+                 } else {                              /* integer */
+                         *((int *) nn) = *((int *) no);
+                 }
+         } while (!(*p++ & NODE_NOMORE));
+      return new;
+}
+#else  /* COPYNODE_TABLE */
+static union node *
+copynode(const union node *n)
 {
+      union node *new;
+
       if (n == NULL)
-           return;
-      funcblocksize += nodesize[n->type];
+        return NULL;
+      new = funcblock;
+      funcblock = (char *) funcblock + nodesize[n->type];
       switch (n->type) {
       case NSEMI:
       case NAND:
       case NOR:
       case NWHILE:
       case NUNTIL:
-           calcsize(n->nbinary.ch2);
-           calcsize(n->nbinary.ch1);
+           new->nbinary.ch2 = copynode(n->nbinary.ch2);
+           new->nbinary.ch1 = copynode(n->nbinary.ch1);
            break;
       case NCMD:
-           calcsize(n->ncmd.redirect);
-           calcsize(n->ncmd.args);
-           calcsize(n->ncmd.assign);
+           new->ncmd.redirect = copynode(n->ncmd.redirect);
+           new->ncmd.args = copynode(n->ncmd.args);
+           new->ncmd.assign = copynode(n->ncmd.assign);
+           new->ncmd.backgnd = n->ncmd.backgnd;
            break;
       case NPIPE:
-           sizenodelist(n->npipe.cmdlist);
+           new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
+           new->npipe.backgnd = n->npipe.backgnd;
            break;
       case NREDIR:
       case NBACKGND:
       case NSUBSHELL:
-           calcsize(n->nredir.redirect);
-           calcsize(n->nredir.n);
+           new->nredir.redirect = copynode(n->nredir.redirect);
+           new->nredir.n = copynode(n->nredir.n);
            break;
       case NIF:
-           calcsize(n->nif.elsepart);
-           calcsize(n->nif.ifpart);
-           calcsize(n->nif.test);
+           new->nif.elsepart = copynode(n->nif.elsepart);
+           new->nif.ifpart = copynode(n->nif.ifpart);
+           new->nif.test = copynode(n->nif.test);
            break;
       case NFOR:
-           funcstringsize += strlen(n->nfor.var) + 1;
-           calcsize(n->nfor.body);
-           calcsize(n->nfor.args);
+           new->nfor.var = nodesavestr(n->nfor.var);
+           new->nfor.body = copynode(n->nfor.body);
+           new->nfor.args = copynode(n->nfor.args);
            break;
       case NCASE:
-           calcsize(n->ncase.cases);
-           calcsize(n->ncase.expr);
+           new->ncase.cases = copynode(n->ncase.cases);
+           new->ncase.expr = copynode(n->ncase.expr);
            break;
       case NCLIST:
-           calcsize(n->nclist.body);
-           calcsize(n->nclist.pattern);
-           calcsize(n->nclist.next);
+           new->nclist.body = copynode(n->nclist.body);
+           new->nclist.pattern = copynode(n->nclist.pattern);
+           new->nclist.next = copynode(n->nclist.next);
            break;
       case NDEFUN:
       case NARG:
-           sizenodelist(n->narg.backquote);
-           funcstringsize += strlen(n->narg.text) + 1;
-           calcsize(n->narg.next);
+           new->narg.backquote = copynodelist(n->narg.backquote);
+           new->narg.text = nodesavestr(n->narg.text);
+           new->narg.next = copynode(n->narg.next);
            break;
       case NTO:
       case NFROM:
       case NFROMTO:
       case NAPPEND:
       case NTOOV:
-           calcsize(n->nfile.fname);
-           calcsize(n->nfile.next);
+           new->nfile.fname = copynode(n->nfile.fname);
+           new->nfile.fd = n->nfile.fd;
+           new->nfile.next = copynode(n->nfile.next);
            break;
       case NTOFD:
       case NFROMFD:
-           calcsize(n->ndup.vname);
-           calcsize(n->ndup.next);
+           new->ndup.vname = copynode(n->ndup.vname);
+           new->ndup.dupfd = n->ndup.dupfd;
+           new->ndup.fd = n->ndup.fd;
+           new->ndup.next = copynode(n->ndup.next);
            break;
       case NHERE:
       case NXHERE:
-           calcsize(n->nhere.doc);
-           calcsize(n->nhere.next);
+           new->nhere.doc = copynode(n->nhere.doc);
+           new->nhere.fd = n->nhere.fd;
+           new->nhere.next = copynode(n->nhere.next);
            break;
       case NNOT:
-           calcsize(n->nnot.com);
+           new->nnot.com = copynode(n->nnot.com);
            break;
       };
+      new->type = n->type;
+      return new;
 }
+#endif /* COPYNODE_TABLE */
 
-
-
+#ifdef CALCSIZE_TABLE
 static void
-sizenodelist(lp)
-       struct nodelist *lp;
+calcsize(const union node *n)
 {
-       while (lp) {
-               funcblocksize += ALIGN(sizeof(struct nodelist));
-               calcsize(lp->n);
-               lp = lp->next;
-       }
-}
+         const unsigned char *p;
 
+      if (n == NULL)
+           return;
+      funcblocksize += (int) nodesize[n->type];
 
+         p = copynode_ops + (int) copynode_ops_index[n->type];
+         do {
+                 const char *no = ((const char *) n) + ((int)(*p & NODE_OFFSETMASK));
 
-static union node *
-copynode(n)
-       union node *n;
+                 if (!(*p & NODE_MBRMASK)) { /* standard node */
+                         calcsize(*((const union node **) no));
+                 } else if ((*p & NODE_MBRMASK) == NODE_CHARPTR) { /* string */
+                         funcstringsize += strlen(*((const char **)no)) + 1;
+                 } else if (*p & NODE_NODELIST) { /* nodelist */
+                         sizenodelist(*((const struct nodelist **) no));
+                 }     /* else integer -- ignore */
+         } while (!(*p++ & NODE_NOMORE));
+}
+#else  /* CALCSIZE_TABLE */
+static void
+calcsize(const union node *n)
 {
-       union node *new;
-
       if (n == NULL)
-           return NULL;
-      new = funcblock;
-      funcblock = (char *) funcblock + nodesize[n->type];
+           return;
+      funcblocksize += nodesize[n->type];
       switch (n->type) {
       case NSEMI:
       case NAND:
       case NOR:
       case NWHILE:
       case NUNTIL:
-           new->nbinary.ch2 = copynode(n->nbinary.ch2);
-           new->nbinary.ch1 = copynode(n->nbinary.ch1);
+           calcsize(n->nbinary.ch2);
+           calcsize(n->nbinary.ch1);
            break;
       case NCMD:
-           new->ncmd.redirect = copynode(n->ncmd.redirect);
-           new->ncmd.args = copynode(n->ncmd.args);
-           new->ncmd.assign = copynode(n->ncmd.assign);
-           new->ncmd.backgnd = n->ncmd.backgnd;
+           calcsize(n->ncmd.redirect);
+           calcsize(n->ncmd.args);
+           calcsize(n->ncmd.assign);
            break;
       case NPIPE:
-           new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
-           new->npipe.backgnd = n->npipe.backgnd;
+           sizenodelist(n->npipe.cmdlist);
            break;
       case NREDIR:
       case NBACKGND:
       case NSUBSHELL:
-           new->nredir.redirect = copynode(n->nredir.redirect);
-           new->nredir.n = copynode(n->nredir.n);
+           calcsize(n->nredir.redirect);
+           calcsize(n->nredir.n);
            break;
       case NIF:
-           new->nif.elsepart = copynode(n->nif.elsepart);
-           new->nif.ifpart = copynode(n->nif.ifpart);
-           new->nif.test = copynode(n->nif.test);
+           calcsize(n->nif.elsepart);
+           calcsize(n->nif.ifpart);
+           calcsize(n->nif.test);
            break;
       case NFOR:
-           new->nfor.var = nodesavestr(n->nfor.var);
-           new->nfor.body = copynode(n->nfor.body);
-           new->nfor.args = copynode(n->nfor.args);
+           funcstringsize += strlen(n->nfor.var) + 1;
+           calcsize(n->nfor.body);
+           calcsize(n->nfor.args);
            break;
       case NCASE:
-           new->ncase.cases = copynode(n->ncase.cases);
-           new->ncase.expr = copynode(n->ncase.expr);
+           calcsize(n->ncase.cases);
+           calcsize(n->ncase.expr);
            break;
       case NCLIST:
-           new->nclist.body = copynode(n->nclist.body);
-           new->nclist.pattern = copynode(n->nclist.pattern);
-           new->nclist.next = copynode(n->nclist.next);
+           calcsize(n->nclist.body);
+           calcsize(n->nclist.pattern);
+           calcsize(n->nclist.next);
            break;
       case NDEFUN:
       case NARG:
-           new->narg.backquote = copynodelist(n->narg.backquote);
-           new->narg.text = nodesavestr(n->narg.text);
-           new->narg.next = copynode(n->narg.next);
+           sizenodelist(n->narg.backquote);
+           funcstringsize += strlen(n->narg.text) + 1;
+           calcsize(n->narg.next);
            break;
       case NTO:
       case NFROM:
       case NFROMTO:
       case NAPPEND:
       case NTOOV:
-           new->nfile.fname = copynode(n->nfile.fname);
-           new->nfile.fd = n->nfile.fd;
-           new->nfile.next = copynode(n->nfile.next);
+           calcsize(n->nfile.fname);
+           calcsize(n->nfile.next);
            break;
       case NTOFD:
       case NFROMFD:
-           new->ndup.vname = copynode(n->ndup.vname);
-           new->ndup.dupfd = n->ndup.dupfd;
-           new->ndup.fd = n->ndup.fd;
-           new->ndup.next = copynode(n->ndup.next);
+           calcsize(n->ndup.vname);
+           calcsize(n->ndup.next);
            break;
       case NHERE:
       case NXHERE:
-           new->nhere.doc = copynode(n->nhere.doc);
-           new->nhere.fd = n->nhere.fd;
-           new->nhere.next = copynode(n->nhere.next);
+           calcsize(n->nhere.doc);
+           calcsize(n->nhere.next);
            break;
       case NNOT:
-           new->nnot.com = copynode(n->nnot.com);
+           calcsize(n->nnot.com);
            break;
       };
-      new->type = n->type;
-       return new;
+}
+#endif /* CALCSIZE_TABLE */
+
+static void
+sizenodelist(const struct nodelist *lp)
+{
+       while (lp) {
+               funcblocksize += ALIGN(sizeof(struct nodelist));
+               calcsize(lp->n);
+               lp = lp->next;
+       }
 }
 
 
 static struct nodelist *
-copynodelist(lp)
-       struct nodelist *lp;
+copynodelist(const struct nodelist *lp)
 {
        struct nodelist *start;
        struct nodelist **lpp;
@@ -8159,76 +9051,21 @@ copynodelist(lp)
 }
 
 
-
 static char *
-nodesavestr(s)
-       char   *s;
+nodesavestr(const char *s)
 {
-#ifdef _GNU_SOURCE
-       char   *rtn = funcstring;
-
-       funcstring = stpcpy(funcstring, s) + 1;
-       return rtn;
-#else
-       register char *p = s;
-       register char *q = funcstring;
+       const char *p = s;
+       char *q = funcstring;
        char   *rtn = funcstring;
 
        while ((*q++ = *p++) != '\0')
                continue;
        funcstring = q;
        return rtn;
-#endif
-}
-
-
-
-/*
- * Free a parse tree.
- */
-
-static void
-freefunc(n)
-       union node *n;
-{
-       if (n)
-               ckfree(n);
 }
-/*     $NetBSD: options.c,v 1.31 2001/02/26 13:06:43 wiz Exp $ */
-
-
-struct optent optlist[NOPTS] = {
-       { "errexit",    'e',    0 },
-       { "noglob",     'f',    0 },
-       { "ignoreeof",  'I',    0 },
-       { "interactive",'i',    0 },
-       { "monitor",    'm',    0 },
-       { "noexec",     'n',    0 },
-       { "stdin",      's',    0 },
-       { "xtrace",     'x',    0 },
-       { "verbose",    'v',    0 },
-       { "vi",         'V',    0 },
-       { "emacs",      'E',    0 },
-       { "noclobber",  'C',    0 },
-       { "allexport",  'a',    0 },
-       { "notify",     'b',    0 },
-       { "nounset",    'u',    0 },
-       { "quietprofile", 'q',  0 },
-};
-static char *arg0;                     /* value of $0 */
-struct shparam shellparam;     /* current positional parameters */
-static char **argptr;                  /* argument list for builtin commands */
-static char *optionarg;                /* set by nextopt (like getopt) */
-static char *optptr;                   /* used by nextopt */
-
-static char *minusc;                   /* argument to -c option */
 
-
-static void options __P((int));
-static void minus_o __P((char *, int));
-static void setoption __P((int, int));
 #ifdef ASH_GETOPTS
-static int getopts __P((char *, char *, char **, int *, int *));
+static int getopts (char *, char *, char **, int *, int *);
 #endif
 
 
@@ -8247,7 +9084,7 @@ procargs(argc, argv)
        if (argc > 0)
                argptr++;
        for (i = 0; i < NOPTS; i++)
-               optlist[i].val = 2;
+               optent_val(i) = 2;
        options(1);
        if (*argptr == NULL && minusc == NULL)
                sflag = 1;
@@ -8256,8 +9093,8 @@ procargs(argc, argv)
        if (mflag == 2)
                mflag = iflag;
        for (i = 0; i < NOPTS; i++)
-               if (optlist[i].val == 2)
-                       optlist[i].val = 0;
+               if (optent_val(i) == 2)
+                       optent_val(i) = 0;
        arg0 = argv[0];
        if (sflag == 0 && minusc == NULL) {
                commandname = argv[0];
@@ -8267,7 +9104,7 @@ procargs(argc, argv)
        }
        /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
        if (argptr && minusc && *argptr)
-               arg0 = *argptr++;
+               arg0 = *argptr++;
 
        shellparam.p = argptr;
        shellparam.optind = 1;
@@ -8281,21 +9118,35 @@ procargs(argc, argv)
 }
 
 
-static void
-optschanged()
+
+/*
+ * Process shell options.  The global variable argptr contains a pointer
+ * to the argument list; we advance it past the options.
+ */
+
+static inline void
+minus_o(const char *name, int val)
 {
-       setinteractive(iflag);
-       setjobctl(mflag);
+       int i;
+
+       if (name == NULL) {
+               out1str("Current option settings\n");
+               for (i = 0; i < NOPTS; i++)
+                       printf("%-16s%s\n", optent_name(optlist[i]),
+                               optent_val(i) ? "on" : "off");
+       } else {
+               for (i = 0; i < NOPTS; i++)
+                       if (equal(name, optent_name(optlist[i]))) {
+                               setoption(optent_letter(optlist[i]), val);
+                               return;
+                       }
+               error("Illegal option -o %s", name);
+       }
 }
 
-/*
- * Process shell options.  The global variable argptr contains a pointer
- * to the argument list; we advance it past the options.
- */
 
 static void
-options(cmdline)
-       int cmdline;
+options(int cmdline)
 {
        char *p;
        int val;
@@ -8307,16 +9158,16 @@ options(cmdline)
                argptr++;
                if ((c = *p++) == '-') {
                        val = 1;
-                        if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
-                                if (!cmdline) {
-                                        /* "-" means turn off -x and -v */
-                                        if (p[0] == '\0')
-                                                xflag = vflag = 0;
-                                        /* "--" means reset params */
-                                        else if (*argptr == NULL)
+                       if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
+                               if (!cmdline) {
+                                       /* "-" means turn off -x and -v */
+                                       if (p[0] == '\0')
+                                               xflag = vflag = 0;
+                                       /* "--" means reset params */
+                                       else if (*argptr == NULL)
                                                setparam(argptr);
-                                }
-                               break;    /* "-" or  "--" terminates options */
+                               }
+                               break;    /* "-" or  "--" terminates options */
                        }
                } else if (c == '+') {
                        val = 0;
@@ -8327,7 +9178,7 @@ options(cmdline)
                while ((c = *p++) != '\0') {
                        if (c == 'c' && cmdline) {
                                char *q;
-#ifdef NOHACK  /* removing this code allows sh -ce 'foo' for compat */
+#ifdef NOHACK   /* removing this code allows sh -ce 'foo' for compat */
                                if (*p == '\0')
 #endif
                                        q = *argptr++;
@@ -8348,39 +9199,15 @@ options(cmdline)
        }
 }
 
-static void
-minus_o(name, val)
-       char *name;
-       int val;
-{
-       int i;
-
-       if (name == NULL) {
-               out1str("Current option settings\n");
-               for (i = 0; i < NOPTS; i++)
-                       out1fmt("%-16s%s\n", optlist[i].name,
-                               optlist[i].val ? "on" : "off");
-       } else {
-               for (i = 0; i < NOPTS; i++)
-                       if (equal(name, optlist[i].name)) {
-                               setoption(optlist[i].letter, val);
-                               return;
-                       }
-               error("Illegal option -o %s", name);
-       }
-}
-
 
 static void
-setoption(flag, val)
-       char flag;
-       int val;
-       {
+setoption(int flag, int val)
+{
        int i;
 
        for (i = 0; i < NOPTS; i++)
-               if (optlist[i].letter == flag) {
-                       optlist[i].val = val;
+               if (optent_letter(optlist[i]) == flag) {
+                       optent_val(i) = val;
                        if (val) {
                                /* #%$ hack for ksh semantics */
                                if (flag == 'V')
@@ -8396,26 +9223,13 @@ setoption(flag, val)
 
 
 
-#ifdef mkinit
-SHELLPROC {
-       int i;
-
-       for (i = 0; i < NOPTS; i++)
-               optlist[i].val = 0;
-       optschanged();
-
-}
-#endif
-
-
 /*
  * Set the shell parameters.
  */
 
 static void
-setparam(argv)
-       char **argv;
-       {
+setparam(char **argv)
+{
        char **newparam;
        char **ap;
        int nparam;
@@ -8440,9 +9254,8 @@ setparam(argv)
  */
 
 static void
-freeparam(param)
-       volatile struct shparam *param;
-       {
+freeparam(volatile struct shparam *param)
+{
        char **ap;
 
        if (param->malloc) {
@@ -8510,13 +9323,27 @@ setcmd(argc, argv)
 
 
 static void
-getoptsreset(value)
-       const char *value;
+getoptsreset(const char *value)
 {
        shellparam.optind = number(value);
        shellparam.optoff = -1;
 }
 
+#ifdef BB_LOCALE_SUPPORT
+static void change_lc_all(const char *value)
+{
+       if(value != 0 && *value != 0)
+               setlocale(LC_ALL, value);
+}
+
+static void change_lc_ctype(const char *value)
+{
+       if(value != 0 && *value != 0)
+               setlocale(LC_CTYPE, value);
+}
+
+#endif
+
 #ifdef ASH_GETOPTS
 /*
  * The getopts builtin.  Shellparam.optnext points to the next argument
@@ -8612,7 +9439,7 @@ atend:
                        goto out;
                }
                optnext++;
-               if (p[0] == '-' && p[1] == '\0')        /* check for "--" */
+               if (p[0] == '-' && p[1] == '\0')        /* check for "--" */
                        goto atend;
        }
 
@@ -8625,7 +9452,7 @@ atend:
                                err |= setvarsafe("OPTARG", s, 0);
                        }
                        else {
-                               outfmt(&errout, "Illegal option -%c\n", c);
+                               out2fmt("Illegal option -%c\n", c);
                                (void) unsetvar("OPTARG");
                        }
                        c = '?';
@@ -8644,7 +9471,7 @@ atend:
                                c = ':';
                        }
                        else {
-                               outfmt(&errout, "No arg for -%c option\n", c);
+                               out2fmt("No arg for -%c option\n", c);
                                (void) unsetvar("OPTARG");
                                c = '?';
                        }
@@ -8666,7 +9493,7 @@ bad:
        p = NULL;
 out:
        *optoff = p ? p - *(optnext - 1) : -1;
-       fmtstr(s, sizeof(s), "%d", *myoptind);
+       snprintf(s, sizeof(s), "%d", *myoptind);
        err |= setvarsafe("OPTIND", s, VNOFUNC);
        s[0] = c;
        s[1] = '\0';
@@ -8674,12 +9501,11 @@ out:
        if (err) {
                *myoptind = 1;
                *optoff = -1;
-               flushall();
                exraise(EXERROR);
        }
        return done;
 }
-#endif 
+#endif
 
 /*
  * XXX - should get rid of.  have all builtins use getopt(3).  the
@@ -8693,9 +9519,8 @@ out:
  */
 
 static int
-nextopt(optstring)
-       const char *optstring;
-       {
+nextopt(const char *optstring)
+{
        char *p;
        const char *q;
        char c;
@@ -8705,7 +9530,7 @@ nextopt(optstring)
                if (p == NULL || *p != '-' || *++p == '\0')
                        return '\0';
                argptr++;
-               if (p[0] == '-' && p[1] == '\0')        /* check for "--" */
+               if (p[0] == '-' && p[1] == '\0')        /* check for "--" */
                        return '\0';
        }
        c = *p++;
@@ -8718,519 +9543,37 @@ nextopt(optstring)
        if (*++q == ':') {
                if (*p == '\0' && (p = *argptr++) == NULL)
                        error("No arg for -%c option", c);
-               optionarg = p;
-               p = NULL;
-       }
-       optptr = p;
-       return c;
-}
-
-
-/*     $NetBSD: output.c,v 1.23 2001/01/07 23:39:07 lukem Exp $        */
-
-/*
- * Shell output routines.  We use our own output routines because:
- *     When a builtin command is interrupted we have to discard
- *             any pending output.
- *     When a builtin command appears in back quotes, we want to
- *             save the output of the command in a region obtained
- *             via malloc, rather than doing a fork and reading the
- *             output of the command via a pipe.
- *     Our output routines may be smaller than the stdio routines.
- */
-
-
-#define OUTBUFSIZ BUFSIZ
-#define MEM_OUT -3             /* output to dynamically allocated memory */
-
-
-#ifdef USE_GLIBC_STDIO
-struct output output = {NULL, NULL, 0, NULL, 0, 1, 0};
-struct output errout = {NULL, NULL, 0, NULL, 0, 2, 0};
-struct output memout = {NULL, NULL, 0, NULL, 0, MEM_OUT, 0};
-#else
-struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
-struct output errout = {NULL, 0, NULL, 0, 2, 0};
-struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0};
-#endif
-struct output *out1 = &output;
-struct output *out2 = &errout;
-
-
-#ifndef USE_GLIBC_STDIO
-static void __outstr __P((const char *, size_t, struct output*));
-#endif
-
-
-#ifdef mkinit
-
-INCLUDE "output.h"
-INCLUDE "memalloc.h"
-
-INIT {
-#ifdef USE_GLIBC_STDIO
-       initstreams();
-#endif
-}
-
-RESET {
-       out1 = &output;
-       out2 = &errout;
-#ifdef USE_GLIBC_STDIO
-       if (memout.stream != NULL)
-               __closememout();
-#endif
-       if (memout.buf != NULL) {
-               ckfree(memout.buf);
-               memout.buf = NULL;
-       }
-}
-
-#endif
-
-
-#ifndef USE_GLIBC_STDIO
-static void
-__outstr(const char *p, size_t len, struct output *dest) {
-       if (!dest->bufsize) {
-               dest->nleft = 0;
-       } else if (dest->buf == NULL) {
-               if (len > dest->bufsize && dest->fd == MEM_OUT) {
-                       dest->bufsize = len;
-               }
-               INTOFF;
-               dest->buf = ckmalloc(dest->bufsize);
-               dest->nextc = dest->buf;
-               dest->nleft = dest->bufsize;
-               INTON;
-       } else if (dest->fd == MEM_OUT) {
-               int offset;
-
-               offset = dest->bufsize;
-               INTOFF;
-               if (dest->bufsize >= len) {
-                       dest->bufsize <<= 1;
-               } else {
-                       dest->bufsize += len;
-               }
-               dest->buf = ckrealloc(dest->buf, dest->bufsize);
-               dest->nleft = dest->bufsize - offset;
-               dest->nextc = dest->buf + offset;
-               INTON;
-       } else {
-               flushout(dest);
-       }
-
-       if (len < dest->nleft) {
-               dest->nleft -= len;
-               memcpy(dest->nextc, p, len);
-               dest->nextc += len;
-               return;
-       }
-
-       if (xwrite(dest->fd, p, len) < len)
-               dest->flags |= OUTPUT_ERR;
-}
-#endif
-
-
-static void
-outstr(p, file)
-       const char *p;
-       struct output *file;
-       {
-#ifdef USE_GLIBC_STDIO
-       INTOFF;
-       fputs(p, file->stream);
-       INTON;
-#else
-       size_t len;
-
-       if (!*p) {
-               return;
-       }
-       len = strlen(p);
-       if ((file->nleft -= len) > 0) {
-               memcpy(file->nextc, p, len);
-               file->nextc += len;
-               return;
-       }
-       __outstr(p, len, file);
-#endif
-}
-
-
-#ifndef USE_GLIBC_STDIO
-
-
-static void
-outcslow(c, dest)
-       char c;
-       struct output *dest;
-       {
-       __outstr(&c, 1, dest);
-}
-#endif
-
-
-static void
-flushall() {
-       flushout(&output);
-#ifdef FLUSHERR
-       flushout(&errout);
-#endif
-}
-
-
-static void
-flushout(dest)
-       struct output *dest;
-       {
-#ifdef USE_GLIBC_STDIO
-       INTOFF;
-       fflush(dest->stream);
-       INTON;
-#else
-       size_t len;
-
-       len = dest->nextc - dest->buf;
-       if (dest->buf == NULL || !len || dest->fd < 0)
-               return;
-       dest->nextc = dest->buf;
-       dest->nleft = dest->bufsize;
-       if (xwrite(dest->fd, dest->buf, len) < len)
-               dest->flags |= OUTPUT_ERR;
-#endif
-}
-
-
-static void
-freestdout() {
-       if (output.buf) {
-               INTOFF;
-               ckfree(output.buf);
-               output.buf = NULL;
-               output.nleft = 0;
-               INTON;
-       }
-       output.flags = 0;
-}
-
-
-static void
-#ifdef __STDC__
-outfmt(struct output *file, const char *fmt, ...)
-#else
-static void
-outfmt(va_alist)
-       va_dcl
-#endif
-{
-       va_list ap;
-#ifndef __STDC__
-       struct output *file;
-       const char *fmt;
-
-       va_start(ap);
-       file = va_arg(ap, struct output *);
-       fmt = va_arg(ap, const char *);
-#else
-       va_start(ap, fmt);
-#endif
-       doformat(file, fmt, ap);
-       va_end(ap);
-}
-
-
-static void
-#ifdef __STDC__
-out1fmt(const char *fmt, ...)
-#else
-out1fmt(va_alist)
-       va_dcl
-#endif
-{
-       va_list ap;
-#ifndef __STDC__
-       const char *fmt;
-
-       va_start(ap);
-       fmt = va_arg(ap, const char *);
-#else
-       va_start(ap, fmt);
-#endif
-       doformat(out1, fmt, ap);
-       va_end(ap);
-}
-
-static void
-#ifdef __STDC__
-fmtstr(char *outbuf, size_t length, const char *fmt, ...)
-#else
-fmtstr(va_alist)
-       va_dcl
-#endif
-{
-       va_list ap;
-#ifndef __STDC__
-       char *outbuf;
-       size_t length;
-       const char *fmt;
-
-       va_start(ap);
-       outbuf = va_arg(ap, char *);
-       length = va_arg(ap, size_t);
-       fmt = va_arg(ap, const char *);
-#else
-       va_start(ap, fmt);
-#endif
-       INTOFF;
-       vsnprintf(outbuf, length, fmt, ap);
-       INTON;
-}
-
-#ifndef USE_GLIBC_STDIO
-/*
- * Formatted output.  This routine handles a subset of the printf formats:
- * - Formats supported: d, u, o, p, X, s, and c.
- * - The x format is also accepted but is treated like X.
- * - The l, ll and q modifiers are accepted.
- * - The - and # flags are accepted; # only works with the o format.
- * - Width and precision may be specified with any format except c.
- * - An * may be given for the width or precision.
- * - The obsolete practice of preceding the width with a zero to get
- *   zero padding is not supported; use the precision field.
- * - A % may be printed by writing %% in the format string.
- */
-
-#define TEMPSIZE 24
-
-#ifdef BSD4_4
-#define HAVE_VASPRINTF 1
-#endif
-
-#if    !HAVE_VASPRINTF
-static const char digit[] = "0123456789ABCDEF";
-#endif
-
-
-static void
-doformat(dest, f, ap)
-       struct output *dest;
-       const char *f;          /* format string */
-       va_list ap;
-{
-#if    HAVE_VASPRINTF
-       char *s, *t;
-       int len;
-
-       INTOFF;
-       len = vasprintf(&t, f, ap);
-       if (len < 0) {
-               return;
-       }
-       s = stalloc(++len);
-       memcpy(s, t, len);
-       free(t);
-       INTON;
-       outstr(s, dest);
-       stunalloc(s);
-#else  /* !HAVE_VASPRINTF */
-       char c;
-       char temp[TEMPSIZE];
-       int flushleft;
-       int sharp;
-       int width;
-       int prec;
-       int islong;
-       int isquad;
-       char *p;
-       int sign;
-#ifdef BSD4_4
-       quad_t l;
-       u_quad_t num;
-#else
-       long l;
-       u_long num;
-#endif
-       unsigned base;
-       int len;
-       int size;
-       int pad;
-
-       while ((c = *f++) != '\0') {
-               if (c != '%') {
-                       outc(c, dest);
-                       continue;
-               }
-               flushleft = 0;
-               sharp = 0;
-               width = 0;
-               prec = -1;
-               islong = 0;
-               isquad = 0;
-               for (;;) {
-                       if (*f == '-')
-                               flushleft++;
-                       else if (*f == '#')
-                               sharp++;
-                       else
-                               break;
-                       f++;
-               }
-               if (*f == '*') {
-                       width = va_arg(ap, int);
-                       f++;
-               } else {
-                       while (is_digit(*f)) {
-                               width = 10 * width + digit_val(*f++);
-                       }
-               }
-               if (*f == '.') {
-                       if (*++f == '*') {
-                               prec = va_arg(ap, int);
-                               f++;
-                       } else {
-                               prec = 0;
-                               while (is_digit(*f)) {
-                                       prec = 10 * prec + digit_val(*f++);
-                               }
-                       }
-               }
-               if (*f == 'l') {
-                       f++;
-                       if (*f == 'l') {
-                               isquad++;
-                               f++;
-                       } else
-                               islong++;
-               } else if (*f == 'q') {
-                       isquad++;
-                       f++;
-               }
-               switch (*f) {
-               case 'd':
-#ifdef BSD4_4
-                       if (isquad)
-                               l = va_arg(ap, quad_t);
-                       else
-#endif
-                       if (islong)
-                               l = va_arg(ap, long);
-                       else
-                               l = va_arg(ap, int);
-                       sign = 0;
-                       num = l;
-                       if (l < 0) {
-                               num = -l;
-                               sign = 1;
-                       }
-                       base = 10;
-                       goto number;
-               case 'u':
-                       base = 10;
-                       goto uns_number;
-               case 'o':
-                       base = 8;
-                       goto uns_number;
-               case 'p':
-                       outc('0', dest);
-                       outc('x', dest);
-                       /*FALLTHROUGH*/
-               case 'x':
-                       /* we don't implement 'x'; treat like 'X' */
-               case 'X':
-                       base = 16;
-uns_number:      /* an unsigned number */
-                       sign = 0;
-#ifdef BSD4_4
-                       if (isquad)
-                               num = va_arg(ap, u_quad_t);
-                       else
-#endif
-                       if (islong)
-                               num = va_arg(ap, unsigned long);
-                       else
-                               num = va_arg(ap, unsigned int);
-number:                  /* process a number */
-                       p = temp + TEMPSIZE - 1;
-                       *p = '\0';
-                       while (num) {
-                               *--p = digit[num % base];
-                               num /= base;
-                       }
-                       len = (temp + TEMPSIZE - 1) - p;
-                       if (prec < 0)
-                               prec = 1;
-                       if (sharp && *f == 'o' && prec <= len)
-                               prec = len + 1;
-                       pad = 0;
-                       if (width) {
-                               size = len;
-                               if (size < prec)
-                                       size = prec;
-                               size += sign;
-                               pad = width - size;
-                               if (flushleft == 0) {
-                                       while (--pad >= 0)
-                                               outc(' ', dest);
-                               }
-                       }
-                       if (sign)
-                               outc('-', dest);
-                       prec -= len;
-                       while (--prec >= 0)
-                               outc('0', dest);
-                       while (*p)
-                               outc(*p++, dest);
-                       while (--pad >= 0)
-                               outc(' ', dest);
-                       break;
-               case 's':
-                       p = va_arg(ap, char *);
-                       pad = 0;
-                       if (width) {
-                               len = strlen(p);
-                               if (prec >= 0 && len > prec)
-                                       len = prec;
-                               pad = width - len;
-                               if (flushleft == 0) {
-                                       while (--pad >= 0)
-                                               outc(' ', dest);
-                               }
-                       }
-                       prec++;
-                       while (--prec != 0 && *p)
-                               outc(*p++, dest);
-                       while (--pad >= 0)
-                               outc(' ', dest);
-                       break;
-               case 'c':
-                       c = va_arg(ap, int);
-                       outc(c, dest);
-                       break;
-               default:
-                       outc(*f, dest);
-                       break;
-               }
-               f++;
+               optionarg = p;
+               p = NULL;
        }
-#endif /* !HAVE_VASPRINTF */
+       optptr = p;
+       return c;
+}
+
+static void
+flushall() {
+       INTOFF;
+       fflush(stdout);
+       INTON;
 }
-#endif
 
 
+static void
+out2fmt(const char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+}
 
 /*
  * Version of write which resumes after a signal is caught.
  */
 
 static int
-xwrite(fd, buf, nbytes)
-       int fd;
-       const char *buf;
-       int nbytes;
-       {
+xwrite(int fd, const char *buf, int nbytes)
+{
        int ntry;
        int i;
        int n;
@@ -9254,51 +9597,6 @@ xwrite(fd, buf, nbytes)
 }
 
 
-#ifdef notdef
-/*
- * Version of ioctl that retries after a signal is caught.
- * XXX unused function
- */
-
-static int
-xioctl(fd, request, arg)
-       int fd;
-       unsigned long request;
-       char * arg;
-{
-       int i;
-
-       while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR);
-       return i;
-}
-#endif
-
-
-#ifdef USE_GLIBC_STDIO
-static void initstreams() {
-       output.stream = stdout;
-       errout.stream = stderr;
-}
-
-
-static void
-openmemout() {
-       INTOFF;
-       memout.stream = open_memstream(&memout.buf, &memout.bufsize);
-       INTON;
-}
-
-
-static int
-__closememout() {
-       int error;
-       error = fclose(memout.stream);
-       memout.stream = NULL;
-       return error;
-}
-#endif
-/*     $NetBSD: parser.c,v 1.46 2001/02/04 19:52:06 christos Exp $     */
-
 /*
  * Shell command parser.
  */
@@ -9308,47 +9606,42 @@ __closememout() {
 
 
 struct heredoc {
-       struct heredoc *next;   /* next here document in list */
-       union node *here;               /* redirection node */
-       char *eofmark;          /* string indicating end of input */
-       int striptabs;          /* if set, strip leading tabs */
+       struct heredoc *next;   /* next here document in list */
+       union node *here;               /* redirection node */
+       char *eofmark;          /* string indicating end of input */
+       int striptabs;          /* if set, strip leading tabs */
 };
 
+static struct heredoc *heredoclist;     /* list of here documents to read */
+static int parsebackquote;              /* nonzero if we are inside backquotes */
+static int doprompt;                    /* if set, prompt the user */
+static int needprompt;                  /* true if interactive and at start of line */
+static int lasttoken;                   /* last token read */
 
+static char *wordtext;                  /* text of last word returned by readtoken */
 
-struct heredoc *heredoclist;   /* list of here documents to read */
-static int parsebackquote;             /* nonzero if we are inside backquotes */
-static int doprompt;                   /* if set, prompt the user */
-static int needprompt;                 /* true if interactive and at start of line */
-static int lasttoken;                  /* last token read */
-static int tokpushback;                /* last token pushed back */
-static char *wordtext;                 /* text of last word returned by readtoken */
-static int checkkwd;            /* 1 == check for kwds, 2 == also eat newlines */
-/* 1 == check for aliases, 2 == also check for assignments */
-static int checkalias;
-struct nodelist *backquotelist;
-union node *redirnode;
-struct heredoc *heredoc;
-static int quoteflag;                  /* set if (part of) last token was quoted */
-static int startlinno;                 /* line # where last token started */
-
-
-static union node *list __P((int));
-static union node *andor __P((void));
-static union node *pipeline __P((void));
-static union node *command __P((void));
-static union node *simplecmd __P((void));
-static union node *makename __P((void));
-static void parsefname __P((void));
-static void parseheredoc __P((void));
-static int peektoken __P((void));
-static int readtoken __P((void));
-static int xxreadtoken __P((void));
-static int readtoken1 __P((int, char const *, char *, int));
-static int noexpand __P((char *));
-static void synexpect __P((int)) __attribute__((noreturn));
-static void synerror __P((const char *)) __attribute__((noreturn));
-static void setprompt __P((int));
+static struct nodelist *backquotelist;
+static union node *redirnode;
+static struct heredoc *heredoc;
+static int quoteflag;                   /* set if (part of) last token was quoted */
+static int startlinno;                  /* line # where last token started */
+
+
+static union node *list (int);
+static union node *andor (void);
+static union node *pipeline (void);
+static union node *command (void);
+static union node *simplecmd (void);
+static void parsefname (void);
+static void parseheredoc (void);
+static char peektoken (void);
+static int readtoken (void);
+static int xxreadtoken (void);
+static int readtoken1 (int, int, const char *, int);
+static int noexpand (char *);
+static void synexpect (int) __attribute__((noreturn));
+static void synerror (const char *) __attribute__((noreturn));
+static void setprompt (int);
 
 
 /*
@@ -9356,7 +9649,7 @@ static void setprompt __P((int));
  * valid parse tree indicating a blank line.)
  */
 
-union node *
+static union node *
 parsecmd(int interact)
 {
        int t;
@@ -9386,7 +9679,7 @@ list(nlflag)
        int tok;
 
        checkkwd = 2;
-       if (nlflag == 0 && tokendlist[peektoken()])
+       if (nlflag == 0 && peektoken())
                return NULL;
        n1 = NULL;
        for (;;) {
@@ -9429,14 +9722,14 @@ list(nlflag)
                                tokpushback++;
                        }
                        checkkwd = 2;
-                       if (tokendlist[peektoken()])
+                       if (peektoken())
                                return n1;
                        break;
                case TEOF:
                        if (heredoclist)
                                parseheredoc();
                        else
-                               pungetc();              /* push back EOF on input */
+                               pungetc();              /* push back EOF on input */
                        return n1;
                default:
                        if (nlflag)
@@ -9532,6 +9825,14 @@ command() {
        n1 = NULL;
        rpp = &redir;
 
+       /* Check for redirection which may precede command */
+       while (readtoken() == TREDIR) {
+               *rpp = n2 = redirnode;
+               rpp = &n2->nfile.next;
+               parsefname();
+       }
+       tokpushback++;
+
        switch (readtoken()) {
        case TIF:
                n1 = (union node *)stalloc(sizeof (struct nif));
@@ -9567,7 +9868,7 @@ command() {
                n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
                n1->nbinary.ch1 = list(0);
                if ((got=readtoken()) != TDO) {
-TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
+TRACE(("expecting DO got %s %s\n", tokname(got), got == TWORD ? wordtext : ""));
                        synexpect(TDO);
                }
                n1->nbinary.ch2 = list(0);
@@ -9701,7 +10002,6 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
                if (!redir)
                        synexpect(-1);
        case TWORD:
-       case TREDIR:
                tokpushback++;
                n1 = simplecmd();
                return n1;
@@ -9766,7 +10066,7 @@ simplecmd() {
                case TREDIR:
                        *rpp = n = redirnode;
                        rpp = &n->nfile.next;
-                       parsefname();   /* read name of redirection file */
+                       parsefname();   /* read name of redirection file */
                        break;
                case TLP:
                        if (
@@ -9776,10 +10076,6 @@ simplecmd() {
                                /* We have a function */
                                if (readtoken() != TRP)
                                        synexpect(TRP);
-#ifdef notdef
-                               if (! goodname(n->narg.text))
-                                       synerror("Bad function name");
-#endif
                                n->type = NDEFUN;
                                checkkwd = 2;
                                n->narg.next = command();
@@ -9805,7 +10101,7 @@ out:
 }
 
 static union node *
-makename() {
+makename(void) {
        union node *n;
 
        n = (union node *)stalloc(sizeof (struct narg));
@@ -9817,7 +10113,7 @@ makename() {
 }
 
 static void fixredir(union node *n, const char *text, int err)
-       {
+{
        TRACE(("Fix redir %s %d\n", text, err));
        if (!err)
                n->ndup.vname = NULL;
@@ -9837,7 +10133,7 @@ static void fixredir(union node *n, const char *text, int err)
 
 
 static void
-parsefname() {
+parsefname(void) {
        union node *n = redirnode;
 
        if (readtoken() != TWORD)
@@ -9854,1358 +10150,1055 @@ parsefname() {
                        while (*wordtext == '\t')
                                wordtext++;
                }
-               if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
-                       synerror("Illegal eof marker for << redirection");
-               rmescapes(wordtext);
-               here->eofmark = wordtext;
-               here->next = NULL;
-               if (heredoclist == NULL)
-                       heredoclist = here;
-               else {
-                       for (p = heredoclist ; p->next ; p = p->next);
-                       p->next = here;
-               }
-       } else if (n->type == NTOFD || n->type == NFROMFD) {
-               fixredir(n, wordtext, 0);
-       } else {
-               n->nfile.fname = makename();
-       }
-}
-
-
-/*
- * Input any here documents.
- */
-
-static void
-parseheredoc() {
-       struct heredoc *here;
-       union node *n;
-
-       while (heredoclist) {
-               here = heredoclist;
-               heredoclist = here->next;
-               if (needprompt) {
-                       setprompt(2);
-                       needprompt = 0;
-               }
-               readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
-                               here->eofmark, here->striptabs);
-               n = (union node *)stalloc(sizeof (struct narg));
-               n->narg.type = NARG;
-               n->narg.next = NULL;
-               n->narg.text = wordtext;
-               n->narg.backquote = backquotelist;
-               here->here->nhere.doc = n;
-       }
-}
-
-static int
-peektoken() {
-       int t;
-
-       t = readtoken();
-       tokpushback++;
-       return (t);
-}
-
-static int
-readtoken() {
-       int t;
-       int savecheckkwd = checkkwd;
-       int savecheckalias = checkalias;
-       struct alias *ap;
-#ifdef DEBUG
-       int alreadyseen = tokpushback;
-#endif
-
-top:
-       t = xxreadtoken();
-       checkalias = savecheckalias;
-
-       if (checkkwd) {
-               /*
-                * eat newlines
-                */
-               if (checkkwd == 2) {
-                       checkkwd = 0;
-                       while (t == TNL) {
-                               parseheredoc();
-                               t = xxreadtoken();
-                       }
-               }
-               checkkwd = 0;
-               /*
-                * check for keywords
-                */
-               if (t == TWORD && !quoteflag)
-               {
-                       const char *const *pp;
-
-                       if ((pp = findkwd(wordtext))) {
-                               lasttoken = t = pp - parsekwd + KWDOFFSET;
-                               TRACE(("keyword %s recognized\n", tokname[t]));
-                               goto out;
-                       }
-               }
-       }
-
-       if (t != TWORD) {
-               if (t != TREDIR) {
-                       checkalias = 0;
-               }
-       } else if (checkalias == 2 && isassignment(wordtext)) {
-               lasttoken = t = TASSIGN;
-       } else if (checkalias) {
-               if (!quoteflag && (ap = lookupalias(wordtext, 1)) != NULL) {
-                       if (*ap->val) {
-                               pushstring(ap->val, strlen(ap->val), ap);
-                       }
-                       checkkwd = savecheckkwd;
-                       goto top;
-               }
-               checkalias = 0;
-       }
-out:
-#ifdef DEBUG
-       if (!alreadyseen)
-           TRACE(("token %s %s\n", tokname[t], t == TWORD || t == TASSIGN ? wordtext : ""));
-       else
-           TRACE(("reread token %s %s\n", tokname[t], t == TWORD || t == TASSIGN ? wordtext : ""));
-#endif
-       return (t);
-}
-
-
-/*
- * Read the next input token.
- * If the token is a word, we set backquotelist to the list of cmds in
- *     backquotes.  We set quoteflag to true if any part of the word was
- *     quoted.
- * If the token is TREDIR, then we set redirnode to a structure containing
- *     the redirection.
- * In all cases, the variable startlinno is set to the number of the line
- *     on which the token starts.
- *
- * [Change comment:  here documents and internal procedures]
- * [Readtoken shouldn't have any arguments.  Perhaps we should make the
- *  word parsing code into a separate routine.  In this case, readtoken
- *  doesn't need to have any internal procedures, but parseword does.
- *  We could also make parseoperator in essence the main routine, and
- *  have parseword (readtoken1?) handle both words and redirection.]
- */
-
-#define RETURN(token)  return lasttoken = token
-
-static int
-xxreadtoken() {
-       int c;
-
-       if (tokpushback) {
-               tokpushback = 0;
-               return lasttoken;
-       }
-       if (needprompt) {
-               setprompt(2);
-               needprompt = 0;
-       }
-       startlinno = plinno;
-       for (;;) {      /* until token or start of word found */
-               c = pgetc_macro();
-               switch (c) {
-               case ' ': case '\t':
-               case PEOA:
-                       continue;
-               case '#':
-                       while ((c = pgetc()) != '\n' && c != PEOF);
-                       pungetc();
-                       continue;
-               case '\\':
-                       if (pgetc() == '\n') {
-                               startlinno = ++plinno;
-                               if (doprompt)
-                                       setprompt(2);
-                               else
-                                       setprompt(0);
-                               continue;
-                       }
-                       pungetc();
-                       goto breakloop;
-               case '\n':
-                       plinno++;
-                       needprompt = doprompt;
-                       RETURN(TNL);
-               case PEOF:
-                       RETURN(TEOF);
-               case '&':
-                       if (pgetc() == '&')
-                               RETURN(TAND);
-                       pungetc();
-                       RETURN(TBACKGND);
-               case '|':
-                       if (pgetc() == '|')
-                               RETURN(TOR);
-                       pungetc();
-                       RETURN(TPIPE);
-               case ';':
-                       if (pgetc() == ';')
-                               RETURN(TENDCASE);
-                       pungetc();
-                       RETURN(TSEMI);
-               case '(':
-                       RETURN(TLP);
-               case ')':
-                       RETURN(TRP);
-               default:
-                       goto breakloop;
-               }
+               if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
+                       synerror("Illegal eof marker for << redirection");
+               rmescapes(wordtext);
+               here->eofmark = wordtext;
+               here->next = NULL;
+               if (heredoclist == NULL)
+                       heredoclist = here;
+               else {
+                       for (p = heredoclist ; p->next ; p = p->next);
+                       p->next = here;
+               }
+       } else if (n->type == NTOFD || n->type == NFROMFD) {
+               fixredir(n, wordtext, 0);
+       } else {
+               n->nfile.fname = makename();
        }
-breakloop:
-       return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
-#undef RETURN
 }
 
 
-
 /*
- * If eofmark is NULL, read a word or a redirection symbol.  If eofmark
- * is not NULL, read a here document.  In the latter case, eofmark is the
- * word which marks the end of the document and striptabs is true if
- * leading tabs should be stripped from the document.  The argument firstc
- * is the first character of the input token or document.
- *
- * Because C does not have internal subroutines, I have simulated them
- * using goto's to implement the subroutine linkage.  The following macros
- * will run code that appears at the end of readtoken1.
+ * Input any here documents.
  */
 
-#define CHECKEND()     {goto checkend; checkend_return:;}
-#define PARSEREDIR()   {goto parseredir; parseredir_return:;}
-#define PARSESUB()     {goto parsesub; parsesub_return:;}
-#define PARSEBACKQOLD()        {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
-#define PARSEBACKQNEW()        {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
-#define        PARSEARITH()    {goto parsearith; parsearith_return:;}
+static void
+parseheredoc() {
+       struct heredoc *here;
+       union node *n;
+
+       while (heredoclist) {
+               here = heredoclist;
+               heredoclist = here->next;
+               if (needprompt) {
+                       setprompt(2);
+                       needprompt = 0;
+               }
+               readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
+                               here->eofmark, here->striptabs);
+               n = (union node *)stalloc(sizeof (struct narg));
+               n->narg.type = NARG;
+               n->narg.next = NULL;
+               n->narg.text = wordtext;
+               n->narg.backquote = backquotelist;
+               here->here->nhere.doc = n;
+       }
+}
+
+static char
+peektoken() {
+       int t;
+
+       t = readtoken();
+       tokpushback++;
+       return tokname_array[t][0];
+}
 
 static int
-readtoken1(firstc, syntax, eofmark, striptabs)
-       int firstc;
-       char const *syntax;
-       char *eofmark;
-       int striptabs;
-       {
-       int c = firstc;
-       char *out;
-       int len;
-       char line[EOFMARKLEN + 1];
-       struct nodelist *bqlist;
-       int quotef;
-       int dblquote;
-       int varnest;    /* levels of variables expansion */
-       int arinest;    /* levels of arithmetic expansion */
-       int parenlevel; /* levels of parens in arithmetic */
-       int dqvarnest;  /* levels of variables expansion within double quotes */
-       int oldstyle;
-       char const *prevsyntax; /* syntax before arithmetic */
-#if __GNUC__
-       /* Avoid longjmp clobbering */
-       (void) &out;
-       (void) &quotef;
-       (void) &dblquote;
-       (void) &varnest;
-       (void) &arinest;
-       (void) &parenlevel;
-       (void) &dqvarnest;
-       (void) &oldstyle;
-       (void) &prevsyntax;
-       (void) &syntax;
+readtoken() {
+       int t;
+
+#ifdef ASH_ALIAS
+       int savecheckalias = checkalias;
+       int savecheckkwd = checkkwd;
+       struct alias *ap;
 #endif
 
-       startlinno = plinno;
-       dblquote = 0;
-       if (syntax == DQSYNTAX)
-               dblquote = 1;
-       quotef = 0;
-       bqlist = NULL;
-       varnest = 0;
-       arinest = 0;
-       parenlevel = 0;
-       dqvarnest = 0;
+#ifdef DEBUG
+       int alreadyseen = tokpushback;
+#endif
 
-       STARTSTACKSTR(out);
-       loop: { /* for each line, until end of word */
-#if ATTY
-               if (c == '\034' && doprompt
-                && attyset() && ! equal(termval(), "emacs")) {
-                       attyline();
-                       if (syntax == BASESYNTAX)
-                               return readtoken();
-                       c = pgetc();
-                       goto loop;
-               }
+#ifdef ASH_ALIAS
+top:
 #endif
-               CHECKEND();     /* set c to PEOF if at end of here document */
-               for (;;) {      /* until end of line or end of word */
-                       CHECKSTRSPACE(3, out);  /* permit 3 calls to USTPUTC */
-                       switch(syntax[c]) {
-                       case CNL:       /* '\n' */
-                               if (syntax == BASESYNTAX)
-                                       goto endword;   /* exit outer loop */
-                               USTPUTC(c, out);
-                               plinno++;
-                               if (doprompt)
-                                       setprompt(2);
-                               else
-                                       setprompt(0);
-                               c = pgetc();
-                               goto loop;              /* continue outer loop */
-                       case CWORD:
-                               USTPUTC(c, out);
-                               break;
-                       case CCTL:
-                               if ((eofmark == NULL || dblquote) &&
-                                   dqvarnest == 0)
-                                       USTPUTC(CTLESC, out);
-                               USTPUTC(c, out);
-                               break;
-                       case CBACK:     /* backslash */
-                               c = pgetc2();
-                               if (c == PEOF) {
-                                       USTPUTC('\\', out);
-                                       pungetc();
-                               } else if (c == '\n') {
-                                       if (doprompt)
-                                               setprompt(2);
-                                       else
-                                               setprompt(0);
-                               } else {
-                                       if (dblquote && c != '\\' && c != '`' && c != '$'
-                                                        && (c != '"' || eofmark != NULL))
-                                               USTPUTC('\\', out);
-                                       if (SQSYNTAX[c] == CCTL)
-                                               USTPUTC(CTLESC, out);
-                                       else if (eofmark == NULL)
-                                               USTPUTC(CTLQUOTEMARK, out);
-                                       USTPUTC(c, out);
-                                       quotef++;
-                               }
-                               break;
-                       case CSQUOTE:
-                               if (eofmark == NULL)
-                                       USTPUTC(CTLQUOTEMARK, out);
-                               syntax = SQSYNTAX;
-                               break;
-                       case CDQUOTE:
-                               if (eofmark == NULL)
-                                       USTPUTC(CTLQUOTEMARK, out);
-                               syntax = DQSYNTAX;
-                               dblquote = 1;
-                               break;
-                       case CENDQUOTE:
-                               if (eofmark != NULL && arinest == 0 &&
-                                   varnest == 0) {
-                                       USTPUTC(c, out);
-                               } else {
-                                       if (arinest) {
-                                               syntax = ARISYNTAX;
-                                               dblquote = 0;
-                                       } else if (eofmark == NULL &&
-                                                  dqvarnest == 0) {
-                                               syntax = BASESYNTAX;
-                                               dblquote = 0;
-                                       }
-                                       quotef++;
-                               }
-                               break;
-                       case CVAR:      /* '$' */
-                               PARSESUB();             /* parse substitution */
-                               break;
-                       case CENDVAR:   /* '}' */
-                               if (varnest > 0) {
-                                       varnest--;
-                                       if (dqvarnest > 0) {
-                                               dqvarnest--;
-                                       }
-                                       USTPUTC(CTLENDVAR, out);
-                               } else {
-                                       USTPUTC(c, out);
-                               }
-                               break;
-#ifdef ASH_MATH_SUPPORT
-                       case CLP:       /* '(' in arithmetic */
-                               parenlevel++;
-                               USTPUTC(c, out);
-                               break;
-                       case CRP:       /* ')' in arithmetic */
-                               if (parenlevel > 0) {
-                                       USTPUTC(c, out);
-                                       --parenlevel;
-                               } else {
-                                       if (pgetc() == ')') {
-                                               if (--arinest == 0) {
-                                                       USTPUTC(CTLENDARI, out);
-                                                       syntax = prevsyntax;
-                                                       if (syntax == DQSYNTAX)
-                                                               dblquote = 1;
-                                                       else
-                                                               dblquote = 0;
-                                               } else
-                                                       USTPUTC(')', out);
-                                       } else {
-                                               /*
-                                                * unbalanced parens
-                                                *  (don't 2nd guess - no error)
-                                                */
-                                               pungetc();
-                                               USTPUTC(')', out);
-                                       }
-                               }
-                               break;
+
+       t = xxreadtoken();
+
+#ifdef ASH_ALIAS
+       checkalias = savecheckalias;
 #endif
-                       case CBQUOTE:   /* '`' */
-                               PARSEBACKQOLD();
-                               break;
-                       case CEOF:
-                               goto endword;           /* exit outer loop */
-                       case CIGN:
-                               break;
-                       default:
-                               if (varnest == 0)
-                                       goto endword;   /* exit outer loop */
-                               if (c != PEOA) {
-                                       USTPUTC(c, out);
-                               }
+
+       if (checkkwd) {
+               /*
+                * eat newlines
+                */
+               if (checkkwd == 2) {
+                       checkkwd = 0;
+                       while (t == TNL) {
+                               parseheredoc();
+                               t = xxreadtoken();
                        }
-                       c = pgetc_macro();
                }
-       }
-endword:
-       if (syntax == ARISYNTAX)
-               synerror("Missing '))'");
-       if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
-               synerror("Unterminated quoted string");
-       if (varnest != 0) {
-               startlinno = plinno;
-               synerror("Missing '}'");
-       }
-       USTPUTC('\0', out);
-       len = out - stackblock();
-       out = stackblock();
-       if (eofmark == NULL) {
-               if ((c == '>' || c == '<')
-                && quotef == 0
-                && len <= 2
-                && (*out == '\0' || is_digit(*out))) {
-                       PARSEREDIR();
-                       return lasttoken = TREDIR;
-               } else {
-                       pungetc();
+               checkkwd = 0;
+               /*
+                * check for keywords
+                */
+               if (t == TWORD && !quoteflag)
+               {
+                       const char *const *pp;
+
+                       if ((pp = findkwd(wordtext))) {
+                               lasttoken = t = pp - tokname_array;
+                               TRACE(("keyword %s recognized\n", tokname(t)));
+                               goto out;
+                       }
                }
        }
-       quoteflag = quotef;
-       backquotelist = bqlist;
-       grabstackblock(len);
-       wordtext = out;
-       return lasttoken = TWORD;
-/* end of readtoken routine */
 
 
-
-/*
- * Check to see whether we are at the end of the here document.  When this
- * is called, c is set to the first character of the next input line.  If
- * we are at the end of the here document, this routine sets the c to PEOF.
- */
-
-checkend: {
-       if (eofmark) {
-               if (c == PEOA) {
-                       c = pgetc2();
-               }
-               if (striptabs) {
-                       while (c == '\t') {
-                               c = pgetc2();
-                       }
+       if (t != TWORD) {
+               if (t != TREDIR) {
+                       checkalias = 0;
                }
-               if (c == *eofmark) {
-                       if (pfgets(line, sizeof line) != NULL) {
-                               char *p, *q;
-
-                               p = line;
-                               for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
-                               if (*p == '\n' && *q == '\0') {
-                                       c = PEOF;
-                                       plinno++;
-                                       needprompt = doprompt;
-                               } else {
-                                       pushstring(line, strlen(line), NULL);
-                               }
+       } else if (checkalias == 2 && isassignment(wordtext)) {
+               lasttoken = t = TASSIGN;
+#ifdef ASH_ALIAS
+       } else if (checkalias) {
+               if (!quoteflag && (ap = lookupalias(wordtext, 1)) != NULL) {
+                       if (*ap->val) {
+                               pushstring(ap->val, strlen(ap->val), ap);
                        }
+                       checkkwd = savecheckkwd;
+                       goto top;
                }
+               checkalias = 0;
+#endif
        }
-       goto checkend_return;
+out:
+#ifdef DEBUG
+       if (!alreadyseen)
+           TRACE(("token %s %s\n", tokname(t), t == TWORD || t == TASSIGN ? wordtext : ""));
+       else
+           TRACE(("reread token %s %s\n", tokname(t), t == TWORD || t == TASSIGN ? wordtext : ""));
+#endif
+       return (t);
 }
 
 
 /*
- * Parse a redirection operator.  The variable "out" points to a string
- * specifying the fd to be redirected.  The variable "c" contains the
- * first character of the redirection operator.
+ * Read the next input token.
+ * If the token is a word, we set backquotelist to the list of cmds in
+ *      backquotes.  We set quoteflag to true if any part of the word was
+ *      quoted.
+ * If the token is TREDIR, then we set redirnode to a structure containing
+ *      the redirection.
+ * In all cases, the variable startlinno is set to the number of the line
+ *      on which the token starts.
+ *
+ * [Change comment:  here documents and internal procedures]
+ * [Readtoken shouldn't have any arguments.  Perhaps we should make the
+ *  word parsing code into a separate routine.  In this case, readtoken
+ *  doesn't need to have any internal procedures, but parseword does.
+ *  We could also make parseoperator in essence the main routine, and
+ *  have parseword (readtoken1?) handle both words and redirection.]
  */
 
-parseredir: {
-       char fd = *out;
-       union node *np;
+#define NEW_xxreadtoken
+#ifdef NEW_xxreadtoken
 
-       np = (union node *)stalloc(sizeof (struct nfile));
-       if (c == '>') {
-               np->nfile.fd = 1;
-               c = pgetc();
-               if (c == '>')
-                       np->type = NAPPEND;
-               else if (c == '&')
-                       np->type = NTOFD;
-               else if (c == '|')
-                       np->type = NTOOV;
-               else {
-                       np->type = NTO;
-                       pungetc();
-               }
-       } else {        /* c == '<' */
-               np->nfile.fd = 0;
-               switch (c = pgetc()) {
-               case '<':
-                       if (sizeof (struct nfile) != sizeof (struct nhere)) {
-                               np = (union node *)stalloc(sizeof (struct nhere));
-                               np->nfile.fd = 0;
-                       }
-                       np->type = NHERE;
-                       heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
-                       heredoc->here = np;
-                       if ((c = pgetc()) == '-') {
-                               heredoc->striptabs = 1;
-                       } else {
-                               heredoc->striptabs = 0;
+static const char xxreadtoken_chars[] = "\n()&|;"; /* singles must be first! */
+static const char xxreadtoken_tokens[] = {
+       TNL, TLP, TRP,                          /* only single occurrence allowed */
+       TBACKGND, TPIPE, TSEMI,         /* if single occurrence */
+       TEOF,                                           /* corresponds to trailing nul */
+       TAND, TOR, TENDCASE,            /* if double occurrence */
+};
+
+#define xxreadtoken_doubles \
+       (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
+#define xxreadtoken_singles \
+       (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
+
+static int
+xxreadtoken() {
+       int c;
+
+       if (tokpushback) {
+               tokpushback = 0;
+               return lasttoken;
+       }
+       if (needprompt) {
+               setprompt(2);
+               needprompt = 0;
+       }
+       startlinno = plinno;
+       for (;;) {      /* until token or start of word found */
+               c = pgetc_macro();
+
+               if ((c!=' ') && (c!='\t')
+#ifdef ASH_ALIAS
+                       && (c!=PEOA)
+#endif
+                       ) {
+                       if (c=='#') {
+                               while ((c = pgetc()) != '\n' && c != PEOF);
                                pungetc();
-                       }
-                       break;
+                       } else if (c=='\\') {
+                               if (pgetc() != '\n') {
+                                       pungetc();
+                                       goto READTOKEN1;
+                               }
+                               startlinno = ++plinno;
+                               setprompt(doprompt ? 2 : 0);
+                       } else {
+                               const char *p
+                                       = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
 
-               case '&':
-                       np->type = NFROMFD;
-                       break;
+                               if (c!=PEOF) {
+                                       if (c=='\n') {
+                                               plinno++;
+                                               needprompt = doprompt;
+                                       }
 
-               case '>':
-                       np->type = NFROMTO;
-                       break;
+                                       p = strchr(xxreadtoken_chars, c);
+                                       if (p == NULL) {
+                                       READTOKEN1:
+                                               return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
+                                       }
+                       
+                                       if (p-xxreadtoken_chars >= xxreadtoken_singles) {
+                                               if (pgetc() == *p) { /* double occurrence? */
+                                                       p += xxreadtoken_doubles + 1;
+                                               } else {
+                                                       pungetc();
+                                               }
+                                       }
+                               }
 
-               default:
-                       np->type = NFROM;
-                       pungetc();
-                       break;
+                               return lasttoken = xxreadtoken_tokens[p-xxreadtoken_chars];
+                       }
                }
        }
-       if (fd != '\0')
-               np->nfile.fd = digit_val(fd);
-       redirnode = np;
-       goto parseredir_return;
 }
 
 
-/*
- * Parse a substitution.  At this point, we have read the dollar sign
- * and nothing else.
- */
+#else
+#define RETURN(token)   return lasttoken = token
 
-parsesub: {
-       int subtype;
-       int typeloc;
-       int flags;
-       char *p;
-       static const char types[] = "}-+?=";
+static int
+xxreadtoken() {
+       int c;
 
-       c = pgetc();
-       if (
-               c <= PEOA  ||
-               (c != '(' && c != '{' && !is_name(c) && !is_special(c))
-       ) {
-               USTPUTC('$', out);
-               pungetc();
-       } else if (c == '(') {  /* $(command) or $((arith)) */
-               if (pgetc() == '(') {
-                       PARSEARITH();
-               } else {
+       if (tokpushback) {
+               tokpushback = 0;
+               return lasttoken;
+       }
+       if (needprompt) {
+               setprompt(2);
+               needprompt = 0;
+       }
+       startlinno = plinno;
+       for (;;) {      /* until token or start of word found */
+               c = pgetc_macro();
+               switch (c) {
+               case ' ': case '\t':
+#ifdef ASH_ALIAS
+               case PEOA:
+#endif
+                       continue;
+               case '#':
+                       while ((c = pgetc()) != '\n' && c != PEOF);
                        pungetc();
-                       PARSEBACKQNEW();
-               }
-       } else {
-               USTPUTC(CTLVAR, out);
-               typeloc = out - stackblock();
-               USTPUTC(VSNORMAL, out);
-               subtype = VSNORMAL;
-               if (c == '{') {
-                       c = pgetc();
-                       if (c == '#') {
-                               if ((c = pgetc()) == '}')
-                                       c = '#';
-                               else
-                                       subtype = VSLENGTH;
-                       }
-                       else
-                               subtype = 0;
-               }
-               if (c > PEOA && is_name(c)) {
-                       do {
-                               STPUTC(c, out);
-                               c = pgetc();
-                       } while (c > PEOA && is_in_name(c));
-               } else if (is_digit(c)) {
-                       do {
-                               USTPUTC(c, out);
-                               c = pgetc();
-                       } while (is_digit(c));
-               }
-               else if (is_special(c)) {
-                       USTPUTC(c, out);
-                       c = pgetc();
-               }
-               else
-badsub:                        synerror("Bad substitution");
-
-               STPUTC('=', out);
-               flags = 0;
-               if (subtype == 0) {
-                       switch (c) {
-                       case ':':
-                               flags = VSNUL;
-                               c = pgetc();
-                               /*FALLTHROUGH*/
-                       default:
-                               p = strchr(types, c);
-                               if (p == NULL)
-                                       goto badsub;
-                               subtype = p - types + VSNORMAL;
-                               break;
-                       case '%':
-                       case '#':
-                               {
-                                       int cc = c;
-                                       subtype = c == '#' ? VSTRIMLEFT :
-                                                            VSTRIMRIGHT;
-                                       c = pgetc();
-                                       if (c == cc)
-                                               subtype++;
-                                       else
-                                               pungetc();
-                                       break;
-                               }
+                       continue;
+               case '\\':
+                       if (pgetc() == '\n') {
+                               startlinno = ++plinno;
+                               if (doprompt)
+                                       setprompt(2);
+                               else
+                                       setprompt(0);
+                               continue;
                        }
-               } else {
                        pungetc();
-               }
-               if (dblquote || arinest)
-                       flags |= VSQUOTE;
-               *(stackblock() + typeloc) = subtype | flags;
-               if (subtype != VSNORMAL) {
-                       varnest++;
-                       if (dblquote) {
-                               dqvarnest++;
-                       }
+                       goto breakloop;
+               case '\n':
+                       plinno++;
+                       needprompt = doprompt;
+                       RETURN(TNL);
+               case PEOF:
+                       RETURN(TEOF);
+               case '&':
+                       if (pgetc() == '&')
+                               RETURN(TAND);
+                       pungetc();
+                       RETURN(TBACKGND);
+               case '|':
+                       if (pgetc() == '|')
+                               RETURN(TOR);
+                       pungetc();
+                       RETURN(TPIPE);
+               case ';':
+                       if (pgetc() == ';')
+                               RETURN(TENDCASE);
+                       pungetc();
+                       RETURN(TSEMI);
+               case '(':
+                       RETURN(TLP);
+               case ')':
+                       RETURN(TRP);
+               default:
+                       goto breakloop;
                }
        }
-       goto parsesub_return;
+breakloop:
+       return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
+#undef RETURN
 }
+#endif
 
 
 /*
- * Called to parse command substitutions.  Newstyle is set if the command
- * is enclosed inside $(...); nlpp is a pointer to the head of the linked
- * list of commands (passed by reference), and savelen is the number of
- * characters on the top of the stack which must be preserved.
+ * If eofmark is NULL, read a word or a redirection symbol.  If eofmark
+ * is not NULL, read a here document.  In the latter case, eofmark is the
+ * word which marks the end of the document and striptabs is true if
+ * leading tabs should be stripped from the document.  The argument firstc
+ * is the first character of the input token or document.
+ *
+ * Because C does not have internal subroutines, I have simulated them
+ * using goto's to implement the subroutine linkage.  The following macros
+ * will run code that appears at the end of readtoken1.
  */
 
-parsebackq: {
-       struct nodelist **nlpp;
-       int savepbq;
-       union node *n;
-       char *volatile str;
-       struct jmploc jmploc;
-       struct jmploc *volatile savehandler;
-       int savelen;
-       int saveprompt;
-#ifdef __GNUC__
-       (void) &saveprompt;
-#endif
-
-       savepbq = parsebackquote;
-       if (setjmp(jmploc.loc)) {
-               if (str)
-                       ckfree(str);
-               parsebackquote = 0;
-               handler = savehandler;
-               longjmp(handler->loc, 1);
-       }
-       INTOFF;
-       str = NULL;
-       savelen = out - stackblock();
-       if (savelen > 0) {
-               str = ckmalloc(savelen);
-               memcpy(str, stackblock(), savelen);
-       }
-       savehandler = handler;
-       handler = &jmploc;
-       INTON;
-        if (oldstyle) {
-                /* We must read until the closing backquote, giving special
-                   treatment to some slashes, and then push the string and
-                   reread it as input, interpreting it normally.  */
-                char *pout;
-                int pc;
-                int psavelen;
-                char *pstr;
+#define CHECKEND()      {goto checkend; checkend_return:;}
+#define PARSEREDIR()    {goto parseredir; parseredir_return:;}
+#define PARSESUB()      {goto parsesub; parsesub_return:;}
+#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
+#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
+#define PARSEARITH()    {goto parsearith; parsearith_return:;}
 
+static int
+readtoken1(int firstc, int syntax, const char *eofmark, int striptabs)
+{
+       int c = firstc;
+       char *out;
+       int len;
+       char line[EOFMARKLEN + 1];
+       struct nodelist *bqlist;
+       int quotef;
+       int dblquote;
+       int varnest;    /* levels of variables expansion */
+       int arinest;    /* levels of arithmetic expansion */
+       int parenlevel; /* levels of parens in arithmetic */
+       int dqvarnest;  /* levels of variables expansion within double quotes */
+       int oldstyle;
+       int prevsyntax; /* syntax before arithmetic */
+#if __GNUC__
+       /* Avoid longjmp clobbering */
+       (void) &out;
+       (void) &quotef;
+       (void) &dblquote;
+       (void) &varnest;
+       (void) &arinest;
+       (void) &parenlevel;
+       (void) &dqvarnest;
+       (void) &oldstyle;
+       (void) &prevsyntax;
+       (void) &syntax;
+#endif
 
-                STARTSTACKSTR(pout);
-               for (;;) {
-                       if (needprompt) {
-                               setprompt(2);
-                               needprompt = 0;
-                       }
-                       switch (pc = pgetc()) {
-                       case '`':
-                               goto done;
+       startlinno = plinno;
+       dblquote = 0;
+       if (syntax == DQSYNTAX)
+               dblquote = 1;
+       quotef = 0;
+       bqlist = NULL;
+       varnest = 0;
+       arinest = 0;
+       parenlevel = 0;
+       dqvarnest = 0;
 
-                       case '\\':
-                                if ((pc = pgetc()) == '\n') {
-                                       plinno++;
+       STARTSTACKSTR(out);
+       loop: { /* for each line, until end of word */
+               CHECKEND();     /* set c to PEOF if at end of here document */
+               for (;;) {      /* until end of line or end of word */
+                       CHECKSTRSPACE(3, out);  /* permit 3 calls to USTPUTC */
+                       switch(SIT(c,syntax)) {
+                       case CNL:       /* '\n' */
+                               if (syntax == BASESYNTAX)
+                                       goto endword;   /* exit outer loop */
+                               USTPUTC(c, out);
+                               plinno++;
+                               if (doprompt)
+                                       setprompt(2);
+                               else
+                                       setprompt(0);
+                               c = pgetc();
+                               goto loop;              /* continue outer loop */
+                       case CWORD:
+                               USTPUTC(c, out);
+                               break;
+                       case CCTL:
+                               if ((eofmark == NULL || dblquote) &&
+                                   dqvarnest == 0)
+                                       USTPUTC(CTLESC, out);
+                               USTPUTC(c, out);
+                               break;
+                       case CBACK:     /* backslash */
+                               c = pgetc2();
+                               if (c == PEOF) {
+                                       USTPUTC('\\', out);
+                                       pungetc();
+                               } else if (c == '\n') {
                                        if (doprompt)
                                                setprompt(2);
                                        else
                                                setprompt(0);
-                                       /*
-                                        * If eating a newline, avoid putting
-                                        * the newline into the new character
-                                        * stream (via the STPUTC after the
-                                        * switch).
-                                        */
-                                       continue;
+                               } else {
+                                       if (dblquote && c != '\\' && c != '`' && c != '$'
+                                                        && (c != '"' || eofmark != NULL))
+                                               USTPUTC('\\', out);
+                                       if (SIT(c,SQSYNTAX) == CCTL)
+                                               USTPUTC(CTLESC, out);
+                                       else if (eofmark == NULL)
+                                               USTPUTC(CTLQUOTEMARK, out);
+                                       USTPUTC(c, out);
+                                       quotef++;
                                }
-                                if (pc != '\\' && pc != '`' && pc != '$'
-                                    && (!dblquote || pc != '"'))
-                                        STPUTC('\\', pout);
-                               if (pc > PEOA) {
-                                       break;
+                               break;
+                       case CSQUOTE:
+                               if (eofmark == NULL)
+                                       USTPUTC(CTLQUOTEMARK, out);
+                               syntax = SQSYNTAX;
+                               break;
+                       case CDQUOTE:
+                               if (eofmark == NULL)
+                                       USTPUTC(CTLQUOTEMARK, out);
+                               syntax = DQSYNTAX;
+                               dblquote = 1;
+                               break;
+                       case CENDQUOTE:
+                               if (eofmark != NULL && arinest == 0 &&
+                                   varnest == 0) {
+                                       USTPUTC(c, out);
+                               } else {
+                                       if (arinest) {
+                                               syntax = ARISYNTAX;
+                                               dblquote = 0;
+                                       } else if (eofmark == NULL &&
+                                                  dqvarnest == 0) {
+                                               syntax = BASESYNTAX;
+                                               dblquote = 0;
+                                       }
+                                       quotef++;
                                }
-                               /* fall through */
-
-                       case PEOF:
-                       case PEOA:
-                               startlinno = plinno;
-                               synerror("EOF in backquote substitution");
-
-                       case '\n':
-                               plinno++;
-                               needprompt = doprompt;
                                break;
-
+                       case CVAR:      /* '$' */
+                               PARSESUB();             /* parse substitution */
+                               break;
+                       case CENDVAR:   /* '}' */
+                               if (varnest > 0) {
+                                       varnest--;
+                                       if (dqvarnest > 0) {
+                                               dqvarnest--;
+                                       }
+                                       USTPUTC(CTLENDVAR, out);
+                               } else {
+                                       USTPUTC(c, out);
+                               }
+                               break;
+#ifdef ASH_MATH_SUPPORT
+                       case CLP:       /* '(' in arithmetic */
+                               parenlevel++;
+                               USTPUTC(c, out);
+                               break;
+                       case CRP:       /* ')' in arithmetic */
+                               if (parenlevel > 0) {
+                                       USTPUTC(c, out);
+                                       --parenlevel;
+                               } else {
+                                       if (pgetc() == ')') {
+                                               if (--arinest == 0) {
+                                                       USTPUTC(CTLENDARI, out);
+                                                       syntax = prevsyntax;
+                                                       if (syntax == DQSYNTAX)
+                                                               dblquote = 1;
+                                                       else
+                                                               dblquote = 0;
+                                               } else
+                                                       USTPUTC(')', out);
+                                       } else {
+                                               /*
+                                                * unbalanced parens
+                                                *  (don't 2nd guess - no error)
+                                                */
+                                               pungetc();
+                                               USTPUTC(')', out);
+                                       }
+                               }
+                               break;
+#endif
+                       case CBQUOTE:   /* '`' */
+                               PARSEBACKQOLD();
+                               break;
+                       case CENDFILE:
+                               goto endword;           /* exit outer loop */
+                       case CIGN:
+                               break;
                        default:
-                               break;
-                       }
-                       STPUTC(pc, pout);
-                }
-done:
-                STPUTC('\0', pout);
-                psavelen = pout - stackblock();
-                if (psavelen > 0) {
-                       pstr = grabstackstr(pout);
-                       setinputstring(pstr);
-                }
-        }
-       nlpp = &bqlist;
-       while (*nlpp)
-               nlpp = &(*nlpp)->next;
-       *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
-       (*nlpp)->next = NULL;
-       parsebackquote = oldstyle;
-
-       if (oldstyle) {
-               saveprompt = doprompt;
-               doprompt = 0;
-       }
-
-       n = list(0);
-
-       if (oldstyle)
-               doprompt = saveprompt;
-       else {
-               if (readtoken() != TRP)
-                       synexpect(TRP);
-       }
+                               if (varnest == 0)
+                                       goto endword;   /* exit outer loop */
+#ifdef ASH_ALIAS
+                               if (c != PEOA)
+#endif
+                                       USTPUTC(c, out);
 
-       (*nlpp)->n = n;
-        if (oldstyle) {
-               /*
-                * Start reading from old file again, ignoring any pushed back
-                * tokens left from the backquote parsing
-                */
-                popfile();
-               tokpushback = 0;
+                       }
+                       c = pgetc_macro();
+               }
        }
-       while (stackblocksize() <= savelen)
-               growstackblock();
-       STARTSTACKSTR(out);
-       if (str) {
-               memcpy(out, str, savelen);
-               STADJUST(savelen, out);
-               INTOFF;
-               ckfree(str);
-               str = NULL;
-               INTON;
+endword:
+       if (syntax == ARISYNTAX)
+               synerror("Missing '))'");
+       if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
+               synerror("Unterminated quoted string");
+       if (varnest != 0) {
+               startlinno = plinno;
+               synerror("Missing '}'");
        }
-       parsebackquote = savepbq;
-       handler = savehandler;
-       if (arinest || dblquote)
-               USTPUTC(CTLBACKQ | CTLQUOTE, out);
-       else
-               USTPUTC(CTLBACKQ, out);
-       if (oldstyle)
-               goto parsebackq_oldreturn;
-       else
-               goto parsebackq_newreturn;
-}
-
-/*
- * Parse an arithmetic expansion (indicate start of one and set state)
- */
-parsearith: {
-
-       if (++arinest == 1) {
-               prevsyntax = syntax;
-               syntax = ARISYNTAX;
-               USTPUTC(CTLARI, out);
-               if (dblquote)
-                       USTPUTC('"',out);
-               else
-                       USTPUTC(' ',out);
-       } else {
-               /*
-                * we collapse embedded arithmetic expansion to
-                * parenthesis, which should be equivalent
-                */
-               USTPUTC('(', out);
+       USTPUTC('\0', out);
+       len = out - stackblock();
+       out = stackblock();
+       if (eofmark == NULL) {
+               if ((c == '>' || c == '<')
+                && quotef == 0
+                && len <= 2
+                && (*out == '\0' || is_digit(*out))) {
+                       PARSEREDIR();
+                       return lasttoken = TREDIR;
+               } else {
+                       pungetc();
+               }
        }
-       goto parsearith_return;
-}
-
-} /* end of readtoken */
-
-
-
-#ifdef mkinit
-INCLUDE "parser.h"
-RESET {
-       tokpushback = 0;
-       checkkwd = 0;
-       checkalias = 0;
-}
-#endif
-
-/*
- * Returns true if the text contains nothing to expand (no dollar signs
- * or backquotes).
- */
-
-static int
-noexpand(text)
-       char *text;
-       {
-       char *p;
-       char c;
+       quoteflag = quotef;
+       backquotelist = bqlist;
+       grabstackblock(len);
+       wordtext = out;
+       return lasttoken = TWORD;
+/* end of readtoken routine */
 
-       p = text;
-       while ((c = *p++) != '\0') {
-               if (c == CTLQUOTEMARK)
-                       continue;
-               if (c == CTLESC)
-                       p++;
-               else if (BASESYNTAX[(int)c] == CCTL)
-                       return 0;
-       }
-       return 1;
-}
 
 
 /*
- * Return true if the argument is a legal variable name (a letter or
- * underscore followed by zero or more letters, underscores, and digits).
+ * Check to see whether we are at the end of the here document.  When this
+ * is called, c is set to the first character of the next input line.  If
+ * we are at the end of the here document, this routine sets the c to PEOF.
  */
 
-static int
-goodname(char *name)
-       {
-       char *p;
+checkend: {
+       if (eofmark) {
+#ifdef ASH_ALIAS
+               if (c == PEOA) {
+                       c = pgetc2();
+               }
+#endif
+               if (striptabs) {
+                       while (c == '\t') {
+                               c = pgetc2();
+                       }
+               }
+               if (c == *eofmark) {
+                       if (pfgets(line, sizeof line) != NULL) {
+                               const char *p, *q;
 
-       p = name;
-       if (! is_name(*p))
-               return 0;
-       while (*++p) {
-               if (! is_in_name(*p))
-                       return 0;
+                               p = line;
+                               for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
+                               if (*p == '\n' && *q == '\0') {
+                                       c = PEOF;
+                                       plinno++;
+                                       needprompt = doprompt;
+                               } else {
+                                       pushstring(line, strlen(line), NULL);
+                               }
+                       }
+               }
        }
-       return 1;
+       goto checkend_return;
 }
 
 
 /*
- * Called when an unexpected token is read during the parse.  The argument
- * is the token that is expected, or -1 if more than one type of token can
- * occur at this point.
+ * Parse a redirection operator.  The variable "out" points to a string
+ * specifying the fd to be redirected.  The variable "c" contains the
+ * first character of the redirection operator.
  */
 
-static void
-synexpect(token)
-       int token;
-{
-       char msg[64];
-
-       if (token >= 0) {
-               fmtstr(msg, 64, "%s unexpected (expecting %s)",
-                       tokname[lasttoken], tokname[token]);
-       } else {
-               fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
-       }
-       synerror(msg);
-       /* NOTREACHED */
-}
-
+parseredir: {
+       char fd = *out;
+       union node *np;
 
-static void
-synerror(msg)
-       const char *msg;
-       {
-       if (commandname)
-               outfmt(&errout, "%s: %d: ", commandname, startlinno);
-       outfmt(&errout, "Syntax error: %s\n", msg);
-       error((char *)NULL);
-       /* NOTREACHED */
-}
+       np = (union node *)stalloc(sizeof (struct nfile));
+       if (c == '>') {
+               np->nfile.fd = 1;
+               c = pgetc();
+               if (c == '>')
+                       np->type = NAPPEND;
+               else if (c == '&')
+                       np->type = NTOFD;
+               else if (c == '|')
+                       np->type = NTOOV;
+               else {
+                       np->type = NTO;
+                       pungetc();
+               }
+       } else {        /* c == '<' */
+               np->nfile.fd = 0;
+               switch (c = pgetc()) {
+               case '<':
+                       if (sizeof (struct nfile) != sizeof (struct nhere)) {
+                               np = (union node *)stalloc(sizeof (struct nhere));
+                               np->nfile.fd = 0;
+                       }
+                       np->type = NHERE;
+                       heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
+                       heredoc->here = np;
+                       if ((c = pgetc()) == '-') {
+                               heredoc->striptabs = 1;
+                       } else {
+                               heredoc->striptabs = 0;
+                               pungetc();
+                       }
+                       break;
 
-static void
-setprompt(int which)
-{
-    whichprompt = which;
-    putprompt(getprompt(NULL));
-}
+               case '&':
+                       np->type = NFROMFD;
+                       break;
 
-/*
- * called by editline -- any expansions to the prompt
- *    should be added here.
- */
-static const char *
-getprompt(void *unused)
-       {
-       switch (whichprompt) {
-       case 0:
-               return "";
-       case 1:
-               return ps1val();
-       case 2:
-               return ps2val();
-       default:
-               return "<internal prompt error>";
-       }
-}
+               case '>':
+                       np->type = NFROMTO;
+                       break;
 
-static int
-isassignment(const char *word) {
-       if (!is_name(*word)) {
-               return 0;
+               default:
+                       np->type = NFROM;
+                       pungetc();
+                       break;
+               }
        }
-       do {
-               word++;
-       } while (is_in_name(*word));
-       return *word == '=';
+       if (fd != '\0')
+               np->nfile.fd = digit_val(fd);
+       redirnode = np;
+       goto parseredir_return;
 }
 
-static const char *const *
-findkwd(const char *s) {
-       return findstring(
-               s, parsekwd, sizeof(parsekwd) / sizeof(const char *)
-       );
-}
-/*     $NetBSD: redir.c,v 1.22 2000/05/22 10:18:47 elric Exp $ */
 
 /*
- * Code for dealing with input/output redirection.
+ * Parse a substitution.  At this point, we have read the dollar sign
+ * and nothing else.
  */
 
-#define EMPTY -2               /* marks an unused slot in redirtab */
-#ifndef PIPE_BUF
-# define PIPESIZE 4096         /* amount of buffering in a pipe */
-#else
-# define PIPESIZE PIPE_BUF
-#endif
-
-
-struct redirtab *redirlist;
-
-/*
- * We keep track of whether or not fd0 has been redirected.  This is for
- * background commands, where we want to redirect fd0 to /dev/null only
- * if it hasn't already been redirected.
-*/
-static int fd0_redirected = 0;
+parsesub: {
+       int subtype;
+       int typeloc;
+       int flags;
+       char *p;
+       static const char types[] = "}-+?=";
 
-/*
- * We also keep track of where fileno2 goes.
- */
-static int fileno2 = 2;
+       c = pgetc();
+       if (
+               c <= PEOA  ||
+               (c != '(' && c != '{' && !is_name(c) && !is_special(c))
+       ) {
+               USTPUTC('$', out);
+               pungetc();
+       } else if (c == '(') {  /* $(command) or $((arith)) */
+               if (pgetc() == '(') {
+                       PARSEARITH();
+               } else {
+                       pungetc();
+                       PARSEBACKQNEW();
+               }
+       } else {
+               USTPUTC(CTLVAR, out);
+               typeloc = out - stackblock();
+               USTPUTC(VSNORMAL, out);
+               subtype = VSNORMAL;
+               if (c == '{') {
+                       c = pgetc();
+                       if (c == '#') {
+                               if ((c = pgetc()) == '}')
+                                       c = '#';
+                               else
+                                       subtype = VSLENGTH;
+                       }
+                       else
+                               subtype = 0;
+               }
+               if (c > PEOA && is_name(c)) {
+                       do {
+                               STPUTC(c, out);
+                               c = pgetc();
+                       } while (c > PEOA && is_in_name(c));
+               } else if (is_digit(c)) {
+                       do {
+                               USTPUTC(c, out);
+                               c = pgetc();
+                       } while (is_digit(c));
+               }
+               else if (is_special(c)) {
+                       USTPUTC(c, out);
+                       c = pgetc();
+               }
+               else
+badsub:                 synerror("Bad substitution");
 
-static int openredirect __P((union node *));
-static void dupredirect __P((union node *, int, char[10 ]));
-static int openhere __P((union node *));
-static int noclobberopen __P((const char *));
+               STPUTC('=', out);
+               flags = 0;
+               if (subtype == 0) {
+                       switch (c) {
+                       case ':':
+                               flags = VSNUL;
+                               c = pgetc();
+                               /*FALLTHROUGH*/
+                       default:
+                               p = strchr(types, c);
+                               if (p == NULL)
+                                       goto badsub;
+                               subtype = p - types + VSNORMAL;
+                               break;
+                       case '%':
+                       case '#':
+                               {
+                                       int cc = c;
+                                       subtype = c == '#' ? VSTRIMLEFT :
+                                                            VSTRIMRIGHT;
+                                       c = pgetc();
+                                       if (c == cc)
+                                               subtype++;
+                                       else
+                                               pungetc();
+                                       break;
+                               }
+                       }
+               } else {
+                       pungetc();
+               }
+               if (dblquote || arinest)
+                       flags |= VSQUOTE;
+               *(stackblock() + typeloc) = subtype | flags;
+               if (subtype != VSNORMAL) {
+                       varnest++;
+                       if (dblquote) {
+                               dqvarnest++;
+                       }
+               }
+       }
+       goto parsesub_return;
+}
 
 
 /*
- * Process a list of redirection commands.  If the REDIR_PUSH flag is set,
- * old file descriptors are stashed away so that the redirection can be
- * undone by calling popredir.  If the REDIR_BACKQ flag is set, then the
- * standard output, and the standard error if it becomes a duplicate of
- * stdout, is saved in memory.
+ * Called to parse command substitutions.  Newstyle is set if the command
+ * is enclosed inside $(...); nlpp is a pointer to the head of the linked
+ * list of commands (passed by reference), and savelen is the number of
+ * characters on the top of the stack which must be preserved.
  */
 
-static void
-redirect(redir, flags)
-       union node *redir;
-       int flags;
-       {
+parsebackq: {
+       struct nodelist **nlpp;
+       int savepbq;
        union node *n;
-       struct redirtab *sv = NULL;
-       int i;
-       int fd;
-       int newfd;
-       int try;
-       char memory[10];        /* file descriptors to write to memory */
+       char *volatile str;
+       struct jmploc jmploc;
+       struct jmploc *volatile savehandler;
+       int savelen;
+       int saveprompt;
+#ifdef __GNUC__
+       (void) &saveprompt;
+#endif
 
-       for (i = 10 ; --i >= 0 ; )
-               memory[i] = 0;
-       memory[1] = flags & REDIR_BACKQ;
-       if (flags & REDIR_PUSH) {
-               sv = ckmalloc(sizeof (struct redirtab));
-               for (i = 0 ; i < 10 ; i++)
-                       sv->renamed[i] = EMPTY;
-               sv->next = redirlist;
-               redirlist = sv;
+       savepbq = parsebackquote;
+       if (setjmp(jmploc.loc)) {
+               if (str)
+                       ckfree(str);
+               parsebackquote = 0;
+               handler = savehandler;
+               longjmp(handler->loc, 1);
        }
-       for (n = redir ; n ; n = n->nfile.next) {
-               fd = n->nfile.fd;
-               try = 0;
-               if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
-                   n->ndup.dupfd == fd)
-                       continue; /* redirect from/to same file descriptor */
+       INTOFF;
+       str = NULL;
+       savelen = out - stackblock();
+       if (savelen > 0) {
+               str = ckmalloc(savelen);
+               memcpy(str, stackblock(), savelen);
+       }
+       savehandler = handler;
+       handler = &jmploc;
+       INTON;
+       if (oldstyle) {
+               /* We must read until the closing backquote, giving special
+                  treatment to some slashes, and then push the string and
+                  reread it as input, interpreting it normally.  */
+               char *pout;
+               int pc;
+               int psavelen;
+               char *pstr;
 
-               INTOFF;
-               newfd = openredirect(n);
-               if (((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) ||
-                   (fd == fileno2)) {
-                       if (newfd == fd) {
-                               try++;
-                       } else if ((i = fcntl(fd, F_DUPFD, 10)) == -1) {
-                               switch (errno) {
-                               case EBADF:
-                                       if (!try) {
-                                               dupredirect(n, newfd, memory);
-                                               try++;
-                                               break;
-                                       }
-                                       /* FALLTHROUGH*/
-                               default:
-                                       if (newfd >= 0) {
-                                               close(newfd);
-                                       }
-                                       INTON;
-                                       error("%d: %s", fd, strerror(errno));
-                                       /* NOTREACHED */
-                               }
+
+               STARTSTACKSTR(pout);
+               for (;;) {
+                       if (needprompt) {
+                               setprompt(2);
+                               needprompt = 0;
                        }
-                       if (!try) {
-                               close(fd);
-                               if (flags & REDIR_PUSH) {
-                                       sv->renamed[fd] = i;
+                       switch (pc = pgetc()) {
+                       case '`':
+                               goto done;
+
+                       case '\\':
+                               if ((pc = pgetc()) == '\n') {
+                                       plinno++;
+                                       if (doprompt)
+                                               setprompt(2);
+                                       else
+                                               setprompt(0);
+                                       /*
+                                        * If eating a newline, avoid putting
+                                        * the newline into the new character
+                                        * stream (via the STPUTC after the
+                                        * switch).
+                                        */
+                                       continue;
                                }
-                               if (fd == fileno2) {
-                                       fileno2 = i;
+                               if (pc != '\\' && pc != '`' && pc != '$'
+                                   && (!dblquote || pc != '"'))
+                                       STPUTC('\\', pout);
+                               if (pc > PEOA) {
+                                       break;
                                }
+                               /* fall through */
+
+                       case PEOF:
+#ifdef ASH_ALIAS
+                       case PEOA:
+#endif
+                               startlinno = plinno;
+                               synerror("EOF in backquote substitution");
+
+                       case '\n':
+                               plinno++;
+                               needprompt = doprompt;
+                               break;
+
+                       default:
+                               break;
                        }
-               } else if (fd != newfd) {
-                       close(fd);
+                       STPUTC(pc, pout);
+               }
+done:
+               STPUTC('\0', pout);
+               psavelen = pout - stackblock();
+               if (psavelen > 0) {
+                       pstr = grabstackstr(pout);
+                       setinputstring(pstr);
                }
-                if (fd == 0)
-                        fd0_redirected++;
-               if (!try)
-                       dupredirect(n, newfd, memory);
-               INTON;
        }
-       if (memory[1])
-               out1 = &memout;
-       if (memory[2])
-               out2 = &memout;
-}
+       nlpp = &bqlist;
+       while (*nlpp)
+               nlpp = &(*nlpp)->next;
+       *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
+       (*nlpp)->next = NULL;
+       parsebackquote = oldstyle;
 
+       if (oldstyle) {
+               saveprompt = doprompt;
+               doprompt = 0;
+       }
 
-static int
-openredirect(redir)
-       union node *redir;
-       {
-       char *fname;
-       int f;
+       n = list(0);
 
-       switch (redir->nfile.type) {
-       case NFROM:
-               fname = redir->nfile.expfname;
-               if ((f = open(fname, O_RDONLY)) < 0)
-                       goto eopen;
-               break;
-       case NFROMTO:
-               fname = redir->nfile.expfname;
-               if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
-                       goto ecreate;
-               break;
-       case NTO:
-               /* Take care of noclobber mode. */
-               if (Cflag) {
-                       fname = redir->nfile.expfname;
-                       if ((f = noclobberopen(fname)) < 0)
-                               goto ecreate;
-                       break;
-               }
-       case NTOOV:
-               fname = redir->nfile.expfname;
-#ifdef O_CREAT
-               if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
-                       goto ecreate;
-#else
-               if ((f = creat(fname, 0666)) < 0)
-                       goto ecreate;
-#endif
-               break;
-       case NAPPEND:
-               fname = redir->nfile.expfname;
-#ifdef O_APPEND
-               if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
-                       goto ecreate;
-#else
-               if ((f = open(fname, O_WRONLY)) < 0
-                && (f = creat(fname, 0666)) < 0)
-                       goto ecreate;
-               lseek(f, (off_t)0, 2);
-#endif
-               break;
-       default:
-#ifdef DEBUG
-               abort();
-#endif
-               /* Fall through to eliminate warning. */
-       case NTOFD:
-       case NFROMFD:
-               f = -1;
-               break;
-       case NHERE:
-       case NXHERE:
-               f = openhere(redir);
-               break;
+       if (oldstyle)
+               doprompt = saveprompt;
+       else {
+               if (readtoken() != TRP)
+                       synexpect(TRP);
        }
 
-       return f;
-ecreate:
-       error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
-eopen:
-       error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
+       (*nlpp)->n = n;
+       if (oldstyle) {
+               /*
+                * Start reading from old file again, ignoring any pushed back
+                * tokens left from the backquote parsing
+                */
+               popfile();
+               tokpushback = 0;
+       }
+       while (stackblocksize() <= savelen)
+               growstackblock();
+       STARTSTACKSTR(out);
+       if (str) {
+               memcpy(out, str, savelen);
+               STADJUST(savelen, out);
+               INTOFF;
+               ckfree(str);
+               str = NULL;
+               INTON;
+       }
+       parsebackquote = savepbq;
+       handler = savehandler;
+       if (arinest || dblquote)
+               USTPUTC(CTLBACKQ | CTLQUOTE, out);
+       else
+               USTPUTC(CTLBACKQ, out);
+       if (oldstyle)
+               goto parsebackq_oldreturn;
+       else
+               goto parsebackq_newreturn;
 }
 
+/*
+ * Parse an arithmetic expansion (indicate start of one and set state)
+ */
+parsearith: {
 
-static void
-dupredirect(redir, f, memory)
-       union node *redir;
-       int f;
-       char memory[10];
-       {
-       int fd = redir->nfile.fd;
-
-       memory[fd] = 0;
-       if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
-               if (redir->ndup.dupfd >= 0) {   /* if not ">&-" */
-                       if (memory[redir->ndup.dupfd])
-                               memory[fd] = 1;
-                       else
-                               dup_as_newfd(redir->ndup.dupfd, fd);
-               }
-               return;
-       }
-
-       if (f != fd) {
-               dup_as_newfd(f, fd);
-               close(f);
+       if (++arinest == 1) {
+               prevsyntax = syntax;
+               syntax = ARISYNTAX;
+               USTPUTC(CTLARI, out);
+               if (dblquote)
+                       USTPUTC('"',out);
+               else
+                       USTPUTC(' ',out);
+       } else {
+               /*
+                * we collapse embedded arithmetic expansion to
+                * parenthesis, which should be equivalent
+                */
+               USTPUTC('(', out);
        }
-       return;
+       goto parsearith_return;
 }
 
+} /* end of readtoken */
+
 
 /*
- * Handle here documents.  Normally we fork off a process to write the
- * data to a pipe.  If the document is short, we can stuff the data in
- * the pipe without forking.
+ * Returns true if the text contains nothing to expand (no dollar signs
+ * or backquotes).
  */
 
 static int
-openhere(redir)
-       union node *redir;
+noexpand(text)
+       char *text;
        {
-       int pip[2];
-       int len = 0;
+       char *p;
+       char c;
 
-       if (pipe(pip) < 0)
-               error("Pipe call failed");
-       if (redir->type == NHERE) {
-               len = strlen(redir->nhere.doc->narg.text);
-               if (len <= PIPESIZE) {
-                       xwrite(pip[1], redir->nhere.doc->narg.text, len);
-                       goto out;
-               }
-       }
-       if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
-               close(pip[0]);
-               signal(SIGINT, SIG_IGN);
-               signal(SIGQUIT, SIG_IGN);
-               signal(SIGHUP, SIG_IGN);
-#ifdef SIGTSTP
-               signal(SIGTSTP, SIG_IGN);
-#endif
-               signal(SIGPIPE, SIG_DFL);
-               if (redir->type == NHERE)
-                       xwrite(pip[1], redir->nhere.doc->narg.text, len);
-               else
-                       expandhere(redir->nhere.doc, pip[1]);
-               _exit(0);
+       p = text;
+       while ((c = *p++) != '\0') {
+               if (c == CTLQUOTEMARK)
+                       continue;
+               if (c == CTLESC)
+                       p++;
+               else if (SIT(c,BASESYNTAX) == CCTL)
+                       return 0;
        }
-out:
-       close(pip[1]);
-       return pip[0];
+       return 1;
 }
 
 
-
 /*
- * Undo the effects of the last redirection.
+ * Return true if the argument is a legal variable name (a letter or
+ * underscore followed by zero or more letters, underscores, and digits).
  */
 
-static void
-popredir() {
-       struct redirtab *rp = redirlist;
-       int i;
+static int
+goodname(const char *name)
+{
+       const char *p;
 
-       INTOFF;
-       for (i = 0 ; i < 10 ; i++) {
-               if (rp->renamed[i] != EMPTY) {
-                        if (i == 0)
-                                fd0_redirected--;
-                       close(i);
-                       if (rp->renamed[i] >= 0) {
-                               dup_as_newfd(rp->renamed[i], i);
-                               close(rp->renamed[i]);
-                       }
-                       if (rp->renamed[i] == fileno2) {
-                               fileno2 = i;
-                       }
-               }
+       p = name;
+       if (! is_name(*p))
+               return 0;
+       while (*++p) {
+               if (! is_in_name(*p))
+                       return 0;
        }
-       redirlist = rp->next;
-       ckfree(rp);
-       INTON;
+       return 1;
 }
 
+
 /*
- * Undo all redirections.  Called on error or interrupt.
+ * Called when an unexpected token is read during the parse.  The argument
+ * is the token that is expected, or -1 if more than one type of token can
+ * occur at this point.
  */
 
-#ifdef mkinit
-
-INCLUDE "redir.h"
-
-RESET {
-       while (redirlist)
-               popredir();
-}
+static void
+synexpect(token)
+       int token;
+{
+       char msg[64];
+       int l;
 
-SHELLPROC {
-       clearredir();
+       l = sprintf(msg, "%s unexpected", tokname(lasttoken));
+       if (token >= 0)
+               sprintf(msg+l, " (expecting %s)", tokname(token));
+       synerror(msg);
+       /* NOTREACHED */
 }
 
-#endif
 
-/* Return true if fd 0 has already been redirected at least once.  */
-static int
-fd0_redirected_p () {
-        return fd0_redirected != 0;
+static void
+synerror(const char *msg)
+{
+       if (commandname)
+               out2fmt("%s: %d: ", commandname, startlinno);
+       out2fmt("Syntax error: %s\n", msg);
+       error((char *)NULL);
+       /* NOTREACHED */
 }
 
+
 /*
- * Discard all saved file descriptors.
+ * called by editline -- any expansions to the prompt
+ *    should be added here.
  */
-
 static void
-clearredir() {
-       struct redirtab *rp;
-       int i;
-
-       for (rp = redirlist ; rp ; rp = rp->next) {
-               for (i = 0 ; i < 10 ; i++) {
-                       if (rp->renamed[i] >= 0) {
-                               close(rp->renamed[i]);
-                               if (rp->renamed[i] == fileno2) {
-                                       fileno2 = -1;
-                               }
-                       }
-                       rp->renamed[i] = EMPTY;
-               }
-       }
-       if (fileno2 != 2 && fileno2 >= 0) {
-               close(fileno2);
-               fileno2 = -1;
-       }
+setprompt(int whichprompt)
+{
+    char *prompt;
+    switch (whichprompt) {
+       case 1:
+               prompt = ps1val();
+               break;
+       case 2:
+               prompt = ps2val();
+               break;
+       default:                /* 0 */
+               prompt = "";
+    }
+    putprompt(prompt);
 }
 
 
-
 /*
- * Copy a file descriptor to be >= to.  Returns -1
- * if the source file descriptor is closed, EMPTY if there are no unused
- * file descriptors left.
+ * Code for dealing with input/output redirection.
  */
 
-static int
-dup_as_newfd(from, to)
-       int from;
-       int to;
-{
-       int newfd;
+#define EMPTY -2                /* marks an unused slot in redirtab */
+#ifndef PIPE_BUF
+# define PIPESIZE 4096          /* amount of buffering in a pipe */
+#else
+# define PIPESIZE PIPE_BUF
+#endif
 
-       newfd = fcntl(from, F_DUPFD, to);
-       if (newfd < 0) {
-               if (errno == EMFILE)
-                       return EMPTY;
-               else
-                       error("%d: %s", from, strerror(errno));
-       }
-       return newfd;
-}
 
 /*
  * Open a file in noclobber mode.
  * The code was copied from bash.
  */
-static int
-noclobberopen(fname)
-       const char *fname;
+static inline int
+noclobberopen(const char *fname)
 {
        int r, fd;
        struct stat finfo, finfo2;
@@ -11250,449 +11243,300 @@ noclobberopen(fname)
         */
         if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
             finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
-               return fd;
+               return fd;
 
        /* The file has been replaced.  badness. */
        close(fd);
        errno = EEXIST;
        return -1;
 }
-/*     $NetBSD: setmode.c,v 1.28 2000/01/25 15:43:43 enami Exp $       */
-
-#ifdef __weak_alias
-__weak_alias(getmode,_getmode)
-__weak_alias(setmode,_setmode)
-#endif
-
-#ifdef __GLIBC__
-#define S_ISTXT __S_ISVTX
-#endif
-
-#define        SET_LEN 6               /* initial # of bitcmd struct to malloc */
-#define        SET_LEN_INCR 4          /* # of bitcmd structs to add as needed */
-
-typedef struct bitcmd {
-       char    cmd;
-       char    cmd2;
-       mode_t  bits;
-} BITCMD;
-
-#define        CMD2_CLR        0x01
-#define        CMD2_SET        0x02
-#define        CMD2_GBITS      0x04
-#define        CMD2_OBITS      0x08
-#define        CMD2_UBITS      0x10
-
-static BITCMD  *addcmd __P((BITCMD *, int, int, int, u_int));
-static void     compress_mode __P((BITCMD *));
-#ifdef SETMODE_DEBUG
-static void     dumpmode __P((BITCMD *));
-#endif
 
 /*
- * Given the old mode and an array of bitcmd structures, apply the operations
- * described in the bitcmd structures to the old mode, and return the new mode.
- * Note that there is no '=' command; a strict assignment is just a '-' (clear
- * bits) followed by a '+' (set bits).
+ * Handle here documents.  Normally we fork off a process to write the
+ * data to a pipe.  If the document is short, we can stuff the data in
+ * the pipe without forking.
  */
-mode_t
-getmode(bbox, omode)
-       const void *bbox;
-       mode_t omode;
-{
-       const BITCMD *set;
-       mode_t clrval, newmode, value;
 
-       _DIAGASSERT(bbox != NULL);
+static inline int
+openhere(const union node *redir)
+{
+       int pip[2];
+       int len = 0;
 
-       set = (const BITCMD *)bbox;
-       newmode = omode;
-       for (value = 0;; set++)
-               switch(set->cmd) {
-               /*
-                * When copying the user, group or other bits around, we "know"
-                * where the bits are in the mode so that we can do shifts to
-                * copy them around.  If we don't use shifts, it gets real
-                * grundgy with lots of single bit checks and bit sets.
-                */
-               case 'u':
-                       value = (newmode & S_IRWXU) >> 6;
-                       goto common;
-
-               case 'g':
-                       value = (newmode & S_IRWXG) >> 3;
-                       goto common;
-
-               case 'o':
-                       value = newmode & S_IRWXO;
-common:                        if (set->cmd2 & CMD2_CLR) {
-                               clrval =
-                                   (set->cmd2 & CMD2_SET) ?  S_IRWXO : value;
-                               if (set->cmd2 & CMD2_UBITS)
-                                       newmode &= ~((clrval<<6) & set->bits);
-                               if (set->cmd2 & CMD2_GBITS)
-                                       newmode &= ~((clrval<<3) & set->bits);
-                               if (set->cmd2 & CMD2_OBITS)
-                                       newmode &= ~(clrval & set->bits);
-                       }
-                       if (set->cmd2 & CMD2_SET) {
-                               if (set->cmd2 & CMD2_UBITS)
-                                       newmode |= (value<<6) & set->bits;
-                               if (set->cmd2 & CMD2_GBITS)
-                                       newmode |= (value<<3) & set->bits;
-                               if (set->cmd2 & CMD2_OBITS)
-                                       newmode |= value & set->bits;
-                       }
-                       break;
+       if (pipe(pip) < 0)
+               error("Pipe call failed");
+       if (redir->type == NHERE) {
+               len = strlen(redir->nhere.doc->narg.text);
+               if (len <= PIPESIZE) {
+                       xwrite(pip[1], redir->nhere.doc->narg.text, len);
+                       goto out;
+               }
+       }
+       if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
+               close(pip[0]);
+               signal(SIGINT, SIG_IGN);
+               signal(SIGQUIT, SIG_IGN);
+               signal(SIGHUP, SIG_IGN);
+#ifdef SIGTSTP
+               signal(SIGTSTP, SIG_IGN);
+#endif
+               signal(SIGPIPE, SIG_DFL);
+               if (redir->type == NHERE)
+                       xwrite(pip[1], redir->nhere.doc->narg.text, len);
+               else
+                       expandhere(redir->nhere.doc, pip[1]);
+               _exit(0);
+       }
+out:
+       close(pip[1]);
+       return pip[0];
+}
 
-               case '+':
-                       newmode |= set->bits;
-                       break;
 
-               case '-':
-                       newmode &= ~set->bits;
-                       break;
+static inline int
+openredirect(const union node *redir)
+{
+       char *fname;
+       int f;
 
-               case 'X':
-                       if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
-                               newmode |= set->bits;
+       switch (redir->nfile.type) {
+       case NFROM:
+               fname = redir->nfile.expfname;
+               if ((f = open(fname, O_RDONLY)) < 0)
+                       goto eopen;
+               break;
+       case NFROMTO:
+               fname = redir->nfile.expfname;
+               if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0)
+                       goto ecreate;
+               break;
+       case NTO:
+               /* Take care of noclobber mode. */
+               if (Cflag) {
+                       fname = redir->nfile.expfname;
+                       if ((f = noclobberopen(fname)) < 0)
+                               goto ecreate;
                        break;
-
-               case '\0':
-               default:
-#ifdef SETMODE_DEBUG
-                       (void)printf("getmode:%04o -> %04o\n", omode, newmode);
-#endif
-                       return (newmode);
                }
+       case NTOOV:
+               fname = redir->nfile.expfname;
+#ifdef O_CREAT
+               if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
+                       goto ecreate;
+#else
+               if ((f = creat(fname, 0666)) < 0)
+                       goto ecreate;
+#endif
+               break;
+       case NAPPEND:
+               fname = redir->nfile.expfname;
+#ifdef O_APPEND
+               if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
+                       goto ecreate;
+#else
+               if ((f = open(fname, O_WRONLY)) < 0
+                && (f = creat(fname, 0666)) < 0)
+                       goto ecreate;
+               lseek(f, (off_t)0, 2);
+#endif
+               break;
+       default:
+#ifdef DEBUG
+               abort();
+#endif
+               /* Fall through to eliminate warning. */
+       case NTOFD:
+       case NFROMFD:
+               f = -1;
+               break;
+       case NHERE:
+       case NXHERE:
+               f = openhere(redir);
+               break;
+       }
+
+       return f;
+ecreate:
+       error("cannot create %s: %s", fname, errmsg(errno, E_CREAT));
+eopen:
+       error("cannot open %s: %s", fname, errmsg(errno, E_OPEN));
 }
 
-#define        ADDCMD(a, b, c, d) do {                                         \
-       if (set >= endset) {                                            \
-               BITCMD *newset;                                         \
-               setlen += SET_LEN_INCR;                                 \
-               newset = realloc(saveset, sizeof(BITCMD) * setlen);     \
-               if (newset == NULL) {                                   \
-                       free(saveset);                                  \
-                       return (NULL);                                  \
-               }                                                       \
-               set = newset + (set - saveset);                         \
-               saveset = newset;                                       \
-               endset = newset + (setlen - 2);                         \
-       }                                                               \
-       set = addcmd(set, (a), (b), (c), (d));                          \
-} while (/*CONSTCOND*/0)
 
-#define        STANDARD_BITS   (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
+/*
+ * Process a list of redirection commands.  If the REDIR_PUSH flag is set,
+ * old file descriptors are stashed away so that the redirection can be
+ * undone by calling popredir.  If the REDIR_BACKQ flag is set, then the
+ * standard output, and the standard error if it becomes a duplicate of
+ * stdout.
+ */
 
-static void *
-setmode(p)
-       const char *p;
+static void
+redirect(union node *redir, int flags)
 {
-       int perm, who;
-       char op, *ep;
-       BITCMD *set, *saveset, *endset;
-       sigset_t mysigset, sigoset;
-       mode_t mask;
-       int equalopdone = 0;    /* pacify gcc */
-       int permXbits, setlen;
-
-       if (!*p)
-               return (NULL);
-
-       /*
-        * Get a copy of the mask for the permissions that are mask relative.
-        * Flip the bits, we want what's not set.  Since it's possible that
-        * the caller is opening files inside a signal handler, protect them
-        * as best we can.
-        */
-       sigfillset(&mysigset);
-       (void)sigprocmask(SIG_BLOCK, &mysigset, &sigoset);
-       (void)umask(mask = umask(0));
-       mask = ~mask;
-       (void)sigprocmask(SIG_SETMASK, &sigoset, NULL);
-
-       setlen = SET_LEN + 2;
-       
-       if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL)
-               return (NULL);
-       saveset = set;
-       endset = set + (setlen - 2);
+       union node *n;
+       struct redirtab *sv = NULL;
+       int i;
+       int fd;
+       int newfd;
+       int try;
+       int fd1dup = flags & REDIR_BACKQ;; /* stdout `cmd` redir to pipe */
 
-       /*
-        * If an absolute number, get it and return; disallow non-octal digits
-        * or illegal bits.
-        */
-       if (isdigit((unsigned char)*p)) {
-               perm = (mode_t)strtol(p, &ep, 8);
-               if (*ep || perm & ~(STANDARD_BITS|S_ISTXT)) {
-                       free(saveset);
-                       return (NULL);
-               }
-               ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);
-               set->cmd = 0;
-               return (saveset);
+       if (flags & REDIR_PUSH) {
+               sv = ckmalloc(sizeof (struct redirtab));
+               for (i = 0 ; i < 10 ; i++)
+                       sv->renamed[i] = EMPTY;
+               sv->next = redirlist;
+               redirlist = sv;
        }
+       for (n = redir ; n ; n = n->nfile.next) {
+               fd = n->nfile.fd;
+               try = 0;
+               if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) &&
+                   n->ndup.dupfd == fd)
+                       continue; /* redirect from/to same file descriptor */
 
-       /*
-        * Build list of structures to set/clear/copy bits as described by
-        * each clause of the symbolic mode.
-        */
-       for (;;) {
-               /* First, find out which bits might be modified. */
-               for (who = 0;; ++p) {
-                       switch (*p) {
-                       case 'a':
-                               who |= STANDARD_BITS;
-                               break;
-                       case 'u':
-                               who |= S_ISUID|S_IRWXU;
-                               break;
-                       case 'g':
-                               who |= S_ISGID|S_IRWXG;
-                               break;
-                       case 'o':
-                               who |= S_IRWXO;
-                               break;
-                       default:
-                               goto getop;
-                       }
-               }
-
-getop:         if ((op = *p++) != '+' && op != '-' && op != '=') {
-                       free(saveset);
-                       return (NULL);
-               }
-               if (op == '=')
-                       equalopdone = 0;
-
-               who &= ~S_ISTXT;
-               for (perm = 0, permXbits = 0;; ++p) {
-                       switch (*p) {
-                       case 'r':
-                               perm |= S_IRUSR|S_IRGRP|S_IROTH;
-                               break;
-                       case 's':
-                               /*
-                                * If specific bits where requested and 
-                                * only "other" bits ignore set-id. 
-                                */
-                               if (who == 0 || (who & ~S_IRWXO))
-                                       perm |= S_ISUID|S_ISGID;
-                               break;
-                       case 't':
-                               /*
-                                * If specific bits where requested and 
-                                * only "other" bits ignore set-id. 
-                                */
-                               if (who == 0 || (who & ~S_IRWXO)) {
-                                       who |= S_ISTXT;
-                                       perm |= S_ISTXT;
-                               }
-                               break;
-                       case 'w':
-                               perm |= S_IWUSR|S_IWGRP|S_IWOTH;
-                               break;
-                       case 'X':
-                               permXbits = S_IXUSR|S_IXGRP|S_IXOTH;
-                               break;
-                       case 'x':
-                               perm |= S_IXUSR|S_IXGRP|S_IXOTH;
-                               break;
-                       case 'u':
-                       case 'g':
-                       case 'o':
-                               /*
-                                * When ever we hit 'u', 'g', or 'o', we have
-                                * to flush out any partial mode that we have,
-                                * and then do the copying of the mode bits.
-                                */
-                               if (perm) {
-                                       ADDCMD(op, who, perm, mask);
-                                       perm = 0;
-                               }
-                               if (op == '=')
-                                       equalopdone = 1;
-                               if (op == '+' && permXbits) {
-                                       ADDCMD('X', who, permXbits, mask);
-                                       permXbits = 0;
-                               }
-                               ADDCMD(*p, who, op, mask);
-                               break;
-
-                       default:
-                               /*
-                                * Add any permissions that we haven't already
-                                * done.
-                                */
-                               if (perm || (op == '=' && !equalopdone)) {
-                                       if (op == '=')
-                                               equalopdone = 1;
-                                       ADDCMD(op, who, perm, mask);
-                                       perm = 0;
+               INTOFF;
+               newfd = openredirect(n);
+               if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) {
+                       if (newfd == fd) {
+                               try++;
+                       } else if ((i = fcntl(fd, F_DUPFD, 10)) == -1) {
+                               switch (errno) {
+                               case EBADF:
+                                       if (!try) {
+                                               dupredirect(n, newfd, fd1dup);
+                                               try++;
+                                               break;
+                                       }
+                                       /* FALLTHROUGH*/
+                               default:
+                                       if (newfd >= 0) {
+                                               close(newfd);
+                                       }
+                                       INTON;
+                                       error("%d: %m", fd);
+                                       /* NOTREACHED */
                                }
-                               if (permXbits) {
-                                       ADDCMD('X', who, permXbits, mask);
-                                       permXbits = 0;
+                       }
+                       if (!try) {
+                               close(fd);
+                               if (flags & REDIR_PUSH) {
+                                       sv->renamed[fd] = i;
                                }
-                               goto apply;
                        }
+               } else if (fd != newfd) {
+                       close(fd);
                }
-
-apply:         if (!*p)
-                       break;
-               if (*p != ',')
-                       goto getop;
-               ++p;
+               if (fd == 0)
+                       fd0_redirected++;
+               if (!try)
+                       dupredirect(n, newfd, fd1dup);
+               INTON;
        }
-       set->cmd = 0;
-#ifdef SETMODE_DEBUG
-       (void)printf("Before compress_mode()\n");
-       dumpmode(saveset);
-#endif
-       compress_mode(saveset);
-#ifdef SETMODE_DEBUG
-       (void)printf("After compress_mode()\n");
-       dumpmode(saveset);
-#endif
-       return (saveset);
 }
 
-static BITCMD *
-addcmd(set, op, who, oparg, mask)
-       BITCMD *set;
-       int oparg, who;
-       int op;
-       u_int mask;
-{
-
-       _DIAGASSERT(set != NULL);
 
-       switch (op) {
-       case '=':
-               set->cmd = '-';
-               set->bits = who ? who : STANDARD_BITS;
-               set++;
+static void
+dupredirect(const union node *redir, int f, int fd1dup)
+{
+       int fd = redir->nfile.fd;
 
-               op = '+';
-               /* FALLTHROUGH */
-       case '+':
-       case '-':
-       case 'X':
-               set->cmd = op;
-               set->bits = (who ? who : mask) & oparg;
-               break;
+       if(fd==1)
+               fd1dup = 0;
+       if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
+               if (redir->ndup.dupfd >= 0) {   /* if not ">&-" */
+                       if (redir->ndup.dupfd!=1 || fd1dup!=1)
+                               dup_as_newfd(redir->ndup.dupfd, fd);
+               }
+               return;
+       }
 
-       case 'u':
-       case 'g':
-       case 'o':
-               set->cmd = op;
-               if (who) {
-                       set->cmd2 = ((who & S_IRUSR) ? CMD2_UBITS : 0) |
-                                   ((who & S_IRGRP) ? CMD2_GBITS : 0) |
-                                   ((who & S_IROTH) ? CMD2_OBITS : 0);
-                       set->bits = (mode_t)~0;
-               } else {
-                       set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS;
-                       set->bits = mask;
-               }
-       
-               if (oparg == '+')
-                       set->cmd2 |= CMD2_SET;
-               else if (oparg == '-')
-                       set->cmd2 |= CMD2_CLR;
-               else if (oparg == '=')
-                       set->cmd2 |= CMD2_SET|CMD2_CLR;
-               break;
+       if (f != fd) {
+               dup_as_newfd(f, fd);
+               close(f);
        }
-       return (set + 1);
+       return;
 }
 
-#ifdef SETMODE_DEBUG
-static void
-dumpmode(set)
-       BITCMD *set;
-{
-
-       _DIAGASSERT(set != NULL);
 
-       for (; set->cmd; ++set)
-               (void)printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
-                   set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
-                   set->cmd2 & CMD2_CLR ? " CLR" : "",
-                   set->cmd2 & CMD2_SET ? " SET" : "",
-                   set->cmd2 & CMD2_UBITS ? " UBITS" : "",
-                   set->cmd2 & CMD2_GBITS ? " GBITS" : "",
-                   set->cmd2 & CMD2_OBITS ? " OBITS" : "");
-}
-#endif
 
 /*
- * Given an array of bitcmd structures, compress by compacting consecutive
- * '+', '-' and 'X' commands into at most 3 commands, one of each.  The 'u',
- * 'g' and 'o' commands continue to be separate.  They could probably be 
- * compacted, but it's not worth the effort.
+ * Undo the effects of the last redirection.
  */
+
 static void
-compress_mode(set)
-       BITCMD *set;
+popredir(void)
 {
-       BITCMD *nset;
-       int setbits, clrbits, Xbits, op;
-
-       _DIAGASSERT(set != NULL);
+       struct redirtab *rp = redirlist;
+       int i;
 
-       for (nset = set;;) {
-               /* Copy over any 'u', 'g' and 'o' commands. */
-               while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
-                       *set++ = *nset++;
-                       if (!op)
-                               return;
+       INTOFF;
+       for (i = 0 ; i < 10 ; i++) {
+               if (rp->renamed[i] != EMPTY) {
+                       if (i == 0)
+                               fd0_redirected--;
+                       close(i);
+                       if (rp->renamed[i] >= 0) {
+                               dup_as_newfd(rp->renamed[i], i);
+                               close(rp->renamed[i]);
+                       }
                }
+       }
+       redirlist = rp->next;
+       ckfree(rp);
+       INTON;
+}
 
-               for (setbits = clrbits = Xbits = 0;; nset++) {
-                       if ((op = nset->cmd) == '-') {
-                               clrbits |= nset->bits;
-                               setbits &= ~nset->bits;
-                               Xbits &= ~nset->bits;
-                       } else if (op == '+') {
-                               setbits |= nset->bits;
-                               clrbits &= ~nset->bits;
-                               Xbits &= ~nset->bits;
-                       } else if (op == 'X')
-                               Xbits |= nset->bits & ~setbits;
-                       else
-                               break;
-               }
-               if (clrbits) {
-                       set->cmd = '-';
-                       set->cmd2 = 0;
-                       set->bits = clrbits;
-                       set++;
-               }
-               if (setbits) {
-                       set->cmd = '+';
-                       set->cmd2 = 0;
-                       set->bits = setbits;
-                       set++;
-               }
-               if (Xbits) {
-                       set->cmd = 'X';
-                       set->cmd2 = 0;
-                       set->bits = Xbits;
-                       set++;
+/*
+ * Discard all saved file descriptors.
+ */
+
+static void
+clearredir(void) {
+       struct redirtab *rp;
+       int i;
+
+       for (rp = redirlist ; rp ; rp = rp->next) {
+               for (i = 0 ; i < 10 ; i++) {
+                       if (rp->renamed[i] >= 0) {
+                               close(rp->renamed[i]);
+                       }
+                       rp->renamed[i] = EMPTY;
                }
        }
 }
-/*     $NetBSD: show.c,v 1.18 1999/10/08 21:10:44 pk Exp $     */
 
 
+/*
+ * Copy a file descriptor to be >= to.  Returns -1
+ * if the source file descriptor is closed, EMPTY if there are no unused
+ * file descriptors left.
+ */
+
+static int
+dup_as_newfd(from, to)
+       int from;
+       int to;
+{
+       int newfd;
+
+       newfd = fcntl(from, F_DUPFD, to);
+       if (newfd < 0) {
+               if (errno == EMFILE)
+                       return EMPTY;
+               else
+                       error("%d: %m", from);
+       }
+       return newfd;
+}
+
 #ifdef DEBUG
-static void shtree __P((union node *, int, char *, FILE*));
-static void shcmd __P((union node *, FILE *));
-static void sharg __P((union node *, FILE *));
-static void indent __P((int, char *, FILE *));
-static void trstring __P((char *));
+static void shtree (union node *, int, char *, FILE*);
+static void shcmd (union node *, FILE *);
+static void sharg (union node *, FILE *);
+static void indent (int, char *, FILE *);
+static void trstring (char *);
 
 
 static void
@@ -11779,16 +11623,27 @@ shcmd(cmd, fp)
        for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) {
                if (! first)
                        putchar(' ');
+#if 1
+               s = "*error*";
+               dftfd = 0;
+               if ((np->nfile.type <= NFROMFD) && (np->nfile.type >= NTO)) {
+                       s = redir_strings[np->nfile.type - NTO];
+                       if (*s == '>') {
+                               dftfd = 1;
+                       }
+               }
+#else
                switch (np->nfile.type) {
-                       case NTO:       s = ">";  dftfd = 1; break;
-                       case NAPPEND:   s = ">>"; dftfd = 1; break;
-                       case NTOFD:     s = ">&"; dftfd = 1; break;
-                       case NTOOV:     s = ">|"; dftfd = 1; break;
-                       case NFROM:     s = "<";  dftfd = 0; break;
-                       case NFROMFD:   s = "<&"; dftfd = 0; break;
-                       case NFROMTO:   s = "<>"; dftfd = 0; break;
-                       default:        s = "*error*"; dftfd = 0; break;
+                       case NTO:       s = ">";  dftfd = 1; break;
+                       case NAPPEND:   s = ">>"; dftfd = 1; break;
+                       case NTOFD:     s = ">&"; dftfd = 1; break;
+                       case NTOOV:     s = ">|"; dftfd = 1; break;
+                       case NFROM:     s = "<";  dftfd = 0; break;
+                       case NFROMFD:   s = "<&"; dftfd = 0; break;
+                       case NFROMTO:   s = "<>"; dftfd = 0; break;
+                       default:        s = "*error*"; dftfd = 0; break;
                }
+#endif
                if (np->nfile.fd != dftfd)
                        fprintf(fp, "%d", np->nfile.fd);
                fputs(s, fp);
@@ -11938,13 +11793,7 @@ static void
 trace(const char *fmt, ...)
 {
        va_list va;
-#ifdef __STDC__
        va_start(va, fmt);
-#else
-       char *fmt;
-       va_start(va);
-       fmt = va_arg(va, char *);
-#endif
        if (tracefile != NULL) {
                (void) vfprintf(tracefile, fmt, va);
                if (strchr(fmt, '\n'))
@@ -11988,7 +11837,7 @@ trstring(s)
                case CTLVAR+CTLQUOTE:  c = 'V';  goto backslash;
                case CTLBACKQ:  c = 'q';  goto backslash;
                case CTLBACKQ+CTLQUOTE:  c = 'Q';  goto backslash;
-backslash:       putc('\\', tracefile);
+backslash:        putc('\\', tracefile);
                        putc(c, tracefile);
                        break;
                default:
@@ -12042,11 +11891,11 @@ opentrace() {
                        else
                                p = "/tmp";
                }
-               scopy(p, s);
+               strcpy(s, p);
                strcat(s, "/trace");
        }
 #else
-       scopy("./trace", s);
+       strcpy(s, "./trace");
 #endif /* not_this_way */
        if ((tracefile = fopen(s, "a")) == NULL) {
                fprintf(stderr, "Can't open %s\n", s);
@@ -12062,356 +11911,6 @@ opentrace() {
 #endif /* DEBUG */
 
 
-/*
- * This file was generated by the mksyntax program.
- */
-
-/* syntax table used when not in quotes */
-static const char basesyntax[257] = {
-      CEOF,    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] = {
-      CEOF,    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] = {
-      CEOF,    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] = {
-      CEOF,    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
-};
-/*     $NetBSD: trap.c,v 1.25 2001/02/04 19:52:07 christos Exp $       */
-
 /*
  * The trap builtin.
  */
@@ -12429,11 +11928,15 @@ trapcmd(argc, argv)
                for (signo = 0 ; signo < NSIG ; signo++) {
                        if (trap[signo] != NULL) {
                                char *p;
+                               const char *sn;
 
                                p = single_quote(trap[signo]);
-                               out1fmt("trap -- %s %s\n", p,
-                                       signal_names[signo] + (signo ? 3 : 0)
-                               );
+                               sn = sys_siglist[signo];
+                               if(sn==NULL)
+                                       sn = u_signal_names(0, &signo, 0);
+                               if(sn==NULL)
+                                       sn = "???";
+                               printf("trap -- %s %s\n", p, sn);
                                stunalloc(p);
                        }
                }
@@ -12467,25 +11970,6 @@ trapcmd(argc, argv)
 
 
 
-/*
- * Clear traps on a fork.
- */
-
-static void
-clear_traps() {
-       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;
-               }
-       }
-}
 
 
 
@@ -12495,8 +11979,7 @@ clear_traps() {
  */
 
 static void
-setsignal(signo)
-       int signo;
+setsignal(int signo)
 {
        int action;
        char *t;
@@ -12517,7 +12000,6 @@ setsignal(signo)
                case SIGQUIT:
 #ifdef DEBUG
                        {
-                       extern int debug;
 
                        if (debug)
                                break;
@@ -12528,7 +12010,7 @@ setsignal(signo)
                        if (iflag)
                                action = S_IGN;
                        break;
-#if JOBS
+#ifdef JOBS
                case SIGTSTP:
                case SIGTTOU:
                        if (mflag)
@@ -12554,25 +12036,17 @@ setsignal(signo)
                if (act.sa_handler == SIG_IGN) {
                        if (mflag && (signo == SIGTSTP ||
                             signo == SIGTTIN || signo == SIGTTOU)) {
-                               *t = S_IGN;     /* don't hard ignore these */
+                               *t = S_IGN;     /* don't hard ignore these */
                        } else
                                *t = S_HARD_IGN;
                } else {
-                       *t = S_RESET;   /* force to be set */
+                       *t = S_RESET;   /* force to be set */
                }
        }
        if (*t == S_HARD_IGN || *t == action)
                return;
-       switch (action) {
-       case S_CATCH:
-               act.sa_handler = onsig;
-               break;
-       case S_IGN:
-               act.sa_handler = SIG_IGN;
-               break;
-       default:
-               act.sa_handler = SIG_DFL;
-       }
+       act.sa_handler = ((action == S_CATCH) ? onsig
+                                         : ((action == S_IGN) ? SIG_IGN : SIG_DFL));
        *t = action;
        act.sa_flags = 0;
        sigemptyset(&act.sa_mask);
@@ -12594,30 +12068,12 @@ ignoresig(signo)
 }
 
 
-#ifdef mkinit
-INCLUDE <signal.h>
-INCLUDE "trap.h"
-
-SHELLPROC {
-       char *sm;
-
-       clear_traps();
-       for (sm = sigmode ; sm < sigmode + NSIG - 1; sm++) {
-               if (*sm == S_IGN)
-                       *sm = S_HARD_IGN;
-       }
-}
-#endif
-
-
-
 /*
  * Signal handler.
  */
 
 static void
-onsig(signo)
-       int signo;
+onsig(int signo)
 {
        if (signo == SIGINT && trap[SIGINT] == NULL) {
                onint();
@@ -12628,14 +12084,14 @@ onsig(signo)
 }
 
 
-
 /*
  * Called to execute a trap.  Perhaps we should avoid entering new trap
  * handlers while we are executing a trap handler.
  */
 
 static void
-dotrap() {
+dotrap(void)
+{
        int i;
        int savestatus;
 
@@ -12655,37 +12111,12 @@ done:
        pendingsigs = 0;
 }
 
-
-
-/*
- * Controls whether the shell is interactive or not.
- */
-
-
-static void
-setinteractive(on)
-       int on;
-{
-       static int is_interactive;
-
-       if (on == is_interactive)
-               return;
-       setsignal(SIGINT);
-       setsignal(SIGQUIT);
-       setsignal(SIGTERM);
-       chkmail(1);
-       is_interactive = on;
-}
-
-
-
 /*
  * Called to exit the shell.
  */
 
 static void
-exitshell(status)
-       int status;
+exitshell(int status)
 {
        struct jmploc loc1, loc2;
        char *p;
@@ -12702,9 +12133,9 @@ exitshell(status)
                trap[0] = NULL;
                evalstring(p, 0);
        }
-l1:   handler = &loc2;                 /* probably unnecessary */
+l1:   handler = &loc2;                  /* probably unnecessary */
        flushall();
-#if JOBS
+#ifdef JOBS
        setjobctl(0);
 #endif
 l2:   _exit(status);
@@ -12714,125 +12145,19 @@ l2:   _exit(status);
 static int decode_signal(const char *string, int minsig)
 {
        int signo;
+       const char *name = u_signal_names(string, &signo, minsig);
 
-       if (is_number(string)) {
-               signo = atoi(string);
-               if (signo >= NSIG) {
-                       return -1;
-               }
-               return signo;
-       }
-
-       signo = minsig;
-       if (!signo) {
-               goto zero;
-       }
-       for (; signo < NSIG; signo++) {
-               if (!strcasecmp(string, &(signal_names[signo])[3])) {
-                       return signo;
-               }
-zero:
-               if (!strcasecmp(string, signal_names[signo])) {
-                       return signo;
-               }
-       }
-
-       return -1;
+       return name ? signo : -1;
 }
-/*     $NetBSD: var.c,v 1.27 2001/02/04 19:52:07 christos Exp $        */
-
-#define VTABSIZE 39
-
-
-struct varinit {
-       struct var *var;
-       int flags;
-       const char *text;
-       void (*func) __P((const char *));
-};
-
-struct localvar *localvars;
-
-#if ATTY
-struct var vatty;
-#endif
-struct var vifs;
-struct var vmail;
-struct var vmpath;
-struct var vpath;
-struct var vps1;
-struct var vps2;
-struct var vvers;
-struct var voptind;
-
-static const char defpathvar[] =
-       "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
-#ifdef IFS_BROKEN
-static const char defifsvar[] = "IFS= \t\n";
-#else
-static const char defifs[] = " \t\n";
-#endif
-
-static const struct varinit varinit[] = {
-#if ATTY
-       { &vatty,       VSTRFIXED|VTEXTFIXED|VUNSET,    "ATTY=",
-         NULL },
-#endif
-#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 },
-       { NULL, 0,                              NULL,
-         NULL }
-};
-
-struct var *vartab[VTABSIZE];
 
-static struct var **hashvar __P((const char *));
-static void showvars __P((const char *, int, int));
-static struct var **findvar __P((struct var **, const char *));
+static struct var **hashvar (const char *);
+static void showvars (const char *, int, int);
+static struct var **findvar (struct var **, const char *);
 
 /*
  * Initialize the varable symbol tables and import the environment
  */
 
-#ifdef mkinit
-INCLUDE <unistd.h>
-INCLUDE "output.h"
-INCLUDE "var.h"
-static char **environ;
-INIT {
-       char **envp;
-       char ppid[32];
-
-       initvar();
-       for (envp = environ ; *envp ; envp++) {
-               if (strchr(*envp, '=')) {
-                       setvareq(*envp, VEXPORT|VTEXTFIXED);
-               }
-       }
-
-       fmtstr(ppid, sizeof(ppid), "%d", (int) getppid());
-       setvar("PPID", ppid, 0);
-}
-#endif
-
-
 /*
  * This routine initializes the builtin variables.  It is called when the
  * shell is initialized and again when a shell procedure is spawned.
@@ -12899,7 +12224,7 @@ setvar(name, val, flags)
        namelen = p - name;
        if (isbad)
                error("%.*s: bad variable name", namelen, name);
-       len = namelen + 2;              /* 2 is space for '=' and '\0' */
+       len = namelen + 2;              /* 2 is space for '=' and '\0' */
        if (val == NULL) {
                flags |= VUNSET;
        } else {
@@ -12996,7 +12321,7 @@ listsetvar(mylist)
  * Find the value of a variable.  Returns NULL if not set.
  */
 
-static char *
+static const char *
 lookupvar(name)
        const char *name;
        {
@@ -13014,11 +12339,10 @@ lookupvar(name)
  * Search the environment of a builtin command.
  */
 
-static char *
-bltinlookup(name)
-       const char *name;
+static const char *
+bltinlookup(const char *name)
 {
-       struct strlist *sp;
+       const struct strlist *sp;
 
        for (sp = cmdenviron ; sp ; sp = sp->next) {
                if (varequal(sp->text, name))
@@ -13065,16 +12389,8 @@ environment() {
  * VSTACK set since these are currently allocated on the stack.
  */
 
-#ifdef mkinit
-static void shprocvar __P((void));
-
-SHELLPROC {
-       shprocvar();
-}
-#endif
-
 static void
-shprocvar() {
+shprocvar(void) {
        struct var **vpp;
        struct var *vp, **prev;
 
@@ -13158,6 +12474,8 @@ found:;
  * The "local" command.
  */
 
+/* funcnest nonzero if we are currently evaluating a function */
+
 static int
 localcmd(argc, argv)
        int argc;
@@ -13165,7 +12483,7 @@ localcmd(argc, argv)
 {
        char *name;
 
-       if (! in_function())
+       if (! funcnest)
                error("Not in a function");
        while ((name = *argptr++) != NULL) {
                mklocal(name);
@@ -13193,8 +12511,8 @@ mklocal(name)
        lvp = ckmalloc(sizeof (struct localvar));
        if (name[0] == '-' && name[1] == '\0') {
                char *p;
-               p = ckmalloc(sizeof optlist);
-               lvp->text = memcpy(p, optlist, sizeof optlist);
+               p = ckmalloc(sizeof optet_vals);
+               lvp->text = memcpy(p, optet_vals, sizeof optet_vals);
                vp = NULL;
        } else {
                vpp = hashvar(name);
@@ -13204,7 +12522,7 @@ mklocal(name)
                                setvareq(savestr(name), VSTRFIXED);
                        else
                                setvar(name, NULL, VSTRFIXED);
-                       vp = *vpp;      /* the new variable */
+                       vp = *vpp;      /* the new variable */
                        lvp->text = NULL;
                        lvp->flags = VUNSET;
                } else {
@@ -13234,8 +12552,8 @@ poplocalvars() {
        while ((lvp = localvars) != NULL) {
                localvars = lvp->next;
                vp = lvp->vp;
-               if (vp == NULL) {       /* $- saved */
-                       memcpy(optlist, lvp->text, sizeof optlist);
+               if (vp == NULL) {       /* $- saved */
+                       memcpy(optet_vals, lvp->text, sizeof optet_vals);
                        ckfree(lvp->text);
                } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
                        (void)unsetvar(vp->text);
@@ -13306,9 +12624,8 @@ unsetcmd(argc, argv)
  */
 
 static int
-unsetvar(s)
-       const char *s;
-       {
+unsetvar(const char *s)
+{
        struct var **vpp;
        struct var *vp;
 
@@ -13342,9 +12659,8 @@ unsetvar(s)
  */
 
 static struct var **
-hashvar(p)
-       const char *p;
-       {
+hashvar(const char *p)
+{
        unsigned int hashval;
 
        hashval = ((unsigned char) *p) << 4;
@@ -13362,9 +12678,8 @@ hashvar(p)
  */
 
 static int
-varequal(p, q)
-       const char *p, *q;
-       {
+varequal(const char *p, const char *q)
+{
        while (*p == *q++) {
                if (*p++ == '=')
                        return 1;
@@ -13391,10 +12706,8 @@ showvars(const char *myprefix, int mask, int xor)
                                len = p - vp->text;
                                p = single_quote(p);
 
-                               out1fmt(
-                                       "%s%s%.*s%s\n", myprefix, sep, len,
-                                       vp->text, p
-                               );
+                               printf("%s%s%.*s%s\n", myprefix, sep, len,
+                                       vp->text, p);
                                stunalloc(p);
                        }
                }
@@ -13415,7 +12728,7 @@ findvar(struct var **vpp, const char *name)
 /*
  * Copyright (c) 1999 Herbert Xu <herbert@debian.org>
  * This file contains code for the times builtin.
- * $Id: ash.c,v 1.3 2001/06/28 16:43:57 andersen Exp $
+ * $Id: ash.c,v 1.27 2001/10/19 00:08:17 andersen Exp $
  */
 static int timescmd (int argc, char **argv)
 {
@@ -13435,10 +12748,47 @@ static int timescmd (int argc, char **argv)
        return 0;
 }
 
+#ifdef ASH_MATH_SUPPORT
+/* The let builtin.  */
+int letcmd(int argc, char **argv)
+{
+       int errcode;
+       long result=0;
+       if (argc == 2) {
+               char *tmp, *expression, p[13];
+               expression = strchr(argv[1], '=');
+               if (!expression) {
+                       /* Cannot use 'error()' here, or the return code
+                        * will be incorrect */
+                       out2fmt("sh: let: syntax error: \"%s\"\n", argv[1]);
+                       return 0;
+               }
+               *expression = '\0';
+               tmp = ++expression;
+               result = arith(tmp, &errcode);
+               if (errcode < 0) {
+                       /* Cannot use 'error()' here, or the return code
+                        * will be incorrect */
+                       out2fmt("sh: let: ");
+                       if(errcode == -2)
+                               out2fmt("divide by zero");
+                       else
+                               out2fmt("syntax error: \"%s=%s\"\n", argv[1], expression);
+                       return 0;
+               }
+               snprintf(p, 12, "%ld", result);
+               setvar(argv[1], savestr(p), 0);
+       } else if (argc >= 3)
+               synerror("invalid operand");
+       return !result;
+}
+#endif
+
+
 
 /*-
  * Copyright (c) 1989, 1991, 1993, 1994
- *     The Regents of the University of California.  All rights reserved.
+ *      The Regents of the University of California.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -13452,8 +12802,8 @@ static int timescmd (int argc, char **argv)
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
- * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change 
- *             ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change> 
+ * 3. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ *              ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
  *
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software