Use the correct buffer when calling dirname, improve an error message, and
[oweals/busybox.git] / ash.c
diff --git a/ash.c b/ash.c
index 99460d3d614e5edc3cb11e96bca80196d31576db..53fb56c2c0246382259f2de9a3facee3f7d9ad6e 100644 (file)
--- a/ash.c
+++ b/ash.c
@@ -26,7 +26,7 @@
  * package.
  *
  * Modified by Erik Andersen <andersee@debian.org> and
- * Vladimir Oleynik <vodz@usa.net> to be used in busybox
+ * Vladimir Oleynik <dzo@simtreas.ru> to be used in busybox
  *
  *
  * Original copyright notice is retained at the end of this file.
 /* These defines allow you to adjust the feature set to be compiled
  * into the ash shell.   As a rule, enabling these options will make
  * ash get bigger...   With all of these options off, ash adds about
- * 62k to busybox on an x86 system.*/
-
+ * 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. */
-#define JOBS
+#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
 #define ASH_ALIAS
 
 /* If you need ash to act as a full Posix shell, with full math
- * support, enable this.   This option needs some work, since it
- * doesn't compile right now... */
-#undef ASH_MATH_SUPPORT
-
-/* This shell builtin is used to indicate how the shell would interpret
- * what you give it.  This command is only useful when debugging, and
- * is obsolete anyways.  Adds about 670 bytes... You probably want to
- * leave this disabled. */
-#undef ASH_TYPE
+ * 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
 
 /* 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 320 bytes. */
+ * standalone shell.   Adds about 272 bytes. */
 #undef ASH_CMDCMD
 
-/* This makes a few common apps that are usually part of busybox
- * anyways to be used as builtins.  This may cause these builtins to be
- * a little bit faster, but leaving this disabled will save you 2k. */
-#undef ASH_BBAPPS_AS_BUILTINS
 
 /* Optimize size vs speed as size */
 #define ASH_OPTIMIZE_FOR_SIZE
  * will generate a core dump. */
 #undef DEBUG
 
-
-
 /* These are here to work with glibc -- Don't change these... */
 #undef FNMATCH_BROKEN
 #undef GLOB_BROKEN
-#undef _GNU_SOURCE
 
 #include <assert.h>
+#include <stddef.h>
 #include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <setjmp.h>
 #include <signal.h>
 #include <stdarg.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #define CSPCL 13                /* these terminate a word */
 #define CIGN 14                 /* character should be ignored */
 
-/* Syntax classes for is_ functions */
-#define ISDIGIT 01              /* a digit */
-#define ISUPPER 02              /* an upper case letter */
-#define ISLOWER 04              /* a lower case letter */
-#define ISUNDER 010             /* an underscore */
-#define ISSPECL 020             /* the name of a special parameter */
-
 #define SYNBASE 130
 #define PEOF -130
 
 
 #define TEOF 0
 #define TNL 1
-#define TSEMI 2
-#define TBACKGND 3
-#define TAND 4
-#define TOR 5
-#define TPIPE 6
-#define TLP 7
-#define TRP 8
-#define TENDCASE 9
-#define TENDBQUOTE 10
-#define TREDIR 11
-#define TWORD 12
-#define TASSIGN 13
+#define 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 TEND 29
 
 
-#define BASESYNTAX (basesyntax + SYNBASE)
-#define DQSYNTAX (dqsyntax + SYNBASE)
-#define SQSYNTAX (sqsyntax + SYNBASE)
-#define ARISYNTAX (arisyntax + SYNBASE)
 
 /* control characters in argument strings */
 #define CTLESC '\201'
 #define CTLENDARI '\207'
 #define CTLQUOTEMARK '\210'
 
-#define is_digit(c)     ((((unsigned char)(c)) - '0') <= 9)
-#define is_alpha(c)     (((c) < CTLESC || (c) > CTLENDARI) && isalpha((unsigned char) (c)))
+
+#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))))
-#define is_special(c)   ((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT))
+
+/*
+ * 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')
 
 
  * so we use _setjmp instead.
  */
 
-#if !defined(__GLIBC__)
+#if defined(BSD)
 #define setjmp(jmploc)  _setjmp(jmploc)
 #define longjmp(jmploc, val)    _longjmp(jmploc, val)
 #endif
@@ -662,452 +645,448 @@ struct shparam {
        int optoff;             /* used by getopts */
 };
 
+/*
+ * When commands are first encountered, they are entered in a hash table.
+ * This ensures that a full path search will not have to be done for them
+ * on each invocation.
+ *
+ * We should investigate converting to a linear search, even though that
+ * would make the command name "hash" a misnomer.
+ */
+#define CMDTABLESIZE 31         /* should be prime */
+#define ARB 1                   /* actual size determined at run time */
+
+
+
+struct tblentry {
+       struct tblentry *next;  /* next entry in hash chain */
+       union param param;      /* definition of builtin function */
+       short cmdtype;          /* index identifying command */
+       char rehash;            /* if set, cd done since entry created */
+       char cmdname[ARB];      /* name of command */
+};
+
+
+static struct tblentry *cmdtable[CMDTABLESIZE];
+static int builtinloc = -1;             /* index in path of %builtin, or -1 */
+static int exerrno = 0;                 /* Last exec error */
+
+
+static void tryexec (char *, char **, char **);
+static void printentry (struct tblentry *, int);
+static void clearcmdentry (int);
+static struct tblentry *cmdlookup (const char *, int);
+static void delete_cmd_entry (void);
+static int path_change (const char *, int *);
+
+
 static void flushall (void);
 static void out2fmt (const char *, ...)
     __attribute__((__format__(__printf__,1,2)));
-static void out1fmt (const char *, ...)
-    __attribute__((__format__(__printf__,1,2)));
 static int xwrite (int, const char *, int);
 
-static void outstr (const char *p, FILE *file) { fputs(p, file); }
+static 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); }
 
+#ifndef ASH_OPTIMIZE_FOR_SIZE
 #define out2c(c)        putc((c), stderr)
+#else
+static void out2c(int c)           { putc(c, stderr); }
+#endif
 
-/* syntax table used when not in quotes */
-static const char basesyntax[257] = {
-      CENDFILE,   CSPCL,   CWORD,   CCTL,
-      CCTL,    CCTL,    CCTL,    CCTL,
-      CCTL,    CCTL,    CCTL,    CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CSPCL,
-      CNL,     CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CSPCL,   CWORD,
-      CDQUOTE, CWORD,   CVAR,    CWORD,
-      CSPCL,   CSQUOTE, CSPCL,   CSPCL,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CSPCL,   CSPCL,   CWORD,
-      CSPCL,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CBACK,   CWORD,
-      CWORD,   CWORD,   CBQUOTE, CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CSPCL,   CENDVAR,
-      CWORD
-};
-
-/* syntax table used when in double quotes */
-static const char dqsyntax[257] = {
-      CENDFILE,   CIGN,    CWORD,   CCTL,
-      CCTL,    CCTL,    CCTL,    CCTL,
-      CCTL,    CCTL,    CCTL,    CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CNL,     CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CCTL,
-      CENDQUOTE,CWORD,  CVAR,    CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CCTL,    CWORD,   CWORD,   CCTL,
-      CWORD,   CCTL,    CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CCTL,    CWORD,   CWORD,   CCTL,
-      CWORD,   CCTL,    CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CCTL,    CBACK,   CCTL,
-      CWORD,   CWORD,   CBQUOTE, CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CENDVAR,
-      CCTL
-};
-
-/* syntax table used when in single quotes */
-static const char sqsyntax[257] = {
-      CENDFILE,   CIGN,    CWORD,   CCTL,
-      CCTL,    CCTL,    CCTL,    CCTL,
-      CCTL,    CCTL,    CCTL,    CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CNL,     CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CCTL,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CENDQUOTE,CWORD,  CWORD,
-      CCTL,    CWORD,   CWORD,   CCTL,
-      CWORD,   CCTL,    CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CCTL,    CWORD,   CWORD,   CCTL,
-      CWORD,   CCTL,    CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CCTL,    CCTL,    CCTL,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CCTL
-};
 
-/* syntax table used when in arithmetic */
-static const char arisyntax[257] = {
-      CENDFILE,   CIGN,    CWORD,   CCTL,
-      CCTL,    CCTL,    CCTL,    CCTL,
-      CCTL,    CCTL,    CCTL,    CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CNL,     CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CDQUOTE, CWORD,   CVAR,    CWORD,
-      CWORD,   CSQUOTE, CLP,     CRP,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CBACK,   CWORD,
-      CWORD,   CWORD,   CBQUOTE, CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CWORD,
-      CWORD,   CWORD,   CWORD,   CENDVAR,
-      CWORD
+#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
 };
 
-/* 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
-};
+#ifdef USE_SIT_FUNCTION
 
-/* Array indicating which tokens mark the end of a list */
-static const char tokendlist[] = {
-       1,
-       0,
-       0,
-       0,
-       0,
-       0,
-       0,
-       0,
-       1,
-       1,
-       1,
-       0,
-       0,
-       0,
-       0,
-       0,
-       1,
-       1,
-       1,
-       1,
-       1,
-       1,
-       0,
-       0,
-       0,
-       1,
-       0,
-       0,
-       0,
-       1,
-};
+#define U_C(c) ((unsigned char)(c))
 
