hush: fix wrong prompt problem on empty interactive commants
[oweals/busybox.git] / shell / ash.c
index f581b5bdf63bc415d44c6f83be8afa218fa0e1e6..6f03ac1c69b07aa35306a8fd8e1a05513729f4fc 100644 (file)
@@ -13,7 +13,7 @@
  * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
  * was re-ported from NetBSD and debianized.
  *
- * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 
 /*
 #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
 # define CLEAR_RANDOM_T(rnd) ((void)0)
 #endif
 
-#define SKIP_definitions 1
-#include "applet_tables.h"
-#undef SKIP_definitions
+#include "NUM_APPLETS.h"
 #if NUM_APPLETS == 1
 /* STANDALONE does not make sense, and won't compile */
 # undef CONFIG_FEATURE_SH_STANDALONE
 # error "Do not even bother, ash will not run on NOMMU machine"
 #endif
 
+//applet:IF_ASH(APPLET(ash, _BB_DIR_BIN, _BB_SUID_DROP))
+//applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, _BB_DIR_BIN, _BB_SUID_DROP, sh))
+//applet:IF_FEATURE_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, _BB_DIR_BIN, _BB_SUID_DROP, bash))
+
+//kbuild:lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o shell_common.o
+//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
+
+//config:config ASH
+//config:      bool "ash"
+//config:      default y
+//config:      depends on !NOMMU
+//config:      help
+//config:        Tha 'ash' shell adds about 60k in the default configuration and is
+//config:        the most complete and most pedantically correct shell included with
+//config:        busybox. This shell is actually a derivative of the Debian 'dash'
+//config:        shell (by Herbert Xu), which was created by porting the 'ash' shell
+//config:        (written by Kenneth Almquist) from NetBSD.
+//config:
+//config:config ASH_BASH_COMPAT
+//config:      bool "bash-compatible extensions"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable bash-compatible extensions.
+//config:
+//config:config ASH_JOB_CONTROL
+//config:      bool "Job control"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable job control in the ash shell.
+//config:
+//config:config ASH_ALIAS
+//config:      bool "alias support"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable alias support in the ash shell.
+//config:
+//config:config ASH_GETOPTS
+//config:      bool "Builtin getopt to parse positional parameters"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable getopts builtin in the ash shell.
+//config:
+//config:config ASH_BUILTIN_ECHO
+//config:      bool "Builtin version of 'echo'"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable support for echo, builtin to ash.
+//config:
+//config:config ASH_BUILTIN_PRINTF
+//config:      bool "Builtin version of 'printf'"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable support for printf, builtin to ash.
+//config:
+//config:config ASH_BUILTIN_TEST
+//config:      bool "Builtin version of 'test'"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable support for test, builtin to ash.
+//config:
+//config:config ASH_CMDCMD
+//config:      bool "'command' command to override shell builtins"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable support for the ash 'command' builtin, which allows
+//config:        you to run the specified command with the specified arguments,
+//config:        even when there is an ash builtin command with the same name.
+//config:
+//config:config ASH_MAIL
+//config:      bool "Check for new mail on interactive shells"
+//config:      default n
+//config:      depends on ASH
+//config:      help
+//config:        Enable "check for new mail" in the ash shell.
+//config:
+//config:config ASH_OPTIMIZE_FOR_SIZE
+//config:      bool "Optimize for size instead of speed"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Compile ash for reduced size at the price of speed.
+//config:
+//config:config ASH_RANDOM_SUPPORT
+//config:      bool "Pseudorandom generator and $RANDOM variable"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        Enable pseudorandom generator and dynamic variable "$RANDOM".
+//config:        Each read of "$RANDOM" will generate a new pseudorandom value.
+//config:        You can reset the generator by using a specified start value.
+//config:        After "unset RANDOM" the generator will switch off and this
+//config:        variable will no longer have special treatment.
+//config:
+//config:config ASH_EXPAND_PRMT
+//config:      bool "Expand prompt string"
+//config:      default y
+//config:      depends on ASH
+//config:      help
+//config:        "PS#" may contain volatile content, such as backquote commands.
+//config:        This option recreates the prompt string from the environment
+//config:        variable each time it is displayed.
+//config:
+
+//usage:#define ash_trivial_usage NOUSAGE_STR
+//usage:#define ash_full_usage ""
+//usage:#define sh_trivial_usage NOUSAGE_STR
+//usage:#define sh_full_usage ""
+//usage:#define bash_trivial_usage NOUSAGE_STR
+//usage:#define bash_full_usage ""
+
 
 /* ============ Hash table sizes. Configurable. */
 
