vodz' latest update to ash.c
[oweals/busybox.git] / shell / ash.c
index e7f3516083dc8d35bc3a2d6893d1ef1a1ec96ffb..5756c6304aaef5c9d5c58e8fc46b68a02b7bd21e 100644 (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.  
+ * 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
  * 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
-
 /* Getopts is used by shell procedures to parse positional parameters.
  * You probably want to leave this disabled, and use the busybox getopt
  * applet if you want to do this sort of thing.  There are some scripts
 
 /* 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
 
 /* Enable this to compile in extra debugging noise.  When debugging is
  * on, debugging info will be written to $HOME/trace and a quit signal
  * will generate a core dump. */
 #undef DEBUG
 
-
-
 /* These are here to work with glibc -- Don't change these... */
 #undef FNMATCH_BROKEN
 #undef GLOB_BROKEN
 #include <setjmp.h>
 #include <signal.h>
 #include <stdarg.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include "busybox.h"
 #include "cmdedit.h"
 
-/* if BB_PWD is defined, then disable ASH_PWD to save space */
-#ifdef BB_PWD
-#undef ASH_PWD
-#endif
-
-
 /*
  * This file was generated by the mksyntax program.
  */
 #define CTLENDARI '\207'
 #define CTLQUOTEMARK '\210'
 
-#define is_digit(c)     ((((unsigned char)(c)) - '0') <= 9)
+#define is_digit(c)     ((c)>='0' && (c)<='9')
 #define is_alpha(c)     (((c) < CTLESC || (c) > CTLENDARI) && isalpha((unsigned char) (c)))
 #define is_name(c)      (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalpha((unsigned char) (c))))
 #define is_in_name(c)   (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalnum((unsigned char) (c))))
 
 #define _DIAGASSERT(x)
 
-#define ATABSIZE 39
+
 
 #define S_DFL 1                 /* default signal handling (SIG_DFL) */
 #define S_CATCH 2               /* signal is caught */
 
 /* flags passed to redirect */
 #define REDIR_PUSH 01           /* save previous values of file descriptors */
-#define REDIR_BACKQ 02          /* save the command output in memory */
+#define REDIR_BACKQ 02          /* save the command output to pipe */
 
 /*
  * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
  * so we use _setjmp instead.
  */
 
-#if !defined(__GLIBC__)
+#if defined(BSD)
 #define setjmp(jmploc)  _setjmp(jmploc)
 #define longjmp(jmploc, val)    _longjmp(jmploc, val)
 #endif
@@ -302,13 +285,16 @@ static volatile int suppressint;
 static volatile int intpending;
 
 #define INTOFF suppressint++
-#ifdef ASH_BBAPPS_AS_BUILTINS
+#ifndef ASH_OPTIMIZE_FOR_SIZE
 #define INTON { if (--suppressint == 0 && intpending) onint(); }
+#define FORCEINTON {suppressint = 0; if (intpending) onint();}
 #else
 static void __inton (void);
+static void forceinton (void);
 #define INTON __inton()
+#define FORCEINTON forceinton()
 #endif
-#define FORCEINTON {suppressint = 0; if (intpending) onint();}
+
 #define CLEAR_PENDING_INT intpending = 0
 #define int_pending() intpending
 
@@ -326,6 +312,7 @@ static pointer stalloc (int);
 static void stunalloc (pointer);
 static void ungrabstackstr (char *, char *);
 static char * growstackstr(void);
+static char * makestrspace(size_t newlen);
 static char *sstrdup (const char *);
 
 /*
@@ -357,10 +344,13 @@ static int stacknleft = MINSIZE;
 #define stackblock() stacknxt
 #define stackblocksize() stacknleft
 #define STARTSTACKSTR(p)        p = stackblock(), sstrnleft = stackblocksize()
+
 #define STPUTC(c, p)    (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c)))
 #define CHECKSTRSPACE(n, p)     { if (sstrnleft < n) p = makestrspace(n); }
-#define USTPUTC(c, p)   (--sstrnleft, *p++ = (c))
 #define STACKSTRNUL(p)  (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0'))
+
+
+#define USTPUTC(c, p)   (--sstrnleft, *p++ = (c))
 #define STUNPUTC(p)     (++sstrnleft, --p)
 #define STTOPC(p)       p[-1]
 #define STADJUST(amount, p)     (p += (amount), sstrnleft -= (amount))
@@ -368,7 +358,6 @@ static int stacknleft = MINSIZE;
 
 #define ckfree(p)       free((pointer)(p))
 
-static char * makestrspace(size_t newlen);
 
 #ifdef DEBUG
 #define TRACE(param)    trace param
@@ -659,71 +648,55 @@ struct shparam {
        int optoff;             /* used by getopts */
 };
 
-struct output {
-#ifdef USE_GLIBC_STDIO
-       FILE *stream;
-#endif
-       char *nextc;
-       int nleft;
-       char *buf;
-       int bufsize;
-       int fd;
-       short flags;
+/*
+ * 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 */
 };
 
-#define OUTBUFSIZ BUFSIZ
-#define MEM_OUT -3              /* output to dynamically allocated memory */
 
+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 *);
 
-#ifdef USE_GLIBC_STDIO
-static struct output output = {NULL, NULL, 0, NULL, 0, 1, 0};
-static struct output errout = {NULL, NULL, 0, NULL, 0, 2, 0};
-static struct output memout = {NULL, NULL, 0, NULL, 0, MEM_OUT, 0};
-#else
-static struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0};
-static struct output errout = {NULL, 0, NULL, 0, 2, 0};
-static struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0};
-#endif
-static struct output *out1 = &output;
-static struct output *out2 = &errout;
 
-#ifndef USE_GLIBC_STDIO
-static void outcslow (char, struct output *);
-#endif
 static void flushall (void);
-static void flushout (struct output *);
-static void freestdout (void);
-static void outfmt (struct output *, const char *, ...)
-    __attribute__((__format__(__printf__,2,3)));
-static void out1fmt (const char *, ...)
+static void out2fmt (const char *, ...)
     __attribute__((__format__(__printf__,1,2)));
-static void fmtstr (char *, size_t, const char *, ...)
-    __attribute__((__format__(__printf__,3,4)));
-#ifndef USE_GLIBC_STDIO
-static void doformat (struct output *, const char *, va_list);
-#endif
 static int xwrite (int, const char *, int);
-#ifdef USE_GLIBC_STDIO
-static void initstreams (void);
-static void openmemout (void);
-static int __closememout (void);
-#endif
 
-static void outstr(const char *p, struct output *file);
+static void outstr (const char *p, FILE *file) { fputs(p, file); }
+static void out1str(const char *p) { outstr(p, stdout); }
+static void out2str(const char *p) { outstr(p, stderr); }
 
-#define OUTPUT_ERR 01           /* error occurred on output */
-
-#ifdef USE_GLIBC_STDIO
-#define outc(c, o)      putc((c), (o)->stream)
-#define doformat(d, f, a)       vfprintf((d)->stream, (f), (a))
+#ifndef ASH_OPTIMIZE_FOR_SIZE
+#define out2c(c)        putc((c), stderr)
 #else
-#define outc(c, file)   (--(file)->nleft < 0? outcslow((c), (file)) : (*(file)->nextc = (c), (file)->nextc++))
+static void out2c(int c)           { putc(c, stderr); }
 #endif
-#define out1c(c)        outc((c), out1)
-#define out2c(c)        outc((c), out2)
-#define out1str(s)      outstr((s), out1)
-#define out2str(s)      outstr((s), out2)
-#define outerr(f)       ((f)->flags & OUTPUT_ERR)
 
 /* syntax table used when not in quotes */
 static const char basesyntax[257] = {
@@ -1243,7 +1216,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
@@ -1366,8 +1339,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 *);
@@ -1389,6 +1362,8 @@ static char *minusc;                    /* argument to -c option */
 #define ALIASINUSE      1
 #define ALIASDEAD       2
 
+#define ATABSIZE 39
+
 struct alias {
        struct alias *next;
        char *name;
@@ -1481,7 +1456,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);
 }
 
@@ -1508,7 +1483,7 @@ aliascmd(int argc, char **argv)
        while ((n = *++argv) != NULL) {
                if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */
                        if ((ap = *__lookupalias(n)) == NULL) {
-                               outfmt(out2, "%s: %s not found\n", "alias", n);
+                               out2fmt("%s: %s not found\n", "alias", n);
                                ret = 1;
                        } else
                                printalias(ap);
@@ -1535,7 +1510,7 @@ unaliascmd(int argc, char **argv)
        }
        for (i = 0; *argptr; argptr++) {
                if (unalias(*argptr)) {
-                       outfmt(out2, "%s: %s not found\n", "unalias", *argptr);
+                       out2fmt("%s: %s not found\n", "unalias", *argptr);
                        i = 1;
                }
        }
@@ -1651,9 +1626,10 @@ 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 **);
-#ifdef ASH_PWD
+#ifndef BB_PWD
 static int pwdcmd (int, char **);
 #endif
 static int readcmd (int, char **);
@@ -1674,18 +1650,14 @@ static int timescmd (int, char **);
 #ifdef ASH_MATH_SUPPORT
 static int expcmd (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);
@@ -1716,7 +1688,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 },
@@ -1725,11 +1697,9 @@ static const struct builtincmd builtincmds[] = {
        { BUILTIN_REGULAR   "bg", bgcmd },
 #endif
        { BUILTIN_SPECIAL   "break", breakcmd },
-       { BUILTIN_SPECIAL   "builtin", bltincmd },      /* Do not disable this builtin ever or bad things happen */
+       { 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
@@ -1741,9 +1711,7 @@ static const struct builtincmd builtincmds[] = {
        { 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 },
@@ -1752,6 +1720,7 @@ 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 },
@@ -1760,7 +1729,7 @@ static const struct builtincmd builtincmds[] = {
        { BUILTIN_NOSPEC    "let", expcmd },
 #endif
        { BUILTIN_ASSIGN    "local", localcmd },
-#ifdef ASH_PWD
+#ifndef BB_PWD
        { BUILTIN_NOSPEC    "pwd", pwdcmd },
 #endif
        { BUILTIN_REGULAR   "read", readcmd },
@@ -1771,12 +1740,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
@@ -1842,8 +1807,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);
@@ -1966,7 +1931,7 @@ docd(dest, print)
        updatepwd(badstat ? NULL : dest);
        INTON;
        if (print && iflag)
-               out1fmt(snlfmt, curdir);
+               printf(snlfmt, curdir);
        return 0;
 }
 
@@ -2049,13 +2014,13 @@ updatepwd(const char *dir)
 }
 
 
