hush: fix \<newline> handling
[oweals/busybox.git] / shell / ash.c
index a799cb1a6a00f13e13d01118c07ae0a6be091ff7..b27b2777ea5975e0c2ddd4ba26c9e21f0e4e5ca5 100644 (file)
  * Original BSD copyright notice is retained at the end of this file.
  */
 
-/*
- * rewrite arith.y to micro stack based cryptic algorithm by
- * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
- *
- * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
- * dynamic variables.
- *
- * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
- * used in busybox and size optimizations,
- * rewrote arith (see notes to this), added locale support,
- * rewrote dynamic variables.
- */
-
 /*
  * The following should be set to reflect the type of system you have:
  *      JOBS -> 1 if you have Berkeley job control, 0 otherwise.
 #include <paths.h>
 #include <setjmp.h>
 #include <fnmatch.h>
+#include "math.h"
 
 #if defined SINGLE_APPLET_MAIN
 /* STANDALONE does not make sense, and won't compile */
 #undef CONFIG_FEATURE_SH_STANDALONE
 #undef ENABLE_FEATURE_SH_STANDALONE
-#undef USE_FEATURE_SH_STANDALONE
-#undef SKIP_FEATURE_SH_STANDALONE(...)
+#undef IF_FEATURE_SH_STANDALONE
+#undef IF_NOT_FEATURE_SH_STANDALONE(...)
 #define ENABLE_FEATURE_SH_STANDALONE 0
-#define USE_FEATURE_SH_STANDALONE(...)
-#define SKIP_FEATURE_SH_STANDALONE(...) __VA_ARGS__
+#define IF_FEATURE_SH_STANDALONE(...)
+#define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
 #endif
 
 #ifndef PIPE_BUF
@@ -260,6 +248,12 @@ static void trace_printf(const char *fmt, ...);
 static void trace_vprintf(const char *fmt, va_list va);
 # define TRACE(param)    trace_printf param
 # define TRACEV(param)   trace_vprintf param
+# define close(fd) do { \
+       int dfd = (fd); \
+       if (close(dfd) < 0) \
+               bb_error_msg("bug on %d: closing %d(%x)", \
+                       __LINE__, dfd, dfd); \
+} while (0)
 #else
 # define TRACE(param)
 # define TRACEV(param)
@@ -355,39 +349,25 @@ raise_interrupt(void)
 } while (0)
 #endif
 
-#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
-static void
+static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
 int_on(void)
 {
+       xbarrier();
        if (--suppressint == 0 && intpending) {
                raise_interrupt();
        }
 }
 #define INT_ON int_on()
-static void
+static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
 force_int_on(void)
 {
+       xbarrier();
        suppressint = 0;
        if (intpending)
                raise_interrupt();
 }
 #define FORCE_INT_ON force_int_on()
 
-#else /* !ASH_OPTIMIZE_FOR_SIZE */
-
-#define INT_ON do { \
-       xbarrier(); \
-       if (--suppressint == 0 && intpending) \
-               raise_interrupt(); \
-} while (0)
-#define FORCE_INT_ON do { \
-       xbarrier(); \
-       suppressint = 0; \
-       if (intpending) \
-               raise_interrupt(); \
-} while (0)
-#endif /* !ASH_OPTIMIZE_FOR_SIZE */
-
 #define SAVE_INT(v) ((v) = suppressint)
 
 #define RESTORE_INT(v) do { \
@@ -1118,6 +1098,14 @@ ash_msg_and_raise_error(const char *msg, ...)
        va_end(ap);
 }
 
+static void raise_error_syntax(const char *) NORETURN;
+static void
+raise_error_syntax(const char *msg)
+{
+       ash_msg_and_raise_error("syntax error: %s", msg);
+       /* NOTREACHED */
+}
+
 static void ash_msg_and_raise(int, const char *, ...) NORETURN;
 static void
 ash_msg_and_raise(int cond, const char *msg, ...)
@@ -1992,7 +1980,7 @@ findvar(struct var **vpp, const char *name)
 /*
  * Find the value of a variable.  Returns NULL if not set.
  */