-static const char *const tokname[] = {
-       "end of file",
-       "newline",
-       "\";\"",
-       "\"&\"",
-       "\"&&\"",
-       "\"||\"",
-       "\"|\"",
-       "\"(\"",
-       "\")\"",
-       "\";;\"",
-       "\"`\"",
-       "redirection",
-       "word",
-       "assignment",
-       "\"!\"",
-       "\"case\"",
-       "\"do\"",
-       "\"done\"",
-       "\"elif\"",
-       "\"else\"",
-       "\"esac\"",
-       "\"fi\"",
-       "\"for\"",
-       "\"if\"",
-       "\"in\"",
-       "\"then\"",
-       "\"until\"",
-       "\"while\"",
-       "\"{\"",
-       "\"}\"",
+static int SIT(int c, int syntax)
+{
+       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
-
-static const char *const parsekwd[] = {
-       "!",
-       "case",
-       "do",
-       "done",
-       "elif",
-       "else",
-       "esac",
-       "fi",
-       "for",
-       "if",
-       "in",
-       "then",
-       "until",
-       "while",
-       "{",
-       "}"
+       /* 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;
+}
 
 static int plinno = 1;          /* input line number */
 
@@ -1193,7 +1172,7 @@ struct localvar {
 };
 
 
-#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN)
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
 #define rmescapes(p) _rmescapes((p), 0)
 static char *_rmescapes (char *, int);
 #else
@@ -1316,8 +1295,8 @@ static void initvar (void);
 static void setvar (const char *, const char *, int);
 static void setvareq (char *, int);
 static void listsetvar (struct strlist *);
-static char *lookupvar (const char *);
-static char *bltinlookup (const char *);
+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 *);
@@ -1433,7 +1412,7 @@ printalias(const struct alias *ap) {
        char *p;
 
        p = single_quote(ap->val);
-       out1fmt("alias %s=%s\n", ap->name, p);
+       printf("alias %s=%s\n", ap->name, p);
        stunalloc(p);
 }
 
@@ -1539,8 +1518,10 @@ __lookupalias(const char *name) {
 #endif
 
 #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.  */
+/* The generated file arith.c has been replaced with a custom hand
+ * written implementation written by Aaron Lehmann <aaronl@vitelus.com>.
+ * This is now part of libbb, so that it can be used by all the shells
+ * in busybox. */
 #define ARITH_NUM 257
 #define ARITH_LPAREN 258
 #define ARITH_RPAREN 259
@@ -1568,12 +1549,6 @@ __lookupalias(const char *name) {
 #define ARITH_BNOT 281
 
 static void expari (int);
-/* From arith.y */
-static int arith (const char *);
-static int expcmd (int , char **);
-static void arith_lex_reset (void);
-static int yylex (void);
-
 #endif
 
 static char *trap[NSIG];                /* trap handler commands */
@@ -1603,6 +1578,7 @@ 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
@@ -1624,20 +1600,16 @@ static int waitcmd (int, char **);
 static int ulimitcmd (int, char **);
 static int timescmd (int, char **);
 #ifdef ASH_MATH_SUPPORT
-static int expcmd (int, char **);
+static int letcmd (int, char **);
 #endif
-#ifdef ASH_TYPE
 static int typecmd (int, char **);
-#endif
 #ifdef ASH_GETOPTS
 static int getoptscmd (int, char **);
 #endif
 
 #ifndef BB_TRUE_FALSE
-# ifdef ASH_BBAPPS_AS_BUILTINS
 static int true_main (int, char **);
 static int false_main (int, char **);
-# endif
 #endif
 
 static void     setpwd (const char *, int);
@@ -1668,7 +1640,7 @@ struct builtincmd {
  * have been warned.
  */
 static const struct builtincmd builtincmds[] = {
-       { BUILTIN_SPECIAL   ".", dotcmd },
+       { BUILTIN_SPECIAL   ".", dotcmd },    /* first, see declare DOTCMD */
        { BUILTIN_SPECIAL   ":", true_main },
 #ifdef ASH_ALIAS
        { BUILTIN_REG_ASSG  "alias", aliascmd },
@@ -1679,9 +1651,7 @@ static const struct builtincmd builtincmds[] = {
        { BUILTIN_SPECIAL   "break", breakcmd },
        { BUILTIN_SPECIAL   "builtin", bltincmd },
        { BUILTIN_REGULAR   "cd", cdcmd },
-#ifdef ASH_BBAPPS_AS_BUILTINS
        { BUILTIN_NOSPEC    "chdir", cdcmd },
-#endif
 #ifdef ASH_CMDCMD
        { BUILTIN_REGULAR   "command", commandcmd },
 #endif
@@ -1689,13 +1659,8 @@ static const struct builtincmd builtincmds[] = {
        { BUILTIN_SPECIAL   "eval", evalcmd },
        { BUILTIN_SPECIAL   "exec", execcmd },
        { BUILTIN_SPECIAL   "exit", exitcmd },
-#ifdef ASH_MATH_SUPPORT
-       { BUILTIN_NOSPEC    "exp", expcmd },
-#endif
        { BUILTIN_SPEC_ASSG "export", exportcmd },
-#ifdef ASH_BBAPPS_AS_BUILTINS
        { BUILTIN_REGULAR   "false", false_main },
-#endif
        { BUILTIN_REGULAR   "fc", histcmd },
 #ifdef JOBS
        { BUILTIN_REGULAR   "fg", fgcmd },
@@ -1704,12 +1669,13 @@ static const struct builtincmd builtincmds[] = {
        { 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_NOSPEC    "let", expcmd },
+       { BUILTIN_REGULAR    "let", letcmd },
 #endif
        { BUILTIN_ASSIGN    "local", localcmd },
 #ifndef BB_PWD
@@ -1723,12 +1689,8 @@ static const struct builtincmd builtincmds[] = {
        { BUILTIN_SPECIAL   "shift", shiftcmd },
        { BUILTIN_SPECIAL   "times", timescmd },
        { BUILTIN_SPECIAL   "trap", trapcmd },
-#ifdef ASH_BBAPPS_AS_BUILTINS
        { BUILTIN_REGULAR   "true", true_main },
-#endif
-#ifdef ASH_TYPE
        { BUILTIN_NOSPEC    "type", typecmd },
-#endif
        { BUILTIN_NOSPEC    "ulimit", ulimitcmd },
        { BUILTIN_REGULAR   "umask", umaskcmd },
 #ifdef ASH_ALIAS
@@ -1739,7 +1701,7 @@ static const struct builtincmd builtincmds[] = {
 };
 #define NUMBUILTINS  (sizeof (builtincmds) / sizeof (struct builtincmd) )
 
-static const struct builtincmd *DOTCMD = &builtincmds[0];
+#define DOTCMD &builtincmds[0]
 static struct builtincmd *BLTINCMD;
 static struct builtincmd *EXECCMD;
 static struct builtincmd *EVALCMD;
@@ -1794,8 +1756,8 @@ static int jobctl;
 #endif
 static int intreceived;
 
-static struct job *makejob (union node *, int);
-static int forkshell (struct job *, union node *, int);
+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);
@@ -1918,7 +1880,7 @@ docd(dest, print)
        updatepwd(badstat ? NULL : dest);
        INTON;
        if (print && iflag)
-               out1fmt(snlfmt, curdir);
+               printf(snlfmt, curdir);
        return 0;
 }
 
@@ -2007,7 +1969,7 @@ pwdcmd(argc, argv)
        int argc;
        char **argv;
 {
-       out1fmt(snlfmt, curdir);
+       printf(snlfmt, curdir);
        return 0;
 }
 #endif
@@ -2093,6 +2055,7 @@ exraise(int e)
        if (handler == NULL)
                abort();
 #endif
+       flushall();
        exception = e;
        longjmp(handler->loc, 1);
 }
@@ -2154,58 +2117,27 @@ exverror(int cond, const char *msg, va_list ap)
                vfprintf(stderr, msg, ap);
                out2c('\n');
        }
-       flushall();
        exraise(cond);
        /* NOTREACHED */
 }
 
 
-#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 *);
-#endif
        exverror(EXERROR, msg, ap);
        /* NOTREACHED */
        va_end(ap);
 }
 
 
-#ifdef __STDC__
 static void
 exerror(int cond, const char *msg, ...)
-#else
-static void
-exerror(va_alist)
-       va_dcl
-#endif
 {
-#ifndef __STDC__
-       int cond;
-       const char *msg;
-#endif
        va_list ap;
-#ifdef __STDC__
        va_start(ap, msg);
-#else
-       va_start(ap);
-       cond = va_arg(ap, int);
-       msg = va_arg(ap, const char *);
-#endif
        exverror(cond, msg, ap);
        /* NOTREACHED */
        va_end(ap);
@@ -2219,7 +2151,7 @@ exerror(va_alist)
 
 struct errname {
        short errcode;          /* error number */
-       short action;           /* operation which encountered the error */
+       char  action;           /* operation which encountered the error */
 };
 
 /*
@@ -2335,19 +2267,12 @@ 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 evalloop (union node *, int);
-static void evalfor (union node *, int);
-static void evalcase (union node *, int);
-static void evalsubshell (union node *, int);
+static void evalsubshell (const union node *, int);
 static void expredir (union node *);
-static void evalpipe (union node *);
-static void evalcommand (union node *, int);
 static void prehash (union node *);
 static void eprintlist (struct strlist *);
 
@@ -2417,125 +2342,68 @@ evalstring(char *s, int flag)
        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 *);
+
 /*
- * Evaluate a parse tree.  The value is left in the global variable
- * exitstatus.
+ * 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 struct builtincmd *find_builtin (const char *);
-static void defun (char *, union node *);
 
 static void
-evaltree(n, flags)
-       union node *n;
-       int flags;
+freefunc(union node *n)
 {
-       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;
-               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;
-               }
-               defun(n->narg.text, n->narg.next);
-               exitstatus = 0;
-               break;
-       }
-       case NNOT:
-               evaltree(n->nnot.com, EV_TESTED);
-               exitstatus = !exitstatus;
-               break;
+       if (n)
+               ckfree(n);
+}
 
-       case NPIPE:
-               evalpipe(n);
-               checkexit = 1;
-               break;
-       case NCMD:
-               evalcommand(n, flags);
-               checkexit = 1;
-               break;
-#ifdef DEBUG
-       default:
-               out1fmt("Node type = %d\n", n->type);
-               break;
-#endif
+
+/*
+ * 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);
        }
-out:
-       if (pendingsigs)
-               dotrap();
-       if (
-               flags & EV_EXIT ||
-               (checkexit && eflag && exitstatus && !(flags & EV_TESTED))
-       )
-               exitshell(exitstatus);
+       cmdp->cmdtype = entry->cmdtype;
+       cmdp->param = entry->u;
+       INTON;
 }
 
-
-static void
-evalloop(n, flags)
-       union node *n;
-       int flags;
+static inline void
+evalloop(const union node *n, int flags)
 {
        int status;
 
@@ -2568,13 +2436,8 @@ skipping:         if (evalskip == SKIPCONT && --skipcount <= 0) {
        exitstatus = status;
 }
 
-static void expandarg (union node *, struct arglist *, int);
-static void fixredir(union node *n, const char *text, int err);
-
 static void
-evalfor(n, flags)
-    union node *n;
-    int flags;
+evalfor(const union node *n, int flags)
 {
        struct arglist arglist;
        union node *argp;
@@ -2611,11 +2474,8 @@ out:
        popstackmark(&smark);
 }
 
-
-static void
-evalcase(n, flags)
-       union node *n;
-       int flags;
+static inline void
+evalcase(const union node *n, int flags)
 {
        union node *cp;
        union node *patp;
@@ -2640,68 +2500,6 @@ out:
        popstackmark(&smark);
 }
 
-/*
- * Kick off a subshell to evaluate a tree.
- */
-
-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;
-       }
-}
-
-
-/*
- * Compute the names of the files in a redirection list.
- */
-
-static void
-expredir(n)
-       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;
-               }
-       }
-}
-
 /*
  * Evaluate a pipeline.  All the processes in the pipeline are children
  * of the process creating the pipeline.  (This differs from some versions
@@ -2709,7 +2507,7 @@ expredir(n)
  * of all the rest.)
  */
 