-#ifdef ASH_PWD
+#ifndef BB_PWD
 static int
 pwdcmd(argc, argv)
        int argc;
        char **argv;
 {
-       out1fmt(snlfmt, curdir);
+       printf(snlfmt, curdir);
        return 0;
 }
 #endif
@@ -2141,6 +2106,7 @@ exraise(int e)
        if (handler == NULL)
                abort();
 #endif
+       flushall();
        exception = e;
        longjmp(handler->loc, 1);
 }
@@ -2198,15 +2164,10 @@ exverror(int cond, const char *msg, va_list ap)
 #endif
        if (msg) {
                if (commandname)
-                       outfmt(&errout, "%s: ", commandname);
-               doformat(&errout, msg, ap);
-#if FLUSHERR
-               outc('\n', &errout);
-#else
-               outcslow('\n', &errout);
-#endif
+                       out2fmt("%s: ", commandname);
+               vfprintf(stderr, msg, ap);
+               out2c('\n');
        }
-       flushall();
        exraise(cond);
        /* NOTREACHED */
 }
@@ -2271,7 +2232,7 @@ exerror(va_alist)
 
 struct errname {
        short errcode;          /* error number */
-       short action;           /* operation which encountered the error */
+       char  action;           /* operation which encountered the error */
 };
 
 /*
@@ -2357,18 +2318,23 @@ errmsg(int e, int action)
                        return strerror(e);
        }
 
-       fmtstr(buf, sizeof buf, "error %d", e);
+       snprintf(buf, sizeof buf, "error %d", e);
        return buf;
 }
 
 
-#ifndef ASH_BBAPPS_AS_BUILTINS
+#ifdef ASH_OPTIMIZE_FOR_SIZE
 static void
 __inton() {
        if (--suppressint == 0 && intpending) {
                onint();
        }
 }
+static void forceinton (void) {
+       suppressint = 0;
+       if (intpending)
+               onint();
+}
 #endif
 
 /* flags in argument to evaltree */
@@ -2382,23 +2348,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 *);
-#ifdef notyet
-static void evalcommand (union node *, int, struct backcmd *);
-#else
-static void evalcommand (union node *, int);
-#endif
 static void prehash (union node *);
 static void eprintlist (struct strlist *);
 