@@ -837,7 +955,8 @@ sharg(union node *arg, FILE *fp)
        for (p = arg->narg.text; *p; p++) {
                switch ((unsigned char)*p) {
                case CTLESC:
-                       putc(*++p, fp);
+                       p++;
+                       putc(*p, fp);
                        break;
                case CTLVAR:
                        putc('$', fp);
@@ -846,8 +965,10 @@ sharg(union node *arg, FILE *fp)
                        if (subtype == VSLENGTH)
                                putc('#', fp);
 
-                       while (*p != '=')
-                               putc(*p++, fp);
+                       while (*p != '=') {
+                               putc(*p, fp);
+                               p++;
+                       }
 
                        if (subtype & VSNUL)
                                putc(':', fp);
@@ -1866,10 +1987,6 @@ extern struct globals_var *const ash_ptr_to_globals_var;
 # define optindval()    (voptind.var_text + 7)
 #endif
 
-
-#define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
-#define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
-
 #if ENABLE_ASH_GETOPTS
 static void FAST_FUNC
 getoptsreset(const char *value)
@@ -1879,24 +1996,26 @@ getoptsreset(const char *value)
 }
 #endif
 
+/* math.h has these, otherwise define our private copies */
+#if !ENABLE_SH_MATH_SUPPORT
+#define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
+#define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
 /*
- * Return of a legal variable name (a letter or underscore followed by zero or
- * more letters, underscores, and digits).
+ * Return the pointer to the first char which is not part of a legal variable name
+ * (a letter or underscore followed by letters, underscores, and digits).
  */
-static char* FAST_FUNC
+static const char*
 endofname(const char *name)
 {
-       char *p;
-
-       p = (char *) name;
-       if (!is_name(*p))
-               return p;
-       while (*++p) {
-               if (!is_in_name(*p))
+       if (!is_name(*name))
+               return name;
+       while (*++name) {
+               if (!is_in_name(*name))
                        break;
        }
-       return p;
+       return name;
 }
+#endif
 
 /*
  * Compares two strings up to the first = or '\0'.  The first
@@ -2079,9 +2198,10 @@ setvareq(char *s, int flags)
 static void
 setvar(const char *name, const char *val, int flags)
 {
-       char *p, *q;
-       size_t namelen;
+       const char *q;
+       char *p;
        char *nameeq;
+       size_t namelen;
        size_t vallen;
 
        q = endofname(name);
@@ -2095,12 +2215,13 @@ setvar(const char *name, const char *val, int flags)
        } else {
                vallen = strlen(val);
        }
+
        INT_OFF;
        nameeq = ckmalloc(namelen + vallen + 2);
-       p = (char *)memcpy(nameeq, name, namelen) + namelen;
+       p = memcpy(nameeq, name, namelen) + namelen;
        if (val) {
                *p++ = '=';
-               p = (char *)memcpy(p, val, vallen) + vallen;
+               p = memcpy(p, val, vallen) + vallen;
        }
        *p = '\0';
        setvareq(nameeq, flags | VNOSAVE);
@@ -2314,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;
 
@@ -4517,6 +4639,7 @@ clear_traps(void)
                        INT_ON;
                }
        }
+       may_have_traps = 0;
 }
 
 /* Lives far away from here, needed for forkchild */
@@ -4872,9 +4995,13 @@ noclobberopen(const char *fname)
         * revealed that it was a regular file, and the file has not been
         * replaced, return the file descriptor.
         */
-       if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode)
-        && finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino)
+       if (fstat(fd, &finfo2) == 0
+        && !S_ISREG(finfo2.st_mode)
+        && finfo.st_dev == finfo2.st_dev
+        && finfo.st_ino == finfo2.st_ino
+       ) {
                return fd;
+       }
 
        /* The file has been replaced.  badness. */
        close(fd);