-static char *
+static const char *
 lookupvar(const char *name)
 {
        struct var *v;
@@ -2018,7 +2006,7 @@ lookupvar(const char *name)
 /*
  * Search the environment of a builtin command.
  */
-static char *
+static const char *
 bltinlookup(const char *name)
 {
        struct strlist *sp;
@@ -2631,7 +2619,7 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 #define USE_SIT_FUNCTION
 #endif
 
-#if ENABLE_ASH_MATH_SUPPORT
+#if ENABLE_SH_MATH_SUPPORT
 static const char S_I_T[][4] = {
 #if ENABLE_ASH_ALIAS
        { CSPCL, CIGN, CIGN, CIGN },            /* 0, PEOA */
@@ -2675,7 +2663,7 @@ static const char S_I_T[][3] = {
        { CCTL, CCTL, CCTL }                    /* 14, CTLESC ... */
 #endif
 };
-#endif /* ASH_MATH_SUPPORT */
+#endif /* SH_MATH_SUPPORT */
 
 #ifdef USE_SIT_FUNCTION
 
@@ -2701,23 +2689,26 @@ SIT(int c, int syntax)
        const char *s;
        int indx;
 
-       if (c == PEOF)          /* 2^8+2 */
+       if (c == PEOF) {         /* 2^8+2 */
                return CENDFILE;
+       }
 #if ENABLE_ASH_ALIAS
-       if (c == PEOA)          /* 2^8+1 */
+       if (c == PEOA) {         /* 2^8+1 */
                indx = 0;
-       else
+       else
 #endif
-
-       if ((unsigned char)c >= (unsigned char)(CTLESC)
-        && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
-       ) {
-               return CCTL;
+       {
+               if ((unsigned char)c >= (unsigned char)(CTLESC)
+                && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK)
+               ) {
+                       return CCTL;
+               }
+               s = strchrnul(spec_symbls, c);
+               if (*s == '\0') {
+                       return CWORD;
+               }
+               indx = syntax_index_table[s - spec_symbls];
        }
-       s = strchrnul(spec_symbls, c);
-       if (*s == '\0')
-               return CWORD;
-       indx = syntax_index_table[s - spec_symbls];
        return S_I_T[indx][syntax];
 }
 
@@ -3020,7 +3011,7 @@ static const char syntax_index_table[258] = {
        /* 257   127      */ CWORD_CWORD_CWORD_CWORD,
 };
 
-#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax])
+#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[(int)(c) + SYNBASE]][syntax])
 
 #endif  /* USE_SIT_FUNCTION */
 
@@ -4228,7 +4219,7 @@ cmdputs(const char *s)
        static const char vstype[VSTYPE + 1][3] = {
                "", "}", "-", "+", "?", "=",
                "%", "%%", "#", "##"
-               USE_ASH_BASH_COMPAT(, ":", "/", "//")
+               IF_ASH_BASH_COMPAT(, ":", "/", "//")
        };
 
        const char *p, *str;
@@ -4267,7 +4258,7 @@ cmdputs(const char *s)
                case CTLBACKQ+CTLQUOTE:
                        str = "\"$(...)\"";
                        goto dostr;
-#if ENABLE_ASH_MATH_SUPPORT
+#if ENABLE_SH_MATH_SUPPORT
                case CTLARI:
                        str = "$((";
                        goto dostr;
@@ -5142,7 +5133,9 @@ redirect(union node *redir, int flags)
                if (newfd < 0) {
                        /* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
                        if (redir->ndup.dupfd < 0) { /* "fd>&-" */
-                               close(fd);
+                               /* Don't want to trigger debugging */
+                               if (fd != -1)
+                                       close(fd);
                        } else {
                                copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
                        }
@@ -5195,7 +5188,7 @@ popredir(int drop, int restore)
                                /*close(fd);*/
                                copyfd(copy, fd | COPYFD_EXACT);
                        }
-                       close(copy);
+                       close(copy & ~COPYFD_RESTORE);
                }
        }
        redirlist = rp->next;
@@ -5250,17 +5243,33 @@ redirectsafe(union node *redir, int flags)
  * We have to deal with backquotes, shell variables, and file metacharacters.
  */
 
-#if ENABLE_ASH_MATH_SUPPORT_64
-typedef int64_t arith_t;
-#define arith_t_type long long
-#else
-typedef long arith_t;
-#define arith_t_type long
-#endif
+#if ENABLE_SH_MATH_SUPPORT
+static arith_t
+ash_arith(const char *s)
+{
+       arith_eval_hooks_t math_hooks;
+       arith_t result;
+       int errcode = 0;
+
+       math_hooks.lookupvar = lookupvar;
+       math_hooks.setvar = setvar;
+       math_hooks.endofname = endofname;
 
-#if ENABLE_ASH_MATH_SUPPORT
-static arith_t dash_arith(const char *);
-static arith_t arith(const char *expr, int *perrcode);
+       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);
+       }
+       INT_ON;
+
+       return result;
+}
 #endif
 
 /*
@@ -5320,11 +5329,7 @@ cvtnum(arith_t num)
        int len;
 
        expdest = makestrspace(32, expdest);
-#if ENABLE_ASH_MATH_SUPPORT_64
-       len = fmtstr(expdest, 32, "%lld", (long long) num);
-#else
-       len = fmtstr(expdest, 32, "%ld", num);
-#endif
+       len = fmtstr(expdest, 32, arith_t_fmt, num);
        STADJUST(len, expdest);
        return len;
 }
@@ -5688,7 +5693,7 @@ expbackq(union node *cmd, int quoted, int quotes)
                stackblock() + startloc));
 }
 
-#if ENABLE_ASH_MATH_SUPPORT
+#if ENABLE_SH_MATH_SUPPORT
 /*
  * Expand arithmetic expression.  Backup to start of expression,
  * evaluate, place result in (backed up) result, adjust string position.
@@ -5743,7 +5748,7 @@ expari(int quotes)
        if (quotes)
                rmescapes(p + 2);
 
-       len = cvtnum(dash_arith(p + 2));
+       len = cvtnum(ash_arith(p + 2));
 
        if (flag != '"')
                recordregion(begoff, begoff + len, 0);
@@ -5774,7 +5779,7 @@ argstr(char *p, int flag, struct strlist *var_str_list)
                CTLVAR,
                CTLBACKQ,
                CTLBACKQ | CTLQUOTE,
-#if ENABLE_ASH_MATH_SUPPORT
+#if ENABLE_SH_MATH_SUPPORT
                CTLENDARI,
 #endif
                0
@@ -5811,7 +5816,7 @@ argstr(char *p, int flag, struct strlist *var_str_list)
                length += strcspn(p + length, reject);
                c = p[length];
                if (c && (!(c & 0x80)
-#if ENABLE_ASH_MATH_SUPPORT
+#if ENABLE_SH_MATH_SUPPORT
                                        || c == CTLENDARI
 #endif
                   )) {
@@ -5889,7 +5894,7 @@ argstr(char *p, int flag, struct strlist *var_str_list)
                        expbackq(argbackq->n, c, quotes);
                        argbackq = argbackq->next;
                        goto start;
-#if ENABLE_ASH_MATH_SUPPORT
+#if ENABLE_SH_MATH_SUPPORT
                case CTLENDARI:
                        p--;
                        expari(quotes);
@@ -6064,9 +6069,9 @@ subevalvar(char *p, char *str, int strloc, int subtype,
        char *startp;
        char *loc;
        char *rmesc, *rmescend;
-       USE_ASH_BASH_COMPAT(char *repl = NULL;)
-       USE_ASH_BASH_COMPAT(char null = '\0';)
-       USE_ASH_BASH_COMPAT(int pos, len, orig_len;)
+       IF_ASH_BASH_COMPAT(char *repl = NULL;)
+       IF_ASH_BASH_COMPAT(char null = '\0';)
+       IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
        int saveherefd = herefd;
        int amount, workloc, resetloc;
        int zero;
@@ -6152,7 +6157,7 @@ subevalvar(char *p, char *str, int strloc, int subtype,
         * stack will need rebasing, and we'll need to remove our work
         * areas each time
         */