@@ -2468,132 +2423,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)
-               ) {
-                       outfmt(out2, "%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:
-#ifdef notyet
-               evalcommand(n, flags, (struct backcmd *)NULL);
-#else
-               evalcommand(n, flags);
-#endif
-               checkexit = 1;
-               break;
-#ifdef DEBUG
-       default:
-               out1fmt("Node type = %d\n", n->type);
-#ifndef USE_GLIBC_STDIO
-               flushout(out1);
-#endif
-               break;
-#endif
+
+/*
+ * 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;
 
@@ -2626,13 +2517,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;
@@ -2669,11 +2555,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;
@@ -2699,84 +2582,21 @@ out:
 }
 
 /*
- * Kick off a subshell to evaluate a tree.
+ * Evaluate a pipeline.  All the processes in the pipeline are children
+ * of the process creating the pipeline.  (This differs from some versions
+ * of the shell, which make the last process in a pipeline the parent
+ * of all the rest.)
  */
 
-static void
-evalsubshell(n, flags)
+static inline void
+evalpipe(n)
        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
- * of the shell, which make the last process in a pipeline the parent
- * of all the rest.)
- */
-
-static void
-evalpipe(n)
-       union node *n;
-{
-       struct job *jp;
-       struct nodelist *lp;
-       int pipelen;
-       int prevfd;
-       int pip[2];
+       struct nodelist *lp;
+       int pipelen;
+       int prevfd;
+       int pip[2];
 
        TRACE(("evalpipe(0x%lx) called\n", (long)n));
        pipelen = 0;
@@ -2830,75 +2650,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;
-       }
-#ifdef notyet
-       /*
-        * For now we disable executing builtins in the same
-        * context as the shell, because we are not keeping
-        * enough state to recover from changes that are
-        * supposed only to affect subshells. eg. echo "`cd /`"
-        */
-       if (n->type == NCMD) {
-               exitstatus = oexitstatus;
-               evalcommand(n, EV_BACKCMD, result);
-       } else
-#endif
-       {
-               exitstatus = 0;
-               if (pipe(pip) < 0)
-                       error("Pipe call failed");
-               jp = makejob(n, 1);
-               if (forkshell(jp, n, FORK_NOJOB) == 0) {
-                       FORCEINTON;
-                       close(pip[0]);
-                       if (pip[1] != 1) {
-                               close(1);
-                               dup_as_newfd(pip[1], 1);
-                               close(pip[1]);
-                       }
-                       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
@@ -2912,17 +2663,9 @@ isassignment(const char *word) {
        return *word == '=';
 }
 
+
 static void
-#ifdef notyet
-evalcommand(cmd, flags, backcmd)
-       union node *cmd;
-       int flags;
-       struct backcmd *backcmd;
-#else
-evalcommand(cmd, flags)
-       union node *cmd;
-       int flags;
-#endif
+evalcommand(union node *cmd, int flags)
 {
        struct stackmark smark;
        union node *argp;
@@ -2933,9 +2676,6 @@ evalcommand(cmd, flags)
        char **envp;
        struct strlist *sp;
        int mode;
-#ifdef notyet
-       int pip[2];
-#endif
        struct cmdentry cmdentry;
        struct job *jp;
        char *volatile savecmdname;
@@ -2975,7 +2715,7 @@ evalcommand(cmd, 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) {
@@ -3006,19 +2746,10 @@ evalcommand(cmd, flags)
 
        /* Print the command if xflag is set. */
        if (xflag) {
-#ifdef FLUSHERR
-               outc('+', &errout);
-#else
-               outcslow('+', &errout);
-#endif
+               out2c('+');
                eprintlist(varlist.list);
                eprintlist(arglist.list);
-#ifdef FLUSHERR
-               outc('\n', &errout);
-               flushout(&errout);
-#else
-               outcslow('\n', &errout);
-#endif
+               out2c('\n');
        }
 
        /* Now locate the command. */
@@ -3046,9 +2777,6 @@ evalcommand(cmd, flags)
                        find_command(argv[0], &cmdentry, findflag, path);
                        if (cmdentry.cmdtype == CMDUNKNOWN) {   /* command not found */
                                exitstatus = 127;
-#ifdef FLUSHERR
-                               flushout(&errout);
-#endif
                                goto out;
                        }
                        /* implement bltin and command here */
@@ -3066,11 +2794,8 @@ evalcommand(cmd, flags)
                                        if (--argc == 0)
                                                goto found;
                                        if (!(bcmd = find_builtin(*argv))) {
-                                               outfmt(&errout, "%s: not found\n", *argv);
+                                               out2fmt("%s: not found\n", *argv);
                                                exitstatus = 127;
-#ifdef FLUSHERR
-                                               flushout(&errout);
-#endif
                                                goto out;
                                        }
                                        cmdentry.u.cmd = bcmd;
@@ -3110,35 +2835,11 @@ found:
        /* Fork off a child process if necessary. */
        if (cmd->ncmd.backgnd
         || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
-#ifdef notyet
-        || ((flags & EV_BACKCMD) != 0
-           && (cmdentry.cmdtype != CMDBUILTIN
-                || cmdentry.u.bcmd == DOTCMD
-                || cmdentry.u.bcmd == EVALCMD))
-#endif
        ) {
                jp = makejob(cmd, 1);
                mode = cmd->ncmd.backgnd;
-#ifdef notyet
-               if (flags & EV_BACKCMD) {
-                       mode = FORK_NOJOB;
-                       if (pipe(pip) < 0)
-                               error("Pipe call failed");
-               }
-#endif
                if (forkshell(jp, cmd, mode) != 0)
                        goto parent;    /* at end of routine */
-#ifdef notyet
-               if (flags & EV_BACKCMD) {
-                       FORCEINTON;
-                       close(pip[0]);
-                       if (pip[1] != 1) {
-                               close(1);
-                               dup_as_newfd(pip[1], 1);
-                               close(pip[1]);
-                       }
-               }
-#endif
                flags |= EV_EXIT;
        }
 
@@ -3201,18 +2902,6 @@ found:
                trputs("builtin command:  ");  trargs(argv);
 #endif
                mode = (cmdentry.u.cmd == EXECCMD)? 0 : REDIR_PUSH;
-#ifdef notyet
-               if (flags == EV_BACKCMD) {
-#ifdef USE_GLIBC_STDIO
-                       openmemout();
-#else
-                       memout.nleft = 0;
-                       memout.nextc = memout.buf;
-                       memout.bufsize = 64;
-#endif
-                       mode |= REDIR_BACKQ;
-               }
-#endif
                redirect(cmd->ncmd.redirect, mode);
                savecmdname = commandname;
                if (IS_BUILTIN_SPECIAL(firstbltin)) {
@@ -3234,10 +2923,6 @@ found:
                exitstatus = (*cmdentry.u.cmd->builtinfunc)(argc, argv);
                flushall();
 cmddone:
-               exitstatus |= outerr(out1);
-               out1 = &output;
-               out2 = &errout;
-               freestdout();
                cmdenviron = NULL;
                if (e != EXSHELLPROC) {
                        commandname = savecmdname;
@@ -3256,23 +2941,6 @@ cmddone:
                }
                if (cmdentry.u.cmd != EXECCMD)
                        popredir();
-#ifdef notyet
-               if (flags == EV_BACKCMD) {
-                       INTOFF;
-#ifdef USE_GLIBC_STDIO
-                       if (__closememout())
-                               error("__closememout() failed: %m");
-#endif
-                       backcmd->buf = memout.buf;
-#ifdef USE_GLIBC_STDIO
-                       backcmd->nleft = memout.bufsize;
-#else
-                       backcmd->nleft = memout.nextc - memout.buf;
-#endif
-                       memout.buf = NULL;
-                       INTON;
-               }
-#endif
        } else {
 #ifdef DEBUG
                trputs("normal command:  ");  trargs(argv);
@@ -3291,12 +2959,6 @@ parent: /* parent process gets here (if we forked) */
                INTOFF;
                exitstatus = waitforjob(jp);
                INTON;
-#ifdef notyet
-       } else if (mode == 2) {
-               backcmd->fd = pip[0];
-               close(pip[1]);
-               backcmd->jp = jp;
-#endif
        }
 
 out:
@@ -3305,110 +2967,333 @@ 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;
-
-       if (n->type == NCMD && n->ncmd.args)
-               if (goodname(n->ncmd.args->narg.text))
-                       find_command(n->ncmd.args->narg.text, &entry, 0,
-                                    pathval());
-}
-
-
-
-/*
- * Builtin commands.  Builtin commands whose functions are closely
- * tied to evaluation are implemented here.
- */
-
-/*
- * No command given, or a bltin command with no arguments.  Set the
- * specified variables.
- */
-
-int
-bltincmd(argc, argv)
-       int argc;
-       char **argv;
-{
-       /*
-        * Preserve exitstatus of a previous possible redirection
-        * as POSIX mandates
-        */
-       return exitstatus;
-}
-
-
-/*
- * Handle break and continue commands.  Break, continue, and return are
- * all handled by setting the evalskip flag.  The evaluation routines
- * above all check this flag, and if it is set they start skipping
- * commands rather than executing them.  The variable skipcount is
- * the number of loops to break/continue, or the number of function
- * levels to return.  (The latter is always 1.)  It should probably
- * be an error to break out of more loops than exist, but it isn't
- * in the standard shell so we don't make it one here.
- */
-
-static int
-breakcmd(argc, argv)
-       int argc;
-       char **argv;
-{
-       int n = argc > 1 ? number(argv[1]) : 1;
-
-       if (n > loopnest)
-               n = loopnest;
-       if (n > 0) {
-               evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
-               skipcount = n;
-       }
-       return 0;
-}
-
-
-/*
- * The return command.
- */
-
-static int
-returncmd(argc, argv)
-       int argc;
-       char **argv;
-{
-       int ret = argc > 1 ? number(argv[1]) : oexitstatus;
-
-       if (funcnest) {
-               evalskip = SKIPFUNC;
-               skipcount = 1;
-               return ret;
-       }
-       else {
-               /* Do what ksh does; skip the rest of the file */
-               evalskip = SKIPFILE;
-               skipcount = 1;
-               return ret;
+       int checkexit = 0;
+       if (n == NULL) {
+               TRACE(("evaltree(NULL) called\n"));
+               goto out;
        }
-}
-
-
-#ifndef BB_TRUE_FALSE
-#ifdef ASH_BBAPPS_AS_BUILTINS
-static int
-false_main(argc, argv)
-       int argc;
+       TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type));
+       switch (n->type) {
+       case NSEMI:
+               evaltree(n->nbinary.ch1, flags & EV_TESTED);
+               if (evalskip)
+                       goto out;
+               evaltree(n->nbinary.ch2, flags);
+               break;
+       case NAND:
+               evaltree(n->nbinary.ch1, EV_TESTED);
+               if (evalskip || exitstatus != 0)
+                       goto out;
+               evaltree(n->nbinary.ch2, flags);
+               break;
+       case NOR:
+               evaltree(n->nbinary.ch1, EV_TESTED);
+               if (evalskip || exitstatus == 0)
+                       goto out;
+               evaltree(n->nbinary.ch2, flags);
+               break;
+       case NREDIR:
+               expredir(n->nredir.redirect);
+               redirect(n->nredir.redirect, REDIR_PUSH);
+               evaltree(n->nredir.n, flags);
+               popredir();
+               break;
+       case NSUBSHELL:
+               evalsubshell(n, flags);
+               break;
+       case NBACKGND:
+               evalsubshell(n, flags);
+               break;
+       case NIF: {
+               evaltree(n->nif.test, EV_TESTED);
+               if (evalskip)
+                       goto out;
+               if (exitstatus == 0)
+                       evaltree(n->nif.ifpart, flags);
+               else if (n->nif.elsepart)
+                       evaltree(n->nif.elsepart, flags);
+               else
+                       exitstatus = 0;
+               break;
+       }
+       case NWHILE:
+       case NUNTIL:
+               evalloop(n, flags);
+               break;
+       case NFOR:
+               evalfor(n, flags);
+               break;
+       case NCASE:
+               evalcase(n, flags);
+               break;
+       case NDEFUN: {
+               struct builtincmd *bcmd;
+               struct cmdentry entry;
+               if (
+                       (bcmd = find_builtin(n->narg.text)) &&
+                       IS_BUILTIN_SPECIAL(bcmd)
+               ) {
+                       out2fmt("%s is a special built-in\n", n->narg.text);
+                       exitstatus = 1;
+                       break;
+               }
+               entry.cmdtype = CMDFUNCTION;
+               entry.u.func = copyfunc(n->narg.next);
+               addcmdentry(n->narg.text, &entry);
+               exitstatus = 0;
+               break;
+       }
+       case NNOT:
+               evaltree(n->nnot.com, EV_TESTED);
+               exitstatus = !exitstatus;
+               break;
+
+       case NPIPE:
+               evalpipe(n);
+               checkexit = 1;
+               break;
+       case NCMD:
+               evalcommand(n, flags);
+               checkexit = 1;
+               break;
+#ifdef DEBUG
+       default:
+               printf("Node type = %d\n", n->type);
+               break;
+#endif
+       }
+out:
+       if (pendingsigs)
+               dotrap();
+       if (
+               flags & EV_EXIT ||
+               (checkexit && eflag && exitstatus && !(flags & EV_TESTED))
+       )
+               exitshell(exitstatus);
+}
+
+/*
+ * Kick off a subshell to evaluate a tree.
+ */
+
+static void
+evalsubshell(const union node *n, int flags)
+{
+       struct job *jp;
+       int backgnd = (n->type == NBACKGND);
+
+       expredir(n->nredir.redirect);
+       jp = makejob(n, 1);
+       if (forkshell(jp, n, backgnd) == 0) {
+               if (backgnd)
+                       flags &=~ EV_TESTED;
+               redirect(n->nredir.redirect, 0);
+               evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
+       }
+       if (! backgnd) {
+               INTOFF;
+               exitstatus = waitforjob(jp);
+               INTON;
+       }
+}
+
+/*
+ * Compute the names of the files in a redirection list.
+ */
+
+static void fixredir(union node *n, const char *text, int err);
+
+static void
+expredir(union node *n)
+{
+       union node *redir;
+
+       for (redir = n ; redir ; redir = redir->nfile.next) {
+               struct arglist fn;
+               fn.lastp = &fn.list;
+               oexitstatus = exitstatus;
+               switch (redir->type) {
+               case NFROMTO:
+               case NFROM:
+               case NTO:
+               case NAPPEND:
+               case NTOOV:
+                       expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
+                       redir->nfile.expfname = fn.list->text;
+                       break;
+               case NFROMFD:
+               case NTOFD:
+                       if (redir->ndup.vname) {
+                               expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
+                               fixredir(redir, fn.list->text, 1);
+                       }
+                       break;
+               }
+       }
+}
+
+
+/*
+ * Execute a command inside back quotes.  If it's a builtin command, we
+ * want to save its output in a block obtained from malloc.  Otherwise
+ * we fork off a subprocess and get the output of the command via a pipe.
+ * Should be called with interrupts off.
+ */
+
+static void
+evalbackcmd(union node *n, struct backcmd *result)
+{
+       int pip[2];
+       struct job *jp;
+       struct stackmark smark;         /* unnecessary */
+
+       setstackmark(&smark);
+       result->fd = -1;
+       result->buf = NULL;
+       result->nleft = 0;
+       result->jp = NULL;
+       if (n == NULL) {
+               exitstatus = 0;
+               goto out;
+       }
+       exitstatus = 0;
+       if (pipe(pip) < 0)
+               error("Pipe call failed");
+       jp = makejob(n, 1);
+       if (forkshell(jp, n, FORK_NOJOB) == 0) {
+               FORCEINTON;
+               close(pip[0]);
+               if (pip[1] != 1) {
+                       close(1);
+                       dup_as_newfd(pip[1], 1);
+                       close(pip[1]);
+               }
+               eflag = 0;
+               evaltree(n, EV_EXIT);
+       }
+       close(pip[1]);
+       result->fd = pip[0];
+       result->jp = jp;
+out:
+       popstackmark(&smark);
+       TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
+               result->fd, result->buf, result->nleft, result->jp));
+}
+
+
+/*
+ * Execute a simple command.
+ */
+
+/*
+ * Search for a command.  This is called before we fork so that the
+ * location of the command will be available in the parent as well as
+ * the child.  The check for "goodname" is an overly conservative
+ * check that the name will not be subject to expansion.
+ */
+
+static void
+prehash(n)
+       union node *n;
+{
+       struct cmdentry entry;
+
+       if (n->type == NCMD && n->ncmd.args)
+               if (goodname(n->ncmd.args->narg.text))
+                       find_command(n->ncmd.args->narg.text, &entry, 0,
+                                    pathval());
+}
+
+
+/*
+ * Builtin commands.  Builtin commands whose functions are closely
+ * tied to evaluation are implemented here.
+ */
+
+/*
+ * No command given, or a bltin command with no arguments.  Set the
+ * specified variables.
+ */
+
+int
+bltincmd(argc, argv)
+       int argc;
+       char **argv;
+{
+       /*
+        * Preserve exitstatus of a previous possible redirection
+        * as POSIX mandates
+        */
+       return exitstatus;
+}
+
+
+/*
+ * Handle break and continue commands.  Break, continue, and return are
+ * all handled by setting the evalskip flag.  The evaluation routines
+ * above all check this flag, and if it is set they start skipping
+ * commands rather than executing them.  The variable skipcount is
+ * the number of loops to break/continue, or the number of function
+ * levels to return.  (The latter is always 1.)  It should probably
+ * be an error to break out of more loops than exist, but it isn't
+ * in the standard shell so we don't make it one here.
+ */
+
+static int
+breakcmd(argc, argv)
+       int argc;
+       char **argv;
+{
+       int n = argc > 1 ? number(argv[1]) : 1;
+
+       if (n > loopnest)
+               n = loopnest;
+       if (n > 0) {
+               evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
+               skipcount = n;
+       }
+       return 0;
+}
+
+
+/*
+ * The return command.
+ */
+
+static int
+returncmd(argc, argv)
+       int argc;
+       char **argv;
+{
+       int ret = argc > 1 ? number(argv[1]) : oexitstatus;
+
+       if (funcnest) {
+               evalskip = SKIPFUNC;
+               skipcount = 1;
+               return ret;
+       }
+       else {
+               /* Do what ksh does; skip the rest of the file */
+               evalskip = SKIPFILE;
+               skipcount = 1;
+               return ret;
+       }
+}
+
+
+#ifndef BB_TRUE_FALSE
+static int
+false_main(argc, argv)
+       int argc;
        char **argv;
 {
        return 1;
@@ -3423,7 +3308,6 @@ true_main(argc, argv)
        return 0;
 }
 #endif
