Stupidity-1, Erik-0
[oweals/busybox.git] / shell / ash.c
index 59aa336cd63c52f6656e690ee4f5a9bdeb5cefbc..0f951e84d835b5991e3ae2b5335ed1eca66c6f78 100644 (file)
  * rewrite arith.y to micro stack based cryptic algorithm by
  * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
  *
- * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2003 to be
+ * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
+ * dynamic variables.
+ *
+ * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2004 to be
  * used in busybox and size optimizations,
- * support locale, rewrited arith (see notes to this)
+ * rewrote arith (see notes to this), added locale support,
+ * rewrote dynamic variables.
  *
  */
 
@@ -88,7 +92,7 @@
 #include <signal.h>
 #include <stdint.h>
 #include <sysexits.h>
-
+#include <time.h>
 #include <fnmatch.h>
 
 
@@ -170,7 +174,7 @@ static const char not_found_msg[] = "%s: not found";
  * We enclose jmp_buf in a structure so that we can declare pointers to
  * jump locations.  The global variable handler contains the location to
  * jump to when an exception occurs, and the global variable exception
- * contains a code identifying the exeception.  To implement nested
+ * contains a code identifying the exception.  To implement nested
  * exception handlers, the user should save the value of handler on entry
  * to an inner scope, set handler to point to a jmploc structure for the
  * inner scope, and restore handler on exit from the scope.
@@ -550,6 +554,29 @@ static int parselleft;                  /* copy of parsefile->lleft */
 
 /* next character in input buffer */
 static char *parsenextc;                /* copy of parsefile->nextc */
+
+struct strpush {
+       struct strpush *prev;   /* preceding string on stack */
+       char *prevstring;
+       int prevnleft;
+#ifdef CONFIG_ASH_ALIAS
+       struct alias *ap;       /* if push was associated with an alias */
+#endif
+       char *string;           /* remember the string since it may change */
+};
+
+struct parsefile {
+       struct parsefile *prev; /* preceding file on stack */
+       int linno;              /* current line */
+       int fd;                 /* file descriptor (or -1 if string) */
+       int nleft;              /* number of chars left in this line */
+       int lleft;              /* number of chars left in this buffer */
+       char *nextc;            /* next char in buffer */
+       char *buf;              /* input buffer */
+       struct strpush *strpush; /* for pushing strings at this level */
+       struct strpush basestrpush; /* so pushing one is fast */
+};
+
 static struct parsefile basepf;         /* top level input file */
 static char basebuf[IBUFSIZ];           /* buffer for top level input file */
 static struct parsefile *parsefile = &basepf;  /* current input file */
@@ -1191,7 +1218,7 @@ static char *nodesavestr(char *);
 
 
 
-static void evalstring(char *, int);
+static void evalstring(char *);
 union node;     /* BLETCH for ansi C */
 static void evaltree(union node *, int);
 static void evalbackcmd(union node *, struct backcmd *);
@@ -1419,7 +1446,21 @@ static void defun(char *, union node *);
 static void unsetfunc(const char *);
 
 #ifdef CONFIG_ASH_MATH_SUPPORT
-static int dash_arith(const char *);
+#ifdef CONFIG_ASH_MATH_SUPPORT_64
+typedef int64_t arith_t;
+#else
+typedef long arith_t;
+#endif
+static arith_t dash_arith(const char *);
+static arith_t arith(const char *expr, int *perrcode);
+#endif
+
+#ifdef CONFIG_ASH_RANDOM_SUPPORT
+static unsigned long rseed;
+static void change_random(const char *);
+# ifndef DYNAMIC_VAR
+#  define DYNAMIC_VAR
+# endif
 #endif
 
 /*      $NetBSD: init.h,v 1.9 2002/11/24 22:35:40 christos Exp $        */
@@ -1442,14 +1483,17 @@ static void reset(void);
 #define VNOFUNC         0x40    /* don't call the callback function */
 #define VNOSET          0x80    /* do not set variable - just readonly test */
 #define VNOSAVE         0x100   /* when text is on the heap before setvareq */
-
+#ifdef DYNAMIC_VAR
+# define VDYNAMIC        0x200   /* dynamic variable */
+# else
+# define VDYNAMIC        0
+#endif
 
 struct var {
        struct var *next;               /* next entry in hash list */
        int flags;                      /* flags are defined above */
        const char *text;               /* name=value */
-       void (*func)(const char *);
-                                       /* function to be called when  */
+       void (*func)(const char *);     /* function to be called when  */
                                        /* the variable gets set/unset */
 };
 
@@ -1477,6 +1521,7 @@ static void change_lc_all(const char *value);
 static void change_lc_ctype(const char *value);
 #endif
 
+
 #define VTABSIZE 39
 
 static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
