Stupidity-1, Erik-0
[oweals/busybox.git] / shell / ash.c
index 1fe1e8290c72fadd196e56312f69c6087f5dae98..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.
@@ -1442,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 $        */
@@ -1465,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 */
 };
 
@@ -1500,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";
@@ -1524,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
 };
 
@@ -1551,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)
 
 /*
@@ -1619,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. */
 
@@ -1637,7 +1665,7 @@ static void out1str(const char *p)
 static void out2str(const char *p)
 {
        outstr(p, stderr);
-       flushout(stderr);
+       flusherr();
 }
 
 /*
@@ -2680,7 +2708,7 @@ static const struct builtincmd bltin = {
  */
 
 /*
- * The eval commmand.
+ * The eval command.
  */
 
 static int
@@ -3276,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;
@@ -3293,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;
                        }
 
@@ -3695,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;
@@ -4126,9 +4148,6 @@ changepath(const char *newval)
                firstchange = 0;
        clearcmdentry(firstchange);
        builtinloc = idx_bltin;
-#ifdef CONFIG_FEATURE_COMMAND_TAB_COMPLETION
-       cmdedit_path_lookup = newval;
-#endif
 }
 
 
@@ -4518,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);
@@ -4563,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());
 }
 
 
@@ -5889,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;
 }
@@ -6026,6 +6049,7 @@ 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 */
@@ -7214,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
@@ -7606,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);
@@ -7896,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);
@@ -8107,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;
@@ -8356,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();
@@ -9021,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)
@@ -9209,10 +9277,10 @@ flushall(void)
 }
 
 void
-flushout(FILE *dest)
+flusherr(void)
 {
        INTOFF;
-       fflush(dest);
+       fflush(stderr);
        INTON;
 }
 
@@ -9256,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 $     */
 
@@ -10926,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;
                }
        }
@@ -10940,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);
@@ -11888,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
  */
 
 
@@ -11973,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)
@@ -12033,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;
 }
 
@@ -12310,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) {
@@ -12426,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;
@@ -12461,7 +12533,7 @@ static int
 letcmd(int argc, char **argv)
 {
        char **ap;
-       long i;
+       arith_t i;
 
        ap = argv + 1;
        if(!*ap)
@@ -12477,7 +12549,7 @@ 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
@@ -12849,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). */
@@ -12886,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.
@@ -12912,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)
@@ -12927,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 /. */
@@ -13032,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;
 
 
@@ -13090,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
@@ -13218,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)
@@ -13281,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;
@@ -13294,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;