-#endif
 
 /*
  * Controls whether the shell is interactive or not.
@@ -3437,6 +3321,7 @@ static void
 setinteractive(int on)
 {
        static int is_interactive;
+       static int do_banner=0;
 
        if (on == is_interactive)
                return;
@@ -3445,6 +3330,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
@@ -3477,46 +3368,9 @@ static void
 eprintlist(struct strlist *sp)
 {
        for (; sp; sp = sp->next) {
-               outfmt(&errout, " %s",sp->text);
+               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
@@ -3646,14 +3500,13 @@ 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.
  * Nul characters in the input are silently discarded.
  */
 
-#ifdef ASH_BBAPPS_AS_BUILTINS
+#ifndef ASH_OPTIMIZE_FOR_SIZE
 #define pgetc_macro()   (--parsenleft >= 0? *parsenextc++ : preadbuffer())
 static int
 pgetc(void)
@@ -3786,13 +3639,25 @@ 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
+       char *name = cmd;
+       char** argv_l=argv;
+       int argc_l;
+#ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN
+       name = get_last_path_component(name);
+#endif
+       argv_l=envp;
+       for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++)
+               putenv(*argv_l);
+       argv_l=argv;
+       for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++)
+       optind = 1;
+       run_applet_by_name(name, argc_l, argv);
+#endif
        execve(cmd, argv, envp);
        e = errno;
        if (e == ENOEXEC) {
@@ -3857,6 +3722,25 @@ 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);
+}
+
+/*
+ * Find a keyword is in a sorted array.
+ */
+
+static const char *const *
+findkwd(const char *s)
+{
+       return  bsearch(s, parsekwd, sizeof(parsekwd) / sizeof(const char *),
+                       sizeof(const char *), pstrcmp);
+}
 
 
 /*** Command hashing code ***/
@@ -3873,14 +3757,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) {
@@ -3894,24 +3781,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;
@@ -3921,6 +3825,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();
@@ -3928,15 +3833,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;
                }
@@ -3945,11 +3852,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.
@@ -3969,7 +3912,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 */
@@ -4126,7 +4069,7 @@ loop:
        if (cmdp && updatetbl)
                delete_cmd_entry();
        if (act & DO_ERR)
-               outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
+               out2fmt("%s: %s\n", name, errmsg(e, E_EXEC));
        entry->cmdtype = CMDUNKNOWN;
        return;
 
@@ -4232,17 +4175,6 @@ clearcmdentry(firstchange)
        INTON;
 }
 
-/*
- * Free a parse tree.
- */
-
-static void
-freefunc(union node *n)
-{
-       if (n)
-               ckfree(n);
-}
-
 
 /*
  * Delete all functions.
@@ -4332,42 +4264,37 @@ delete_cmd_entry() {
 
 
 
-/*
- * Add a new command entry, replacing any existing command entry for
- * the same name.
- */
-
-static void
-addcmdentry(char *name, struct cmdentry *entry)
-{
-       struct tblentry *cmdp;
-
-       INTOFF;
-       cmdp = cmdlookup(name, 1);
-       if (cmdp->cmdtype == CMDFUNCTION) {
-               freefunc(cmdp->param.func);
-       }
-       cmdp->cmdtype = entry->cmdtype;
-       cmdp->param = entry->u;
-       INTON;
-}
-
-
-/*
- * Define a shell function.
- */
 
-static union node *copyfunc(union node *);
 
-static void
-defun(char *name, union node *func)
-{
-       struct cmdentry entry;
+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)),
+};
 
-       entry.cmdtype = CMDFUNCTION;
-       entry.u.func = copyfunc(func);
-       addcmdentry(name, &entry);
-}
 
 
 /*
@@ -4385,141 +4312,29 @@ unsetfunc(char *name)
        }
 }
 
-/*
- * 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:
-       out1c('\n');
-       return 0;
-}
-#endif
-
 #ifdef ASH_CMDCMD
 static int
 commandcmd(argc, argv)
@@ -4542,26 +4357,25 @@ commandcmd(argc, argv)
                case 'V':
                        verbose_verify_only = 1;
                        break;
-               default:
-                       outfmt(out2,
-"command: nextopt returned character code 0%o\n", c);
-                       return EX_SOFTWARE;
                }
 
        if (default_path + verify_only + verbose_verify_only > 1 ||
            !*argptr) {
-                       outfmt(out2,
-"command [-p] command [arg ...]\n");
-                       outfmt(out2,
-"command {-v|-V} command\n");
+                       out2str(
+                               "command [-p] command [arg ...]\n"
+                               "command {-v|-V} command\n");
                        return EX_USAGE;
        }
 
-#ifdef ASH_TYPE
        if (verify_only || verbose_verify_only) {
-               return describe_command(*argptr, verbose_verify_only);
+               char *argv_a[2];
+
+               argv_a[1] = 0;
+               argv_a[0] = *argptr;
+               argptr = argv_a;
+               optptr = verbose_verify_only ? "v" : "V"; /* reverse special */
+               return hashcmd(argc, argv);
        }
-#endif
 
        return 0;
 }
@@ -4635,7 +4449,6 @@ 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 varvalue (char *, int, int);
@@ -4644,21 +4457,21 @@ 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);
@@ -4671,7 +4484,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;
@@ -4687,52 +4500,214 @@ expandhere(union node *arg, int fd)
  * here document expansion.
  */
 