USE_ASH_BASH_COMPAT(restart:)
IF_ASH_BASH_COMPAT(restart:)
 
        amount = expdest - ((char *)stackblock() + resetloc);
        STADJUST(-amount, expdest);
@@ -6283,7 +6288,7 @@ static ssize_t
 varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
 {
        int num;
-       char *p;
+       const char *p;
        int i;
        int sep = 0;
        int sepq = 0;
@@ -6316,14 +6321,13 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
                len = cvtnum(num);
                break;
        case '-':
-               p = makestrspace(NOPTS, expdest);
+               expdest = makestrspace(NOPTS, expdest);
                for (i = NOPTS - 1; i >= 0; i--) {
                        if (optlist[i]) {
-                               USTPUTC(optletters(i), p);
+                               USTPUTC(optletters(i), expdest);
                                len++;
                        }
                }
-               expdest = p;
                break;
        case '@':
                if (sep)
@@ -7079,7 +7083,7 @@ static int builtinloc = -1;     /* index in path of %builtin, or -1 */
 
 
 static void
-tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
+tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp)
 {
        int repeated = 0;
 
@@ -7151,13 +7155,13 @@ shellexec(char **argv, const char *path, int idx)
         || (applet_no = find_applet_by_name(argv[0])) >= 0
 #endif
        ) {
-               tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
+               tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
                e = errno;
        } else {
                e = ENOENT;
                while ((cmdname = padvance(&path, argv[0])) != NULL) {
                        if (--idx < 0 && pathopt == NULL) {
-                               tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
+                               tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
                                if (errno != ENOENT && errno != ENOTDIR)
                                        e = errno;
                        }
@@ -8416,7 +8420,9 @@ evalpipe(union node *n, int flags)
                if (prevfd >= 0)
                        close(prevfd);
                prevfd = pip[0];
-               close(pip[1]);
+               /* Don't want to trigger debugging */
+               if (pip[1] != -1)
+                       close(pip[1]);
        }
        if (n->npipe.pipe_backgnd == 0) {
                exitstatus = waitforjob(jp);
@@ -8700,7 +8706,7 @@ static int getoptscmd(int, char **);
 #if !ENABLE_FEATURE_SH_EXTRA_QUIET
 static int helpcmd(int, char **);
 #endif
-#if ENABLE_ASH_MATH_SUPPORT
+#if ENABLE_SH_MATH_SUPPORT
 static int letcmd(int, char **);
 #endif
 static int readcmd(int, char **);
@@ -8782,7 +8788,7 @@ static const struct builtincmd builtintab[] = {
        { BUILTIN_REGULAR       "jobs", jobscmd },
        { BUILTIN_REGULAR       "kill", killcmd },
 #endif
-#if ENABLE_ASH_MATH_SUPPORT
+#if ENABLE_SH_MATH_SUPPORT
        { BUILTIN_NOSPEC        "let", letcmd },
 #endif
        { BUILTIN_ASSIGN        "local", localcmd },
@@ -10149,14 +10155,6 @@ static struct heredoc *heredoc;
  */
 #define NEOF ((union node *)&tokpushback)
 
-static void raise_error_syntax(const char *) NORETURN;
-static void
-raise_error_syntax(const char *msg)
-{
-       ash_msg_and_raise_error("syntax error: %s", msg);
-       /* NOTREACHED */
-}
-
 /*
  * Called when an unexpected token is read during the parse.  The argument
  * is the token that is expected, or -1 if more than one type of token can
@@ -10791,7 +10789,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
        int parenlevel;      /* levels of parens in arithmetic */
        int dqvarnest;       /* levels of variables expansion within double quotes */
 
-       USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
+       IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
 
 #if __GNUC__
        /* Avoid longjmp clobbering */
@@ -10897,7 +10895,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
                                dblquote = 1;
                                goto quotemark;
                        case CENDQUOTE:
-                               USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
+                               IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;)
                                if (eofmark != NULL && arinest == 0
                                 && varnest == 0
                                ) {
@@ -10925,7 +10923,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
                                        USTPUTC(c, out);
                                }
                                break;
-#if ENABLE_ASH_MATH_SUPPORT
+#if ENABLE_SH_MATH_SUPPORT
                        case CLP:       /* '(' in arithmetic */
                                parenlevel++;
                                USTPUTC(c, out);
@@ -10981,7 +10979,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
                } /* for (;;) */
        }
  endword:
-#if ENABLE_ASH_MATH_SUPPORT
+#if ENABLE_SH_MATH_SUPPORT
        if (syntax == ARISYNTAX)
                raise_error_syntax("missing '))'");
 #endif