-static void
+static inline void
 evalpipe(n)
        union node *n;
 {
@@ -2771,61 +2569,6 @@ evalpipe(n)
        }
 }
 
-
-
-/*
- * Execute a command inside back quotes.  If it's a builtin command, we
- * want to save its output in a block obtained from malloc.  Otherwise
- * we fork off a subprocess and get the output of the command via a pipe.
- * Should be called with interrupts off.
- */
-
-static void
-evalbackcmd(union node *n, struct backcmd *result)
-{
-       int pip[2];
-       struct job *jp;
-       struct stackmark smark;         /* unnecessary */
-
-       setstackmark(&smark);
-       result->fd = -1;
-       result->buf = NULL;
-       result->nleft = 0;
-       result->jp = NULL;
-       if (n == NULL) {
-               exitstatus = 0;
-               goto out;
-       }
-       exitstatus = 0;
-       if (pipe(pip) < 0)
-               error("Pipe call failed");
-       jp = makejob(n, 1);
-       if (forkshell(jp, n, FORK_NOJOB) == 0) {
-               FORCEINTON;
-               close(pip[0]);
-               if (pip[1] != 1) {
-                       close(1);
-                       dup_as_newfd(pip[1], 1);
-                       close(pip[1]);
-               }
-               eflag = 0;
-               evaltree(n, EV_EXIT);
-       }
-       close(pip[1]);
-       result->fd = pip[0];
-       result->jp = jp;
-out:
-       popstackmark(&smark);
-       TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
-               result->fd, result->buf, result->nleft, result->jp));
-}
-
-
-
-/*
- * Execute a simple command.
- */
-
 static void find_command (const char *, struct cmdentry *, int, const char *);
 
 static int
@@ -2839,6 +2582,7 @@ isassignment(const char *word) {
        return *word == '=';
 }
 
+
 static void
 evalcommand(union node *cmd, int flags)
 {
@@ -2890,7 +2634,7 @@ evalcommand(union node *cmd, int flags)
        }
        if (argp) {
                struct builtincmd *bcmd;
-               bool pseudovarflag;
+               int pseudovarflag;
                bcmd = find_builtin(arglist.list->text);
                pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
                for (; argp; argp = argp->narg.next) {
@@ -3142,37 +2886,261 @@ out:
        popstackmark(&smark);
 }
 
-
-
 /*
- * Search for a command.  This is called before we fork so that the
- * location of the command will be available in the parent as well as
- * the child.  The check for "goodname" is an overly conservative
- * check that the name will not be subject to expansion.
+ * Evaluate a parse tree.  The value is left in the global variable
+ * exitstatus.
  */
-
 static void
-prehash(n)
+evaltree(n, flags)
        union node *n;
+       int flags;
 {
-       struct cmdentry entry;
+       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;
 
-       if (n->type == NCMD && n->ncmd.args)
-               if (goodname(n->ncmd.args->narg.text))
-                       find_command(n->ncmd.args->narg.text, &entry, 0,
-                                    pathval());
+       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);
 }
 
-
-
 /*
- * Builtin commands.  Builtin commands whose functions are closely
- * tied to evaluation are implemented here.
+ * 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;
+       }
+}
+
 /*
- * No command given, or a bltin command with no arguments.  Set the
- * specified variables.
+ * Compute the names of the files in a redirection list.
+ */
+
+static void fixredir(union node *n, const char *text, int err);
+
+static void
+expredir(union node *n)
+{
+       union node *redir;
+
+       for (redir = n ; redir ; redir = redir->nfile.next) {
+               struct arglist fn;
+               fn.lastp = &fn.list;
+               oexitstatus = exitstatus;
+               switch (redir->type) {
+               case NFROMTO:
+               case NFROM:
+               case NTO:
+               case NAPPEND:
+               case NTOOV:
+                       expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
+                       redir->nfile.expfname = fn.list->text;
+                       break;
+               case NFROMFD:
+               case NTOFD:
+                       if (redir->ndup.vname) {
+                               expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
+                               fixredir(redir, fn.list->text, 1);
+                       }
+                       break;
+               }
+       }
+}
+
+
+/*
+ * Execute a command inside back quotes.  If it's a builtin command, we
+ * want to save its output in a block obtained from malloc.  Otherwise
+ * we fork off a subprocess and get the output of the command via a pipe.
+ * Should be called with interrupts off.
+ */
+
+static void
+evalbackcmd(union node *n, struct backcmd *result)
+{
+       int pip[2];
+       struct job *jp;
+       struct stackmark smark;         /* unnecessary */
+
+       setstackmark(&smark);
+       result->fd = -1;
+       result->buf = NULL;
+       result->nleft = 0;
+       result->jp = NULL;
+       if (n == NULL) {
+               exitstatus = 0;
+               goto out;
+       }
+       exitstatus = 0;
+       if (pipe(pip) < 0)
+               error("Pipe call failed");
+       jp = makejob(n, 1);
+       if (forkshell(jp, n, FORK_NOJOB) == 0) {
+               FORCEINTON;
+               close(pip[0]);
+               if (pip[1] != 1) {
+                       close(1);
+                       dup_as_newfd(pip[1], 1);
+                       close(pip[1]);
+               }
+               eflag = 0;
+               evaltree(n, EV_EXIT);
+       }
+       close(pip[1]);
+       result->fd = pip[0];
+       result->jp = jp;
+out:
+       popstackmark(&smark);
+       TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
+               result->fd, result->buf, result->nleft, result->jp));
+}
+
+
+/*
+ * Execute a simple command.
+ */
+
+/*
+ * Search for a command.  This is called before we fork so that the
+ * location of the command will be available in the parent as well as
+ * the child.  The check for "goodname" is an overly conservative
+ * check that the name will not be subject to expansion.
+ */
+
+static void
+prehash(n)
+       union node *n;
+{
+       struct cmdentry entry;
+
+       if (n->type == NCMD && n->ncmd.args)
+               if (goodname(n->ncmd.args->narg.text))
+                       find_command(n->ncmd.args->narg.text, &entry, 0,
+                                    pathval());
+}
+
+
+/*
+ * Builtin commands.  Builtin commands whose functions are closely
+ * tied to evaluation are implemented here.
+ */
+
+/*
+ * No command given, or a bltin command with no arguments.  Set the
+ * specified variables.
  */
 
 int
@@ -3242,7 +3210,6 @@ returncmd(argc, argv)
 
 
 #ifndef BB_TRUE_FALSE
-#ifdef ASH_BBAPPS_AS_BUILTINS
 static int
 false_main(argc, argv)
        int argc;
@@ -3260,7 +3227,6 @@ true_main(argc, argv)
        return 0;
 }
 #endif
-#endif
 
 /*
  * Controls whether the shell is interactive or not.
@@ -3274,6 +3240,7 @@ static void
 setinteractive(int on)
 {
        static int is_interactive;
+       static int do_banner=0;
 
        if (on == is_interactive)
                return;
@@ -3282,6 +3249,12 @@ setinteractive(int on)
        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
@@ -3317,43 +3290,6 @@ eprintlist(struct strlist *sp)
                out2fmt(" %s",sp->text);
        }
 }
-/*
- * When commands are first encountered, they are entered in a hash table.
- * This ensures that a full path search will not have to be done for them
- * on each invocation.
- *
- * We should investigate converting to a linear search, even though that
- * would make the command name "hash" a misnomer.
- */
-#define CMDTABLESIZE 31         /* should be prime */
-#define ARB 1                   /* actual size determined at run time */
-
-
-
-struct tblentry {
-       struct tblentry *next;  /* next entry in hash chain */
-       union param param;      /* definition of builtin function */
-       short cmdtype;          /* index identifying command */
-       char rehash;            /* if set, cd done since entry created */
-       char cmdname[ARB];      /* name of command */
-};
-
-
-static struct tblentry *cmdtable[CMDTABLESIZE];
-static int builtinloc = -1;             /* index in path of %builtin, or -1 */
-static int exerrno = 0;                 /* Last exec error */
-
-
-static void tryexec (char *, char **, char **);
-static void printentry (struct tblentry *, int);
-static void clearcmdentry (int);
-static struct tblentry *cmdlookup (const char *, int);
-static void delete_cmd_entry (void);
-#ifdef ASH_TYPE
-static int describe_command (char *, int);
-#endif
-static int path_change (const char *, int *);
-
 
 /*
  * Exec a program.  Never returns.  If you change this routine, you may
@@ -3483,7 +3419,6 @@ initshellproc(void) {
 
 static int preadbuffer(void);
 static void pushfile (void);
-static int preadfd (void);
 
 /*
  * Read a character from the script, returning PEOF on end of file.
@@ -3623,11 +3558,8 @@ setinputfile(const char *fname, int push)
 
 
 static void
-tryexec(cmd, argv, envp)
-       char *cmd;
-       char **argv;
-       char **envp;
-       {
+tryexec(char *cmd, char **argv, char **envp)
+{
        int e;
 
 #ifdef BB_FEATURE_SH_STANDALONE_SHELL
@@ -3641,7 +3573,7 @@ tryexec(cmd, argv, 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++);
+       for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++)
        optind = 1;
        run_applet_by_name(name, argc_l, argv);
 #endif
@@ -3709,6 +3641,26 @@ padvance(const char **path, const char *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 ***/
@@ -3725,14 +3677,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) {
@@ -3746,24 +3701,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;
@@ -3773,6 +3745,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();
@@ -3780,15 +3753,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;
                }