-static void
-expandarg(arg, arglist, flag)
-       union node *arg;
-       struct arglist *arglist;
-       int flag;
-{
-       struct strlist *sp;
-       char *p;
+static void
+expandarg(arg, arglist, flag)
+       union node *arg;
+       struct arglist *arglist;
+       int flag;
+{
+       struct strlist *sp;
+       char *p;
+
+       argbackq = arg->narg.backquote;
+       STARTSTACKSTR(expdest);
+       ifsfirst.next = NULL;
+       ifslastp = NULL;
+       argstr(arg->narg.text, flag);
+       if (arglist == NULL) {
+               return;                 /* here document expanded */
+       }
+       STPUTC('\0', expdest);
+       p = grabstackstr(expdest);
+       exparg.lastp = &exparg.list;
+       /*
+        * TODO - EXP_REDIR
+        */
+       if (flag & EXP_FULL) {
+               ifsbreakup(p, &exparg);
+               *exparg.lastp = NULL;
+               exparg.lastp = &exparg.list;
+               expandmeta(exparg.list, flag);
+       } else {
+               if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
+                       rmescapes(p);
+               sp = (struct strlist *)stalloc(sizeof (struct strlist));
+               sp->text = p;
+               *exparg.lastp = sp;
+               exparg.lastp = &sp->next;
+       }
+       ifsfree();
+       *exparg.lastp = NULL;
+       if (exparg.list) {
+               *arglist->lastp = exparg.list;
+               arglist->lastp = exparg.lastp;
+       }
+}
+
+
+/*
+ * Expand a variable, and return a pointer to the next character in the
+ * input string.
+ */
+
+static inline char *
+evalvar(p, flag)
+       char *p;
+       int flag;
+{
+       int subtype;
+       int varflags;
+       char *var;
+       const char *val;
+       int patloc;
+       int c;
+       int set;
+       int special;
+       int startloc;
+       int varlen;
+       int easy;
+       int quotes = flag & (EXP_FULL | EXP_CASE);
+
+       varflags = *p++;
+       subtype = varflags & VSTYPE;
+       var = p;
+       special = 0;
+       if (! is_name(*p))
+               special = 1;
+       p = strchr(p, '=') + 1;
+again: /* jump here after setting a variable with ${var=text} */
+       if (special) {
+               set = varisset(var, varflags & VSNUL);
+               val = NULL;
+       } else {
+               val = lookupvar(var);
+               if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
+                       val = NULL;
+                       set = 0;
+               } else
+                       set = 1;
+       }
+       varlen = 0;
+       startloc = expdest - stackblock();
+       if (set && subtype != VSPLUS) {
+               /* insert the value of the variable */
+               if (special) {
+                       varvalue(var, varflags & VSQUOTE, flag);
+                       if (subtype == VSLENGTH) {
+                               varlen = expdest - stackblock() - startloc;
+                               STADJUST(-varlen, expdest);
+                       }
+               } else {
+                       if (subtype == VSLENGTH) {
+                               varlen = strlen(val);
+                       } else {
+                               strtodest(
+                                       val,
+                                       varflags & VSQUOTE ?
+                                               DQSYNTAX : BASESYNTAX,
+                                       quotes
+                               );
+                       }
+               }
+       }
+
+       if (subtype == VSPLUS)
+               set = ! set;
+
+       easy = ((varflags & VSQUOTE) == 0 ||
+               (*var == '@' && shellparam.nparam != 1));
+
+
+       switch (subtype) {
+       case VSLENGTH:
+               expdest = cvtnum(varlen, expdest);
+               goto record;
+
+       case VSNORMAL:
+               if (!easy)
+                       break;
+record:
+               recordregion(startloc, expdest - stackblock(),
+                            varflags & VSQUOTE);
+               break;
+
+       case VSPLUS:
+       case VSMINUS:
+               if (!set) {
+                       argstr(p, flag);
+                       break;
+               }
+               if (easy)
+                       goto record;
+               break;
+
+       case VSTRIMLEFT:
+       case VSTRIMLEFTMAX:
+       case VSTRIMRIGHT:
+       case VSTRIMRIGHTMAX:
+               if (!set)
+                       break;
+               /*
+                * Terminate the string and start recording the pattern
+                * right after it
+                */
+               STPUTC('\0', expdest);
+               patloc = expdest - stackblock();
+               if (subevalvar(p, NULL, patloc, subtype,
+                              startloc, varflags, quotes) == 0) {
+                       int amount = (expdest - stackblock() - patloc) + 1;
+                       STADJUST(-amount, expdest);
+               }
+               /* Remove any recorded regions beyond start of variable */
+               removerecordregions(startloc);
+               goto record;
+
+       case VSASSIGN:
+       case VSQUESTION:
+               if (!set) {
+                       if (subevalvar(p, var, 0, subtype, startloc,
+                                      varflags, quotes)) {
+                               varflags &= ~VSNUL;
+                               /*
+                                * Remove any recorded regions beyond
+                                * start of variable
+                                */
+                               removerecordregions(startloc);
+                               goto again;
+                       }
+                       break;
+               }
+               if (easy)
+                       goto record;
+               break;
 
-       argbackq = arg->narg.backquote;
-       STARTSTACKSTR(expdest);
-       ifsfirst.next = NULL;
-       ifslastp = NULL;
-       argstr(arg->narg.text, flag);
-       if (arglist == NULL) {
-               return;                 /* here document expanded */
-       }
-       STPUTC('\0', expdest);
-       p = grabstackstr(expdest);
-       exparg.lastp = &exparg.list;
-       /*
-        * TODO - EXP_REDIR
-        */
-       if (flag & EXP_FULL) {
-               ifsbreakup(p, &exparg);
-               *exparg.lastp = NULL;
-               exparg.lastp = &exparg.list;
-               expandmeta(exparg.list, flag);
-       } else {
-               if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
-                       rmescapes(p);
-               sp = (struct strlist *)stalloc(sizeof (struct strlist));
-               sp->text = p;
-               *exparg.lastp = sp;
-               exparg.lastp = &sp->next;
+#ifdef DEBUG
+       default:
+               abort();
+#endif
        }
-       ifsfree();
-       *exparg.lastp = NULL;
-       if (exparg.list) {
-               *arglist->lastp = exparg.list;
-               arglist->lastp = exparg.lastp;
+
+       if (subtype != VSNORMAL) {      /* skip to end of alternative */
+               int nesting = 1;
+               for (;;) {
+                       if ((c = *p++) == CTLESC)
+                               p++;
+                       else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
+                               if (set)
+                                       argbackq = argbackq->next;
+                       } else if (c == CTLVAR) {
+                               if ((*p++ & VSTYPE) != VSNORMAL)
+                                       nesting++;
+                       } else if (c == CTLENDVAR) {
+                               if (--nesting == 0)
+                                       break;
+                       }
+               }
        }
+       return p;
 }
 
 
-
 /*
  * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
  * characters to allow for further processing.  Otherwise treat
@@ -4940,7 +4915,7 @@ expari(int flag)
        if (quotes)
                rmescapes(p+2);
        result = arith(p+2);
-       fmtstr(p, 12, "%d", result);
+       snprintf(p, 12, "%d", result);
 
        while (*p++)
                ;
@@ -5099,7 +5074,7 @@ subevalvar(p, str, strloc, subtype, startloc, varflags, quotes)
 
        case VSQUESTION:
                if (*p != CTLENDVAR) {
-                       outfmt(&errout, snlfmt, startp);
+                       out2fmt(snlfmt, startp);
                        error((char *)NULL);
                }
                error("%.*s: parameter %snot set", p - str - 1,
@@ -5174,178 +5149,16 @@ recordleft:
        while (loc != str - 1)
                *startp++ = *loc++;
        return 1;
-
-recordright:
-       amount = loc - expdest;
-       STADJUST(amount, expdest);
-       STPUTC('\0', expdest);
-       STADJUST(-1, expdest);
-       return 1;
-}
-
-
-/*
- * Expand a variable, and return a pointer to the next character in the
- * input string.
- */
-
-static char *
-evalvar(p, flag)
-       char *p;
-       int flag;
-{
-       int subtype;
-       int varflags;
-       char *var;
-       char *val;
-       int patloc;
-       int c;
-       int set;
-       int special;
-       int startloc;
-       int varlen;
-       int easy;
-       int quotes = flag & (EXP_FULL | EXP_CASE);
-
-       varflags = *p++;
-       subtype = varflags & VSTYPE;
-       var = p;
-       special = 0;
-       if (! is_name(*p))
-               special = 1;
-       p = strchr(p, '=') + 1;
-again: /* jump here after setting a variable with ${var=text} */
-       if (special) {
-               set = varisset(var, varflags & VSNUL);
-               val = NULL;
-       } else {
-               val = lookupvar(var);
-               if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
-                       val = NULL;
-                       set = 0;
-               } else
-                       set = 1;
-       }
-       varlen = 0;
-       startloc = expdest - stackblock();
-       if (set && subtype != VSPLUS) {
-               /* insert the value of the variable */
-               if (special) {
-                       varvalue(var, varflags & VSQUOTE, flag);
-                       if (subtype == VSLENGTH) {
-                               varlen = expdest - stackblock() - startloc;
-                               STADJUST(-varlen, expdest);
-                       }
-               } else {
-                       if (subtype == VSLENGTH) {
-                               varlen = strlen(val);
-                       } else {
-                               strtodest(
-                                       val,
-                                       varflags & VSQUOTE ?
-                                               DQSYNTAX : BASESYNTAX,
-                                       quotes
-                               );
-                       }
-               }
-       }
-
-       if (subtype == VSPLUS)
-               set = ! set;
-
-       easy = ((varflags & VSQUOTE) == 0 ||
-               (*var == '@' && shellparam.nparam != 1));
-
-
-       switch (subtype) {
-       case VSLENGTH:
-               expdest = cvtnum(varlen, expdest);
-               goto record;
-
-       case VSNORMAL:
-               if (!easy)
-                       break;
-record:
-               recordregion(startloc, expdest - stackblock(),
-                            varflags & VSQUOTE);
-               break;
-
-       case VSPLUS:
-       case VSMINUS:
-               if (!set) {
-                       argstr(p, flag);
-                       break;
-               }
-               if (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;
+
+recordright:
+       amount = loc - expdest;
+       STADJUST(amount, expdest);
+       STPUTC('\0', expdest);
+       STADJUST(-1, expdest);
+       return 1;
 }
 
+
 /*
  * Test whether a specialized variable is set.
  */
@@ -5636,7 +5449,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;
@@ -5881,7 +5694,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
@@ -5951,7 +5764,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)
@@ -6109,7 +5922,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)
 {
@@ -6126,12 +5939,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) {
@@ -6229,12 +6038,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 renamed[10];
 };
 
 static struct redirtab *redirlist;
@@ -6260,13 +6067,6 @@ init(void) {
              basepf.nextc = basepf.buf = basebuf;
       }
 
-      /* from output.c: */
-      {
-#ifdef USE_GLIBC_STDIO
-             initstreams();
-#endif
-      }
-
       /* from var.c: */
       {
              char **envp;
@@ -6279,7 +6079,7 @@ init(void) {
                      }
              }
 
-             fmtstr(ppid, sizeof(ppid), "%d", (int) getppid());
+             snprintf(ppid, sizeof(ppid), "%d", (int) getppid());
              setvar("PPID", ppid, 0);
       }
 }
@@ -6328,19 +6128,6 @@ reset(void) {
                      popredir();
       }
 
-      /* from output.c: */
-      {
-             out1 = &output;
-             out2 = &errout;
-#ifdef USE_GLIBC_STDIO
-             if (memout.stream != NULL)
-                     __closememout();
-#endif
-             if (memout.buf != NULL) {
-                     ckfree(memout.buf);
-                     memout.buf = NULL;
-             }
-      }
 }
 
 
@@ -6387,7 +6174,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;
@@ -6409,7 +6196,7 @@ pfgets(char *line, int len)
        return line;
 }
 
-static int
+static inline int
 preadfd(void)
 {
     int nr;
@@ -6513,10 +6300,7 @@ preadbuffer(void)
        }
        if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
                return PEOF;
-       flushout(&output);
-#ifdef FLUSHERR
-       flushout(&errout);
-#endif
+       flushall();
 
 again:
        if (parselleft <= 0) {
@@ -6557,9 +6341,6 @@ check:
 
        if (vflag) {
                out2str(parsenextc);
-#ifdef FLUSHERR
-               flushout(out2);
-#endif
        }
 
        *q = savec;
@@ -6600,16 +6381,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;
@@ -6648,7 +6426,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);
 
 
@@ -6665,17 +6442,7 @@ fd0_redirected_p () {
        return fd0_redirected != 0;
 }
 