@@ -1501,18 +1546,21 @@ static struct var varinit[] = {
 #endif
 
        { 0,    VSTRFIXED|VTEXTFIXED,           defpathvar,     changepath },
-       { 0,    VSTRFIXED|VTEXTFIXED,           "PS1=$ ",       0 },
-       { 0,    VSTRFIXED|VTEXTFIXED,           "PS2=> ",       0 },
-       { 0,    VSTRFIXED|VTEXTFIXED,           "PS4=+ ",       0 },
+       { 0,    VSTRFIXED|VTEXTFIXED,           "PS1=$ ",       0          },
+       { 0,    VSTRFIXED|VTEXTFIXED,           "PS2=> ",       0          },
+       { 0,    VSTRFIXED|VTEXTFIXED,           "PS4=+ ",       0          },
 #ifdef CONFIG_ASH_GETOPTS
        { 0,    VSTRFIXED|VTEXTFIXED,           "OPTIND=1",     getoptsreset },
 #endif
+#ifdef CONFIG_ASH_RANDOM_SUPPORT
+       {0, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
+#endif
 #ifdef CONFIG_LOCALE_SUPPORT
-       {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL=", change_lc_all},
-       {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE=", change_lc_ctype},
+       {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
+       {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
 #endif
 #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
-       {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE=", NULL},
+       {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
 #endif
 };
 
@@ -1528,7 +1576,11 @@ static struct var varinit[] = {
 #define vps2 (&vps1)[1]
 #define vps4 (&vps2)[1]
 #define voptind (&vps4)[1]
-
+#ifdef CONFIG_ASH_GETOPTS
+#define vrandom (&voptind)[1]
+#else
+#define vrandom (&vps4)[1]
+#endif
 #define defpath (defpathvar + 5)
 
 /*
@@ -1573,28 +1625,6 @@ static inline int varequal(const char *a, const char *b) {
 
 static int loopnest;            /* current loop nesting level */
 
-struct strpush {
-       struct strpush *prev;   /* preceding string on stack */
-       char *prevstring;
-       int prevnleft;
-#ifdef CONFIG_ASH_ALIAS
-       struct alias *ap;       /* if push was associated with an alias */
-#endif
-       char *string;           /* remember the string since it may change */
-};
-
-struct parsefile {
-       struct parsefile *prev; /* preceding file on stack */
-       int linno;              /* current line */
-       int fd;                 /* file descriptor (or -1 if string) */
-       int nleft;              /* number of chars left in this line */
-       int lleft;              /* number of chars left in this buffer */
-       char *nextc;            /* next char in buffer */
-       char *buf;              /* input buffer */
-       struct strpush *strpush; /* for pushing strings at this level */
-       struct strpush basestrpush; /* so pushing one is fast */
-};
-
 /*
  * The parsefile structure pointed to by the global variable parsefile
  * contains information about the current file being read.
@@ -1618,12 +1648,11 @@ extern char **environ;
 static void outstr(const char *, FILE *);
 static void outcslow(int, FILE *);
 static void flushall(void);
-static void flushout(FILE *);
+static void flusherr(void);
 static int  out1fmt(const char *, ...)
     __attribute__((__format__(__printf__,1,2)));
 static int fmtstr(char *, size_t, const char *, ...)
     __attribute__((__format__(__printf__,3,4)));
-static void xwrite(int, const void *, size_t);
 
 static int preverrout_fd;   /* save fd2 before print debug if xflag is set. */
 
@@ -1636,7 +1665,7 @@ static void out1str(const char *p)
 static void out2str(const char *p)
 {
        outstr(p, stderr);
-       flushout(stderr);
+       flusherr();
 }
 
 /*
@@ -2679,7 +2708,7 @@ static const struct builtincmd bltin = {
  */
 
 /*
- * The eval commmand.
+ * The eval command.
  */
 
 static int
@@ -2703,7 +2732,7 @@ evalcmd(int argc, char **argv)
                        STPUTC('\0', concat);
                        p = grabstackstr(concat);
                }
-               evalstring(p, EV_TESTED);
+               evalstring(p);
        }
        return exitstatus;
 }
@@ -2714,7 +2743,7 @@ evalcmd(int argc, char **argv)
  */
 
 static void
-evalstring(char *s, int flag)
+evalstring(char *s)
 {
        union node *n;
        struct stackmark smark;
@@ -2723,7 +2752,7 @@ evalstring(char *s, int flag)
        setinputstring(s);
 
        while ((n = parsecmd(0)) != NEOF) {
-               evaltree(n, flag);
+               evaltree(n, 0);
                popstackmark(&smark);
                if (evalskip)
                        break;
@@ -3275,7 +3304,7 @@ evalcommand(union node *cmd, int flags)
                        }
                        sp = arglist.list;
                }
-               xwrite(preverrout_fd, "\n", 1);
+               bb_full_write(preverrout_fd, "\n", 1);
        }
 
        cmd_is_exec = 0;
@@ -3292,7 +3321,7 @@ evalcommand(union node *cmd, int flags)
                        find_command(argv[0], &cmdentry, cmd_flag, path);
                        if (cmdentry.cmdtype == CMDUNKNOWN) {
                                status = 127;
-                               flushout(stderr);
+                               flusherr();
                                goto bail;
                        }
 
@@ -3475,10 +3504,17 @@ funcdone:
 }
 
 
+static inline int
+goodname(const char *p)
+{
+       return !*endofname(p);
+}
+
 /*
  * 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 child.  The check for "goodname" is an overly conservative
+ * check that the name will not be subject to expansion.
  */
 
 static void
@@ -3487,7 +3523,9 @@ prehash(union node *n)
        struct cmdentry entry;
 
        if (n->type == NCMD && n->ncmd.args)
-               find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
+               if (goodname(n->ncmd.args->narg.text))
+                       find_command(n->ncmd.args->narg.text, &entry, 0,
+                                    pathval());
 }
 
 
@@ -3685,15 +3723,9 @@ tryexec(char *cmd, char **argv, char **envp)
        int flg_bb = 0;
        char *name = cmd;
 
-#ifdef CONFIG_FEATURE_SH_APPLETS_ALWAYS_WIN
-       name = bb_get_last_path_component(name);
-       if(find_applet_by_name(name) != NULL)
-               flg_bb = 1;
-#else
        if(strchr(name, '/') == NULL && find_applet_by_name(name) != NULL) {
                flg_bb = 1;
        }
-#endif
        if(flg_bb) {
                char **ap;
                char **new;
@@ -3729,7 +3761,10 @@ repeat:
                for (ap = argv; *ap; ap++)
                        ;
                ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
-               *ap++ = cmd = (char *)DEFAULT_SHELL;
+               ap[1] = cmd;
+               *ap = cmd = (char *)DEFAULT_SHELL;
+               ap += 2;
+               argv++;
                while ((*ap++ = *argv++))
                        ;
                argv = new;
@@ -4492,10 +4527,9 @@ static char *exptilde(char *, char *, int);
 static void expbackq(union node *, int, int);
 static const char *subevalvar(char *, char *, int, int, int, int, int);
 static char *evalvar(char *, int);
-static int varisset(char *, int);
 static void strtodest(const char *, int, int);
 static void memtodest(const char *p, size_t len, int syntax, int quotes);
-static void varvalue(char *, int, int);
+static ssize_t varvalue(char *, int, int);
 static void recordregion(int, int, int);
 static void removerecordregions(int);
 static void ifsbreakup(char *, struct arglist *);
@@ -4503,7 +4537,7 @@ static void ifsfree(void);
 static void expandmeta(struct strlist *, int);
 static int patmatch(char *, const char *);
 
-static int cvtnum(long);
+static int cvtnum(arith_t);
 static size_t esclen(const char *, const char *);
 static char *scanleft(char *, char *, char *, char *, int, int);
 static char *scanright(char *, char *, char *, char *, int, int);
@@ -4548,7 +4582,7 @@ expandhere(union node *arg, int fd)
 {
        herefd = fd;
        expandarg(arg, (struct arglist *)NULL, 0);
-       xwrite(fd, stackblock(), expdest - (char *)stackblock());
+       bb_full_write(fd, stackblock(), expdest - (char *)stackblock());
 }
 
 
@@ -5108,9 +5142,8 @@ evalvar(char *p, int flag)
        char *var;
        int patloc;
        int c;
-       int set;
        int startloc;
-       size_t varlen;
+       ssize_t varlen;
        int easy;
        int quotes;
        int quoted;
@@ -5121,48 +5154,22 @@ evalvar(char *p, int flag)
        quoted = varflags & VSQUOTE;
        var = p;
        easy = (!quoted || (*var == '@' && shellparam.nparam));
-       varlen = 0;
        startloc = expdest - (char *)stackblock();
        p = strchr(p, '=') + 1;
 
-       if (!is_name(*var)) {
-               set = varisset(var, varflags & VSNUL);
-               set--;
-               if (subtype == VSPLUS)
-                       goto vsplus;
-               if (++set) {
-                       varvalue(var, quoted, flag);
-                       if (subtype == VSLENGTH) {
-                               varlen =
-                                       expdest - (char *)stackblock() -
-                                       startloc;
-                               STADJUST(-varlen, expdest);
-                               goto vslen;
-                       }
-               }
-       } else {
-               const char *val;
 again:
-               /* jump here after setting a variable with ${var=text} */
-               val = lookupvar(var);
-               set = !val || ((varflags & VSNUL) && !*val);
-               if (subtype == VSPLUS)
-                       goto vsplus;
-               if (--set) {
-                       varlen = strlen(val);
-                       if (subtype == VSLENGTH)
-                               goto vslen;
-                       memtodest(
-                               val, varlen, quoted ? DQSYNTAX : BASESYNTAX,
-                               quotes
-                       );
-               }
-       }
+       varlen = varvalue(var, varflags, flag);
+       if (varflags & VSNUL)
+               varlen--;
 
+       if (subtype == VSPLUS) {
+               varlen = -1 - varlen;
+               goto vsplus;
+       }
 
        if (subtype == VSMINUS) {
 vsplus:
-               if (!set) {
+               if (varlen < 0) {
                        argstr(
                                p, flag | EXP_TILDE |
                                        (quoted ?  EXP_QWORD : EXP_WORD)
@@ -5175,7 +5182,7 @@ vsplus:
        }
 
        if (subtype == VSASSIGN || subtype == VSQUESTION) {
-               if (!set) {
+               if (varlen < 0) {
                        if (subevalvar(p, var, 0, subtype, startloc,
                                       varflags, 0)) {
                                varflags &= ~VSNUL;
@@ -5193,12 +5200,11 @@ vsplus:
                goto end;
        }
 
-       if (!set && uflag)
+       if (varlen < 0 && uflag)
                varunset(p, var, 0, 0);
 
        if (subtype == VSLENGTH) {
-vslen:
-               cvtnum(varlen);
+               cvtnum(varlen > 0 ? varlen : 0);
                goto record;
        }
 
@@ -5222,7 +5228,7 @@ record:
        }
 #endif
 
-       if (set) {
+       if (varlen >= 0) {
                /*
                 * Terminate the string and start recording the pattern
                 * right after it
@@ -5248,7 +5254,7 @@ end:
                        if ((c = *p++) == CTLESC)
                                p++;
                        else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
-                               if (set)
+                               if (varlen >= 0)
                                        argbackq = argbackq->next;
                        } else if (c == CTLVAR) {
                                if ((*p++ & VSTYPE) != VSNORMAL)
@@ -5263,47 +5269,6 @@ end:
 }
 
 
-
-/*
- * Test whether a specialized variable is set.
- */
-
-static int
-varisset(char *name, int nulok)
-{
-       if (*name == '!')
-               return backgndpid != 0;
-       else if (*name == '@' || *name == '*') {
-               if (*shellparam.p == NULL)
-                       return 0;
-
-               if (nulok) {
-                       char **av;
-
-                       for (av = shellparam.p; *av; av++)
-                               if (**av != '\0')
-                                       return 1;
-                       return 0;
-               }
-       } else if (is_digit(*name)) {
-               char *ap;
-               int num = atoi(name);
-
-               if (num > shellparam.nparam)
-                       return 0;
-
-               if (num == 0)
-                       ap = arg0;
-               else
-                       ap = shellparam.p[num - 1];
-
-               if (nulok && (ap == NULL || *ap == '\0'))
-                       return 0;
-       }
-       return 1;
-}
-
-
 /*
  * Put a string on the stack.
  */
@@ -5338,19 +5303,24 @@ strtodest(const char *p, int syntax, int quotes)
  * Add the value of a specialized variable to the stack string.
  */
 
-static void
-varvalue(char *name, int quoted, int flags)
+static ssize_t
+varvalue(char *name, int varflags, int flags)
 {
        int num;
        char *p;
        int i;
-       int sep;
+       int sep = 0;
        int sepq = 0;
+       ssize_t len = 0;
        char **ap;
        int syntax;
-       int allow_split = flags & EXP_FULL;
+       int quoted = varflags & VSQUOTE;
+       int subtype = varflags & VSTYPE;
        int quotes = flags & (EXP_FULL | EXP_CASE);
 
+       if (quoted && (flags & EXP_FULL))
+               sep = 1 << CHAR_BIT;
+
        syntax = quoted ? DQSYNTAX : BASESYNTAX;
        switch (*name) {
        case '$':
@@ -5364,48 +5334,86 @@ varvalue(char *name, int quoted, int flags)
                goto numvar;
        case '!':
                num = backgndpid;
+               if (num == 0)
+                       return -1;
 numvar:
-               cvtnum(num);
+               len = cvtnum(num);
                break;
        case '-':
-               for (i = 0 ; i < NOPTS ; i++) {
-                       if (optlist[i])
-                               STPUTC(optletters(i), expdest);
+               p = makestrspace(NOPTS, expdest);
+               for (i = NOPTS - 1; i >= 0; i--) {
+                       if (optlist[i]) {
+                               USTPUTC(optletters(i), p);
+                               len++;
+                       }
                }
+               expdest = p;
                break;
        case '@':
-               if (allow_split && quoted) {
-                       sep = 1 << CHAR_BIT;
+               if (sep)
                        goto param;
-               }
                /* fall through */
        case '*':
                sep = ifsset() ? ifsval()[0] : ' ';
-               if (quotes) {
-                       sepq = (SIT(sep, syntax) == CCTL) || (SIT(sep, syntax) == CBACK);
-               }
+               if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK))
+                       sepq = 1;
 param:
-               for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
-                       strtodest(p, syntax, quotes);
-                       if (*ap && sep) {
-                               p = expdest;
+               if (!(ap = shellparam.p))
+                       return -1;
+               while ((p = *ap++)) {
+                       size_t partlen;
+
+                       partlen = strlen(p);
+
+                       len += partlen;
+                       if (len > partlen && sep) {
+                               char *q;
+
+                               len++;
+                               if (subtype == VSPLUS || subtype == VSLENGTH) {
+                                       continue;
+                               }
+                               q = expdest;
                                if (sepq)
-                                       STPUTC(CTLESC, p);
-                               STPUTC(sep, p);
-                               expdest = p;
+                                       STPUTC(CTLESC, q);
+                               STPUTC(sep, q);
+                               expdest = q;
                        }
+
+                       if (!(subtype == VSPLUS || subtype == VSLENGTH))
+                               memtodest(p, partlen, syntax, quotes);
                }
-               break;
+               return len;
        case '0':
-               strtodest(arg0, syntax, quotes);
-               break;
-       default:
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
                num = atoi(name);
-               if (num > 0 && num <= shellparam.nparam) {
-                       strtodest(shellparam.p[num - 1], syntax, quotes);
-               }
-               break;
+               if (num < 0 || num > shellparam.nparam)
+                       return -1;
+               p = num ? shellparam.p[num - 1] : arg0;
+               goto value;
+       default:
+               p = lookupvar(name);
+value:
+               if (!p)
+                       return -1;
+
+               len = strlen(p);
+               if (!(subtype == VSPLUS || subtype == VSLENGTH))
+                       memtodest(p, len, syntax, quotes);
+               return len;
        }
+
+       if (subtype == VSPLUS || subtype == VSLENGTH)
+               STADJUST(-len, expdest);
+       return len;
 }
 
 
@@ -5900,12 +5908,16 @@ casematch(union node *pattern, char *val)
  */
 
 static int
-cvtnum(long num)
+cvtnum(arith_t num)
 {
        int len;
 
        expdest = makestrspace(32, expdest);
+#ifdef CONFIG_ASH_MATH_SUPPORT_64
+       len = fmtstr(expdest, 32, "%lld", (long long) num);
+#else
        len = fmtstr(expdest, 32, "%ld", num);
+#endif
        STADJUST(len, expdest);
        return len;
 }
@@ -6037,10 +6049,16 @@ retry:
        if (!iflag || parsefile->fd)
                nr = safe_read(parsefile->fd, buf, BUFSIZ - 1);
        else {
+               cmdedit_path_lookup = pathval();
                nr = cmdedit_read_input((char *) cmdedit_prompt, buf);
                if(nr == 0) {
                        /* Ctrl+C presend */
-                       raise(SIGINT);
+                       if(trap[SIGINT]) {
+                               buf[0] = '\n';
+                               buf[1] = 0;
+                               raise(SIGINT);
+                               return 1;
+                       }
                        goto retry;
                }
                if(nr < 0) {
@@ -6678,25 +6696,28 @@ sprint_status(char *s, int status, int sigonly)
        int st;
 
        col = 0;
-       st = WEXITSTATUS(status);
        if (!WIFEXITED(status)) {
-               st = WSTOPSIG(status);
 #if JOBS
-               if (!WIFSTOPPED(status))
-                       st = WTERMSIG(status);
+               if (WIFSTOPPED(status))
+                       st = WSTOPSIG(status);
+               else
 #endif
+                       st = WTERMSIG(status);
                if (sigonly) {
                        if (st == SIGINT || st == SIGPIPE)
                                goto out;
+#if JOBS
                        if (WIFSTOPPED(status))
                                goto out;
+#endif
                }
                st &= 0x7f;
-               col = fmtstr(s, 32, u_signal_names(NULL, &st, 0));
+               col = fmtstr(s, 32, strsignal(st));
                if (WCOREDUMP(status)) {
                        col += fmtstr(s + col, 16, " (core dumped)");
                }
        } else if (!sigonly) {
+               st = WEXITSTATUS(status);
                if (st)
                        col = fmtstr(s, 16, "Done(%d)", st);
                else
@@ -7217,7 +7238,7 @@ forkshell(struct job *jp, union node *n, int mode)
  * the interactive program catches interrupts, the user doesn't want
  * these interrupts to also abort the loop.  The approach we take here
  * is to have the shell ignore interrupt signals while waiting for a
- * forground process to terminate, and then send itself an interrupt
+ * foreground process to terminate, and then send itself an interrupt
  * signal if the child process was terminated by an interrupt signal.
  * Unfortunately, some programs want to do a bit of cleanup and then
  * exit on interrupt; unless these processes terminate themselves by
@@ -7442,6 +7463,8 @@ cmdtxt(union node *n)
        const char *p;
        char s[2];
 
+       if (!n)
+               return;
        switch (n->type) {
        default:
 #if DEBUG
@@ -7607,7 +7630,7 @@ cmdputs(const char *s)
        int quoted = 0;
        static const char *const vstype[16] = {
                nullstr, "}", "-", "+", "?", "=",
-               "#", "##", "%", "%%"
+               "%", "%%", "#", "##", nullstr
        };
 
        nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
@@ -7897,6 +7920,10 @@ ash_main(int argc, char **argv)
        trputs("Shell args:  ");  trargs(argv);
 #endif
        rootpid = getpid();
+
+#ifdef CONFIG_ASH_RANDOM_SUPPORT
+       rseed = rootpid + ((time_t)time((time_t *)0));
+#endif
        rootshell = 1;
        init();
        setstackmark(&smark);
@@ -7939,7 +7966,7 @@ state2:
 state3:
        state = 4;
        if (minusc)
-               evalstring(minusc, 0);
+               evalstring(minusc);
 
        if (sflag || minusc == NULL) {
 #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
@@ -7981,8 +8008,8 @@ cmdloop(int top)
        int numeof = 0;
 
        TRACE(("cmdloop(%d) called\n", top));
-       setstackmark(&smark);
        for (;;) {
+               setstackmark(&smark);
                if (pendingsigs)
                        dotrap();
 #if JOBS
@@ -8013,13 +8040,11 @@ cmdloop(int top)
                        evaltree(n, 0);
                }
                popstackmark(&smark);
-               setstackmark(&smark);
-               if (evalskip == SKIPFILE) {
+               if (evalskip) {
                        evalskip = 0;
                        break;
                }
        }
-       popstackmark(&smark);
 }
 
 
@@ -8110,21 +8135,40 @@ find_dot_file(char *name)
        /* NOTREACHED */
 }
 
-int
-dotcmd(int argc, char **argv)
+static int dotcmd(int argc, char **argv)
 {
+       struct strlist *sp;
+       volatile struct shparam saveparam;
+
        exitstatus = 0;
 
-       if (argc >= 2) {                /* That's what SVR2 does */
+       for (sp = cmdenviron; sp; sp = sp->next)
+               setvareq(bb_xstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
+
+       if (argc >= 2) {        /* That's what SVR2 does */
                char *fullname;
                struct stackmark smark;
 
                setstackmark(&smark);
                fullname = find_dot_file(argv[1]);
+
+               if (argc > 2) {
+                       saveparam = shellparam;
+                       shellparam.malloc = 0;
+                       shellparam.nparam = argc - 2;
+                       shellparam.p = argv + 2;
+               };
+
                setinputfile(fullname, 1);
                commandname = fullname;
                cmdloop(0);
                popfile();
+
+               if (argc > 2) {
+                       freeparam(&shellparam);
+                       shellparam = saveparam;
+               };
+
                popstackmark(&smark);
        }
        return exitstatus;
@@ -8359,7 +8403,7 @@ growstackstr(void)
 {
        size_t len = stackblocksize();
        if (herefd >= 0 && len >= 1024) {
-               xwrite(herefd, stackblock(), len);
+               bb_full_write(herefd, stackblock(), len);
                return stackblock();
        }
        growstackblock();
@@ -9024,6 +9068,27 @@ static void change_lc_ctype(const char *value)
 
 #endif
 
+#ifdef CONFIG_ASH_RANDOM_SUPPORT
+/* Roughly copied from bash.. */
+static void change_random(const char *value)
+{
+       if(value == NULL) {
+               /* "get", generate */
+               char buf[16];
+
+               rseed = rseed * 1103515245 + 12345;
+               sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
+               /* set without recursion */
+               setvar(vrandom.text, buf, VNOFUNC);
+               vrandom.flags &= ~VNOFUNC;
+       } else {
+               /* set/reset */
+               rseed = strtoul(value, (char **)NULL, 10);
+       }
+}
+#endif
+
+
 #ifdef CONFIG_ASH_GETOPTS
 static int
 getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
@@ -9032,18 +9097,19 @@ getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *opt
        char c = '?';
        int done = 0;
        int err = 0;
-       char s[10];
-       char **optnext = optfirst + *param_optind - 1;
+       char s[12];
+       char **optnext;
+
+       if(*param_optind < 1)
+               return 1;
+       optnext = optfirst + *param_optind - 1;
 
-       if (*param_optind <= 1 || *optoff < 0 || !(*(optnext - 1)) ||
-           strlen(*(optnext - 1)) < *optoff)
+       if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
                p = NULL;
        else
-               p = *(optnext - 1) + *optoff;
+               p = optnext[-1] + *optoff;
        if (p == NULL || *p == '\0') {
                /* Current word is done, advance */
-               if (optnext == NULL)
-                       return 1;
                p = *optnext;
                if (p == NULL || *p != '-' || *++p == '\0') {
 atend:
@@ -9211,10 +9277,10 @@ flushall(void)
 }
 
 void
-flushout(FILE *dest)
+flusherr(void)
 {
        INTOFF;
-       fflush(dest);
+       fflush(stderr);
        INTON;
 }
 
@@ -9258,20 +9324,6 @@ fmtstr(char *outbuf, size_t length, const char *fmt, ...)
 }
 
 
-/*
- * Version of write which resumes after a signal is caught.
- */
-
-static void
-xwrite(int fd, const void *p, size_t n)
-{
-       ssize_t i;
-
-       do {
-               i = bb_full_write(fd, p, n);
-       } while (i < 0 && errno == EINTR);
-}
-
 
 /*      $NetBSD: parser.c,v 1.54 2002/11/24 22:35:42 christos Exp $     */
 
@@ -9313,11 +9365,6 @@ static void synerror(const char *) __attribute__((__noreturn__));
 static void setprompt(int);
 
 
-static inline int
-goodname(const char *p)
-{
-       return !*endofname(p);
-}
 
 static inline int
 isassignment(const char *p)
@@ -10773,7 +10820,7 @@ noexpand(char *text)
  * more letters, underscores, and digits).
  */
 
-char *
+static char *
 endofname(const char *name)
 {
        char *p;
@@ -10933,7 +10980,7 @@ openhere(union node *redir)
        if (redir->type == NHERE) {
                len = strlen(redir->nhere.doc->narg.text);
                if (len <= PIPESIZE) {
-                       xwrite(pip[1], redir->nhere.doc->narg.text, len);
+                       bb_full_write(pip[1], redir->nhere.doc->narg.text, len);
                        goto out;
                }
        }
@@ -10947,7 +10994,7 @@ openhere(union node *redir)
 #endif
                signal(SIGPIPE, SIG_DFL);
                if (redir->type == NHERE)
-                       xwrite(pip[1], redir->nhere.doc->narg.text, len);
+                       bb_full_write(pip[1], redir->nhere.doc->narg.text, len);
                else
                        expandhere(redir->nhere.doc, pip[1]);
                _exit(0);
@@ -11770,7 +11817,7 @@ dotrap(void)
                p = trap[p - q + 1];
                if (!p)
                        continue;
-               evalstring(p, 0);
+               evalstring(p);
                exitstatus = savestatus;
        }
 }
@@ -11853,16 +11900,17 @@ exitshell(void)
        struct jmploc loc;
        char *p;
        int status;
+       int jmp;
 
+       jmp = setjmp(loc.loc);
        status = exitstatus;
        TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
-       if (setjmp(loc.loc)) {
+       if (jmp)
                goto out;
-       }
        handler = &loc;
        if ((p = trap[0]) != NULL && *p != '\0') {
                trap[0] = NULL;
-               evalstring(p, 0);
+               evalstring(p);
        }
        flushall();
 #ifdef CONFIG_FEATURE_COMMAND_SAVEHISTORY
@@ -11894,7 +11942,7 @@ static int vpcmp(const void *, const void *);
 static struct var **findvar(struct var **, const char *);
 
 /*
- * Initialize the varable symbol tables and import the environment
+ * Initialize the variable symbol tables and import the environment
  */
 
 
@@ -11979,10 +12027,13 @@ setvareq(char *s, int flags)
        flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
        vp = *findvar(vpp, s);
        if (vp) {
-               if (vp->flags & VREADONLY) {
+               if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
+                       const char *n;
+
                        if (flags & VNOSAVE)
                                free(s);
-                       error("%.*s: is read only", strchrnul(s, '=') - s, s);
+                       n = vp->text;
+                       error("%.*s: is read only", strchrnul(n, '=') - n, n);
                }
 
                if (flags & VNOSET)
@@ -12039,9 +12090,21 @@ lookupvar(const char *name)
 {
        struct var *v;
 
-       if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) {
-               return strchrnul(v->text, '=') + 1;
+       if ((v = *findvar(hashvar(name), name))) {
+#ifdef DYNAMIC_VAR
+       /*
+        * Dynamic variables are implemented roughly the same way they are
+        * in bash. Namely, they're "special" so long as they aren't unset.
+        * As soon as they're unset, they're no longer dynamic, and dynamic
+        * lookup will no longer happen at that point. -- PFM.
+        */
+               if((v->flags & VDYNAMIC))
+                       (*v->func)(NULL);
+#endif
+               if(!(v->flags & VUNSET))
+                       return strchrnul(v->text, '=') + 1;
        }
+
        return NULL;
 }
 
@@ -12316,6 +12379,9 @@ unsetvar(const char *s)
                retval = 1;
                if (flags & VREADONLY)
                        goto out;
+#ifdef DYNAMIC_VAR
+               vp->flags &= ~VDYNAMIC;
+#endif
                if (flags & VUNSET)
                        goto ok;
                if ((flags & VSTRFIXED) == 0) {
@@ -12432,10 +12498,10 @@ static int timescmd(int ac, char **av)
 }
 
 #ifdef CONFIG_ASH_MATH_SUPPORT
-static int
+static arith_t
 dash_arith(const char *s)
 {
-       long result;
+       arith_t result;
        int errcode = 0;
 
        INTOFF;
@@ -12467,7 +12533,7 @@ static int
 letcmd(int argc, char **argv)
 {
        char **ap;
-       long i;
+       arith_t i;
 
        ap = argv + 1;
        if(!*ap)
@@ -12483,13 +12549,13 @@ letcmd(int argc, char **argv)
 /*      $NetBSD: miscbltin.c,v 1.31 2002/11/24 22:35:41 christos Exp $  */
 
 /*
- * Miscelaneous builtins.
+ * Miscellaneous builtins.
  */
 
 #undef rflag
 
 #ifdef __GLIBC__
-#if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
+#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 1
 typedef enum __rlimit_resource rlim_t;
 #endif
 #endif
@@ -12686,34 +12752,86 @@ static const struct limits limits[] = {
        { "locked memory(kbytes)",      RLIMIT_MEMLOCK, 1024, 'l' },
 #endif
 #ifdef RLIMIT_NPROC
-       { "process(processes)",         RLIMIT_NPROC,      1, 'p' },
+       { "process",                    RLIMIT_NPROC,      1, 'p' },
 #endif
 #ifdef RLIMIT_NOFILE
-       { "nofiles(descriptors)",       RLIMIT_NOFILE,     1, 'n' },
+       { "nofiles",                    RLIMIT_NOFILE,     1, 'n' },
 #endif
-#ifdef RLIMIT_VMEM
-       { "vmemory(kbytes)",            RLIMIT_VMEM,    1024, 'v' },
+#ifdef RLIMIT_AS
+       { "vmemory(kbytes)",            RLIMIT_AS,      1024, 'v' },
 #endif
-#ifdef RLIMIT_SWAP
-       { "swap(kbytes)",               RLIMIT_SWAP,    1024, 'w' },
+#ifdef RLIMIT_LOCKS
+       { "locks",                      RLIMIT_LOCKS,      1, 'w' },
 #endif
        { (char *) 0,                   0,                 0,  '\0' }
 };
 
+enum limtype { SOFT = 0x1, HARD = 0x2 };
+
+static void printlim(enum limtype how, const struct rlimit *limit,
+                       const struct limits *l)
+{
+       rlim_t val;
+
+       val = limit->rlim_max;
+       if (how & SOFT)
+               val = limit->rlim_cur;
+
+       if (val == RLIM_INFINITY)
+               out1fmt("unlimited\n");
+       else {
+               val /= l->factor;
+               out1fmt("%lld\n", (long long) val);
+       }
+}
+
 int
 ulimitcmd(int argc, char **argv)
 {
        int     c;
        rlim_t val = 0;
-       enum { SOFT = 0x1, HARD = 0x2 }
-                       how = SOFT | HARD;
+       enum limtype how = SOFT | HARD;
        const struct limits     *l;
        int             set, all = 0;
        int             optc, what;
        struct rlimit   limit;
 
        what = 'f';
-       while ((optc = nextopt("HSatfdsmcnpl")) != '\0')
+       while ((optc = nextopt("HSa"
+#ifdef RLIMIT_CPU
+                               "t"
+#endif
+#ifdef RLIMIT_FSIZE
+                               "f"
+#endif
+#ifdef RLIMIT_DATA
+                               "d"
+#endif
+#ifdef RLIMIT_STACK
+                               "s"
+#endif
+#ifdef RLIMIT_CORE
+                               "c"
+#endif
+#ifdef RLIMIT_RSS
+                               "m"
+#endif
+#ifdef RLIMIT_MEMLOCK
+                               "l"
+#endif
+#ifdef RLIMIT_NPROC
+                               "p"
+#endif
+#ifdef RLIMIT_NOFILE
+                               "n"
+#endif
+#ifdef RLIMIT_AS
+                               "v"
+#endif
+#ifdef RLIMIT_LOCKS
+                               "w"
+#endif
+                                               )) != '\0')
                switch (optc) {
                case 'H':
                        how = HARD;
@@ -12728,10 +12846,8 @@ ulimitcmd(int argc, char **argv)
                        what = optc;
                }
 
-       for (l = limits; l->name && l->option != what; l++)
+       for (l = limits; l->option != what; l++)
                ;
-       if (!l->name)
-               error("internal error (%c)", what);
 
        set = *argptr ? 1 : 0;
        if (set) {
@@ -12758,19 +12874,8 @@ ulimitcmd(int argc, char **argv)
        if (all) {
                for (l = limits; l->name; l++) {
                        getrlimit(l->cmd, &limit);
-                       if (how & SOFT)
-                               val = limit.rlim_cur;
-                       else if (how & HARD)
-                               val = limit.rlim_max;
-
                        out1fmt("%-20s ", l->name);
-                       if (val == RLIM_INFINITY)
-                               out1fmt("unlimited\n");
-                       else
-                       {
-                               val /= l->factor;
-                               out1fmt("%lld\n", (long long) val);
-                       }
+                       printlim(how, &limit, l);
                }
                return 0;
        }
@@ -12784,18 +12889,7 @@ ulimitcmd(int argc, char **argv)
                if (setrlimit(l->cmd, &limit) < 0)
                        error("error setting limit (%m)");
        } else {
-               if (how & SOFT)
-                       val = limit.rlim_cur;
-               else if (how & HARD)
-                       val = limit.rlim_max;
-
-               if (val == RLIM_INFINITY)
-                       out1fmt("unlimited\n");
-               else
-               {
-                       val /= l->factor;
-                       out1fmt("%lld\n", (long long) val);
-               }
+               printlim(how, &limit, l);
        }
        return 0;
 }
@@ -12827,14 +12921,14 @@ ulimitcmd(int argc, char **argv)
 
 /* This is my infix parser/evaluator. It is optimized for size, intended
  * as a replacement for yacc-based parsers. However, it may well be faster
- * than a comparable parser writen in yacc. The supported operators are
+ * than a comparable parser written in yacc. The supported operators are
  * listed in #defines below. Parens, order of operations, and error handling
- * are supported. This code is threadsafe. The exact expression format should
+ * are supported. This code is thread safe. The exact expression format should
  * be that which POSIX specifies for shells. */
 
 /* The code uses a simple two-stack algorithm. See
  * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
- * for a detailed explaination of the infix-to-postfix algorithm on which
+ * for a detailed explanation of the infix-to-postfix algorithm on which
  * this is based (this code differs in that it applies operators immediately
  * to the stack instead of adding them to a queue to end up with an
  * expression). */
@@ -12864,7 +12958,7 @@ ulimitcmd(int argc, char **argv)
  *    parens and then checking that all binary ops and right parens are
  *    preceded by a valid expression (NUM_TOKEN).
  *
- * Note: It may be desireable to replace Aaron's test for whitespace with
+ * Note: It may be desirable to replace Aaron's test for whitespace with
  * ctype's isspace() if it is used by another busybox applet or if additional
  * whitespace chars should be considered.  Look below the "#include"s for a
  * precompiler test.
@@ -12890,7 +12984,7 @@ ulimitcmd(int argc, char **argv)
  * - realize comma separated - expr, expr
  * - realise ++expr --expr expr++ expr--
  * - realise expr ? expr : expr (but, second expr calculate always)
- * - allow hexdecimal and octal numbers
+ * - allow hexadecimal and octal numbers
  * - was restored loses XOR operator
  * - remove one goto label, added three ;-)
  * - protect $((num num)) as true zero expr (Manuel`s error)
@@ -12905,7 +12999,7 @@ ulimitcmd(int argc, char **argv)
 typedef unsigned char operator;
 
 /* An operator's token id is a bit of a bitfield. The lower 5 bits are the
- * precedence, and 3 high bits are an ID unique accross operators of that
+ * precedence, and 3 high bits are an ID unique across operators of that
  * precedence. The ID portion is so that multiple operators can have the
  * same precedence, ensuring that the leftmost one is evaluated first.
  * Consider * and /. */
@@ -13010,11 +13104,11 @@ static inline int is_right_associativity(operator prec)
 
 
 typedef struct ARITCH_VAR_NUM {
-       long val;
-       long contidional_second_val;
+       arith_t val;
+       arith_t contidional_second_val;
        char contidional_second_val_initialized;
        char *var;      /* if NULL then is regular number,
-                          else is varable name */
+                          else is variable name */
 } v_n_t;
 
 
@@ -13068,9 +13162,8 @@ static int arith_lookup_val(v_n_t *t)
 static inline int
 arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
 {
-       long numptr_val;
        v_n_t *numptr_m1;
-       long rez;
+       arith_t numptr_val, rez;
        int ret_arith_lookup_val;
 
        if (NUMPTR == numstack) goto err; /* There is no operator that can work
@@ -13196,7 +13289,7 @@ arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
                        goto err;
                }
                /* save to shell variable */
-               sprintf(buf, "%ld", rez);
+               sprintf(buf, "%lld", (long long) rez);
                setvar(numptr_m1->var, buf, 0);
                /* after saving, make previous value for v++ or v-- */
                if(op == TOK_POST_INC)
@@ -13259,7 +13352,7 @@ static const char op_tokens[] = {
 #define endexpression &op_tokens[sizeof(op_tokens)-7]
 
 
-extern long arith (const char *expr, int *perrcode)
+static arith_t arith (const char *expr, int *perrcode)
 {
     register char arithval; /* Current character under analysis */
     operator lasttok, op;
@@ -13272,7 +13365,7 @@ extern long arith (const char *expr, int *perrcode)
 
     /* Stack of integers */
     /* The proof that there can be no more than strlen(startbuf)/2+1 integers
-     * in any given correct or incorrect expression is left as an excersize to
+     * in any given correct or incorrect expression is left as an exercise to
      * the reader. */
     v_n_t *numstack = alloca(((datasizes)/2)*sizeof(v_n_t)),
            *numstackptr = numstack;