@@ -3797,11 +3772,47 @@ 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.
@@ -3821,7 +3832,7 @@ find_command(const char *name, struct cmdentry *entry, int act, const char *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 */
@@ -4084,17 +4095,6 @@ clearcmdentry(firstchange)
        INTON;
 }
 
-/*
- * Free a parse tree.
- */
-
-static void
-freefunc(union node *n)
-{
-       if (n)
-               ckfree(n);
-}
-
 
 /*
  * Delete all functions.
@@ -4184,194 +4184,77 @@ delete_cmd_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)),
+};
+
+
+
 /*
- * Add a new command entry, replacing any existing command entry for
- * the same name.
+ * Delete a function if it exists.
  */
 
 static void
-addcmdentry(char *name, struct cmdentry *entry)
+unsetfunc(char *name)
 {
        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;
-}
-
-
-/*
- * Define a shell function.
- */
-
-static union node *copyfunc(union node *);
-
-static void
-defun(char *name, union node *func)
-{
-       struct cmdentry entry;
-
-       entry.cmdtype = CMDFUNCTION;
-       entry.u.func = copyfunc(func);
-       addcmdentry(name, &entry);
-}
-
-
-/*
- * Delete a function if it exists.
- */
-
-static void
-unsetfunc(char *name)
-{
-       struct tblentry *cmdp;
-
-       if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
+       if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
                freefunc(cmdp->param.func);
                delete_cmd_entry();
        }
 }
 
-/*
- * Wrapper around strcmp for qsort/bsearch/...
- */
-static int
-pstrcmp(const void *a, const void *b)
-{
-       return strcmp((const char *) a, *(const char *const *) b);
-}
-
-/*
- * Find a keyword is in a sorted array.
- */
-
-static const char *const *
-findkwd(const char *s)
-{
-       return  bsearch(s, parsekwd, sizeof(parsekwd) / sizeof(const char *),
-                       sizeof(const char *), pstrcmp);
-}
 
-#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(char *command, int verbose)
-{
-       struct cmdentry entry;
-       struct tblentry *cmdp;
-#ifdef ASH_ALIAS
-       const struct alias *ap;
-#endif
-       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;
-       }
-
-#ifdef ASH_ALIAS
-       /* 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;
-       }
-#endif
-       /* Then check if it is a tracked alias */
-       if ((cmdp = cmdlookup(command, 0)) != NULL) {
-               entry.cmdtype = cmdp->cmdtype;
-               entry.u = cmdp->param;
-       } else {
-               /* Finally use brute force */
-               find_command(command, &entry, DO_ABS, path);
-       }
-
-       switch (entry.cmdtype) {
-       case CMDNORMAL: {
-               int j = entry.u.index;
-               char *p;
-               if (j == -1) {
-                       p = command;
-               } else {
-                       do {
-                               p = padvance(&path, command);
-                               stunalloc(p);
-                       } while (--j >= 0);
-               }
-               if (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",
-                               IS_BUILTIN_SPECIAL(entry.u.cmd) ?
-                                       "special " : nullstr
-                       );
-               } else {
-                       out1str(command);
-               }
-               break;
-
-       default:
-               if (verbose) {
-                       out1str(": not found\n");
-               }
-               return 127;
-       }
-
-out:
-       putchar('\n');
-       return 0;
-}
-#endif
-
 #ifdef ASH_CMDCMD
 static int
 commandcmd(argc, argv)
@@ -4394,26 +4277,25 @@ commandcmd(argc, argv)
                case 'V':
                        verbose_verify_only = 1;
                        break;
-               default:
-                       out2fmt(
-"command: nextopt returned character code 0%o\n", c);
-                       return EX_SOFTWARE;
                }
 
        if (default_path + verify_only + verbose_verify_only > 1 ||
            !*argptr) {
-                       out2fmt(
-"command [-p] command [arg ...]\n");
-                       out2fmt(
-"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;
 }
@@ -4487,30 +4369,29 @@ 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 char *evalvar (char *, int);
 static int varisset (char *, int);
-static void strtodest (const char *, const 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__) && !defined(FNMATCH_BROKEN)
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
 #define preglob(p) _rmescapes((p), RMESCAPE_ALLOC | RMESCAPE_GLOB)
 #if !defined(GLOB_BROKEN)
 static void addglob (const glob_t *);
 #endif
 #endif
-#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
+#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
 static void expmeta (char *, char *);
 #endif
-#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN))
+#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 (char *, char *, int);
+#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
 static int patmatch2 (char *, char *, int);
 #else
 static int pmatch (char *, char *, int);
@@ -4523,7 +4404,7 @@ static char *cvtnum (int, char *);
  */
 
 /* arg: the document, fd: where to write the expanded version */
-static void
+static inline void
 expandhere(union node *arg, int fd)
 {
        herefd = fd;
@@ -4584,108 +4465,270 @@ expandarg(arg, arglist, flag)
 }
 
 
-
 /*
- * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
- * characters to allow for further processing.  Otherwise treat
- * $@ like $* since no splitting will be performed.
+ * Expand a variable, and return a pointer to the next character in the
+ * input string.
  */
 
-static void
-argstr(p, flag)
+static inline char *
+evalvar(p, flag)
        char *p;
        int flag;
 {
-       char c;
-       int quotes = flag & (EXP_FULL | EXP_CASE);      /* do CTLESC */
-       int firsteq = 1;
+       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);
 
-       if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
-               p = exptilde(p, flag);
-       for (;;) {
-               switch (c = *p++) {
-               case '\0':
-               case CTLENDVAR: /* ??? */
-                       goto breakloop;
-               case CTLQUOTEMARK:
-                       /* "$@" syntax adherence hack */
-                       if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
-                               break;
-                       if ((flag & EXP_FULL) != 0)
-                               STPUTC(c, expdest);
-                       break;
-               case CTLESC:
-                       if (quotes)
-                               STPUTC(c, expdest);
-                       c = *p++;
-                       STPUTC(c, expdest);
-                       break;
-               case CTLVAR:
-                       p = evalvar(p, flag);
-                       break;
-               case CTLBACKQ:
-               case CTLBACKQ|CTLQUOTE:
-                       expbackq(argbackq->n, c & CTLQUOTE, flag);
-                       argbackq = argbackq->next;
-                       break;
-#ifdef ASH_MATH_SUPPORT
-               case CTLENDARI:
-                       expari(flag);
-                       break;
-#endif
-               case ':':
-               case '=':
-                       /*
-                        * sort of a hack - expand tildes in variable
-                        * assignments (after the first '=' and after ':'s).
-                        */
-                       STPUTC(c, expdest);
-                       if (flag & EXP_VARTILDE && *p == '~') {
-                               if (c == '=') {
-                                       if (firsteq)
-                                               firsteq = 0;
-                                       else
-                                               break;
-                               }
-                               p = exptilde(p, flag);
+       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
+                               );
                        }
-                       break;
-               default:
-                       STPUTC(c, expdest);
                }
        }
-breakloop:;
-       return;
-}
 