-/*
- * We also keep track of where fileno2 goes.
- */
-static int fileno2 = 2;
-
-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
 /*
@@ -6699,9 +6466,9 @@ static void setjobctl(int enable)
        if (enable) {
                do { /* while we are in the background */
 #ifdef OLD_TTY_DRIVER
-                       if (ioctl(fileno2, TIOCGPGRP, (char *)&initialpgrp) < 0) {
+                       if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) {
 #else
-                       initialpgrp = tcgetpgrp(fileno2);
+                       initialpgrp = tcgetpgrp(2);
                        if (initialpgrp < 0) {
 #endif
                                out2str("sh: can't access tty; job cenabletrol turned off\n");
@@ -6716,7 +6483,7 @@ static void setjobctl(int enable)
                        }
                } while (0);
 #ifdef OLD_TTY_DRIVER
-               if (ioctl(fileno2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
+               if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
                        out2str("sh: need new tty driver to run job cenabletrol; job cenabletrol turned off\n");
                        mflag = 0;
                        return;
@@ -6727,16 +6494,16 @@ static void setjobctl(int enable)
                setsignal(SIGTTIN);
                setpgid(0, rootpid);
 #ifdef OLD_TTY_DRIVER
-               ioctl(fileno2, TIOCSPGRP, (char *)&rootpid);
+               ioctl(2, TIOCSPGRP, (char *)&rootpid);
 #else
-               tcsetpgrp(fileno2, rootpid);
+               tcsetpgrp(2, rootpid);
 #endif
        } else { /* turning job cenabletrol off */
                setpgid(0, initialpgrp);
 #ifdef OLD_TTY_DRIVER
-               ioctl(fileno2, TIOCSPGRP, (char *)&initialpgrp);
+               ioctl(2, TIOCSPGRP, (char *)&initialpgrp);
 #else
-               tcsetpgrp(fileno2, initialpgrp);
+               tcsetpgrp(2, initialpgrp);
 #endif
                setsignal(SIGTSTP);
                setsignal(SIGTTOU);
@@ -6781,6 +6548,7 @@ static char *signal_names[NSIG + 2] = {
     "SIGIO",
     "SIGPWR",
     "SIGSYS",
+#ifdef SIGRTMIN
     "SIGRTMIN",
     "SIGRTMIN+1",
     "SIGRTMIN+2",
@@ -6813,6 +6581,7 @@ static char *signal_names[NSIG + 2] = {
     "SIGRTMAX-2",
     "SIGRTMAX-1",
     "SIGRTMAX",
+#endif
     "DEBUG",
     (char *)0x0,
 };
@@ -6879,7 +6648,7 @@ usage:
                if (!*argptr) {
                        out1str("0\n");
                        for (i = 1; i < NSIG; i++) {
-                               out1fmt(snlfmt, signal_names[i] + 3);
+                               printf(snlfmt, signal_names[i] + 3);
                        }
                        return 0;
                }
@@ -6887,7 +6656,7 @@ usage:
                if (signo > 128)
                        signo -= 128;
                if (0 < signo && signo < NSIG)
-                               out1fmt(snlfmt, signal_names[signo] + 3);
+                               printf(snlfmt, signal_names[signo] + 3);
                else
                        error("invalid signal number or exit status: %s",
                              *argptr);
@@ -6924,9 +6693,9 @@ fgcmd(argc, argv)
                error("job not created under job control");
        pgrp = jp->ps[0].pid;
 #ifdef OLD_TTY_DRIVER
-       ioctl(fileno2, TIOCSPGRP, (char *)&pgrp);
+       ioctl(2, TIOCSPGRP, (char *)&pgrp);
 #else
-       tcsetpgrp(fileno2, pgrp);
+       tcsetpgrp(2, pgrp);
 #endif
        restartjob(jp);
        INTOFF;
@@ -7022,10 +6791,10 @@ showjobs(change)
                procno = jp->nprocs;
                for (ps = jp->ps ; ; ps++) {    /* for each process */
                        if (ps == jp->ps)
-                               fmtstr(s, 64, "[%d] %ld ", jobno,
+                               snprintf(s, 64, "[%d] %ld ", jobno,
                                    (long)ps->pid);
                        else
-                               fmtstr(s, 64, "    %ld ",
+                               snprintf(s, 64, "    %ld ",
                                    (long)ps->pid);
                        out1str(s);
                        col = strlen(s);
@@ -7033,7 +6802,7 @@ showjobs(change)
                        if (ps->status == -1) {
                                /* don't print anything */
                        } else if (WIFEXITED(ps->status)) {
-                               fmtstr(s, 64, "Exit %d",
+                               snprintf(s, 64, "Exit %d",
                                       WEXITSTATUS(ps->status));
                        } else {
 #ifdef JOBS
@@ -7045,13 +6814,13 @@ showjobs(change)
                                if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F])
                                        strcpy(s, sys_siglist[i & 0x7F]);
                                else
-                                       fmtstr(s, 64, "Signal %d", i & 0x7F);
+                                       snprintf(s, 64, "Signal %d", i & 0x7F);
                                if (WCOREDUMP(ps->status))
                                        strcat(s, " (core dumped)");
                        }
                        out1str(s);
                        col += strlen(s);
-                       out1fmt(
+                       printf(
                                "%*c%s\n", 30 - col >= 0 ? 30 - col : 0, ' ',
                                ps->cmd
                        );
@@ -7071,10 +6840,9 @@ showjobs(change)
  */
 
 static void
-freejob(jp)
-       struct job *jp;
-       {
-       struct procstat *ps;
+freejob(struct job *jp)
+{
+       const struct procstat *ps;
        int i;
 
        INTOFF;
@@ -7209,9 +6977,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;
@@ -7277,10 +7043,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";
 
@@ -7315,10 +7083,10 @@ forkshell(struct job *jp, union node *n, int mode)
                        if (mode == FORK_FG) {
                                /*** this causes superfluous TIOCSPGRPS ***/
 #ifdef OLD_TTY_DRIVER
-                               if (ioctl(fileno2, TIOCSPGRP, (char *)&pgrp) < 0)
+                               if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0)
                                        error("TIOCSPGRP failed, errno=%d", errno);
 #else
-                               if (tcsetpgrp(fileno2, pgrp) < 0)
+                               if (tcsetpgrp(2, pgrp) < 0)
                                        error("tcsetpgrp failed, errno=%d", errno);
 #endif
                        }
@@ -7356,6 +7124,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;
@@ -7363,6 +7132,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) {
@@ -7400,9 +7170,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
@@ -7436,10 +7205,10 @@ waitforjob(jp)
 #ifdef JOBS
        if (jp->jobctl) {
 #ifdef OLD_TTY_DRIVER
-               if (ioctl(fileno2, TIOCSPGRP, (char *)&mypgrp) < 0)
+               if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0)
                        error("TIOCSPGRP failed, errno=%d\n", errno);
 #else
-               if (tcsetpgrp(fileno2, mypgrp) < 0)
+               if (tcsetpgrp(2, mypgrp) < 0)
                        error("tcsetpgrp failed, errno=%d\n", errno);
 #endif
        }
@@ -7483,10 +7252,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;
@@ -7549,22 +7355,19 @@ dowait(block, job)
 
                if (sig != 0 && sig != SIGINT && sig != SIGPIPE) {
                        if (thisjob != job)
-                               outfmt(out2, "%d: ", pid);
+                               out2fmt("%d: ", pid);
 #ifdef JOBS
                        if (sig == SIGTSTP && rootshell && iflag)
-                               outfmt(out2, "%%%ld ",
+                               out2fmt("%%%ld ",
                                    (long)(job - jobtab + 1));
 #endif
                        if (sig < NSIG && sys_siglist[sig])
                                out2str(sys_siglist[sig]);
                        else
-                               outfmt(out2, "Signal %d", sig);
+                               out2fmt("Signal %d", sig);
                        if (core)
                                out2str(" - core dumped");
                        out2c('\n');
-#ifdef FLUSHERR
-                       flushout(&errout);
-#endif
                } else {
                        TRACE(("Not printing status: status=%d, sig=%d\n",
                               status, sig));
@@ -7579,46 +7382,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
@@ -7889,10 +7652,8 @@ chkmail(int silent)
                if (stat(p, &statb) < 0)
                        statb.st_size = 0;
                if (statb.st_size > mailtime[i] && ! silent) {
-                       outfmt(
-                               &errout, snlfmt,
-                               pathopt? pathopt : "you have mail"
-                       );
+                       out2fmt(snlfmt,
+                               pathopt? pathopt : "you have mail");
                }
                mailtime[i] = statb.st_size;
        }
@@ -7908,10 +7669,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 **);
 
@@ -7932,13 +7691,17 @@ shell_main(argc, 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
@@ -7980,9 +7743,6 @@ shell_main(argc, argv)
                reset();
                if (exception == EXINT) {
                        out2c('\n');
-#ifdef FLUSHERR
-                       flushout(out2);
-#endif
                }
                popstackmark(&smark);
                FORCEINTON;                             /* enable interrupts */
@@ -8079,7 +7839,7 @@ cmdloop(int top)
                        inter++;
                        showjobs(1);
                        chkmail(0);
-                       flushout(&output);
+                       flushall();
                }
                n = parsecmd(inter);
                /* showtree(n); DEBUG */
@@ -8173,7 +7933,7 @@ readcmdfile(const char *name)
  */
 
 
-static char *
+static inline char *
 find_dot_file(mybasename)
        char *mybasename;
 {
@@ -8242,6 +8002,7 @@ exitcmd(argc, argv)
        exitshell(exitstatus);
        /* NOTREACHED */
 }
+
 static pointer
 stalloc(int nbytes)
 {
@@ -8442,13 +8203,13 @@ ungrabstackstr(char *s, char *p)
 
 #undef rflag
 
-#ifdef __GLIBC__
+//#ifdef __GLIBC__
 static mode_t getmode(const void *, mode_t);
 static void *setmode(const char *);
+//#endif
 
 #if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
-typedef enum __rlimit_resource rlim_t;
-#endif
+typedef long rlim_t;
 #endif
 
 
@@ -8557,7 +8318,7 @@ umaskcmd(argc, argv)
        int i;
        int symbolic_mode = 0;
 
-       while ((i = nextopt("S")) != '\0') {
+       while (nextopt("S") != '\0') {
                symbolic_mode = 1;
        }
 
@@ -8597,12 +8358,12 @@ umaskcmd(argc, argv)
                                o[i++] = 'x';
                        o[i] = '\0';
 
-                       out1fmt("u=%s,g=%s,o=%s\n", u, g, o);
+                       printf("u=%s,g=%s,o=%s\n", u, g, o);
                } 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')
@@ -8747,13 +8508,13 @@ ulimitcmd(argc, argv)
                        else if (how & HARD)
                                val = limit.rlim_max;
 
-                       out1fmt("%-20s ", l->name);
+                       printf("%-20s ", l->name);
                        if (val == RLIM_INFINITY)
-                               out1fmt("unlimited\n");
+                               printf("unlimited\n");
                        else
                        {
                                val /= l->factor;
-                               out1fmt("%lld\n", (long long) val);
+                               printf("%lld\n", (long long) val);
                        }
                }
                return 0;
@@ -8774,11 +8535,11 @@ ulimitcmd(argc, argv)
                        val = limit.rlim_max;
 
                if (val == RLIM_INFINITY)
-                       out1fmt("unlimited\n");
+                       printf("unlimited\n");
                else
                {
                        val /= l->factor;
-                       out1fmt("%lld\n", (long long) val);
+                       printf("%lld\n", (long long) val);
                }
        }
        return 0;
@@ -8788,10 +8549,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;
@@ -8869,12 +8628,8 @@ single_quote(const char *s) {
 
                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;
                }
@@ -8889,13 +8644,9 @@ single_quote(const char *s) {
                        break;
                default:
                        *q = '"';
-#ifdef _GNU_SOURCE
-                       *(char *) mempcpy(q + 1, s, len2) = '"';
-#else
                        q += 1 + len2;
                        memcpy(q + 1, s, len2);
                        *q = '"';
-#endif
                        s += len2;
                }
 
@@ -8919,80 +8670,17 @@ 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 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.
- */
-
-static 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);
-}
-
-
+static void sizenodelist (const struct nodelist *);
+static struct nodelist *copynodelist (const struct nodelist *);
+static char *nodesavestr (const char *);
 
 static void
-calcsize(n)
-       union node *n;
+calcsize(const union node *n)
 {
       if (n == NULL)
            return;
@@ -9069,11 +8757,8 @@ calcsize(n)
       };
 }
 
-
-
 static void
-sizenodelist(lp)
-       struct nodelist *lp;
+sizenodelist(const struct nodelist *lp)
 {
        while (lp) {
                funcblocksize += ALIGN(sizeof(struct nodelist));
@@ -9083,12 +8768,10 @@ sizenodelist(lp)
 }
 
 
-
 static union node *
-copynode(n)
-       union node *n;
+copynode(const union node *n)
 {
-       union node *new;
+      union node *new;
 
       if (n == NULL)
            return NULL;
@@ -9171,13 +8854,12 @@ copynode(n)
            break;
       };
       new->type = n->type;
-       return new;
+      return new;
 }
 
 
 static struct nodelist *
-copynodelist(lp)
-       struct nodelist *lp;
+copynodelist(const struct nodelist *lp)
 {
        struct nodelist *start;
        struct nodelist **lpp;
@@ -9195,10 +8877,8 @@ copynodelist(lp)
 }
 
 
-
 static char *
-nodesavestr(s)
-       char   *s;
+nodesavestr(const char *s)
 {
 #ifdef _GNU_SOURCE
        char   *rtn = funcstring;
@@ -9206,8 +8886,8 @@ nodesavestr(s)
        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')
@@ -9277,9 +8957,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;
@@ -9318,39 +9018,17 @@ options(cmdline)
                                if (q == NULL || minusc != NULL)
                                        error("Bad -c option");
                                minusc = q;
-#ifdef NOHACK
-                               break;
-#endif
-                       } else if (c == 'o') {
-                               minus_o(*argptr, val);
-                               if (*argptr)
-                                       argptr++;
-                       } else {
-                               setoption(c, val);
-                       }
-               }
-       }
-}
-
-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;
+#ifdef NOHACK
+                               break;
+#endif
+                       } else if (c == 'o') {
+                               minus_o(*argptr, val);
+                               if (*argptr)
+                                       argptr++;
+                       } else {
+                               setoption(c, val);
                        }