@@ -4938,7 +5065,7 @@ openredirect(union node *redir)
                break;
        case NFROMTO:
                fname = redir->nfile.expfname;
-               f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
+               f = open(fname, O_RDWR|O_CREAT, 0666);
                if (f < 0)
                        goto ecreate;
                break;
@@ -5317,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;
@@ -5393,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;
 }
@@ -5570,7 +5694,7 @@ removerecordregions(int endoff)
                return;
 
        if (ifsfirst.endoff > endoff) {
-               while (ifsfirst.next != NULL) {
+               while (ifsfirst.next) {
                        struct ifsregion *ifsp;
                        INT_OFF;
                        ifsp = ifsfirst.next->next;
@@ -5578,9 +5702,9 @@ removerecordregions(int endoff)
                        ifsfirst.next = ifsp;
                        INT_ON;
                }
-               if (ifsfirst.begoff > endoff)
+               if (ifsfirst.begoff > endoff) {
                        ifslastp = NULL;
-               else {
+               else {
                        ifslastp = &ifsfirst;
                        ifsfirst.endoff = endoff;
                }
@@ -5589,8 +5713,8 @@ removerecordregions(int endoff)
 
        ifslastp = &ifsfirst;
        while (ifslastp->next && ifslastp->next->begoff < endoff)
-               ifslastp=ifslastp->next;
-       while (ifslastp->next != NULL) {
+               ifslastp = ifslastp->next;
+       while (ifslastp->next) {
                struct ifsregion *ifsp;
                INT_OFF;
                ifsp = ifslastp->next->next;
@@ -5767,9 +5891,9 @@ expbackq(union node *cmd, int quoted, int quotes)
 
        if (quoted == 0)
                recordregion(startloc, dest - (char *)stackblock(), 0);
-       TRACE(("evalbackq: size=%d: \"%.*s\"\n",
-               (dest - (char *)stackblock()) - startloc,
-               (dest - (char *)stackblock()) - startloc,
+       TRACE(("evalbackq: size:%d:'%.*s'\n",
+               (int)((dest - (char *)stackblock()) - startloc),
+               (int)((dest - (char *)stackblock()) - startloc),
                stackblock() + startloc));
 }
 
@@ -5884,7 +6008,7 @@ argstr(char *p, int flags, struct strlist *var_str_list)
                flags &= ~EXP_TILDE;
  tilde:
                q = p;
-               if (*q == CTLESC && (flags & EXP_QWORD))
+               if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
                        q++;
                if (*q == '~')
                        p = exptilde(p, q, flags);
@@ -5898,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++;
@@ -5947,8 +6069,8 @@ argstr(char *p, int flags, struct strlist *var_str_list)
                        /* "$@" syntax adherence hack */
                        if (!inquotes
                         && memcmp(p, dolatstr, 4) == 0
-                        && (  p[4] == CTLQUOTEMARK
-                           || (p[4] == CTLENDVAR && p[5] == CTLQUOTEMARK)
+                        && (  p[4] == (char)CTLQUOTEMARK
+                           || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
                            )
                        ) {
                                p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
@@ -5983,47 +6105,20 @@ argstr(char *p, int flags, struct strlist *var_str_list)
 #endif
                }
        }
- breakloop:
-       ;
+ breakloop: ;
 }
 
 static char *
-scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int quotes,
-       int zero)
-{
-// This commented out code was added by James Simmons <jsimmons@infradead.org>
-// as part of a larger change when he added support for ${var/a/b}.
-// However, it broke # and % operators:
-//
-//var=ababcdcd
-//                 ok       bad
-//echo ${var#ab}   abcdcd   abcdcd
-//echo ${var##ab}  abcdcd   abcdcd
-//echo ${var#a*b}  abcdcd   ababcdcd  (!)
-//echo ${var##a*b} cdcd     cdcd
-//echo ${var#?}    babcdcd  ababcdcd  (!)
-//echo ${var##?}   babcdcd  babcdcd
-//echo ${var#*}    ababcdcd babcdcd   (!)
-//echo ${var##*}
-//echo ${var%cd}   ababcd   ababcd
-//echo ${var%%cd}  ababcd   abab      (!)
-//echo ${var%c*d}  ababcd   ababcd
-//echo ${var%%c*d} abab     ababcdcd  (!)
-//echo ${var%?}    ababcdc  ababcdc
-//echo ${var%%?}   ababcdc  ababcdcd  (!)
-//echo ${var%*}    ababcdcd ababcdcd
-//echo ${var%%*}
-//
-// Commenting it back out helped. Remove it completely if it really
-// is not needed.
-
-       char *loc, *loc2; //, *full;
+scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
+               char *pattern, int quotes, int zero)
+{
+       char *loc, *loc2;
        char c;
 
        loc = startp;
        loc2 = rmesc;
        do {
-               int match; // = strlen(str);
+               int match;
                const char *s = loc2;
 
                c = *loc2;
@@ -6031,35 +6126,22 @@ scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM, char *str, int
                        *loc2 = '\0';
                        s = rmesc;
                }
-               match = pmatch(str, s); // this line was deleted
-
-//             // chop off end if its '*'
-//             full = strrchr(str, '*');
-//             if (full && full != str)
-//                     match--;
-//
-//             // If str starts with '*' replace with s.
-//             if ((*str == '*') && strlen(s) >= match) {
-//                     full = xstrdup(s);
-//                     strncpy(full+strlen(s)-match+1, str+1, match-1);
-//             } else
-//                     full = xstrndup(str, match);
-//             match = strncmp(s, full, strlen(full));
-//             free(full);
-//
+               match = pmatch(pattern, s);
+
                *loc2 = c;
-               if (match) // if (!match)
+               if (match)
                        return loc;
                if (quotes && (unsigned char)*loc == CTLESC)
                        loc++;
                loc++;
                loc2++;
        } while (c);
-       return 0;
+       return NULL;
 }
 
 static char *
-scanright(char *startp, char *rmesc, char *rmescend, char *pattern, int quotes, int match_at_start)
+scanright(char *startp, char *rmesc, char *rmescend,
+               char *pattern, int quotes, int match_at_start)
 {
 #if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
        int try2optimize = match_at_start;
@@ -6125,7 +6207,7 @@ scanright(char *startp, char *rmesc, char *rmescend, char *pattern, int quotes,
                        }
                }
        }
-       return 0;
+       return NULL;
 }
 
 static void varunset(const char *, const char *, const char *, int) NORETURN;
@@ -6145,16 +6227,18 @@ varunset(const char *end, const char *var, const char *umsg, int varflags)
                        msg = umsg;
                }
        }