-static char *
-exptilde(p, flag)
-       char *p;
-       int flag;
-{
-       char c, *startp = p;
-       struct passwd *pw;
-       const char *home;
-       int quotes = flag & (EXP_FULL | EXP_CASE);
+       if (subtype == VSPLUS)
+               set = ! set;
 
-       while ((c = *p) != '\0') {
-               switch(c) {
-               case CTLESC:
-                       return (startp);
-               case CTLQUOTEMARK:
-                       return (startp);
-               case ':':
-                       if (flag & EXP_VARTILDE)
-                               goto done;
-                       break;
-               case '/':
-                       goto done;
-               }
-               p++;
-       }
-done:
-       *p = '\0';
-       if (*(startp+1) == '\0') {
+       easy = ((varflags & VSQUOTE) == 0 ||
+               (*var == '@' && shellparam.nparam != 1));
+
+
+       switch (subtype) {
+       case VSLENGTH:
+               expdest = cvtnum(varlen, expdest);
+               goto record;
+
+       case VSNORMAL:
+               if (!easy)
+                       break;
+record:
+               recordregion(startloc, expdest - stackblock(),
+                            varflags & VSQUOTE);
+               break;
+
+       case VSPLUS:
+       case VSMINUS:
+               if (!set) {
+                       argstr(p, flag);
+                       break;
+               }
+               if (easy)
+                       goto record;
+               break;
+
+       case VSTRIMLEFT:
+       case VSTRIMLEFTMAX:
+       case VSTRIMRIGHT:
+       case VSTRIMRIGHTMAX:
+               if (!set)
+                       break;
+               /*
+                * Terminate the string and start recording the pattern
+                * right after it
+                */
+               STPUTC('\0', expdest);
+               patloc = expdest - stackblock();
+               if (subevalvar(p, NULL, patloc, subtype,
+                              startloc, varflags, quotes) == 0) {
+                       int amount = (expdest - stackblock() - patloc) + 1;
+                       STADJUST(-amount, expdest);
+               }
+               /* Remove any recorded regions beyond start of variable */
+               removerecordregions(startloc);
+               goto record;
+
+       case VSASSIGN:
+       case VSQUESTION:
+               if (!set) {
+                       if (subevalvar(p, var, 0, subtype, startloc,
+                                      varflags, quotes)) {
+                               varflags &= ~VSNUL;
+                               /*
+                                * Remove any recorded regions beyond
+                                * start of variable
+                                */
+                               removerecordregions(startloc);
+                               goto again;
+                       }
+                       break;
+               }
+               if (easy)
+                       goto record;
+               break;
+
+#ifdef DEBUG
+       default:
+               abort();
+#endif
+       }
+
+       if (subtype != VSNORMAL) {      /* skip to end of alternative */
+               int nesting = 1;
+               for (;;) {
+                       if ((c = *p++) == CTLESC)
+                               p++;
+                       else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
+                               if (set)
+                                       argbackq = argbackq->next;
+                       } else if (c == CTLVAR) {
+                               if ((*p++ & VSTYPE) != VSNORMAL)
+                                       nesting++;
+                       } else if (c == CTLENDVAR) {
+                               if (--nesting == 0)
+                                       break;
+                       }
+               }
+       }
+       return p;
+}
+
+
+/*
+ * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
+ * characters to allow for further processing.  Otherwise treat
+ * $@ like $* since no splitting will be performed.
+ */
+
+static void
+argstr(p, flag)
+       char *p;
+       int flag;
+{
+       char c;
+       int quotes = flag & (EXP_FULL | EXP_CASE);      /* do CTLESC */
+       int firsteq = 1;
+
+       if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
+               p = exptilde(p, flag);
+       for (;;) {
+               switch (c = *p++) {
+               case '\0':
+               case CTLENDVAR: /* ??? */
+                       goto breakloop;
+               case CTLQUOTEMARK:
+                       /* "$@" syntax adherence hack */
+                       if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
+                               break;
+                       if ((flag & EXP_FULL) != 0)
+                               STPUTC(c, expdest);
+                       break;
+               case CTLESC:
+                       if (quotes)
+                               STPUTC(c, expdest);
+                       c = *p++;
+                       STPUTC(c, expdest);
+                       break;
+               case CTLVAR:
+                       p = evalvar(p, flag);
+                       break;
+               case CTLBACKQ:
+               case CTLBACKQ|CTLQUOTE:
+                       expbackq(argbackq->n, c & CTLQUOTE, flag);
+                       argbackq = argbackq->next;
+                       break;
+#ifdef ASH_MATH_SUPPORT
+               case CTLENDARI:
+                       expari(flag);
+                       break;
+#endif
+               case ':':
+               case '=':
+                       /*
+                        * sort of a hack - expand tildes in variable
+                        * assignments (after the first '=' and after ':'s).
+                        */
+                       STPUTC(c, expdest);
+                       if (flag & EXP_VARTILDE && *p == '~') {
+                               if (c == '=') {
+                                       if (firsteq)
+                                               firsteq = 0;
+                                       else
+                                               break;
+                               }
+                               p = exptilde(p, flag);
+                       }
+                       break;
+               default:
+                       STPUTC(c, expdest);
+               }
+       }
+breakloop:;
+       return;
+}
+
+static char *
+exptilde(p, flag)
+       char *p;
+       int flag;
+{
+       char c, *startp = p;
+       struct passwd *pw;
+       const char *home;
+       int quotes = flag & (EXP_FULL | EXP_CASE);
+
+       while ((c = *p) != '\0') {
+               switch(c) {
+               case CTLESC:
+                       return (startp);
+               case CTLQUOTEMARK:
+                       return (startp);
+               case ':':
+                       if (flag & EXP_VARTILDE)
+                               goto done;
+                       break;
+               case '/':
+                       goto done;
+               }
+               p++;
+       }
+done:
+       *p = '\0';
+       if (*(startp+1) == '\0') {
                if ((home = lookupvar("HOME")) == NULL)
                        goto lose;
        } else {
@@ -4753,6 +4796,7 @@ static void
 expari(int flag)
 {
        char *p, *start;
+       int errcode;
        int result;
        int begoff;
        int quotes = flag & (EXP_FULL | EXP_CASE);
@@ -4791,7 +4835,13 @@ expari(int flag)
        removerecordregions(begoff);
        if (quotes)
                rmescapes(p+2);
-       result = arith(p+2);
+       result = arith(p+2, &errcode);
+       if (errcode < 0) {
+               if(errcode == -2)
+                       error("divide by zero");
+               else
+                       error("syntax error: \"%s\"\n", p+2);
+       }
        snprintf(p, 12, "%d", result);
 
        while (*p++)
@@ -4824,7 +4874,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;
@@ -4872,7 +4922,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;
@@ -4881,7 +4931,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);
                }
@@ -5037,181 +5087,19 @@ recordright:
 
 
 /*
- * Expand a variable, and return a pointer to the next character in the
- * input string.
+ * Test whether a specialized variable is set.
  */
 
-static char *
-evalvar(p, flag)
-       char *p;
-       int flag;
+static int
+varisset(name, nulok)
+       char *name;
+       int nulok;
 {
-       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 (easy)
-                       goto record;
-               break;
-
-       case VSTRIMLEFT:
-       case VSTRIMLEFTMAX:
-       case VSTRIMRIGHT:
-       case VSTRIMRIGHTMAX:
-               if (!set)
-                       break;
-               /*
-                * Terminate the string and start recording the pattern
-                * right after it
-                */
-               STPUTC('\0', expdest);
-               patloc = expdest - stackblock();
-               if (subevalvar(p, NULL, patloc, subtype,
-                              startloc, varflags, quotes) == 0) {
-                       int amount = (expdest - stackblock() - patloc) + 1;
-                       STADJUST(-amount, expdest);
-               }
-               /* Remove any recorded regions beyond start of variable */
-               removerecordregions(startloc);
-               goto record;
-
-       case VSASSIGN:
-       case VSQUESTION:
-               if (!set) {
-                       if (subevalvar(p, var, 0, subtype, startloc,
-                                      varflags, quotes)) {
-                               varflags &= ~VSNUL;
-                               /*
-                                * Remove any recorded regions beyond
-                                * start of variable
-                                */
-                               removerecordregions(startloc);
-                               goto again;
-                       }
-                       break;
-               }
-               if (easy)
-                       goto record;
-               break;
-
-#ifdef DEBUG
-       default:
-               abort();
-#endif
-       }
-
-       if (subtype != VSNORMAL) {      /* skip to end of alternative */
-               int nesting = 1;
-               for (;;) {
-                       if ((c = *p++) == CTLESC)
-                               p++;
-                       else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
-                               if (set)
-                                       argbackq = argbackq->next;
-                       } else if (c == CTLVAR) {
-                               if ((*p++ & VSTYPE) != VSNORMAL)
-                                       nesting++;
-                       } else if (c == CTLENDVAR) {
-                               if (--nesting == 0)
-                                       break;
-                       }
-               }
-       }
-       return p;
-}
-
-/*
- * Test whether a specialized variable is set.
- */
-
-static int
-varisset(name, nulok)
-       char *name;
-       int nulok;
-{
-       if (*name == '!')
-               return backgndpid != -1;
-       else if (*name == '@' || *name == '*') {
-               if (*shellparam.p == NULL)
-                       return 0;
+       if (*name == '!')
+               return backgndpid != -1;
+       else if (*name == '@' || *name == '*') {
+               if (*shellparam.p == NULL)
+                       return 0;
 
                if (nulok) {
                        char **av;
@@ -5244,13 +5132,10 @@ varisset(name, nulok)
  */
 
 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);
        }
@@ -5261,10 +5146,7 @@ strtodest(p, syntax, quotes)
  */
 
 static void
-varvalue(name, quoted, flags)
-       char *name;
-       int quoted;
-       int flags;
+varvalue(char *name, int quoted, int flags)
 {
        int num;
        char *p;
@@ -5272,7 +5154,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);
 
@@ -5307,7 +5189,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 ; ) {
@@ -5488,7 +5370,7 @@ addfname(const char *name)
  * 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;
@@ -5503,9 +5385,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);
@@ -5642,7 +5524,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;
@@ -5733,7 +5615,7 @@ expmeta(enddir, name)
 
 
 
-#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
@@ -5803,7 +5685,7 @@ 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(char *pattern, char *string, int squoted)
@@ -5961,7 +5843,7 @@ 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(char *str, int flag)
 {
@@ -5978,12 +5860,8 @@ _rmescapes(char *str, int 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) {
@@ -6081,12 +5959,10 @@ static int histcmd(argc, argv)
 }
 
 
-static int whichprompt;         /* 1 == PS1, 2 == PS2 */
-
-
 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 */
 };
 
 static struct redirtab *redirlist;
@@ -6136,10 +6012,8 @@ init(void) {
  * interactive shell and control is returned to the main command loop.
  */
 
-#ifdef ASH_ALIAS
 /* 1 == check for aliases, 2 == also check for assignments */
-static int checkalias;
-#endif
+static int checkalias;  /* also used in no alias mode for check assignments */
 
 static void
 reset(void) {
@@ -6162,9 +6036,7 @@ reset(void) {
       {
              tokpushback = 0;
              checkkwd = 0;
-#ifdef ASH_ALIAS
              checkalias = 0;
-#endif
       }
 
       /* from redir.c: */
@@ -6182,7 +6054,6 @@ reset(void) {
  */
 
 #ifdef BB_FEATURE_COMMAND_EDITING
-unsigned int shell_context;
 static const char * cmdedit_prompt;
 static inline void putprompt(const char *s) {
     cmdedit_prompt = s;
@@ -6219,7 +6090,7 @@ static inline int pgetc2() { return pgetc_macro(); }
  * Read a line from the script.
  */
 
-static char *
+static inline char *
 pfgets(char *line, int len)
 {
        char *p = line;
@@ -6241,7 +6112,7 @@ pfgets(char *line, int len)
        return line;
 }
 
-static int
+static inline int
 preadfd(void)
 {
     int nr;
@@ -6251,23 +6122,17 @@ preadfd(void)
 retry:
 #ifdef BB_FEATURE_COMMAND_EDITING
        {
-           if (parsefile->fd)
-               nr = read(parsefile->fd, buf, BUFSIZ - 1);
+           if (!iflag || parsefile->fd)
+                   nr = safe_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();
+                   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) {
@@ -6426,16 +6291,13 @@ pushstring(char *s, int len, void *ap)
 }
 
 
-
-
 /*
  * Like setinputfile, but takes input from a string.
  */
 
 static void
-setinputstring(string)
-       char *string;
-       {
+setinputstring(char *string)
+{
        INTOFF;
        pushfile();
        parsenextc = string;
@@ -6474,7 +6336,6 @@ static void restartjob (struct job *);
 static void freejob (struct job *);
 static struct job *getjob (const char *);
 static int dowait (int, struct job *);
-static int waitproc (int, int *);
 static void waitonint(int);
 
 
@@ -6491,12 +6352,7 @@ fd0_redirected_p () {
        return fd0_redirected != 0;
 }
 
-static int openredirect (union node *);
-static void dupredirect (union node *, int, char[10 ]);
-static int openhere (union node *);
-static int noclobberopen (const char *);
-
-
+static void dupredirect (const union node *, int, int fd1dup);
 
 #ifdef JOBS
 /*
@@ -6525,7 +6381,7 @@ static void setjobctl(int enable)
                        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;
                        }
@@ -6538,7 +6394,7 @@ static void setjobctl(int enable)
                } while (0);
 #ifdef OLD_TTY_DRIVER
                if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
-                       out2str("sh: need new tty driver to run job cenabletrol; job cenabletrol turned off\n");
+                       out2str("sh: need new tty driver to run job control; job control turned off\n");
                        mflag = 0;
                        return;
                }
@@ -6552,7 +6408,7 @@ static void setjobctl(int enable)
 #else
                tcsetpgrp(2, rootpid);
 #endif
-       } else { /* turning job cenabletrol off */
+       } else { /* turning job control off */
                setpgid(0, initialpgrp);
 #ifdef OLD_TTY_DRIVER
                ioctl(2, TIOCSPGRP, (char *)&initialpgrp);
@@ -6568,78 +6424,6 @@ static void setjobctl(int enable)
 #endif
 
 
-/* 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,
-};
-
-
-
 #ifdef JOBS
 static int
 killcmd(argc, argv)
@@ -6697,18 +6481,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);
@@ -6872,7 +6658,7 @@ showjobs(change)
                        }
                        out1str(s);
                        col += strlen(s);
-                       out1fmt(
+                       printf(
                                "%*c%s\n", 30 - col >= 0 ? 30 - col : 0, ' ',
                                ps->cmd
                        );
@@ -6892,10 +6678,9 @@ showjobs(change)
  */
 
 static void
-freejob(jp)
-       struct job *jp;
-       {
-       struct procstat *ps;
+freejob(struct job *jp)
+{
+       const struct procstat *ps;
        int i;
 
        INTOFF;
@@ -7030,9 +6815,7 @@ currentjob:
  */
 
 static struct job *
-makejob(node, nprocs)
-       union node *node;
-       int nprocs;
+makejob(const union node *node, int nprocs)
 {
        int i;
        struct job *jp;
@@ -7098,10 +6881,12 @@ makejob(node, nprocs)
 
 
 static int
-forkshell(struct job *jp, union node *n, 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";
 
@@ -7177,6 +6962,7 @@ forkshell(struct job *jp, union node *n, int mode)
                }
                return pid;
        }
+#ifdef JOBS
        if (rootshell && mode != FORK_NOJOB && mflag) {
                if (jp == NULL || jp->nprocs == 0)
                        pgrp = pid;
@@ -7184,6 +6970,7 @@ forkshell(struct job *jp, union node *n, int mode)
                        pgrp = jp->ps[0].pid;
                setpgid(pid, pgrp);
        }
+#endif
        if (mode == FORK_BG)
                backgndpid = pid;               /* set $! */
        if (jp) {
@@ -7221,9 +7008,8 @@ forkshell(struct job *jp, union node *n, int mode)
  */
 
 static int
-waitforjob(jp)
-       struct job *jp;
-       {
+waitforjob(struct job *jp)
+{
 #ifdef JOBS
        int mypgrp = getpgrp();
 #endif
@@ -7304,10 +7090,47 @@ waitforjob(jp)
  * 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;
@@ -7397,46 +7220,6 @@ 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.
- *
- */
-
-static int
-waitproc(block, status)
-       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);
-}
 
 /*
  * return 1 if there are stopped jobs, otherwise 0
@@ -7510,23 +7293,235 @@ cmdputs(const char *s)
        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(const union node *n)
 {
-       union node *np;
-       struct nodelist *lp;
        const char *p;
-       int i;
-       char s[2];
 
        if (n == NULL)
                return;
-       switch (n->type) {
-       case NSEMI:
-               cmdtxt(n->nbinary.ch1);
-               cmdputs("; ");
-               cmdtxt(n->nbinary.ch2);
+
+       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;
+       int i;
+       char s[2];
+
+       if (n == NULL)
+               return;
+       switch (n->type) {
+       case NSEMI:
+               cmdtxt(n->nbinary.ch1);
+               cmdputs("; ");
+               cmdtxt(n->nbinary.ch2);
                break;
        case NAND:
                cmdtxt(n->nbinary.ch1);
@@ -7638,7 +7633,7 @@ redir:
                break;
        }
 }
-
+#endif /* CMDTXT_TABLE */
 
 static char *
 commandtext(const union node *n)
@@ -7724,10 +7719,8 @@ extern int etext();
 #endif
 
 static void read_profile (const char *);
-static char *find_dot_file (char *);
 static void cmdloop (int);
 static void options (int);
-static void minus_o (char *, int);
 static void setoption (int, int);
 static void procargs (int, char **);
 
@@ -7741,20 +7734,24 @@ static void procargs (int, char **);
  */
 
 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");
        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
@@ -7769,27 +7766,17 @@ 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);
                }
@@ -7840,14 +7827,14 @@ state2:
 state3:
        state = 4;
        if (sflag == 0 || minusc) {
-               static int sigs[] =  {
+               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++)
@@ -7931,8 +7918,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)
@@ -7941,19 +7928,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();
 }
 
@@ -7986,7 +7969,7 @@ readcmdfile(const char *name)
  */
 
 
-static char *
+static inline char *
 find_dot_file(mybasename)
        char *mybasename;
 {
@@ -8055,6 +8038,7 @@ exitcmd(argc, argv)
        exitshell(exitstatus);
        /* NOTREACHED */
 }
+
 static pointer
 stalloc(int nbytes)
 {
@@ -8255,13 +8239,8 @@ ungrabstackstr(char *s, char *p)
 
 #undef rflag
 
-#ifdef __GLIBC__
-static 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
 
 
@@ -8274,9 +8253,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;
@@ -8298,7 +8275,7 @@ readcmd(argc, argv)
                        rflag = 1;
        }
        if (prompt && isatty(0)) {
-               putprompt(prompt);
+               out2str(prompt);     /* read without cmdedit */
                flushall();
        }
        if (*(ap = argptr) == NULL)
@@ -8365,12 +8342,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;
        }
 
@@ -8381,41 +8366,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')
@@ -8424,17 +8394,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);
                }
        }
@@ -8453,46 +8416,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
@@ -8500,6 +8462,7 @@ ulimitcmd(argc, argv)
        int argc;
        char **argv;
 {
+       static const char unlimited_string[] = "unlimited";
        int     c;
        rlim_t val = 0;
        enum { SOFT = 0x1, HARD = 0x2 }
@@ -8510,25 +8473,59 @@ ulimitcmd(argc, argv)
        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) {
@@ -8536,7 +8533,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;
@@ -8552,48 +8549,42 @@ 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;
-                               out1fmt("%lld\n", (long long) val);
+                               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 (%m)");
-       } else {
-               if (how & SOFT)
-                       val = limit.rlim_cur;
-               else if (how & HARD)
-                       val = limit.rlim_max;
-
-               if (val == RLIM_INFINITY)
-                       out1fmt("unlimited\n");
-               else
-               {
-                       val /= l->factor;
-                       out1fmt("%lld\n", (long long) val);
-               }
+       if (!set) {
+               goto OUTPUT_LIMIT;
        }
+
+       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;
 }
 /*
@@ -8601,10 +8592,8 @@ ulimitcmd(argc, argv)
  */
 
 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;
@@ -8667,49 +8656,28 @@ 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:
+               if (len2 > 1) {
                        *q = '"';
-#ifdef _GNU_SOURCE
-                       *(char *) mempcpy(q + 1, s, len2) = '"';
-#else
                        q += 1 + len2;
                        memcpy(q + 1, s, len2);
                        *q = '"';
-#endif
                        s += len2;
+               } else if (len2 == 1) {
+                       *q++ = '\\';
+                       *q = '\'';
+                       s++;
                }
 
                STADJUST(len1p + len2p, p);
@@ -8732,265 +8700,372 @@ sstrdup(const char *p)
 }
 
 
-/*
- * This file was generated by the mknodes program.
- */
-
 /*
  * Routine for dealing with parsed shell commands.
  */
 
 
-static int     funcblocksize;           /* size of structures in function */
-static int     funcstringsize;          /* size of strings in node */
-static pointer funcblock;              /* block to allocate function from */
-static char   *funcstring;              /* block to allocate strings from */
-
-static const short nodesize[26] = {
-      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)),
+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 */
+};
 
-static void calcsize (union node *);
-static void sizenodelist (struct nodelist *);
-static union node *copynode (union node *);
-static struct nodelist *copynodelist (struct nodelist *);
-static char *nodesavestr (char *);
-
-
-
-/*
- * Make a copy of a parse tree.
- */
+#if NODE_CHARPTR != NODE_MBRMASK
+#error NODE_CHARPTR != NODE_MBRMASK!!!
+#endif
+#endif /* defined(CALCSIZE_TABLE) || defined(COPYNODE_TABLE) */
 
+#ifdef COPYNODE_TABLE
 static union node *
-copyfunc(union node *n)
+copynode(const 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);
-}
+      union node *new;
+         const unsigned char *p;
 
-
-
-static void
-calcsize(n)
-       union node *n;
+      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;
@@ -9008,26 +9083,17 @@ 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
 }
 
 #ifdef ASH_GETOPTS