-               error("Illegal option -o %s", name);
+               }
        }
 }
 
@@ -9607,7 +9285,7 @@ atend:
                                err |= setvarsafe("OPTARG", s, 0);
                        }
                        else {
-                               outfmt(&errout, "Illegal option -%c\n", c);
+                               out2fmt("Illegal option -%c\n", c);
                                (void) unsetvar("OPTARG");
                        }
                        c = '?';
@@ -9626,7 +9304,7 @@ atend:
                                c = ':';
                        }
                        else {
-                               outfmt(&errout, "No arg for -%c option\n", c);
+                               out2fmt("No arg for -%c option\n", c);
                                (void) unsetvar("OPTARG");
                                c = '?';
                        }
@@ -9648,7 +9326,7 @@ bad:
        p = NULL;
 out:
        *optoff = p ? p - *(optnext - 1) : -1;
-       fmtstr(s, sizeof(s), "%d", *myoptind);
+       snprintf(s, sizeof(s), "%d", *myoptind);
        err |= setvarsafe("OPTIND", s, VNOFUNC);
        s[0] = c;
        s[1] = '\0';
@@ -9656,7 +9334,6 @@ out:
        if (err) {
                *myoptind = 1;
                *optoff = -1;
-               flushall();
                exraise(EXERROR);
        }
        return done;
@@ -9675,9 +9352,8 @@ out:
  */
 
 static int
-nextopt(optstring)
-       const char *optstring;
-       {
+nextopt(const char *optstring)
+{
        char *p;
        const char *q;
        char c;
@@ -9707,249 +9383,23 @@ nextopt(optstring)
        return c;
 }
 
-
-/*
- * Shell output routines.  We use our own output routines because:
- *      When a builtin command is interrupted we have to discard
- *              any pending output.
- *      When a builtin command appears in back quotes, we want to
- *              save the output of the command in a region obtained
- *              via malloc, rather than doing a fork and reading the
- *              output of the command via a pipe.
- *      Our output routines may be smaller than the stdio routines.
- */
-
-
-
-#ifndef USE_GLIBC_STDIO
-static void __outstr (const char *, size_t, struct output*);
-#endif
-
-
-#ifndef USE_GLIBC_STDIO
-static void
-__outstr(const char *p, size_t len, struct output *dest) {
-       if (!dest->bufsize) {
-               dest->nleft = 0;
-       } else if (dest->buf == NULL) {
-               if (len > dest->bufsize && dest->fd == MEM_OUT) {
-                       dest->bufsize = len;
-               }
-               INTOFF;
-               dest->buf = ckmalloc(dest->bufsize);
-               dest->nextc = dest->buf;
-               dest->nleft = dest->bufsize;
-               INTON;
-       } else if (dest->fd == MEM_OUT) {
-               int offset;
-
-               offset = dest->bufsize;
-               INTOFF;
-               if (dest->bufsize >= len) {
-                       dest->bufsize <<= 1;
-               } else {
-                       dest->bufsize += len;
-               }
-               dest->buf = ckrealloc(dest->buf, dest->bufsize);
-               dest->nleft = dest->bufsize - offset;
-               dest->nextc = dest->buf + offset;
-               INTON;
-       } else {
-               flushout(dest);
-       }
-
-       if (len < dest->nleft) {
-               dest->nleft -= len;
-               memcpy(dest->nextc, p, len);
-               dest->nextc += len;
-               return;
-       }
-
-       if (xwrite(dest->fd, p, len) < len)
-               dest->flags |= OUTPUT_ERR;
-}
-#endif
-
-
-static void
-outstr(const char *p, struct output *file)
-{
-#ifdef USE_GLIBC_STDIO
-       INTOFF;
-       fputs(p, file->stream);
-       INTON;
-#else
-       size_t len;
-
-       if (!*p) {
-               return;
-       }
-       len = strlen(p);
-       if ((file->nleft -= len) > 0) {
-               memcpy(file->nextc, p, len);
-               file->nextc += len;
-               return;
-       }
-       __outstr(p, len, file);
-#endif
-}
-
-
-#ifndef USE_GLIBC_STDIO
-
-
-static void
-outcslow(c, dest)
-       char c;
-       struct output *dest;
-       {
-       __outstr(&c, 1, dest);
-}
-#endif
-
-
 static void
 flushall() {
-       flushout(&output);
-#ifdef FLUSHERR
-       flushout(&errout);
-#endif
-}
-
-
-static void
-flushout(dest)
-       struct output *dest;
-       {
-#ifdef USE_GLIBC_STDIO
        INTOFF;
-       fflush(dest->stream);
+       fflush(stdout);
        INTON;
-#else
-       size_t len;
-
-       len = dest->nextc - dest->buf;
-       if (dest->buf == NULL || !len || dest->fd < 0)
-               return;
-       dest->nextc = dest->buf;
-       dest->nleft = dest->bufsize;
-       if (xwrite(dest->fd, dest->buf, len) < len)
-               dest->flags |= OUTPUT_ERR;
-#endif
-}
-
-
-static void
-freestdout() {
-       if (output.buf) {
-               INTOFF;
-               ckfree(output.buf);
-               output.buf = NULL;
-               output.nleft = 0;
-               INTON;
-       }
-       output.flags = 0;
-}
-
-
-static void
-#ifdef __STDC__
-outfmt(struct output *file, const char *fmt, ...)
-#else
-static void
-outfmt(va_alist)
-       va_dcl
-#endif
-{
-       va_list ap;
-#ifndef __STDC__
-       struct output *file;
-       const char *fmt;
-
-       va_start(ap);
-       file = va_arg(ap, struct output *);
-       fmt = va_arg(ap, const char *);
-#else
-       va_start(ap, fmt);
-#endif
-       doformat(file, fmt, ap);
-       va_end(ap);
 }
 
 
 static void
-#ifdef __STDC__
-out1fmt(const char *fmt, ...)
-#else
-out1fmt(va_alist)
-       va_dcl
-#endif
+out2fmt(const char *fmt, ...)
 {
        va_list ap;
-#ifndef __STDC__
-       const char *fmt;
-
-       va_start(ap);
-       fmt = va_arg(ap, const char *);
-#else
        va_start(ap, fmt);
-#endif
-       doformat(out1, fmt, ap);
+       vfprintf(stderr, fmt, ap);
        va_end(ap);
 }
 
-static void
-#ifdef __STDC__
-fmtstr(char *outbuf, size_t length, const char *fmt, ...)
-#else
-fmtstr(va_alist)
-       va_dcl
-#endif
-{
-       va_list ap;
-#ifndef __STDC__
-       char *outbuf;
-       size_t length;
-       const char *fmt;
-
-       va_start(ap);
-       outbuf = va_arg(ap, char *);
-       length = va_arg(ap, size_t);
-       fmt = va_arg(ap, const char *);
-#else
-       va_start(ap, fmt);
-#endif
-       INTOFF;
-       vsnprintf(outbuf, length, fmt, ap);
-       INTON;
-}
-
-#ifndef USE_GLIBC_STDIO
-
-static void
-doformat(struct output *dest, const char *f, va_list ap)
-{
-       char *pm;
-       int size = BUFSIZ;
-
-       while(size) {
-               int nchars;
-
-               pm = xmalloc(size);
-               nchars = vsnprintf(pm, size, f, ap);
-               if(nchars > -1) {
-                       outstr(pm, dest);
-                       size = 0;
-                       }
-               else
-                       size *= 2;
-               free(pm);
-       }
-}
-#endif
-
-
-
 /*
  * Version of write which resumes after a signal is caught.
  */
@@ -9980,29 +9430,6 @@ xwrite(int fd, const char *buf, int nbytes)
 }
 
 