@@ -10996,7 +10994,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
        len = out - (char *)stackblock();
        out = stackblock();
        if (eofmark == NULL) {
-               if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>'))
+               if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
                 && quotef == 0
                ) {
                        if (isdigit_str9(out)) {
@@ -11158,7 +11156,7 @@ parsesub: {
                pungetc();
        } else if (c == '(') {  /* $(command) or $((arith)) */
                if (pgetc() == '(') {
-#if ENABLE_ASH_MATH_SUPPORT
+#if ENABLE_SH_MATH_SUPPORT
                        PARSEARITH();
 #else
                        raise_error_syntax("you disabled math support for $((arith)) syntax");
@@ -11413,7 +11411,7 @@ parsebackq: {
        goto parsebackq_newreturn;
 }
 
-#if ENABLE_ASH_MATH_SUPPORT
+#if ENABLE_SH_MATH_SUPPORT
 /*
  * Parse an arithmetic expansion (indicate start of one and set state)
  */
@@ -11490,7 +11488,7 @@ xxreadtoken(void)
        startlinno = g_parsefile->linno;
        for (;;) {                      /* until token or start of word found */
                c = pgetc_fast();
-               if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA))
+               if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
                        continue;
 
                if (c == '#') {
@@ -11850,7 +11848,9 @@ cmdloop(int top)
 #endif
                }
                n = parsecmd(inter);
-               /* showtree(n); DEBUG */
+#if DEBUG
+               showtree(n);
+#endif
                if (n == NEOF) {
                        if (!top || numeof >= 50)
                                break;
@@ -12232,7 +12232,9 @@ helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
        unsigned col;
        unsigned i;
 
-       out1fmt("\nBuilt-in commands:\n-------------------\n");
+       out1fmt("\n"
+               "Built-in commands:\n"
+               "------------------\n");
        for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
                col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
                                        builtintab[i].name + 1);
@@ -12374,34 +12376,12 @@ timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
        return 0;
 }
 
-#if ENABLE_ASH_MATH_SUPPORT
-static arith_t
-dash_arith(const char *s)
-{
-       arith_t result;
-       int errcode = 0;
-
-       INT_OFF;
-       result = arith(s, &errcode);
-       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);
-       }
-       INT_ON;
-
-       return result;
-}
-
+#if ENABLE_SH_MATH_SUPPORT
 /*
- *  The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
- *  Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
+ * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell.
+ * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
  *
- *  Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
+ * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
  */
 static int
 letcmd(int argc UNUSED_PARAM, char **argv)
@@ -12412,12 +12392,12 @@ letcmd(int argc UNUSED_PARAM, char **argv)
        if (!*argv)
                ash_msg_and_raise_error("expression expected");
        do {
-               i = dash_arith(*argv);
+               i = ash_arith(*argv);
        } while (*++argv);
 
        return !i;
 }
-#endif /* ASH_MATH_SUPPORT */
+#endif /* SH_MATH_SUPPORT */
 
 
 /* ============ miscbltin.c
@@ -12474,8 +12454,8 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
        rflag = 0;
        prompt = NULL;
        while ((i = nextopt("p:u:r"
-               USE_ASH_READ_TIMEOUT("t:")
-               USE_ASH_READ_NCHARS("n:s")
+               IF_ASH_READ_TIMEOUT("t:")
+               IF_ASH_READ_NCHARS("n:s")
        )) != '\0') {
                switch (i) {
                case 'p':
@@ -12564,7 +12544,7 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 #endif
 
        status = 0;
-       startword = 1;
+       startword = 2;
        backslash = 0;
 #if ENABLE_ASH_READ_TIMEOUT
        if (timeout) /* NB: ensuring end_ms is nonzero */