@@ -9090,9 +9156,29 @@ procargs(argc, argv)
  * to the argument list; we advance it past the options.
  */
 
+static inline void
+minus_o(const char *name, int val)
+{
+       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);
+       }
+}
+
+
 static void
-options(cmdline)
-       int cmdline;
+options(int cmdline)
 {
        char *p;
        int val;
@@ -9145,28 +9231,6 @@ 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", 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);
-       }
-}
-
 
 static void
 setoption(int flag, int val)
@@ -9469,7 +9533,6 @@ out:
        if (err) {
                *myoptind = 1;
                *optoff = -1;
-               flushall();
                exraise(EXERROR);
        }
        return done;
@@ -9488,9 +9551,8 @@ out:
  */
 
 static int
-nextopt(optstring)
-       const char *optstring;
-       {
+nextopt(const char *optstring)
+{
        char *p;
        const char *q;
        char c;
@@ -9537,16 +9599,6 @@ out2fmt(const char *fmt, ...)
        va_end(ap);
 }
 
-
-static void
-out1fmt(const char *fmt, ...)
-{
-       va_list ap;
-       va_start(ap, fmt);
-       vfprintf(stdout, fmt, ap);
-       va_end(ap);
-}
-
 /*
  * Version of write which resumes after a signal is caught.
  */
@@ -9602,7 +9654,7 @@ static char *wordtext;                  /* text of last word returned by readtok
 
 static struct nodelist *backquotelist;
 static union node *redirnode;
-struct heredoc *heredoc;
+static struct heredoc *heredoc;
 static int quoteflag;                   /* set if (part of) last token was quoted */
 static int startlinno;                  /* line # where last token started */
 
@@ -9614,10 +9666,10 @@ static union node *command (void);
 static union node *simplecmd (void);
 static void parsefname (void);
 static void parseheredoc (void);
-static int peektoken (void);
+static char peektoken (void);
 static int readtoken (void);
 static int xxreadtoken (void);
-static int readtoken1 (int, char const *, char *, int);
+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));
@@ -9659,7 +9711,7 @@ list(nlflag)
        int tok;
 
        checkkwd = 2;