-#ifdef USE_GLIBC_STDIO
-static void initstreams() {
-       output.stream = stdout;
-       errout.stream = stderr;
-}
-
-
-static void
-openmemout() {
-       INTOFF;
-       memout.stream = open_memstream(&memout.buf, &memout.bufsize);
-       INTON;
-}
-
-
-static int
-__closememout() {
-       int error;
-       error = fclose(memout.stream);
-       memout.stream = NULL;
-       return error;
-}
-#endif
 /*
  * Shell command parser.
  */
@@ -10722,7 +10149,9 @@ xxreadtoken() {
                c = pgetc_macro();
                switch (c) {
                case ' ': case '\t':
+#ifdef ASH_ALIAS
                case PEOA:
+#endif
                        continue;
                case '#':
                        while ((c = pgetc()) != '\n' && c != PEOF);
@@ -10969,9 +10398,11 @@ readtoken1(firstc, syntax, eofmark, striptabs)
                        default:
                                if (varnest == 0)
                                        goto endword;   /* exit outer loop */
-                               if (c != PEOA) {
+#ifdef ASH_ALIAS
+                               if (c != PEOA)
+#endif
                                        USTPUTC(c, out);
-                               }
+
                        }
                        c = pgetc_macro();
                }
@@ -11459,10 +10890,10 @@ synexpect(token)
        char msg[64];
 
        if (token >= 0) {
-               fmtstr(msg, 64, "%s unexpected (expecting %s)",
+               snprintf(msg, 64, "%s unexpected (expecting %s)",
                        tokname[lasttoken], tokname[token]);
        } else {
-               fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
+               snprintf(msg, 64, "%s unexpected", tokname[lasttoken]);
        }
        synerror(msg);
        /* NOTREACHED */
@@ -11473,8 +10904,8 @@ static void
 synerror(const char *msg)
 {
        if (commandname)
-               outfmt(&errout, "%s: %d: ", commandname, startlinno);
-       outfmt(&errout, "Syntax error: %s\n", msg);
+               out2fmt("%s: %d: ", commandname, startlinno);
+       out2fmt("Syntax error: %s\n", msg);
        error((char *)NULL);
        /* NOTREACHED */
 }
@@ -11484,26 +10915,21 @@ synerror(const char *msg)
  * called by editline -- any expansions to the prompt
  *    should be added here.
  */
-static 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);
 }
 
 
@@ -11519,98 +10945,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, is saved in memory.
+ * 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) ||
-                   (fd == fileno2)) {
-                       if (newfd == fd) {
-                               try++;
-                       } else if ((i = fcntl(fd, F_DUPFD, 10)) == -1) {
-                               switch (errno) {
-                               case EBADF:
-                                       if (!try) {
-                                               dupredirect(n, newfd, memory);
-                                               try++;
-                                               break;
-                                       }
-                                       /* FALLTHROUGH*/
-                               default:
-                                       if (newfd >= 0) {
-                                               close(newfd);
-                                       }
-                                       INTON;
-                                       error("%d: %m", fd);
-                                       /* NOTREACHED */
-                               }
-                       }
-                       if (!try) {
-                               close(fd);
-                               if (flags & REDIR_PUSH) {
-                                       sv->renamed[fd] = i;
-                               }
-                               if (fd == fileno2) {
-                                       fileno2 = 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 (memory[1])
-               out1 = &memout;
-       if (memory[2])
-               out2 = &memout;
+       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;
 
@@ -11678,17 +11115,90 @@ 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
+redirect(union node *redir, int flags)
+{
+       union node *n;
+       struct redirtab *sv = NULL;
+       int i;
+       int fd;
+       int newfd;
+       int try;
+       int fd1dup = flags & REDIR_BACKQ;; /* stdout `cmd` redir to pipe */
+
+       if (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 */
+
+               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;
+       }
+}
+
+
 static void
-dupredirect(union node *redir, int f, char memory[10])
+dupredirect(const union node *redir, int f, int fd1dup)
 {
        int fd = redir->nfile.fd;
 
-       memory[fd] = 0;
+       if(fd==1)
+               fd1dup = 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
+                       if (redir->ndup.dupfd!=1 || fd1dup!=1)
                                dup_as_newfd(redir->ndup.dupfd, fd);
                }
                return;
@@ -11702,48 +11212,6 @@ dupredirect(union node *redir, int f, char memory[10])
 }
 
 
-/*
- * 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);
-       }
-out:
-       close(pip[1]);
-       return pip[0];
-}
-
 
 /*
  * Undo the effects of the last redirection.
@@ -11765,9 +11233,6 @@ popredir(void)
                                dup_as_newfd(rp->renamed[i], i);
                                close(rp->renamed[i]);
                        }
-                       if (rp->renamed[i] == fileno2) {
-                               fileno2 = i;
-                       }
                }
        }
        redirlist = rp->next;
@@ -11788,17 +11253,10 @@ clearredir(void) {
                for (i = 0 ; i < 10 ; i++) {
                        if (rp->renamed[i] >= 0) {
                                close(rp->renamed[i]);
-                               if (rp->renamed[i] == fileno2) {
-                                       fileno2 = -1;
-                               }
                        }
                        rp->renamed[i] = EMPTY;
                }
        }
-       if (fileno2 != 2 && fileno2 >= 0) {
-               close(fileno2);
-               fileno2 = -1;
-       }
 }
 
 
@@ -11825,70 +11283,17 @@ 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__
+#ifndef S_ISTXT
+#if defined(__GLIBC__) && __GLIBC__ >= 2
 #define S_ISTXT __S_ISVTX
+#else
+#define S_ISTXT S_ISVTX
+#endif
 #endif
 
 #define SET_LEN 6               /* initial # of bitcmd struct to malloc */
@@ -12046,7 +11451,7 @@ setmode(p)
         * If an absolute number, get it and return; disallow non-octal digits
         * or illegal bits.
         */
-       if (isdigit((unsigned char)*p)) {
+       if (is_digit((unsigned char)*p)) {
                perm = (mode_t)strtol(p, &ep, 8);
                if (*ep || perm & ~(STANDARD_BITS|S_ISTXT)) {
                        free(saveset);
@@ -12701,7 +12106,7 @@ trapcmd(argc, argv)
                                char *p;
 
                                p = single_quote(trap[signo]);
-                               out1fmt("trap -- %s %s\n", p,
+                               printf("trap -- %s %s\n", p,
                                        signal_names[signo] + (signo ? 3 : 0)
                                );
                                stunalloc(p);
@@ -13115,7 +12520,7 @@ listsetvar(mylist)
  * Find the value of a variable.  Returns NULL if not set.
  */
 
-static char *
+static const char *
 lookupvar(name)
        const char *name;
        {
@@ -13133,11 +12538,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))
@@ -13419,9 +12823,8 @@ unsetcmd(argc, argv)
  */
 
 static int
-unsetvar(s)
-       const char *s;
-       {
+unsetvar(const char *s)
+{
        struct var **vpp;
        struct var *vp;
 
@@ -13455,9 +12858,8 @@ unsetvar(s)
  */
 
 static struct var **
-hashvar(p)
-       const char *p;
-       {
+hashvar(const char *p)
+{
        unsigned int hashval;
 
        hashval = ((unsigned char) *p) << 4;
@@ -13475,9 +12877,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;
@@ -13504,10 +12905,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);
                        }
                }
@@ -13528,7 +12927,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.5 2001/07/05 05:24:12 andersen Exp $
+ * $Id: ash.c,v 1.8 2001/07/10 06:09:16 andersen Exp $
  */
 static int timescmd (int argc, char **argv)
 {