-       ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail);
+       ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
 }
 
 #if ENABLE_ASH_BASH_COMPAT
 static char *
-parse_sub_pattern(char *arg, int inquotes)
+parse_sub_pattern(char *arg, int varflags)
 {
        char *idx, *repl = NULL;
        unsigned char c;
 
+       //char *org_arg = arg;
+       //bb_error_msg("arg:'%s' varflags:%x", arg, varflags);
        idx = arg;
        while (1) {
                c = *arg;
@@ -6168,32 +6252,47 @@ parse_sub_pattern(char *arg, int inquotes)
                        }
                }
                *idx++ = c;
-               if (!inquotes && c == '\\' && arg[1] == '\\')
-                       arg++; /* skip both \\, not just first one */
                arg++;
+               /*
+                * Example: v='ab\c'; echo ${v/\\b/_\\_\z_}
+                * The result is a_\_z_c (not a\_\_z_c)!
+                *
+                * Enable debug prints in this function and you'll see:
+                * ash: arg:'\\b/_\\_z_' varflags:d
+                * ash: pattern:'\\b' repl:'_\_z_'
+                * That is, \\b is interpreted as \\b, but \\_ as \_!
+                * IOW: search pattern and replace string treat backslashes
+                * differently! That is the reason why we check repl below:
+                */
+               if (c == '\\' && *arg == '\\' && repl && !(varflags & VSQUOTE))
+                       arg++; /* skip both '\', not just first one */
        }
        *idx = c; /* NUL */
