fixes for bugs discovered by randomconfig builds and tests
[oweals/busybox.git] / shell / ash.c
index b618a47f9dd3c47041e44e040781551e2a059864..5671a524b44795fff63669c4a69dcf5b99b94081 100644 (file)
@@ -43,7 +43,9 @@
 #include <sys/times.h>
 
 #include "shell_common.h"
-#include "math.h"
+#if ENABLE_SH_MATH_SUPPORT
+# include "math.h"
+#endif
 #if ENABLE_ASH_RANDOM_SUPPORT
 # include "random.h"
 #else
@@ -2433,12 +2435,13 @@ static const char *expandstr(const char *ps);
 #endif
 
 static void
-setprompt(int whichprompt)
+setprompt_if(smallint do_set, int whichprompt)
 {
        const char *prompt;
-#if ENABLE_ASH_EXPAND_PRMT
-       struct stackmark smark;
-#endif
+       IF_ASH_EXPAND_PRMT(struct stackmark smark;)
+
+       if (!do_set)
+               return;
 
        needprompt = 0;
 
@@ -5441,25 +5444,17 @@ redirectsafe(union node *redir, int flags)
 static arith_t
 ash_arith(const char *s)
 {
-       arith_eval_hooks_t math_hooks;
+       arith_state_t math_state;
        arith_t result;
-       int errcode = 0;
 
-       math_hooks.lookupvar = lookupvar;
-       math_hooks.setvar    = setvar2;
-       //math_hooks.endofname = endofname;
+       math_state.lookupvar = lookupvar;
+       math_state.setvar    = setvar2;
+       //math_state.endofname = endofname;
 
        INT_OFF;
-       result = arith(s, &errcode, &math_hooks);
-       if (errcode < 0) {
-               if (errcode == -3)
-                       ash_msg_and_raise_error("exponent less than 0");
-               if (errcode == -2)
-                       ash_msg_and_raise_error("divide by zero");
-               if (errcode == -5)
-                       ash_msg_and_raise_error("expression recursion loop detected");
-               raise_error_syntax(s);
-       }
+       result = arith(&math_state, s);
+       if (math_state.errmsg)
+               ash_msg_and_raise_error(math_state.errmsg);
        INT_ON;
 
        return result;
@@ -5517,13 +5512,18 @@ static struct arglist exparg;
 /*
  * Our own itoa().
  */
+#if !ENABLE_SH_MATH_SUPPORT
+/* cvtnum() is used even if math support is off (to prepare $? values and such) */
+typedef long arith_t;
+# define ARITH_FMT "%ld"
+#endif
 static int
 cvtnum(arith_t num)
 {
        int len;
 
        expdest = makestrspace(32, expdest);
-       len = fmtstr(expdest, 32, arith_t_fmt, num);
+       len = fmtstr(expdest, 32, ARITH_FMT, num);
        STADJUST(len, expdest);
        return len;
 }
@@ -6022,9 +6022,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
                c = p[length];
                if (c) {
                        if (!(c & 0x80)
-#if ENABLE_SH_MATH_SUPPORT
-                        || c == CTLENDARI
-#endif
+                       IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
                        ) {
                                /* c == '=' || c == ':' || c == CTLENDARI */
                                length++;
@@ -6107,8 +6105,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
 #endif
                }
        }
- breakloop:
-       ;
+ breakloop: ;
 }
 
 static char *
@@ -7633,7 +7630,7 @@ hashcd(void)
                for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
                        if (cmdp->cmdtype == CMDNORMAL
                         || (cmdp->cmdtype == CMDBUILTIN
-                            && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
+                            && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
                             && builtinloc > 0)
                        ) {
                                cmdp->rehash = 1;
@@ -8224,7 +8221,7 @@ static int evalstring(char *s, int mask);
 
 /* Called to execute a trap.
  * Single callsite - at the end of evaltree().
- * If we return non-zero, exaltree raises EXEXIT exception.
+ * If we return non-zero, evaltree raises EXEXIT exception.
  *
  * Perhaps we should avoid entering new trap handlers
  * while we are executing a trap handler. [is it a TODO?]
@@ -8414,11 +8411,15 @@ evaltree(union node *n, int flags)
 
  out:
        exception_handler = savehandler;
+
  out1:
+       /* Order of checks below is important:
+        * signal handlers trigger before exit caused by "set -e".
+        */
+       if (pending_sig && dotrap())
+               goto exexit;
        if (checkexit & exitstatus)
                evalskip |= SKIPEVAL;
-       else if (pending_sig && dotrap())
-               goto exexit;
 
        if (flags & EV_EXIT) {
  exexit:
@@ -8750,7 +8751,7 @@ poplocalvars(void)
        while ((lvp = localvars) != NULL) {
                localvars = lvp->next;
                vp = lvp->vp;
-               TRACE(("poplocalvar %s\n", vp ? vp->text : "-"));
+               TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
                if (vp == NULL) {       /* $- saved */
                        memcpy(optlist, lvp->text, sizeof(optlist));
                        free((char*)lvp->text);
@@ -11054,159 +11055,156 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
        STARTSTACKSTR(out);
  loop:
        /* For each line, until end of word */
-       {
-               CHECKEND();     /* set c to PEOF if at end of here document */
-               for (;;) {      /* until end of line or end of word */
-                       CHECKSTRSPACE(4, out);  /* permit 4 calls to USTPUTC */
-                       switch (SIT(c, syntax)) {
-                       case CNL:       /* '\n' */
-                               if (syntax == BASESYNTAX)
-                                       goto endword;   /* exit outer loop */
-                               USTPUTC(c, out);
-                               g_parsefile->linno++;
-                               if (doprompt)
-                                       setprompt(2);
-                               c = pgetc();
-                               goto loop;              /* continue outer loop */
-                       case CWORD:
-                               USTPUTC(c, out);
-                               break;
-                       case CCTL:
-                               if (eofmark == NULL || dblquote)
-                                       USTPUTC(CTLESC, out);
+       CHECKEND();     /* set c to PEOF if at end of here document */
+       for (;;) {      /* until end of line or end of word */
+               CHECKSTRSPACE(4, out);  /* permit 4 calls to USTPUTC */
+               switch (SIT(c, syntax)) {
+               case CNL:       /* '\n' */
+                       if (syntax == BASESYNTAX)
+                               goto endword;   /* exit outer loop */
+                       USTPUTC(c, out);
+                       g_parsefile->linno++;
+                       setprompt_if(doprompt, 2);
+                       c = pgetc();
+                       goto loop;              /* continue outer loop */
+               case CWORD:
+                       USTPUTC(c, out);
+                       break;
+               case CCTL:
+                       if (eofmark == NULL || dblquote)
+                               USTPUTC(CTLESC, out);
 #if ENABLE_ASH_BASH_COMPAT
-                               if (c == '\\' && bash_dollar_squote) {
-                                       c = decode_dollar_squote();
-                                       if (c & 0x100) {
-                                               USTPUTC('\\', out);
-                                               c = (unsigned char)c;
-                                       }
+                       if (c == '\\' && bash_dollar_squote) {
+                               c = decode_dollar_squote();
+                               if (c & 0x100) {
+                                       USTPUTC('\\', out);
+                                       c = (unsigned char)c;
                                }
+                       }
 #endif
-                               USTPUTC(c, out);
-                               break;
-                       case CBACK:     /* backslash */
-                               c = pgetc_without_PEOA();
-                               if (c == PEOF) {
+                       USTPUTC(c, out);
+                       break;
+               case CBACK:     /* backslash */
+                       c = pgetc_without_PEOA();
+                       if (c == PEOF) {
+                               USTPUTC(CTLESC, out);
+                               USTPUTC('\\', out);
+                               pungetc();
+                       } else if (c == '\n') {
+                               setprompt_if(doprompt, 2);
+                       } else {
+#if ENABLE_ASH_EXPAND_PRMT
+                               if (c == '$' && pssyntax) {
                                        USTPUTC(CTLESC, out);
                                        USTPUTC('\\', out);
-                                       pungetc();
-                               } else if (c == '\n') {
-                                       if (doprompt)
-                                               setprompt(2);
-                               } else {
-#if ENABLE_ASH_EXPAND_PRMT
-                                       if (c == '$' && pssyntax) {
-                                               USTPUTC(CTLESC, out);
-                                               USTPUTC('\\', out);
-                                       }
+                               }
 #endif
-                                       /* Backslash is retained if we are in "str" and next char isn't special */
-                                       if (dblquote
-                                        && c != '\\'
-                                        && c != '`'
-                                        && c != '$'
-                                        && (c != '"' || eofmark != NULL)
-                                       ) {
-                                               USTPUTC(CTLESC, out);
-                                               USTPUTC('\\', out);
-                                       }
-                                       if (SIT(c, SQSYNTAX) == CCTL)
-                                               USTPUTC(CTLESC, out);
-                                       USTPUTC(c, out);
-                                       quotef = 1;
+                               /* Backslash is retained if we are in "str" and next char isn't special */
+                               if (dblquote
+                                && c != '\\'
+                                && c != '`'
+                                && c != '$'
+                                && (c != '"' || eofmark != NULL)
+                               ) {
+                                       USTPUTC(CTLESC, out);
+                                       USTPUTC('\\', out);
                                }
-                               break;
-                       case CSQUOTE:
-                               syntax = SQSYNTAX;
+                               if (SIT(c, SQSYNTAX) == CCTL)
+                                       USTPUTC(CTLESC, out);
+                               USTPUTC(c, out);
+                               quotef = 1;
+                       }
+                       break;
+               case CSQUOTE:
+                       syntax = SQSYNTAX;
  quotemark:
-                               if (eofmark == NULL) {
-                                       USTPUTC(CTLQUOTEMARK, out);
+                       if (eofmark == NULL) {
+                               USTPUTC(CTLQUOTEMARK, out);
+                       }
+                       break;
+               case CDQUOTE:
+                       syntax = DQSYNTAX;
+                       dblquote = 1;
+                       goto quotemark;
+               case CENDQUOTE:
+                       IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
+                       if (eofmark != NULL && arinest == 0
+                        && varnest == 0
+                       ) {
+                               USTPUTC(c, out);
+                       } else {
+                               if (dqvarnest == 0) {
+                                       syntax = BASESYNTAX;
+                                       dblquote = 0;
                                }
-                               break;
-                       case CDQUOTE:
-                               syntax = DQSYNTAX;
-                               dblquote = 1;
+                               quotef = 1;
                                goto quotemark;
-                       case CENDQUOTE:
-                               IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
-                               if (eofmark != NULL && arinest == 0
-                                && varnest == 0
-                               ) {
-                                       USTPUTC(c, out);
-                               } else {
-                                       if (dqvarnest == 0) {
-                                               syntax = BASESYNTAX;
-                                               dblquote = 0;
-                                       }
-                                       quotef = 1;
-                                       goto quotemark;
-                               }
-                               break;
-                       case CVAR:      /* '$' */
-                               PARSESUB();             /* parse substitution */
-                               break;
-                       case CENDVAR:   /* '}' */
-                               if (varnest > 0) {
-                                       varnest--;
-                                       if (dqvarnest > 0) {
-                                               dqvarnest--;
-                                       }
-                                       c = CTLENDVAR;
+                       }
+                       break;
+               case CVAR:      /* '$' */
+                       PARSESUB();             /* parse substitution */
+                       break;
+               case CENDVAR:   /* '}' */
+                       if (varnest > 0) {
+                               varnest--;
+                               if (dqvarnest > 0) {
+                                       dqvarnest--;
                                }
-                               USTPUTC(c, out);
-                               break;
+                               c = CTLENDVAR;
+                       }
+                       USTPUTC(c, out);
+                       break;
 #if ENABLE_SH_MATH_SUPPORT
-                       case CLP:       /* '(' in arithmetic */
-                               parenlevel++;
-                               USTPUTC(c, out);
-                               break;
-                       case CRP:       /* ')' in arithmetic */
-                               if (parenlevel > 0) {
-                                       parenlevel--;
-                               } else {
-                                       if (pgetc() == ')') {
-                                               if (--arinest == 0) {
-                                                       syntax = prevsyntax;
-                                                       dblquote = (syntax == DQSYNTAX);
-                                                       c = CTLENDARI;
-                                               }
-                                       } else {
-                                               /*
-                                                * unbalanced parens
-                                                * (don't 2nd guess - no error)
-                                                */
-                                               pungetc();
+               case CLP:       /* '(' in arithmetic */
+                       parenlevel++;
+                       USTPUTC(c, out);
+                       break;
+               case CRP:       /* ')' in arithmetic */
+                       if (parenlevel > 0) {
+                               parenlevel--;
+                       } else {
+                               if (pgetc() == ')') {
+                                       if (--arinest == 0) {
+                                               syntax = prevsyntax;
+                                               dblquote = (syntax == DQSYNTAX);
+                                               c = CTLENDARI;
                                        }
+                               } else {
+                                       /*
+                                        * unbalanced parens
+                                        * (don't 2nd guess - no error)
+                                        */
+                                       pungetc();
                                }
-                               USTPUTC(c, out);
-                               break;
+                       }
+                       USTPUTC(c, out);
+                       break;
 #endif
-                       case CBQUOTE:   /* '`' */
-                               PARSEBACKQOLD();
-                               break;
-                       case CENDFILE:
-                               goto endword;           /* exit outer loop */
-                       case CIGN:
-                               break;
-                       default:
-                               if (varnest == 0) {
+               case CBQUOTE:   /* '`' */
+                       PARSEBACKQOLD();
+                       break;
+               case CENDFILE:
+                       goto endword;           /* exit outer loop */
+               case CIGN:
+                       break;
+               default:
+                       if (varnest == 0) {
 #if ENABLE_ASH_BASH_COMPAT
-                                       if (c == '&') {
-                                               if (pgetc() == '>')
-                                                       c = 0x100 + '>'; /* flag &> */
-                                               pungetc();
-                                       }
-#endif
-                                       goto endword;   /* exit outer loop */
+                               if (c == '&') {
+                                       if (pgetc() == '>')
+                                               c = 0x100 + '>'; /* flag &> */
+                                       pungetc();
                                }
-                               IF_ASH_ALIAS(if (c != PEOA))
-                                       USTPUTC(c, out);
+#endif
+                               goto endword;   /* exit outer loop */
                        }
-                       c = pgetc_fast();
-               } /* for (;;) */
-       }
+                       IF_ASH_ALIAS(if (c != PEOA))
+                               USTPUTC(c, out);
+               }
+               c = pgetc_fast();
+       } /* for (;;) */
  endword:
+
 #if ENABLE_SH_MATH_SUPPORT
        if (syntax == ARISYNTAX)
                raise_error_syntax("missing '))'");
@@ -11542,16 +11540,14 @@ parsebackq: {
                   treatment to some slashes, and then push the string and
                   reread it as input, interpreting it normally.  */
                char *pout;
-               int pc;
                size_t psavelen;
                char *pstr;
 
-
                STARTSTACKSTR(pout);
                for (;;) {
-                       if (needprompt) {
-                               setprompt(2);
-                       }
+                       int pc;
+
+                       setprompt_if(needprompt, 2);
                        pc = pgetc();
                        switch (pc) {
                        case '`':
@@ -11561,8 +11557,7 @@ parsebackq: {
                                pc = pgetc();
                                if (pc == '\n') {
                                        g_parsefile->linno++;
-                                       if (doprompt)
-                                               setprompt(2);
+                                       setprompt_if(doprompt, 2);
                                        /*
                                         * If eating a newline, avoid putting
                                         * the newline into the new character
@@ -11725,9 +11720,7 @@ xxreadtoken(void)
                tokpushback = 0;
                return lasttoken;
        }
-       if (needprompt) {
-               setprompt(2);
-       }
+       setprompt_if(needprompt, 2);
        startlinno = g_parsefile->linno;
        for (;;) {                      /* until token or start of word found */
                c = pgetc_fast();
@@ -11744,8 +11737,7 @@ xxreadtoken(void)
                                break; /* return readtoken1(...) */
                        }
                        startlinno = ++g_parsefile->linno;
-                       if (doprompt)
-                               setprompt(2);
+                       setprompt_if(doprompt, 2);
                } else {
                        const char *p;
 
@@ -11791,9 +11783,7 @@ xxreadtoken(void)
                tokpushback = 0;
                return lasttoken;
        }
-       if (needprompt) {
-               setprompt(2);
-       }
+       setprompt_if(needprompt, 2);
        startlinno = g_parsefile->linno;
        for (;;) {      /* until token or start of word found */
                c = pgetc_fast();
@@ -11809,8 +11799,7 @@ xxreadtoken(void)
                case '\\':
                        if (pgetc() == '\n') {
                                startlinno = ++g_parsefile->linno;
-                               if (doprompt)
-                                       setprompt(2);
+                               setprompt_if(doprompt, 2);
                                continue;
                        }
                        pungetc();
@@ -11936,8 +11925,7 @@ parsecmd(int interact)
 
        tokpushback = 0;
        doprompt = interact;
-       if (doprompt)
-               setprompt(doprompt);
+       setprompt_if(doprompt, doprompt);
        needprompt = 0;
        t = readtoken();
        if (t == TEOF)
@@ -11961,10 +11949,8 @@ parseheredoc(void)
        heredoclist = NULL;
 
        while (here) {
-               if (needprompt) {
-                       setprompt(2);
-               }
-               readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
+               setprompt_if(needprompt, 2);
+               readtoken1(pgetc(), here->here->type == NHERE ? SQSYNTAX : DQSYNTAX,
                                here->eofmark, here->striptabs);
                n = stzalloc(sizeof(struct narg));
                n->narg.type = NARG;
@@ -12346,7 +12332,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
                        }
                        if ((act & DO_NOFUNC)
                         || !prefix(pathopt, "func")
-                       ) {     /* ignore unimplemented options */
+                       ) {     /* ignore unimplemented options */
                                continue;
                        }
                }
@@ -12861,7 +12847,7 @@ init(void)
        /* bash re-enables SIGHUP which is SIG_IGNed on entry.
         * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
         */
-        signal(SIGHUP, SIG_DFL);
+       signal(SIGHUP, SIG_DFL);
 
        /* from var.c: */
        {
@@ -13034,10 +13020,12 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
                if (e == EXERROR)
                        exitstatus = 2;
                s = state;
-               if (e == EXEXIT || s == 0 || iflag == 0 || shlvl)
+               if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
                        exitshell();
-               if (e == EXINT)
+               }
+               if (e == EXINT) {
                        outcslow('\n', stderr);
+               }
 
                popstackmark(&smark);
                FORCE_INT_ON; /* enable interrupts */
@@ -13130,6 +13118,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
                _mcleanup();
        }
 #endif
+       TRACE(("End of main reached\n"));
        exitshell();
        /* NOTREACHED */
 }