@@ -12572,6 +12552,8 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 #endif
        STARTSTACKSTR(p);
        do {
+               const char *is_ifs;
+
 #if ENABLE_ASH_READ_TIMEOUT
                if (end_ms) {
                        struct pollfd pfd[1];
@@ -12601,25 +12583,36 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
                        continue;
                }
                if (!rflag && c == '\\') {
-                       backslash++;
+                       backslash = 1;
                        continue;
                }
                if (c == '\n')
                        break;
-               if (startword && *ifs == ' ' && strchr(ifs, c)) {
-                       continue;
+               /* $IFS splitting */
+/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */
+               is_ifs = strchr(ifs, c);
+               if (startword && is_ifs) {
+                       if (isspace(c))
+                               continue;
+                       /* it is a non-space ifs char */
+                       startword--;
+                       if (startword == 1) /* first one? */
+                               continue; /* yes, it is not next word yet */
                }
                startword = 0;
-               if (ap[1] != NULL && strchr(ifs, c) != NULL) {
+               if (ap[1] != NULL && is_ifs) {
+                       const char *beg;
                        STACKSTRNUL(p);
-                       setvar(*ap, stackblock(), 0);
+                       beg = stackblock();
+                       setvar(*ap, beg, 0);
                        ap++;
-                       startword = 1;
+                       /* can we skip one non-space ifs char? (2: yes) */
+                       startword = isspace(c) ? 2 : 1;
                        STARTSTACKSTR(p);
-               } else {
- put:
-                       STPUTC(c, p);
+                       continue;
                }
+ put:
+               STPUTC(c, p);
        }
 /* end of do {} while: */
 #if ENABLE_ASH_READ_NCHARS
@@ -12633,8 +12626,8 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 #endif
 
        STACKSTRNUL(p);
-       /* Remove trailing blanks */
-       while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
+       /* Remove trailing space ifs chars */
+       while ((char *)stackblock() <= --p && isspace(*p) && strchr(ifs, *p) != NULL)
                *p = '\0';
        setvar(*ap, stackblock(), 0);
        while (*++ap != NULL)
@@ -12653,6 +12646,8 @@ umaskcmd(int argc UNUSED_PARAM, char **argv)
                S_IROTH, S_IWOTH, S_IXOTH
        };
 
+       /* TODO: use bb_parse_mode() instead */
+
        char *ap;
        mode_t mask;
        int i;
@@ -12719,7 +12714,6 @@ umaskcmd(int argc UNUSED_PARAM, char **argv)
  *
  * Public domain.
  */
-
 struct limits {
        uint8_t cmd;          /* RLIMIT_xxx fit into it */
        uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
@@ -12928,654 +12922,6 @@ ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
        return 0;
 }
 
-
-/* ============ Math support */
-
-#if ENABLE_ASH_MATH_SUPPORT
-
-/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
-
-   Permission is hereby granted, free of charge, to any person obtaining
-   a copy of this software and associated documentation files (the
-   "Software"), to deal in the Software without restriction, including
-   without limitation the rights to use, copy, modify, merge, publish,
-   distribute, sublicense, and/or sell copies of the Software, and to
-   permit persons to whom the Software is furnished to do so, subject to
-   the following conditions:
-
-   The above copyright notice and this permission notice shall be
-   included in all copies or substantial portions of the Software.
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-
-/* 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 written in yacc. The supported operators are
- * listed in #defines below. Parens, order of operations, and error handling
- * 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 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). */
-
-/* To use the routine, call it with an expression string and error return
- * pointer */
-
-/*
- * Aug 24, 2001              Manuel Novoa III
- *
- * Reduced the generated code size by about 30% (i386) and fixed several bugs.
- *
- * 1) In arith_apply():
- *    a) Cached values of *numptr and &(numptr[-1]).
- *    b) Removed redundant test for zero denominator.
- *
- * 2) In arith():
- *    a) Eliminated redundant code for processing operator tokens by moving
- *       to a table-based implementation.  Also folded handling of parens
- *       into the table.
- *    b) Combined all 3 loops which called arith_apply to reduce generated
- *       code size at the cost of speed.
- *
- * 3) The following expressions were treated as valid by the original code:
- *       1()  ,    0!  ,    1 ( *3 )   .
- *    These bugs have been fixed by internally enclosing the expression in
- *    parens and then checking that all binary ops and right parens are
- *    preceded by a valid expression (NUM_TOKEN).
- *
- * 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.
- */
-
-/*
- * Aug 26, 2001              Manuel Novoa III
- *
- * Return 0 for null expressions.  Pointed out by Vladimir Oleynik.
- *
- * Merge in Aaron's comments previously posted to the busybox list,
- * modified slightly to take account of my changes to the code.
- *
- */
-
-/*
- *  (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
- *
- * - allow access to variable,
- *   used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
- * - realize assign syntax (VAR=expr, +=, *= etc)
- * - realize exponentiation (** operator)
- * - realize comma separated - expr, expr
- * - realise ++expr --expr expr++ expr--
- * - realise expr ? expr : expr (but, second expr calculate always)
- * - 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)
- * - always use special isspace(), see comment from bash ;-)
- */
-
-#define arith_isspace(arithval) \
-       (arithval == ' ' || arithval == '\n' || arithval == '\t')
-
-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 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 /. */
-
-#define tok_decl(prec,id) (((id)<<5)|(prec))
-#define PREC(op) ((op) & 0x1F)
-
-#define TOK_LPAREN tok_decl(0,0)
-
-#define TOK_COMMA tok_decl(1,0)
-
-#define TOK_ASSIGN tok_decl(2,0)
-#define TOK_AND_ASSIGN tok_decl(2,1)
-#define TOK_OR_ASSIGN tok_decl(2,2)
-#define TOK_XOR_ASSIGN tok_decl(2,3)
-#define TOK_PLUS_ASSIGN tok_decl(2,4)
-#define TOK_MINUS_ASSIGN tok_decl(2,5)
-#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
-#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
-
-#define TOK_MUL_ASSIGN tok_decl(3,0)
-#define TOK_DIV_ASSIGN tok_decl(3,1)
-#define TOK_REM_ASSIGN tok_decl(3,2)
-
-/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
-#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
-
-/* conditional is right associativity too */
-#define TOK_CONDITIONAL tok_decl(4,0)
-#define TOK_CONDITIONAL_SEP tok_decl(4,1)
-
-#define TOK_OR tok_decl(5,0)
-
-#define TOK_AND tok_decl(6,0)
-
-#define TOK_BOR tok_decl(7,0)
-
-#define TOK_BXOR tok_decl(8,0)
-
-#define TOK_BAND tok_decl(9,0)
-
-#define TOK_EQ tok_decl(10,0)
-#define TOK_NE tok_decl(10,1)
-
-#define TOK_LT tok_decl(11,0)
-#define TOK_GT tok_decl(11,1)
-#define TOK_GE tok_decl(11,2)
-#define TOK_LE tok_decl(11,3)
-
-#define TOK_LSHIFT tok_decl(12,0)
-#define TOK_RSHIFT tok_decl(12,1)
-
-#define TOK_ADD tok_decl(13,0)
-#define TOK_SUB tok_decl(13,1)
-
-#define TOK_MUL tok_decl(14,0)
-#define TOK_DIV tok_decl(14,1)
-#define TOK_REM tok_decl(14,2)
-
-/* exponent is right associativity */
-#define TOK_EXPONENT tok_decl(15,1)
-
-/* For now unary operators. */
-#define UNARYPREC 16
-#define TOK_BNOT tok_decl(UNARYPREC,0)
-#define TOK_NOT tok_decl(UNARYPREC,1)
-
-#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
-#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
-
-#define PREC_PRE (UNARYPREC+2)
-
-#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
-#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
-
-#define PREC_POST (UNARYPREC+3)
-
-#define TOK_POST_INC tok_decl(PREC_POST, 0)
-#define TOK_POST_DEC tok_decl(PREC_POST, 1)
-
-#define SPEC_PREC (UNARYPREC+4)
-
-#define TOK_NUM tok_decl(SPEC_PREC, 0)
-#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
-
-#define NUMPTR (*numstackptr)
-
-static int
-tok_have_assign(operator op)
-{
-       operator prec = PREC(op);
-
-       convert_prec_is_assing(prec);
-       return (prec == PREC(TOK_ASSIGN) ||
-                       prec == PREC_PRE || prec == PREC_POST);
-}
-
-static int
-is_right_associativity(operator prec)
-{
-       return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
-               || prec == PREC(TOK_CONDITIONAL));
-}
-
-typedef struct {
-       arith_t val;
-       arith_t contidional_second_val;
-       char contidional_second_val_initialized;
-       char *var;      /* if NULL then is regular number,
-                          else is variable name */
-} v_n_t;
-
-typedef struct chk_var_recursive_looped_t {
-       const char *var;
-       struct chk_var_recursive_looped_t *next;
-} chk_var_recursive_looped_t;
-
-static chk_var_recursive_looped_t *prev_chk_var_recursive;
-
-static int
-arith_lookup_val(v_n_t *t)
-{
-       if (t->var) {
-               const char * p = lookupvar(t->var);
-
-               if (p) {
-                       int errcode;
-
-                       /* recursive try as expression */
-                       chk_var_recursive_looped_t *cur;
-                       chk_var_recursive_looped_t cur_save;
-
-                       for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
-                               if (strcmp(cur->var, t->var) == 0) {
-                                       /* expression recursion loop detected */
-                                       return -5;
-                               }
-                       }
-                       /* save current lookuped var name */
-                       cur = prev_chk_var_recursive;
-                       cur_save.var = t->var;
-                       cur_save.next = cur;
-                       prev_chk_var_recursive = &cur_save;
-
-                       t->val = arith (p, &errcode);
-                       /* restore previous ptr after recursiving */
-                       prev_chk_var_recursive = cur;
-                       return errcode;
-               }
-               /* allow undefined var as 0 */
-               t->val = 0;
-       }
-       return 0;
-}
-
-/* "applying" a token means performing it on the top elements on the integer
- * stack. For a unary operator it will only change the top element, but a
- * binary operator will pop two arguments and push a result */
-static int
-arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
-{
-       v_n_t *numptr_m1;
-       arith_t numptr_val, rez;
-       int ret_arith_lookup_val;
-
-       /* There is no operator that can work without arguments */
-       if (NUMPTR == numstack) goto err;
-       numptr_m1 = NUMPTR - 1;
-
-       /* check operand is var with noninteger value */
-       ret_arith_lookup_val = arith_lookup_val(numptr_m1);
-       if (ret_arith_lookup_val)
-               return ret_arith_lookup_val;
-
-       rez = numptr_m1->val;
-       if (op == TOK_UMINUS)
-               rez *= -1;
-       else if (op == TOK_NOT)
-               rez = !rez;
-       else if (op == TOK_BNOT)
-               rez = ~rez;
-       else if (op == TOK_POST_INC || op == TOK_PRE_INC)
-               rez++;
-       else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
-               rez--;
-       else if (op != TOK_UPLUS) {
-               /* Binary operators */
-
-               /* check and binary operators need two arguments */
-               if (numptr_m1 == numstack) goto err;
-
-               /* ... and they pop one */
-               --NUMPTR;
-               numptr_val = rez;
-               if (op == TOK_CONDITIONAL) {
-                       if (!numptr_m1->contidional_second_val_initialized) {
-                               /* protect $((expr1 ? expr2)) without ": expr" */
-                               goto err;
-                       }
-                       rez = numptr_m1->contidional_second_val;
-               } else if (numptr_m1->contidional_second_val_initialized) {
-                       /* protect $((expr1 : expr2)) without "expr ? " */
-                       goto err;
-               }
-               numptr_m1 = NUMPTR - 1;
-               if (op != TOK_ASSIGN) {
-                       /* check operand is var with noninteger value for not '=' */
-                       ret_arith_lookup_val = arith_lookup_val(numptr_m1);
-                       if (ret_arith_lookup_val)
-                               return ret_arith_lookup_val;
-               }
-               if (op == TOK_CONDITIONAL) {
-                       numptr_m1->contidional_second_val = rez;
-               }
-               rez = numptr_m1->val;
-               if (op == TOK_BOR || op == TOK_OR_ASSIGN)
-                       rez |= numptr_val;
-               else if (op == TOK_OR)
-                       rez = numptr_val || rez;
-               else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
-                       rez &= numptr_val;
-               else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
-                       rez ^= numptr_val;
-               else if (op == TOK_AND)
-                       rez = rez && numptr_val;
-               else if (op == TOK_EQ)
-                       rez = (rez == numptr_val);
-               else if (op == TOK_NE)
-                       rez = (rez != numptr_val);
-               else if (op == TOK_GE)
-                       rez = (rez >= numptr_val);
-               else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
-                       rez >>= numptr_val;
-               else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
-                       rez <<= numptr_val;
-               else if (op == TOK_GT)
-                       rez = (rez > numptr_val);
-               else if (op == TOK_LT)
-                       rez = (rez < numptr_val);
-               else if (op == TOK_LE)
-                       rez = (rez <= numptr_val);
-               else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
-                       rez *= numptr_val;
-               else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
-                       rez += numptr_val;
-               else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
-                       rez -= numptr_val;
-               else if (op == TOK_ASSIGN || op == TOK_COMMA)
-                       rez = numptr_val;
-               else if (op == TOK_CONDITIONAL_SEP) {
-                       if (numptr_m1 == numstack) {
-                               /* protect $((expr : expr)) without "expr ? " */
-                               goto err;
-                       }
-                       numptr_m1->contidional_second_val_initialized = op;
-                       numptr_m1->contidional_second_val = numptr_val;
-               } else if (op == TOK_CONDITIONAL) {
-                       rez = rez ?
-                               numptr_val : numptr_m1->contidional_second_val;
-               } else if (op == TOK_EXPONENT) {
-                       if (numptr_val < 0)
-                               return -3;      /* exponent less than 0 */
-                       else {
-                               arith_t c = 1;
-
-                               if (numptr_val)
-                                       while (numptr_val--)
-                                               c *= rez;
-                               rez = c;
-                       }
-               } else if (numptr_val==0)          /* zero divisor check */
-                       return -2;
-               else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
-                       rez /= numptr_val;
-               else if (op == TOK_REM || op == TOK_REM_ASSIGN)
-                       rez %= numptr_val;
-       }
-       if (tok_have_assign(op)) {
-               char buf[sizeof(arith_t_type)*3 + 2];
-
-               if (numptr_m1->var == NULL) {
-                       /* Hmm, 1=2 ? */
-                       goto err;
-               }
-               /* save to shell variable */
-#if ENABLE_ASH_MATH_SUPPORT_64
-               snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
-#else
-               snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
-#endif
-               setvar(numptr_m1->var, buf, 0);
-               /* after saving, make previous value for v++ or v-- */
-               if (op == TOK_POST_INC)
-                       rez--;
-               else if (op == TOK_POST_DEC)
-                       rez++;
-       }
-       numptr_m1->val = rez;
-       /* protect geting var value, is number now */
-       numptr_m1->var = NULL;
-       return 0;
- err:
-       return -1;
-}
-
-/* longest must be first */
-static const char op_tokens[] ALIGN1 = {
-       '<','<','=',0, TOK_LSHIFT_ASSIGN,
-       '>','>','=',0, TOK_RSHIFT_ASSIGN,
-       '<','<',    0, TOK_LSHIFT,
-       '>','>',    0, TOK_RSHIFT,
-       '|','|',    0, TOK_OR,
-       '&','&',    0, TOK_AND,
-       '!','=',    0, TOK_NE,
-       '<','=',    0, TOK_LE,
-       '>','=',    0, TOK_GE,
-       '=','=',    0, TOK_EQ,
-       '|','=',    0, TOK_OR_ASSIGN,
-       '&','=',    0, TOK_AND_ASSIGN,
-       '*','=',    0, TOK_MUL_ASSIGN,
-       '/','=',    0, TOK_DIV_ASSIGN,
-       '%','=',    0, TOK_REM_ASSIGN,
-       '+','=',    0, TOK_PLUS_ASSIGN,
-       '-','=',    0, TOK_MINUS_ASSIGN,
-       '-','-',    0, TOK_POST_DEC,
-       '^','=',    0, TOK_XOR_ASSIGN,
-       '+','+',    0, TOK_POST_INC,
-       '*','*',    0, TOK_EXPONENT,
-       '!',        0, TOK_NOT,
-       '<',        0, TOK_LT,
-       '>',        0, TOK_GT,
-       '=',        0, TOK_ASSIGN,
-       '|',        0, TOK_BOR,
-       '&',        0, TOK_BAND,
-       '*',        0, TOK_MUL,
-       '/',        0, TOK_DIV,
-       '%',        0, TOK_REM,
-       '+',        0, TOK_ADD,
-       '-',        0, TOK_SUB,
-       '^',        0, TOK_BXOR,
-       /* uniq */
-       '~',        0, TOK_BNOT,
-       ',',        0, TOK_COMMA,
-       '?',        0, TOK_CONDITIONAL,
-       ':',        0, TOK_CONDITIONAL_SEP,
-       ')',        0, TOK_RPAREN,
-       '(',        0, TOK_LPAREN,
-       0
-};
-/* ptr to ")" */
-#define endexpression (&op_tokens[sizeof(op_tokens)-7])
-
-static arith_t
-arith(const char *expr, int *perrcode)
-{
-       char arithval; /* Current character under analysis */
-       operator lasttok, op;
-       operator prec;
-       operator *stack, *stackptr;
-       const char *p = endexpression;
-       int errcode;
-       v_n_t *numstack, *numstackptr;
-       unsigned datasizes = strlen(expr) + 2;
-
-       /* 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 exercise to
-        * the reader. */
-       numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
-       /* Stack of operator tokens */
-       stackptr = stack = alloca(datasizes * sizeof(stack[0]));
-
-       *stackptr++ = lasttok = TOK_LPAREN;     /* start off with a left paren */
-       *perrcode = errcode = 0;
-
-       while (1) {
-               arithval = *expr;
-               if (arithval == 0) {
-                       if (p == endexpression) {
-                               /* Null expression. */
-                               return 0;
-                       }
-
-                       /* This is only reached after all tokens have been extracted from the
-                        * input stream. If there are still tokens on the operator stack, they
-                        * are to be applied in order. At the end, there should be a final
-                        * result on the integer stack */
-
-                       if (expr != endexpression + 1) {
-                               /* If we haven't done so already, */
-                               /* append a closing right paren */
-                               expr = endexpression;
-                               /* and let the loop process it. */
-                               continue;
-                       }
-                       /* At this point, we're done with the expression. */
-                       if (numstackptr != numstack+1) {
-                               /* ... but if there isn't, it's bad */
- err:
-                               *perrcode = -1;
-                               return *perrcode;
-                       }
-                       if (numstack->var) {
-                               /* expression is $((var)) only, lookup now */
-                               errcode = arith_lookup_val(numstack);
-                       }
- ret:
-                       *perrcode = errcode;
-                       return numstack->val;
-               }
-
-               /* Continue processing the expression. */
-               if (arith_isspace(arithval)) {
-                       /* Skip whitespace */
-                       goto prologue;
-               }
-               p = endofname(expr);
-               if (p != expr) {
-                       size_t var_name_size = (p-expr) + 1;  /* trailing zero */
-
-                       numstackptr->var = alloca(var_name_size);
-                       safe_strncpy(numstackptr->var, expr, var_name_size);
-                       expr = p;
- num:
-                       numstackptr->contidional_second_val_initialized = 0;
-                       numstackptr++;
-                       lasttok = TOK_NUM;
-                       continue;
-               }
-               if (isdigit(arithval)) {
-                       numstackptr->var = NULL;
-#if ENABLE_ASH_MATH_SUPPORT_64
-                       numstackptr->val = strtoll(expr, (char **) &expr, 0);
-#else
-                       numstackptr->val = strtol(expr, (char **) &expr, 0);
-#endif
-                       goto num;
-               }
-               for (p = op_tokens; ; p++) {
-                       const char *o;
-
-                       if (*p == 0) {
-                               /* strange operator not found */
-                               goto err;
-                       }
-                       for (o = expr; *p && *o == *p; p++)
-                               o++;
-                       if (!*p) {
-                               /* found */
-                               expr = o - 1;
-                               break;
-                       }
-                       /* skip tail uncompared token */
-                       while (*p)
-                               p++;
-                       /* skip zero delim */
-                       p++;
-               }
-               op = p[1];
-
-               /* post grammar: a++ reduce to num */
-               if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
-                       lasttok = TOK_NUM;
-
-               /* Plus and minus are binary (not unary) _only_ if the last
-                * token was as number, or a right paren (which pretends to be
-                * a number, since it evaluates to one). Think about it.
-                * It makes sense. */
-               if (lasttok != TOK_NUM) {
-                       switch (op) {
-                       case TOK_ADD:
-                               op = TOK_UPLUS;
-                               break;
-                       case TOK_SUB:
-                               op = TOK_UMINUS;
-                               break;
-                       case TOK_POST_INC:
-                               op = TOK_PRE_INC;
-                               break;
-                       case TOK_POST_DEC:
-                               op = TOK_PRE_DEC;
-                               break;
-                       }
-               }
-               /* We don't want a unary operator to cause recursive descent on the
-                * stack, because there can be many in a row and it could cause an
-                * operator to be evaluated before its argument is pushed onto the
-                * integer stack. */
-               /* But for binary operators, "apply" everything on the operator
-                * stack until we find an operator with a lesser priority than the
-                * one we have just extracted. */
-               /* Left paren is given the lowest priority so it will never be
-                * "applied" in this way.
-                * if associativity is right and priority eq, applied also skip
-                */
-               prec = PREC(op);
-               if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
-                       /* not left paren or unary */
-                       if (lasttok != TOK_NUM) {
-                               /* binary op must be preceded by a num */
-                               goto err;
-                       }
-                       while (stackptr != stack) {
-                               if (op == TOK_RPAREN) {
-                                       /* The algorithm employed here is simple: while we don't
-                                        * hit an open paren nor the bottom of the stack, pop
-                                        * tokens and apply them */
-                                       if (stackptr[-1] == TOK_LPAREN) {
-                                               --stackptr;
-                                               /* Any operator directly after a */
-                                               lasttok = TOK_NUM;
-                                               /* close paren should consider itself binary */
-                                               goto prologue;
-                                       }
-                               } else {
-                                       operator prev_prec = PREC(stackptr[-1]);
-
-                                       convert_prec_is_assing(prec);
-                                       convert_prec_is_assing(prev_prec);
-                                       if (prev_prec < prec)
-                                               break;
-                                       /* check right assoc */
-                                       if (prev_prec == prec && is_right_associativity(prec))
-                                               break;
-                               }
-                               errcode = arith_apply(*--stackptr, numstack, &numstackptr);
-                               if (errcode) goto ret;
-                       }
-                       if (op == TOK_RPAREN) {
-                               goto err;
-                       }
-               }
-
-               /* Push this operator to the stack and remember it. */
-               *stackptr++ = lasttok = op;
- prologue:
-               ++expr;
-       } /* while */
-}
-#endif /* ASH_MATH_SUPPORT */
-
-
 /* ============ main() and helpers */
 
 /*
@@ -13763,7 +13109,7 @@ extern int etext();
 int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int ash_main(int argc UNUSED_PARAM, char **argv)
 {
-       char *shinit;
+       const char *shinit;
        volatile smallint state;
        struct jmploc jmploc;
        struct stackmark smark;
@@ -13868,7 +13214,8 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
        if (minusc) {
                /* evalstring pushes parsefile stack.
                 * Ensure we don't falsely claim that 0 (stdin)
-                * is one of stacked source fds */
+                * is one of stacked source fds.
+                * Testcase: ash -c 'exec 1>&0' must not complain. */
                if (!sflag)
                        g_parsefile->fd = -1;
                evalstring(minusc, 0);