+       //bb_error_msg("pattern:'%s' repl:'%s'", org_arg, repl);
 
        return repl;
 }
 #endif /* ENABLE_ASH_BASH_COMPAT */
 
 static const char *
-subevalvar(char *p, char *str, int strloc, int subtype,
+subevalvar(char *p, char *varname, int strloc, int subtype,
                int startloc, int varflags, int quotes, struct strlist *var_str_list)
 {
        struct nodelist *saveargbackq = argbackq;
        char *startp;
        char *loc;
        char *rmesc, *rmescend;
-       IF_ASH_BASH_COMPAT(char *repl = NULL;)
-       IF_ASH_BASH_COMPAT(char null = '\0';)
+       char *str;
+       IF_ASH_BASH_COMPAT(const char *repl = NULL;)
        IF_ASH_BASH_COMPAT(int pos, len, orig_len;)
        int saveherefd = herefd;
        int amount, workloc, resetloc;
        int zero;
        char *(*scan)(char*, char*, char*, char*, int, int);
 
+       //bb_error_msg("subevalvar(p:'%s',varname:'%s',strloc:%d,subtype:%d,startloc:%d,varflags:%x,quotes:%d)",
+       //              p, varname, strloc, subtype, startloc, varflags, quotes);
+
        herefd = -1;
        argstr(p, (subtype != VSASSIGN && subtype != VSQUESTION) ? EXP_CASE : 0,
                        var_str_list);
@@ -6204,11 +6303,15 @@ subevalvar(char *p, char *str, int strloc, int subtype,
 
        switch (subtype) {
        case VSASSIGN:
-               setvar(str, startp, 0);
+               setvar(varname, startp, 0);
                amount = startp - expdest;
                STADJUST(amount, expdest);
                return startp;
 
+       case VSQUESTION:
+               varunset(p, varname, startp, varflags);
+               /* NOTREACHED */
+
 #if ENABLE_ASH_BASH_COMPAT
        case VSSUBSTR:
                loc = str = stackblock() + strloc;
@@ -6269,11 +6372,8 @@ subevalvar(char *p, char *str, int strloc, int subtype,
                STADJUST(amount, expdest);
                return loc;
 #endif
-
-       case VSQUESTION:
-               varunset(p, str, startp, varflags);
-               /* NOTREACHED */
        }
+
        resetloc = expdest - (char *)stackblock();
 
        /* We'll comeback here if we grow the stack while handling
@@ -6306,14 +6406,15 @@ subevalvar(char *p, char *str, int strloc, int subtype,
                char *idx, *end;
 
                if (!repl) {
-                       repl = parse_sub_pattern(str, varflags & VSQUOTE);
+                       repl = parse_sub_pattern(str, varflags);
+                       //bb_error_msg("repl:'%s'", repl);
                        if (!repl)
-                               repl = &null;
+                               repl = nullstr;
                }
 
                /* If there's no pattern to match, return the expansion unmolested */
                if (str[0] == '\0')
-                       return 0;
+                       return NULL;
 
                len = 0;
                idx = startp;
@@ -6321,6 +6422,7 @@ subevalvar(char *p, char *str, int strloc, int subtype,
                while (idx < end) {
  try_to_match:
                        loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
+                       //bb_error_msg("scanright('%s'):'%s'", str, loc);
                        if (!loc) {
                                /* No match, advance */
                                char *restart_detect = stackblock();
@@ -6359,8 +6461,13 @@ subevalvar(char *p, char *str, int strloc, int subtype,
                                idx = loc;
                        }
 
-                       for (loc = repl; *loc; loc++) {
+                       //bb_error_msg("repl:'%s'", repl);
+                       for (loc = (char*)repl; *loc; loc++) {
                                char *restart_detect = stackblock();
+                               if (quotes && *loc == '\\') {
+                                       STPUTC(CTLESC, expdest);
+                                       len++;
+                               }
                                STPUTC(*loc, expdest);
                                if (stackblock() != restart_detect)
                                        goto restart;
@@ -6368,6 +6475,7 @@ subevalvar(char *p, char *str, int strloc, int subtype,
                        }
 
                        if (subtype == VSREPLACE) {
+                               //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
                                while (*idx) {
                                        char *restart_detect = stackblock();
                                        STPUTC(*idx, expdest);
@@ -6383,11 +6491,11 @@ subevalvar(char *p, char *str, int strloc, int subtype,
                /* We've put the replaced text into a buffer at workloc, now
                 * move it to the right place and adjust the stack.
                 */
-               startp = stackblock() + startloc;
                STPUTC('\0', expdest);
-               memmove(startp, stackblock() + workloc, len);
-               startp[len++] = '\0';
-               amount = expdest - ((char *)stackblock() + startloc + len - 1);
+               startp = (char *)stackblock() + startloc;
+               memmove(startp, (char *)stackblock() + workloc, len + 1);
+               //bb_error_msg("startp:'%s'", startp);
+               amount = expdest - (startp + len);
                STADJUST(-amount, expdest);
                return startp;
        }
@@ -6616,8 +6724,8 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
  vsplus:
                if (varlen < 0) {
                        argstr(
-                               p, flags | EXP_TILDE |
-                                       (quoted ? EXP_QWORD : EXP_WORD),
+                               p,
+                               flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
                                var_str_list
                        );
                        goto end;
@@ -6687,7 +6795,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list)
                 */
                STPUTC('\0', expdest);
                patloc = expdest - (char *)stackblock();
-               if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype,
+               if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype,
                                startloc, varflags,
 //TODO: | EXP_REDIR too? All other such places do it too
                                /* quotes: */ flags & (EXP_FULL | EXP_CASE),
@@ -6850,13 +6958,11 @@ addfname(const char *name)
        exparg.lastp = &sp->next;
 }
 
-static char *expdir;
-
 /*
  * Do metacharacter (i.e. *, ?, [...]) expansion.
  */
 static void
-expmeta(char *enddir, char *name)
+expmeta(char *expdir, char *enddir, char *name)
 {
        char *p;
        const char *cp;
@@ -6955,7 +7061,7 @@ expmeta(char *enddir, char *name)
                                for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
                                        continue;
                                p[-1] = '/';
-                               expmeta(p, endname);
+                               expmeta(expdir, p, endname);
                        }
                }
        }
@@ -7037,6 +7143,7 @@ expandmeta(struct strlist *str /*, int flag*/)
        /* TODO - EXP_REDIR */
 
        while (str) {
+               char *expdir;
                struct strlist **savelastp;
                struct strlist *sp;
                char *p;
@@ -7053,8 +7160,7 @@ expandmeta(struct strlist *str /*, int flag*/)
                        int i = strlen(str->text);
                        expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
                }
-
-               expmeta(expdir, p);
+               expmeta(expdir, expdir, p);
                free(expdir);
                if (p != str->text)
                        free(p);
@@ -7524,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;
@@ -8115,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?]
@@ -8305,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:
@@ -8641,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);
@@ -9103,19 +9213,15 @@ evalcommand(union node *cmd, int flags)
        /* Print the command if xflag is set. */
        if (xflag) {
                int n;
-               const char *p = " %s";
+               const char *p = " %s" + 1;
 
-               p++;
                fdprintf(preverrout_fd, p, expandstr(ps4val()));
-
                sp = varlist.list;
                for (n = 0; n < 2; n++) {
                        while (sp) {
                                fdprintf(preverrout_fd, p, sp->text);
                                sp = sp->next;
-                               if (*p == '%') {
-                                       p--;
-                               }
+                               p = " %s";
                        }
                        sp = arglist.list;
                }
@@ -9303,7 +9409,7 @@ evalbltin(const struct builtincmd *cmd, int argc, char **argv)
 static int
 goodname(const char *p)
 {
-       return !*endofname(p);
+       return endofname(p)[0] == '\0';
 }
 
 
@@ -10934,7 +11040,6 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs)
        startlinno = g_parsefile->linno;
        bqlist = NULL;
        quotef = 0;
-       oldstyle = 0;
        prevsyntax = 0;
 #if ENABLE_ASH_EXPAND_PRMT
        pssyntax = (syntax == PSSYNTAX);
@@ -10950,160 +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
-                                       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--;
-                                       }
-                                       USTPUTC(CTLENDVAR, out);
-                               } else {
-                                       USTPUTC(c, out);
+                       }
+                       break;
+               case CVAR:      /* '$' */
+                       PARSESUB();             /* parse substitution */
+                       break;
+               case CENDVAR:   /* '}' */
+                       if (varnest > 0) {
+                               varnest--;
+                               if (dqvarnest > 0) {
+                                       dqvarnest--;
                                }
-                               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) {
-                                       USTPUTC(c, out);
-                                       --parenlevel;
-                               } else {
-                                       if (pgetc() == ')') {
-                                               if (--arinest == 0) {
-                                                       USTPUTC(CTLENDARI, out);
-                                                       syntax = prevsyntax;
-                                                       dblquote = (syntax == DQSYNTAX);
-                                               } else
-                                                       USTPUTC(')', out);
-                                       } else {
-                                               /*
-                                                * unbalanced parens
-                                                *  (don't 2nd guess - no error)
-                                                */
-                                               pungetc();
-                                               USTPUTC(')', out);
+               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();
                                }
-                               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 '))'");
@@ -11264,8 +11365,6 @@ parsesub: {
        unsigned char subtype;
        int typeloc;
        int flags;
-       char *p;
-       static const char types[] ALIGN1 = "}-+?=";
 
        c = pgetc();
        if (c > 255 /* PEOA or PEOF */
@@ -11278,7 +11377,8 @@ parsesub: {
 #endif
                        USTPUTC('$', out);
                pungetc();
-       } else if (c == '(') {  /* $(command) or $((arith)) */
+       } else if (c == '(') {
+               /* $(command) or $((arith)) */
                if (pgetc() == '(') {
 #if ENABLE_SH_MATH_SUPPORT
                        PARSEARITH();
@@ -11290,6 +11390,7 @@ parsesub: {
                        PARSEBACKQNEW();
                }
        } else {
+               /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
                USTPUTC(CTLVAR, out);
                typeloc = out - (char *)stackblock();
                USTPUTC(VSNORMAL, out);
@@ -11299,76 +11400,90 @@ parsesub: {
                        if (c == '#') {
                                c = pgetc();
                                if (c == '}')
-                                       c = '#';
+                                       c = '#'; /* ${#} - same as $# */
                                else
-                                       subtype = VSLENGTH;
-                       } else
+                                       subtype = VSLENGTH; /* ${#VAR} */
+                       } else {
                                subtype = 0;
+                       }
                }
                if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) {
+                       /* $[{[#]]NAME[}] */
                        do {
                                STPUTC(c, out);
                                c = pgetc();
                        } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c));
                } else if (isdigit(c)) {
+                       /* $[{[#]]NUM[}] */
                        do {
                                STPUTC(c, out);
                                c = pgetc();
                        } while (isdigit(c));
                } else if (is_special(c)) {
+                       /* $[{[#]]<specialchar>[}] */
                        USTPUTC(c, out);
                        c = pgetc();
                } else {
  badsub:
                        raise_error_syntax("bad substitution");
                }
-               if (c != '}' && subtype == VSLENGTH)
+               if (c != '}' && subtype == VSLENGTH) {
+                       /* ${#VAR didn't end with } */
                        goto badsub;
+               }
 
                STPUTC('=', out);
                flags = 0;
                if (subtype == 0) {
+                       /* ${VAR...} but not $VAR or ${#VAR} */
+                       /* c == first char after VAR */
                        switch (c) {
                        case ':':
                                c = pgetc();
 #if ENABLE_ASH_BASH_COMPAT
                                if (c == ':' || c == '$' || isdigit(c)) {
-                                       pungetc();
+//TODO: support more general format ${v:EXPR:EXPR},
+// where EXPR follows $(()) rules
                                        subtype = VSSUBSTR;
-                                       break;
+                                       pungetc();
+                                       break; /* "goto do_pungetc" is bigger (!) */
                                }
 #endif
                                flags = VSNUL;
                                /*FALLTHROUGH*/
-                       default:
-                               p = strchr(types, c);
+                       default: {
+                               static const char types[] ALIGN1 = "}-+?=";
+                               const char *p = strchr(types, c);
                                if (p == NULL)
                                        goto badsub;
                                subtype = p - types + VSNORMAL;
                                break;
+                       }
                        case '%':
                        case '#': {
                                int cc = c;
-                               subtype = c == '#' ? VSTRIMLEFT : VSTRIMRIGHT;
+                               subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
                                c = pgetc();
-                               if (c == cc)
-                                       subtype++;
-                               else
-                                       pungetc();
+                               if (c != cc)
+                                       goto do_pungetc;
+                               subtype++;
                                break;
                        }
 #if ENABLE_ASH_BASH_COMPAT
                        case '/':
+                               /* ${v/[/]pattern/repl} */
+//TODO: encode pattern and repl separately.
+// Currently ${v/$var_with_slash/repl} is horribly broken
                                subtype = VSREPLACE;
                                c = pgetc();
-                               if (c == '/')
-                                       subtype++; /* VSREPLACEALL */
-                               else
-                                       pungetc();
+                               if (c != '/')
+                                       goto do_pungetc;
+                               subtype++; /* VSREPLACEALL */
                                break;
 #endif
                        }
                } else {
+ do_pungetc:
                        pungetc();
                }
                if (dblquote || arinest)
@@ -11425,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 '`':
@@ -11444,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
@@ -11608,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();
@@ -11627,8 +11737,7 @@ xxreadtoken(void)
                                break; /* return readtoken1(...) */
                        }
                        startlinno = ++g_parsefile->linno;
-                       if (doprompt)
-                               setprompt(2);
+                       setprompt_if(doprompt, 2);
                } else {
                        const char *p;
 
@@ -11674,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();
@@ -11692,8 +11799,7 @@ xxreadtoken(void)
                case '\\':
                        if (pgetc() == '\n') {
                                startlinno = ++g_parsefile->linno;
-                               if (doprompt)
-                                       setprompt(2);
+                               setprompt_if(doprompt, 2);
                                continue;
                        }
                        pungetc();
@@ -11819,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)
@@ -11844,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;
@@ -11938,7 +12041,6 @@ evalcmd(int argc UNUSED_PARAM, char **argv)
                        p = grabstackstr(concat);
                }
                evalstring(p, ~SKIPEVAL);
-
        }
        return exitstatus;
 }
@@ -12230,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;
                        }
                }
@@ -12745,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: */
        {
@@ -12918,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 */
@@ -12959,7 +13063,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
                }
        }
 #endif
-       if (/* argv[0] && */ argv[0][0] == '-')
+       if (argv[0] && argv[0][0] == '-')
                isloginsh = 1;
        if (isloginsh) {
                state = 1;
@@ -13014,6 +13118,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
                _mcleanup();
        }
 #endif
+       TRACE(("End of main reached\n"));
        exitshell();
        /* NOTREACHED */
 }