-       if (nlflag == 0 && tokendlist[peektoken()])
+       if (nlflag == 0 && peektoken())
                return NULL;
        n1 = NULL;
        for (;;) {
@@ -9702,7 +9754,7 @@ list(nlflag)
                                tokpushback++;
                        }
                        checkkwd = 2;
-                       if (tokendlist[peektoken()])
+                       if (peektoken())
                                return n1;
                        break;
                case TEOF:
@@ -9840,7 +9892,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);
@@ -10019,9 +10071,7 @@ simplecmd() {
        redir = NULL;
        rpp = &redir;
 
-#ifdef ASH_ALIAS
        checkalias = 2;
-#endif
        for (;;) {
                switch (readtoken()) {
                case TWORD:
@@ -10171,21 +10221,22 @@ parseheredoc() {
        }
 }
 
-static int
+static char
 peektoken() {
        int t;
 
        t = readtoken();
        tokpushback++;
-       return (t);
+       return tokname_array[t][0];
 }
 
 static int
 readtoken() {
        int t;
+
 #ifdef ASH_ALIAS
-       int savecheckkwd = checkkwd;
        int savecheckalias = checkalias;
+       int savecheckkwd = checkkwd;
        struct alias *ap;
 #endif
 
@@ -10223,20 +10274,21 @@ top:
                        const char *const *pp;
 
                        if ((pp = findkwd(wordtext))) {
-                               lasttoken = t = pp - parsekwd + KWDOFFSET;
-                               TRACE(("keyword %s recognized\n", tokname[t]));
+                               lasttoken = t = pp - tokname_array;
+                               TRACE(("keyword %s recognized\n", tokname(t)));
                                goto out;
                        }
                }
        }
 
-#ifdef ASH_ALIAS
+
        if (t != TWORD) {
                if (t != TREDIR) {
                        checkalias = 0;
                }
        } 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) {
@@ -10246,14 +10298,14 @@ top:
                        goto top;
                }
                checkalias = 0;
-       }
 #endif
+       }
 out:
 #ifdef DEBUG
        if (!alreadyseen)
-           TRACE(("token %s %s\n", tokname[t], t == TWORD || t == TASSIGN ? wordtext : ""));
+           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 : ""));
+           TRACE(("reread token %s %s\n", tokname(t), t == TWORD || t == TASSIGN ? wordtext : ""));
 #endif
        return (t);
 }
@@ -10371,12 +10423,8 @@ breakloop:
 #define PARSEARITH()    {goto parsearith; parsearith_return:;}
 
 static int
-readtoken1(firstc, syntax, eofmark, striptabs)
-       int firstc;
-       char const *syntax;
-       char *eofmark;
-       int striptabs;
-       {
+readtoken1(int firstc, int syntax, const char *eofmark, int striptabs)
+{
        int c = firstc;
        char *out;
        int len;
@@ -10389,7 +10437,7 @@ readtoken1(firstc, syntax, eofmark, striptabs)
        int parenlevel; /* levels of parens in arithmetic */
        int dqvarnest;  /* levels of variables expansion within double quotes */
        int oldstyle;
-       char const *prevsyntax; /* syntax before arithmetic */
+       int prevsyntax; /* syntax before arithmetic */
 #if __GNUC__
        /* Avoid longjmp clobbering */
        (void) &out;
@@ -10420,7 +10468,7 @@ readtoken1(firstc, syntax, eofmark, striptabs)
                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]) {
+                       switch(SIT(c,syntax)) {
                        case CNL:       /* '\n' */
                                if (syntax == BASESYNTAX)
                                        goto endword;   /* exit outer loop */
@@ -10455,7 +10503,7 @@ readtoken1(firstc, syntax, eofmark, striptabs)
                                        if (dblquote && c != '\\' && c != '`' && c != '$'
                                                         && (c != '"' || eofmark != NULL))
                                                USTPUTC('\\', out);
-                                       if (SQSYNTAX[c] == CCTL)
+                                       if (SIT(c,SQSYNTAX) == CCTL)
                                                USTPUTC(CTLESC, out);
                                        else if (eofmark == NULL)
                                                USTPUTC(CTLQUOTEMARK, out);
@@ -10606,7 +10654,7 @@ checkend: {
                }
                if (c == *eofmark) {
                        if (pfgets(line, sizeof line) != NULL) {
-                               char *p, *q;
+                               const char *p, *q;
 
                                p = line;
                                for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
@@ -10996,7 +11044,7 @@ noexpand(text)
                        continue;
                if (c == CTLESC)
                        p++;
-               else if (BASESYNTAX[(int)c] == CCTL)
+               else if (SIT(c,BASESYNTAX) == CCTL)
                        return 0;
        }
        return 1;
@@ -11035,13 +11083,11 @@ synexpect(token)
        int token;
 {
        char msg[64];
+       int l;
 
-       if (token >= 0) {
-               snprintf(msg, 64, "%s unexpected (expecting %s)",
-                       tokname[lasttoken], tokname[token]);
-       } else {
-               snprintf(msg, 64, "%s unexpected", tokname[lasttoken]);
-       }
+       l = sprintf(msg, "%s unexpected", tokname(lasttoken));
+       if (token >= 0)
+               sprintf(msg+l, " (expecting %s)", tokname(token));
        synerror(msg);
        /* NOTREACHED */
 }
@@ -11062,26 +11108,21 @@ synerror(const char *msg)
  * called by editline -- any expansions to the prompt
  *    should be added here.
  */
-static inline const char *
-getprompt(void *unused)
+static void
+setprompt(int whichprompt)
 {
-       switch (whichprompt) {
-       case 0:
-               return "";
+    char *prompt;
+    switch (whichprompt) {
        case 1:
-               return ps1val();
+               prompt = ps1val();
+               break;
        case 2:
-               return ps2val();
-       default:
-               return "<internal prompt error>";
-       }
-}
-
-static void
-setprompt(int which)
-{
-    whichprompt = which;
-    putprompt(getprompt(NULL));
+               prompt = ps2val();
+               break;
+       default:                /* 0 */
+               prompt = "";
+    }
+    putprompt(prompt);
 }
 
 
@@ -11097,90 +11138,109 @@ setprompt(int which)
 #endif
 
 
-
 /*
- * 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.
+ * Open a file in noclobber mode.
+ * The code was copied from bash.
  */
+static inline int
+noclobberopen(const char *fname)
+{
+       int r, fd;
+       struct stat finfo, finfo2;
 
-static void
-redirect(redir, flags)
-       union node *redir;
-       int flags;
-       {
-       union node *n;
-       struct redirtab *sv = NULL;
-       int i;
-       int fd;
-       int newfd;
-       int try;
-       char memory[10];        /* file descriptors to write to memory */
-
-       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;
+       /*
+        * If the file exists and is a regular file, return an error
+        * immediately.
+        */
+       r = stat(fname, &finfo);
+       if (r == 0 && S_ISREG(finfo.st_mode)) {
+               errno = EEXIST;
+               return -1;
        }
-       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;
-               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, memory);
-                                               try++;
-                                               break;
-                                       }
-                                       /* FALLTHROUGH*/
-                               default:
-                                       if (newfd >= 0) {
-                                               close(newfd);
-                                       }
-                                       INTON;
-                                       error("%d: %m", fd);
-                                       /* NOTREACHED */
-                               }
-                       }
-                       if (!try) {
-                               close(fd);
-                               if (flags & REDIR_PUSH) {
-                                       sv->renamed[fd] = i;
-                               }
-                       }
-               } else if (fd != newfd) {
-                       close(fd);
+       /*
+        * If the file was not present (r != 0), make sure we open it
+        * exclusively so that if it is created before we open it, our open
+        * will fail.  Make sure that we do not truncate an existing file.
+        * Note that we don't turn on O_EXCL unless the stat failed -- if the
+        * file was not a regular file, we leave O_EXCL off.
+        */
+       if (r != 0)
+               return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
+       fd = open(fname, O_WRONLY|O_CREAT, 0666);
+
+       /* If the open failed, return the file descriptor right away. */
+       if (fd < 0)
+               return fd;
+
+       /*
+        * OK, the open succeeded, but the file may have been changed from a
+        * non-regular file to a regular file between the stat and the open.
+        * We are assuming that the O_EXCL open handles the case where FILENAME
+        * did not exist and is symlinked to an existing file between the stat
+        * and open.
+        */
+
+       /*
+        * If we can open it and fstat the file descriptor, and neither check
+        * revealed that it was a regular file, and the file has not been
+        * replaced, return the file descriptor.
+        */
+        if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
+            finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
+               return fd;
+
+       /* The file has been replaced.  badness. */
+       close(fd);
+       errno = EEXIST;
+       return -1;
+}
+
+/*
+ * Handle here documents.  Normally we fork off a process to write the
+ * data to a pipe.  If the document is short, we can stuff the data in
+ * the pipe without forking.
+ */
+
+static inline int
+openhere(const union node *redir)
+{
+       int pip[2];
+       int len = 0;
+
+       if (pipe(pip) < 0)
+               error("Pipe call failed");
+       if (redir->type == NHERE) {
+               len = strlen(redir->nhere.doc->narg.text);
+               if (len <= PIPESIZE) {
+                       xwrite(pip[1], redir->nhere.doc->narg.text, len);
+                       goto out;
                }
-               if (fd == 0)
-                       fd0_redirected++;
-               if (!try)
-                       dupredirect(n, newfd, memory);
-               INTON;
        }
+       if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
+               close(pip[0]);
+               signal(SIGINT, SIG_IGN);
+               signal(SIGQUIT, SIG_IGN);
+               signal(SIGHUP, SIG_IGN);
+#ifdef SIGTSTP
+               signal(SIGTSTP, SIG_IGN);
+#endif
+               signal(SIGPIPE, SIG_DFL);
+               if (redir->type == NHERE)
+                       xwrite(pip[1], redir->nhere.doc->narg.text, len);
+               else
+                       expandhere(redir->nhere.doc, pip[1]);
+               _exit(0);
+       }
+out:
+       close(pip[1]);
+       return pip[0];
 }
 
 
-static int
-openredirect(redir)
-       union node *redir;
-       {
+static inline int
+openredirect(const union node *redir)
+{
        char *fname;
        int f;
 
@@ -11248,73 +11308,104 @@ eopen:
 }
 
 
+/*
+ * 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
-dupredirect(union node *redir, int f, char memory[10])
+redirect(union node *redir, int flags)
 {
-       int fd = redir->nfile.fd;
+       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 */
 
-       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 (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 */
 
-       if (f != fd) {
-               dup_as_newfd(f, fd);
-               close(f);
+               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 (!try) {
+                               close(fd);
+                               if (flags & REDIR_PUSH) {
+                                       sv->renamed[fd] = i;
+                               }
+                       }
+               } else if (fd != newfd) {
+                       close(fd);
+               }
+               if (fd == 0)
+                       fd0_redirected++;
+               if (!try)
+                       dupredirect(n, newfd, fd1dup);
+               INTON;
        }
-       return;
 }
 
-
-/*
- * Handle here documents.  Normally we fork off a process to write the
- * data to a pipe.  If the document is short, we can stuff the data in
- * the pipe without forking.
- */
-
-static int
-openhere(redir)
-       union node *redir;
-       {
-       int pip[2];
-       int len = 0;
-
-       if (pipe(pip) < 0)
-               error("Pipe call failed");
-       if (redir->type == NHERE) {
-               len = strlen(redir->nhere.doc->narg.text);
-               if (len <= PIPESIZE) {
-                       xwrite(pip[1], redir->nhere.doc->narg.text, len);
-                       goto out;
-               }
-       }
-       if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
-               close(pip[0]);
-               signal(SIGINT, SIG_IGN);
-               signal(SIGQUIT, SIG_IGN);
-               signal(SIGHUP, SIG_IGN);
-#ifdef SIGTSTP
-               signal(SIGTSTP, SIG_IGN);
-#endif
-               signal(SIGPIPE, SIG_DFL);
-               if (redir->type == NHERE)
-                       xwrite(pip[1], redir->nhere.doc->narg.text, len);
-               else
-                       expandhere(redir->nhere.doc, pip[1]);
-               _exit(0);
+
+static void
+dupredirect(const union node *redir, int f, int fd1dup)
+{
+       int fd = redir->nfile.fd;
+
+       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;
        }
-out:
-       close(pip[1]);
-       return pip[0];
+
+       if (f != fd) {
+               dup_as_newfd(f, fd);
+               close(f);
+       }
+       return;
 }
 
 
+
 /*
  * Undo the effects of the last redirection.
  */
@@ -11385,488 +11476,6 @@ dup_as_newfd(from, to)
        return newfd;
 }
 
-/*
- * Open a file in noclobber mode.
- * The code was copied from bash.
- */
-static int
-noclobberopen(const char *fname)
-{
-       int r, fd;
-       struct stat finfo, finfo2;
-
-       /*
-        * If the file exists and is a regular file, return an error
-        * immediately.
-        */
-       r = stat(fname, &finfo);
-       if (r == 0 && S_ISREG(finfo.st_mode)) {
-               errno = EEXIST;
-               return -1;
-       }
-
-       /*
-        * If the file was not present (r != 0), make sure we open it
-        * exclusively so that if it is created before we open it, our open
-        * will fail.  Make sure that we do not truncate an existing file.
-        * Note that we don't turn on O_EXCL unless the stat failed -- if the
-        * file was not a regular file, we leave O_EXCL off.
-        */
-       if (r != 0)
-               return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
-       fd = open(fname, O_WRONLY|O_CREAT, 0666);
-
-       /* If the open failed, return the file descriptor right away. */
-       if (fd < 0)
-               return fd;
-
-       /*
-        * OK, the open succeeded, but the file may have been changed from a
-        * non-regular file to a regular file between the stat and the open.
-        * We are assuming that the O_EXCL open handles the case where FILENAME
-        * did not exist and is symlinked to an existing file between the stat
-        * and open.
-        */
-
-       /*
-        * If we can open it and fstat the file descriptor, and neither check
-        * revealed that it was a regular file, and the file has not been
-        * replaced, return the file descriptor.
-        */
-        if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) &&
-            finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
-               return fd;
-
-       /* The file has been replaced.  badness. */
-       close(fd);
-       errno = EEXIST;
-       return -1;
-}
-/*#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 (BITCMD *, int, int, int, u_int);
-static void      compress_mode (BITCMD *);
-#ifdef SETMODE_DEBUG
-static void      dumpmode (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).
- */
-static mode_t
-getmode(bbox, omode)
-       const void *bbox;
-       mode_t omode;
-{
-       const BITCMD *set;
-       mode_t clrval, newmode, value;
-
-       _DIAGASSERT(bbox != NULL);
-
-       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;
-
-               case '+':
-                       newmode |= set->bits;
-                       break;
-
-               case '-':
-                       newmode &= ~set->bits;
-                       break;
-
-               case 'X':
-                       if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
-                               newmode |= set->bits;
-                       break;
-
-               case '\0':
-               default:
-#ifdef SETMODE_DEBUG
-                       (void)printf("getmode:%04o -> %04o\n", omode, newmode);
-#endif
-                       return (newmode);
-               }
-}
-
-#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)
-
-static void *
-setmode(p)
-       const char *p;
-{
-       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);
-
-       /*
-        * 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);
-       }
-
-       /*
-        * 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;
-                               }
-                               if (permXbits) {
-                                       ADDCMD('X', who, permXbits, mask);
-                                       permXbits = 0;
-                               }
-                               goto apply;
-                       }
-               }
-
-apply:          if (!*p)
-                       break;
-               if (*p != ',')
-                       goto getop;
-               ++p;
-       }
-       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++;
-
-               op = '+';
-               /* FALLTHROUGH */
-       case '+':
-       case '-':
-       case 'X':
-               set->cmd = op;
-               set->bits = (who ? who : mask) & oparg;
-               break;
-
-       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;
-       }
-       return (set + 1);
-}
-
-#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.
- */
-static void
-compress_mode(set)
-       BITCMD *set;
-{
-       BITCMD *nset;
-       int setbits, clrbits, Xbits, op;
-
-       _DIAGASSERT(set != NULL);
-
-       for (nset = set;;) {
-               /* Copy over any 'u', 'g' and 'o' commands. */
-               while ((op = nset->cmd) != '+' && op != '-' && op != 'X') {
-                       *set++ = *nset++;
-                       if (!op)
-                               return;
-               }
-
-               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++;
-               }
-       }
-}
 #ifdef DEBUG
 static void shtree (union node *, int, char *, FILE*);
 static void shcmd (union node *, FILE *);
@@ -11959,6 +11568,16 @@ 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;
@@ -11969,6 +11588,7 @@ shcmd(cmd, fp)
                        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);
@@ -12118,13 +11738,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'))
@@ -12259,11 +11873,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);
                        }
                }
@@ -12372,16 +11990,8 @@ setsignal(int signo)
        }
        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);
@@ -12480,30 +12090,11 @@ 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)) {
-               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;
 }
+
 static struct var **hashvar (const char *);
 static void showvars (const char *, int, int);
 static struct var **findvar (struct var **, const char *);
@@ -12675,7 +12266,7 @@ listsetvar(mylist)
  * Find the value of a variable.  Returns NULL if not set.
  */
 
-static char *
+static const char *
 lookupvar(name)
        const char *name;
        {
@@ -12693,11 +12284,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))
@@ -12979,9 +12569,8 @@ unsetcmd(argc, argv)
  */
 
 static int
-unsetvar(s)
-       const char *s;
-       {
+unsetvar(const char *s)
+{
        struct var **vpp;
        struct var *vp;
 
@@ -13015,9 +12604,8 @@ unsetvar(s)
  */
 
 static struct var **
-hashvar(p)
-       const char *p;
-       {
+hashvar(const char *p)
+{
        unsigned int hashval;
 
        hashval = ((unsigned char) *p) << 4;
@@ -13035,9 +12623,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;
@@ -13064,10 +12651,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);
                        }
                }
@@ -13088,7 +12673,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.6 2001/07/06 04:26:23 andersen Exp $
+ * $Id: ash.c,v 1.22 2001/08/12 17:32:56 mjn3 Exp $
  */
 static int timescmd (int argc, char **argv)
 {
@@ -13108,6 +12693,43 @@ 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