get rid of global "struct bb_applet *current_applet"
[oweals/busybox.git] / shell / ash.c
index 90936fcc0364a1f7608f116beb1cd6152aa90802..af96c4d1da2365077ce4fd4441db598219c9a07d 100644 (file)
 #if DEBUG
 #define _GNU_SOURCE
 #endif
-#include "busybox.h"
+#include "busybox.h" /* for struct bb_applet */
 #include <paths.h>
 #include <setjmp.h>
 #include <fnmatch.h>
 #if JOBS || ENABLE_ASH_READ_NCHARS
 #include <termios.h>
 #endif
+extern char **environ;
 
 #if defined(__uClinux__)
 #error "Do not even bother, ash will not run on uClinux"
@@ -90,19 +91,19 @@ static const char *const optletters_optnames[] = {
        "a"   "allexport",
        "b"   "notify",
        "u"   "nounset",
-       "\0"  "vi",
+       "\0"  "vi"
 #if DEBUG
-       "\0"  "nolog",
-       "\0"  "debug",
+       ,"\0"  "nolog"
+       ,"\0"  "debug"
 #endif
 };
 
 #define optletters(n) optletters_optnames[(n)][0]
 #define optnames(n) (&optletters_optnames[(n)][1])
 
-#define NOPTS (sizeof(optletters_optnames)/sizeof(optletters_optnames[0]))
+enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
 
-static char optlist[NOPTS];
+static char optlist[NOPTS] ALIGN1;
 
 #define eflag optlist[0]
 #define fflag optlist[1]
@@ -126,21 +127,13 @@ static char optlist[NOPTS];
 
 /* ============ Misc data */
 
-#ifdef __GLIBC__
-/* glibc sucks */
-static int *dash_errno;
-#undef errno
-#define errno (*dash_errno)
-#endif
-
-static char nullstr[1];                /* zero length string */
-static const char homestr[] = "HOME";
-static const char snlfmt[] = "%s\n";
-static const char illnum[] = "Illegal number: %s";
+static char nullstr[1] ALIGN1;  /* zero length string */
+static const char homestr[] ALIGN1 = "HOME";
+static const char snlfmt[] ALIGN1 = "%s\n";
+static const char illnum[] ALIGN1 = "Illegal number: %s";
 
-static char *minusc;                   /* argument to -c option */
+static char *minusc;  /* argument to -c option */
 
-static int isloginsh;
 /* pid of main shell */
 static int rootpid;
 /* shell level: 0 for the main shell, 1 for its children, and so on */
@@ -148,6 +141,7 @@ static int shlvl;
 #define rootshell (!shlvl)
 /* trap handler commands */
 static char *trap[NSIG];
+static smallint isloginsh;
 /* current value of signal */
 static char sigmode[NSIG - 1];
 /* indicates specified signal received */
@@ -453,7 +447,9 @@ out2str(const char *p)
 #define VSTRIMLEFTMAX   0x9             /* ${var##pattern} */
 #define VSLENGTH        0xa             /* ${#var} */
 
-static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
+static const char dolatstr[] ALIGN1 = {
+       CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
+};
 
 #define NCMD 0
 #define NPIPE 1
@@ -730,7 +726,7 @@ opentrace(void)
                }
        }
 #ifdef O_APPEND
-       flags = fcntl(fileno(tracefile), F_GETFL, 0);
+       flags = fcntl(fileno(tracefile), F_GETFL);
        if (flags >= 0)
                fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
 #endif
@@ -846,23 +842,24 @@ shcmd(union node *cmd, FILE *fp)
 
        first = 1;
        for (np = cmd->ncmd.args; np; np = np->narg.next) {
-               if (! first)
-                       putchar(' ');
+               if (!first)
+                       putc(' ', fp);
                sharg(np, fp);
                first = 0;
        }
        for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
-               if (! first)
-                       putchar(' ');
+               if (!first)
+                       putc(' ', fp);
+               dftfd = 0;
                switch (np->nfile.type) {
-               case NTO:       s = ">";  dftfd = 1; break;
-               case NCLOBBER:  s = ">|"; dftfd = 1; break;
-               case NAPPEND:   s = ">>"; dftfd = 1; break;
-               case NTOFD:     s = ">&"; dftfd = 1; break;
-               case NFROM:     s = "<";  dftfd = 0; break;
-               case NFROMFD:   s = "<&"; dftfd = 0; break;
-               case NFROMTO:   s = "<>"; dftfd = 0; break;
-               default:        s = "*error*"; dftfd = 0; break;
+               case NTO:      s = ">>"+1; dftfd = 1; break;
+               case NCLOBBER: s = ">|"; dftfd = 1; break;
+               case NAPPEND:  s = ">>"; dftfd = 1; break;
+               case NTOFD:    s = ">&"; dftfd = 1; break;
+               case NFROM:    s = "<";  break;
+               case NFROMFD:  s = "<&"; break;
+               case NFROMTO:  s = "<>"; break;
+               default:       s = "*error*"; break;
                }
                if (np->nfile.fd != dftfd)
                        fprintf(fp, "%d", np->nfile.fd);
@@ -1587,12 +1584,11 @@ static unsigned long rseed;
 # define VDYNAMIC       0
 #endif
 
-static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
 #ifdef IFS_BROKEN
-static const char defifsvar[] = "IFS= \t\n";
+static const char defifsvar[] ALIGN1 = "IFS= \t\n";
 #define defifs (defifsvar + 4)
 #else
-static const char defifs[] = " \t\n";
+static const char defifs[] ALIGN1 = " \t\n";
 #endif
 
 struct shparam {
@@ -1680,7 +1676,7 @@ static struct var varinit[] = {
        { NULL, VSTRFIXED|VTEXTFIXED|VUNSET,    "MAIL\0",       changemail },
        { NULL, VSTRFIXED|VTEXTFIXED|VUNSET,    "MAILPATH\0",   changemail },
 #endif
-       { NULL, VSTRFIXED|VTEXTFIXED,           defpathvar,     changepath },
+       { NULL, VSTRFIXED|VTEXTFIXED,           bb_PATH_root_path, changepath },
        { NULL, VSTRFIXED|VTEXTFIXED,           "PS1=$ ",       NULL },
        { NULL, VSTRFIXED|VTEXTFIXED,           "PS2=> ",       NULL },
        { NULL, VSTRFIXED|VTEXTFIXED,           "PS4=+ ",       NULL },
@@ -1716,7 +1712,6 @@ static struct var varinit[] = {
 #else
 #define vrandom (&vps4)[1]
 #endif
-#define defpath (defpathvar + 5)
 
 /*
  * The following macros access the values of the above variables.
@@ -1747,7 +1742,6 @@ struct redirtab {
 
 static struct redirtab *redirlist;
 static int nullredirs;
-extern char **environ;
 static int preverrout_fd;   /* save fd2 before print debug if xflag is set. */
 
 #define VTABSIZE 39
@@ -1846,7 +1840,7 @@ initvar(void)
                vps1.text = "PS1=# ";
 #endif
        vp = varinit;
-       end = vp + sizeof(varinit) / sizeof(varinit[0]);
+       end = vp + ARRAY_SIZE(varinit);
        do {
                vpp = hashvar(vp->text);
                vp->next = *vpp;
@@ -2158,8 +2152,8 @@ padvance(const char **path, const char *name)
 
 /* ============ Prompt */
 
-static int doprompt;                   /* if set, prompt the user */
-static int needprompt;                 /* true if interactive and at start of line */
+static smallint doprompt;                   /* if set, prompt the user */
+static smallint needprompt;                 /* true if interactive and at start of line */
 
 #if ENABLE_FEATURE_EDITING
 static line_input_t *line_input_state;
@@ -2464,7 +2458,7 @@ pwdcmd(int argc, char **argv)
 
 /* ============ ... */
 
-#define IBUFSIZ (BUFSIZ + 1)
+#define IBUFSIZ COMMON_BUFSIZE
 #define basebuf bb_common_bufsiz1       /* buffer for top level input file */
 
 /* Syntax classes */
@@ -2500,6 +2494,7 @@ pwdcmd(int argc, char **argv)
 #define DQSYNTAX   1    /* in double quotes */
 #define SQSYNTAX   2    /* in single quotes */
 #define ARISYNTAX  3    /* in arithmetic */
+#define PSSYNTAX   4    /* prompt */
 
 #if ENABLE_ASH_OPTIMIZE_FOR_SIZE
 #define USE_SIT_FUNCTION
@@ -2556,16 +2551,16 @@ static const char S_I_T[][3] = {
 static int
 SIT(int c, int syntax)
 {
-       static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
+       static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
 #if ENABLE_ASH_ALIAS
-       static const char syntax_index_table[] = {
+       static const char syntax_index_table[] ALIGN1 = {
                1, 2, 1, 3, 4, 5, 1, 6,         /* "\t\n !\"$&'" */
                7, 8, 3, 3, 3, 3, 1, 1,         /* "()*-/:;<" */
                3, 1, 3, 3, 9, 3, 10, 1,        /* "=>?[\\]`|" */
                11, 3                           /* "}~" */
        };
 #else
-       static const char syntax_index_table[] = {
+       static const char syntax_index_table[] ALIGN1 = {
                0, 1, 0, 2, 3, 4, 0, 5,         /* "\t\n !\"$&'" */
                6, 7, 2, 2, 2, 2, 0, 0,         /* "()*-/:;<" */
                2, 0, 2, 2, 8, 2, 9, 0,         /* "=>?[\\]`|" */
@@ -3149,20 +3144,18 @@ struct job {
 };
 
 static pid_t backgndpid;        /* pid of last background process */
-static int job_warning;         /* user was warned about stopped jobs */
-#if JOBS
-static int jobctl;              /* true if doing job control */
-#endif
+static smallint job_warning;    /* user was warned about stopped jobs (can be 2, 1 or 0). */
 
 static struct job *makejob(union node *, int);
 static int forkshell(struct job *, union node *, int);
 static int waitforjob(struct job *);
 
-#if ! JOBS
-#define setjobctl(on)   /* do nothing */
+#if !JOBS
+enum { jobctl = 0 };
+#define setjobctl(on) do {} while (0)
 #else
+static smallint jobctl;              /* true if doing job control */
 static void setjobctl(int);
-static void showjobs(FILE *, int);
 #endif
 
 /*
@@ -3476,7 +3469,7 @@ setjobctl(int on)
                close(ofd);
                if (fd < 0)
                        goto out;
-               fcntl(fd, F_SETFD, FD_CLOEXEC);
+               close_on_exec_on(fd);
                do { /* while we are in the background */
                        pgrp = tcgetpgrp(fd);
                        if (pgrp < 0) {
@@ -3501,7 +3494,9 @@ setjobctl(int on)
                /* turning job control off */
                fd = ttyfd;
                pgrp = initialpgrp;
-               xtcsetpgrp(fd, pgrp);
+               /* was xtcsetpgrp, but this can make exiting ash
+                * with pty already deleted loop forever */
+               tcsetpgrp(fd, pgrp);
                setpgid(0, pgrp);
                setsignal(SIGTSTP);
                setsignal(SIGTTOU);
@@ -3517,91 +3512,22 @@ setjobctl(int on)
 static int
 killcmd(int argc, char **argv)
 {
-       int signo = -1;
-       int list = 0;
-       int i;
-       pid_t pid;
-       struct job *jp;
-
-       if (argc <= 1) {
- usage:
-               ash_msg_and_raise_error(
-"usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n"
-"kill -l [exitstatus]"
-               );
-       }
-
-       if (**++argv == '-') {
-               signo = get_signum(*argv + 1);
-               if (signo < 0) {
-                       int c;
-
-                       while ((c = nextopt("ls:")) != '\0') {
-                               switch (c) {
-                               default:
-#if DEBUG
-                                       abort();
-#endif
-                               case 'l':
-                                       list = 1;
-                                       break;
-                               case 's':
-                                       signo = get_signum(optionarg);
-                                       if (signo < 0) {
-                                               ash_msg_and_raise_error(
-                                                       "invalid signal number or name: %s",
-                                                       optionarg
-                                               );
-                                       }
-                                       break;
-                               }
-                       }
-                       argv = argptr;
-               } else
-                       argv++;
-       }
-
-       if (!list && signo < 0)
-               signo = SIGTERM;
-
-       if ((signo < 0 || !*argv) ^ list) {
-               goto usage;
-       }
-
-       if (list) {
-               const char *name;
-
-               if (!*argv) {
-                       for (i = 1; i < NSIG; i++) {
-                               name = get_signame(i);
-                               if (!isdigit(*name))
-                                       out1fmt(snlfmt, name);
+       if (argv[1] && strcmp(argv[1], "-l") != 0) {
+               int i = 1;
+               do {
+                       if (argv[i][0] == '%') {
+                               struct job *jp = getjob(argv[i], 0);
+                               unsigned pid = jp->ps[0].pid;
+                               /* Enough space for ' -NNN<nul>' */
+                               argv[i] = alloca(sizeof(int)*3 + 3);
+                               /* kill_main has matching code to expect
+                                * leading space. Needed to not confuse
+                                * negative pids with "kill -SIGNAL_NO" syntax */
+                               sprintf(argv[i], " -%u", pid);
                        }
-                       return 0;
-               }
-               name = get_signame(signo);
-               if (!isdigit(*name))
-                       ash_msg_and_raise_error("invalid signal number or exit status: %s", *argptr);
-               out1fmt(snlfmt, name);
-               return 0;
+               } while (argv[++i]);
        }
-
-       i = 0;
-       do {
-               if (**argv == '%') {
-                       jp = getjob(*argv, 0);
-                       pid = -jp->ps[0].pid;
-               } else {
-                       pid = **argv == '-' ?
-                               -number(*argv + 1) : number(*argv);
-               }
-               if (kill(pid, signo) != 0) {
-                       ash_msg("(%d) - %m", pid);
-                       i = 1;
-               }
-       } while (*++argv);
-
-       return i;
+       return kill_main(argc, argv);
 }
 
 static void
@@ -3640,7 +3566,8 @@ restartjob(struct job *jp, int mode)
                if (WIFSTOPPED(ps->status)) {
                        ps->status = -1;
                }
-       } while (ps++, --i);
+               ps++;
+       } while (--i);
  out:
        status = (mode == FORK_FG) ? waitforjob(jp) : 0;
        INT_ON;
@@ -3849,7 +3776,7 @@ showjob(FILE *out, struct job *jp, int mode)
        struct procstat *ps;
        struct procstat *psend;
        int col;
-       int indent;
+       int indent_col;
        char s[80];
 
        ps = jp->ps;
@@ -3861,7 +3788,7 @@ showjob(FILE *out, struct job *jp, int mode)
        }
 
        col = fmtstr(s, 16, "[%d]   ", jobno(jp));
-       indent = col;
+       indent_col = col;
 
        if (jp == curjob)
                s[col - 2] = '+';
@@ -3887,7 +3814,7 @@ showjob(FILE *out, struct job *jp, int mode)
 
        do {
                /* for each process */
-               col = fmtstr(s, 48, " |\n%*c%d ", indent, ' ', ps->pid) - 3;
+               col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3;
  start:
                fprintf(out, "%s%*c%s",
                        s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd
@@ -3910,11 +3837,32 @@ showjob(FILE *out, struct job *jp, int mode)
        }
 }
 
+/*
+ * Print a list of jobs.  If "change" is nonzero, only print jobs whose
+ * statuses have changed since the last call to showjobs.
+ */
+static void
+showjobs(FILE *out, int mode)
+{
+       struct job *jp;
+
+       TRACE(("showjobs(%x) called\n", mode));
+
+       /* If not even one one job changed, there is nothing to do */
+       while (dowait(DOWAIT_NORMAL, NULL) > 0)
+               continue;
+
+       for (jp = curjob; jp; jp = jp->prev_job) {
+               if (!(mode & SHOW_CHANGED) || jp->changed) {
+                       showjob(out, jp, mode);
+               }
+       }
+}
+
 static int
 jobscmd(int argc, char **argv)
 {
        int mode, m;
-       FILE *out;
 
        mode = 0;
        while ((m = nextopt("lp"))) {
@@ -3924,38 +3872,16 @@ jobscmd(int argc, char **argv)
                        mode = SHOW_PGID;
        }
 
-       out = stdout;
        argv = argptr;
        if (*argv) {
                do
-                       showjob(out, getjob(*argv,0), mode);
+                       showjob(stdout, getjob(*argv,0), mode);
                while (*++argv);
        } else
-               showjobs(out, mode);
+               showjobs(stdout, mode);
 
        return 0;
 }
-
-/*
- * Print a list of jobs.  If "change" is nonzero, only print jobs whose
- * statuses have changed since the last call to showjobs.
- */
-static void
-showjobs(FILE *out, int mode)
-{
-       struct job *jp;
-
-       TRACE(("showjobs(%x) called\n", mode));
-
-       /* If not even one one job changed, there is nothing to do */
-       while (dowait(DOWAIT_NORMAL, NULL) > 0)
-               continue;
-
-       for (jp = curjob; jp; jp = jp->prev_job) {
-               if (!(mode & SHOW_CHANGED) || jp->changed)
-                       showjob(out, jp, mode);
-       }
-}
 #endif /* JOBS */
 
 static int
@@ -4117,6 +4043,8 @@ makejob(union node *node, int nprocs)
        }
        memset(jp, 0, sizeof(*jp));
 #if JOBS
+       /* jp->jobctl is a bitfield.
+        * "jp->jobctl |= jobctl" likely to give awful code */
        if (jobctl)
                jp->jobctl = 1;
 #endif
@@ -4449,8 +4377,11 @@ clear_traps(void)
                }
        }
 }
-/* lives far away from here, needed for forkchild */
+
+/* Lives far away from here, needed for forkchild */
 static void closescript(void);
+
+/* Called after fork(), in child */
 static void
 forkchild(struct job *jp, union node *n, int mode)
 {
@@ -4499,6 +4430,7 @@ forkchild(struct job *jp, union node *n, int mode)
        jobless = 0;
 }
 
+/* Called after fork(), in parent */
 static void
 forkparent(struct job *jp, union node *n, int mode, pid_t pid)
 {
@@ -5068,8 +5000,9 @@ esclen(const char *start, const char *p)
 static char *
 _rmescapes(char *str, int flag)
 {
+       static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
+
        char *p, *q, *r;
-       static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
        unsigned inquotes;
        int notescaped;
        int globbing;
@@ -5389,8 +5322,7 @@ expbackq(union node *cmd, int quoted, int quotes)
                p = buf;
        }
 
-       if (in.buf)
-               free(in.buf);
+       free(in.buf);
        if (in.fd >= 0) {
                close(in.fd);
                back_exitstatus = waitforjob(in.jp);
@@ -5484,7 +5416,7 @@ static char *evalvar(char *p, int flag);
 static void
 argstr(char *p, int flag)
 {
-       static const char spclchars[] = {
+       static const char spclchars[] ALIGN1 = {
                '=',
                ':',
                CTLQUOTEMARK,
@@ -6330,7 +6262,7 @@ expsort(struct strlist *str)
 static void
 expandmeta(struct strlist *str, int flag)
 {
-       static const char metachars[] = {
+       static const char metachars[] ALIGN1 = {
                '*', '?', '[', 0
        };
        /* TODO - EXP_REDIR */
@@ -6538,12 +6470,10 @@ tryexec(char *cmd, char **argv, char **envp)
 
                a = find_applet_by_name(cmd);
                if (a) {
-                       if (a->noexec) {
-                               current_applet = a;
-                               run_current_applet_and_exit(argv);
-                       }
+                       if (a->noexec)
+                               run_appletstruct_and_exit(a, argv);
                        /* re-exec ourselves with the new arguments */
-                       execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp);
+                       execve(bb_busybox_exec_path, argv, envp);
                        /* If they called chroot or otherwise made the binary no longer
                         * executable, fall through */
                }
@@ -6950,21 +6880,15 @@ static const char *const *
 findkwd(const char *s)
 {
        return bsearch(s, tokname_array + KWDOFFSET,
-                       (sizeof(tokname_array) / sizeof(char *)) - KWDOFFSET,
-                       sizeof(char *), pstrcmp);
+                       ARRAY_SIZE(tokname_array) - KWDOFFSET,
+                       sizeof(tokname_array[0]), pstrcmp);
 }
 
 /*
  * Locate and print what a word is...
  */
-#if ENABLE_ASH_CMDCMD
 static int
 describe_command(char *command, int describe_command_verbose)
-#else
-#define describe_command_verbose 1
-static int
-describe_command(char *command)
-#endif
 {
        struct cmdentry entry;
        struct tblentry *cmdp;
@@ -6987,13 +6911,12 @@ describe_command(char *command)
        /* Then look at the aliases */
        ap = lookupalias(command, 0);
        if (ap != NULL) {
-               if (describe_command_verbose) {
-                       out1fmt(" is an alias for %s", ap->val);
-               } else {
+               if (!describe_command_verbose) {
                        out1str("alias ");
                        printalias(ap);
                        return 0;
                }
+               out1fmt(" is an alias for %s", ap->val);
                goto out;
        }
 #endif
@@ -7062,15 +6985,17 @@ describe_command(char *command)
 static int
 typecmd(int argc, char **argv)
 {
-       int i;
+       int i = 1;
        int err = 0;
+       int verbose = 1;
 
-       for (i = 1; i < argc; i++) {
-#if ENABLE_ASH_CMDCMD
-               err |= describe_command(argv[i], 1);
-#else
-               err |= describe_command(argv[i]);
-#endif
+       /* type -p ... ? (we don't bother checking for 'p') */
+       if (argv[1] && argv[1][0] == '-') {
+               i++;
+               verbose = 0;
+       }
+       while (i < argc) {
+               err |= describe_command(argv[i++], verbose);
        }
        return err;
 }
@@ -7816,16 +7741,16 @@ setinteractive(int on)
 #if !ENABLE_FEATURE_SH_EXTRA_QUIET
        if (is_interactive > 1) {
                /* Looks like they want an interactive shell */
-               static smallint do_banner;
+               static smallint did_banner;
 
-               if (!do_banner) {
+               if (!did_banner) {
                        out1fmt(
                                "\n\n"
-                               "%s Built-in shell (ash)\n"
+                               "%s built-in shell (ash)\n"
                                "Enter 'help' for a list of built-in commands."
                                "\n\n",
-                               BB_BANNER);
-                       do_banner = 1;
+                               bb_banner);
+                       did_banner = 1;
                }
        }
 #endif
@@ -7951,7 +7876,7 @@ parse_command_args(char **argv, const char **path)
                do {
                        switch (c) {
                        case 'p':
-                               *path = defpath;
+                               *path = bb_default_path;
                                break;
                        default:
                                /* run 'typecmd' for other options */
@@ -8173,7 +8098,6 @@ static const struct builtincmd builtintab[] = {
        { BUILTIN_REGULAR       "wait", waitcmd },
 };
 
-#define NUMBUILTINS (sizeof(builtintab) / sizeof(builtintab[0]))
 
 #define COMMANDCMD (builtintab + 5 + \
        2 * ENABLE_ASH_BUILTIN_TEST + \
@@ -8195,7 +8119,7 @@ find_builtin(const char *name)
        struct builtincmd *bp;
 
        bp = bsearch(
-               name, builtintab, NUMBUILTINS, sizeof(builtintab[0]),
+               name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
                pstrcmp
        );
        return bp;
@@ -8549,11 +8473,6 @@ enum {
        INPUT_NOFILE_OK = 2,
 };
 
-/*
- * NEOF is returned by parsecmd when it encounters an end of file.  It
- * must be distinct from NULL, so we use the address of a variable that
- * happens to be handy.
- */
 static int plinno = 1;                  /* input line number */
 /* number of characters left in input buffer */
 static int parsenleft;                  /* copy of parsefile->nleft */
@@ -8633,7 +8552,7 @@ preadfd(void)
 
        if (nr < 0) {
                if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
-                       int flags = fcntl(0, F_GETFL, 0);
+                       int flags = fcntl(0, F_GETFL);
                        if (flags >= 0 && flags & O_NONBLOCK) {
                                flags &=~ O_NONBLOCK;
                                if (fcntl(0, F_SETFL, flags) >= 0) {
@@ -8860,8 +8779,7 @@ popfile(void)
        INT_OFF;
        if (pf->fd >= 0)
                close(pf->fd);
-       if (pf->buf)
-               free(pf->buf);
+       free(pf->buf);
        while (pf->strpush)
                popstring();
        parsefile = pf->prev;
@@ -8904,7 +8822,7 @@ closescript(void)
 static void
 setinputfd(int fd, int push)
 {
-       fcntl(fd, F_SETFD, FD_CLOEXEC);
+       close_on_exec_on(fd);
        if (push) {
                pushfile();
                parsefile->buf = 0;
@@ -8974,7 +8892,7 @@ setinputstring(char *string)
 /* times of mailboxes */
 static time_t mailtime[MAXMBOXES];
 /* Set if MAIL or MAILPATH is changed. */
-static int mail_var_path_changed;
+static smallint mail_var_path_changed;
 
 /*
  * Print appropriate message(s) if mail has arrived.
@@ -9025,7 +8943,7 @@ chkmail(void)
 static void
 changemail(const char *val)
 {
-       mail_var_path_changed++;
+       mail_var_path_changed = 1;
 }
 
 #endif /* ASH_MAIL */
@@ -9106,8 +9024,11 @@ options(int cmdline)
        if (cmdline)
                minusc = NULL;
        while ((p = *argptr) != NULL) {
-               argptr++;
                c = *p++;
+               if (c != '-' && c != '+')
+                       break;
+               argptr++;
+               val = 0; /* val = 0 if c == '+' */
                if (c == '-') {
                        val = 1;
                        if (p[0] == '\0' || LONE_DASH(p)) {
@@ -9121,20 +9042,20 @@ options(int cmdline)
                                }
                                break;    /* "-" or  "--" terminates options */
                        }
-               } else if (c == '+') {
-                       val = 0;
-               } else {
-                       argptr--;
-                       break;
                }
+               /* first char was + or - */
                while ((c = *p++) != '\0') {
+                       /* bash 3.2 indeed handles -c CMD and +c CMD the same */
                        if (c == 'c' && cmdline) {
-                               minusc = p;     /* command is after shell args*/
+                               minusc = p;     /* command is after shell args */
                        } else if (c == 'o') {
                                minus_o(*argptr, val);
                                if (*argptr)
                                        argptr++;
-                       } else if (cmdline && (c == '-')) {     // long options
+                       } else if (cmdline && (c == 'l')) { /* -l or +l == --login */
+                               isloginsh = 1;
+                       /* bash does not accept +-login, we also won't */
+                       } else if (cmdline && val && (c == '-')) { /* long options */
                                if (strcmp(p, "login") == 0)
                                        isloginsh = 1;
                                break;
@@ -9369,15 +9290,20 @@ getoptscmd(int argc, char **argv)
 
 /* ============ Shell parser */
 
-static int tokpushback;                /* last token pushed back */
+/*
+ * NEOF is returned by parsecmd when it encounters an end of file.  It
+ * must be distinct from NULL, so we use the address of a variable that
+ * happens to be handy.
+ */
+static smallint tokpushback;           /* last token pushed back */
 #define NEOF ((union node *)&tokpushback)
-static int parsebackquote;             /* nonzero if we are inside backquotes */
+static smallint parsebackquote;        /* nonzero if we are inside backquotes */
 static int lasttoken;                  /* last token read */
 static char *wordtext;                 /* text of last word returned by readtoken */
 static struct nodelist *backquotelist;
 static union node *redirnode;
 static struct heredoc *heredoc;
-static int quoteflag;                  /* set if (part of) last token was quoted */
+static smallint quoteflag;             /* set if (part of) last token was quoted */
 
 static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
 static void
@@ -9471,7 +9397,7 @@ list(int nlflag)
                                if (nlflag == 1)
                                        return n1;
                        } else {
-                               tokpushback++;
+                               tokpushback = 1;
                        }
                        checkkwd = CHKNL | CHKKWD | CHKALIAS;
                        if (peektoken())
@@ -9486,7 +9412,7 @@ list(int nlflag)
                default:
                        if (nlflag == 1)
                                raise_error_unexpected_syntax(-1);
-                       tokpushback++;
+                       tokpushback = 1;
                        return n1;
                }
        }
@@ -9506,7 +9432,7 @@ andor(void)
                } else if (t == TOR) {
                        t = NOR;
                } else {
-                       tokpushback++;
+                       tokpushback = 1;
                        return n1;
                }
                checkkwd = CHKNL | CHKKWD | CHKALIAS;
@@ -9532,7 +9458,7 @@ pipeline(void)
                negate = !negate;
                checkkwd = CHKKWD | CHKALIAS;
        } else
-               tokpushback++;
+               tokpushback = 1;
        n1 = parse_command();
        if (readtoken() == TPIPE) {
                pipenode = stalloc(sizeof(struct npipe));
@@ -9551,7 +9477,7 @@ pipeline(void)
                lp->next = NULL;
                n1 = pipenode;
        }
-       tokpushback++;
+       tokpushback = 1;
        if (negate) {
                n2 = stalloc(sizeof(struct nnot));
                n2->type = NNOT;
@@ -9709,7 +9635,7 @@ simplecmd(void)
                        }
                        /* fall through */
                default:
-                       tokpushback++;
+                       tokpushback = 1;
                        goto out;
                }
        }
@@ -9763,7 +9689,7 @@ parse_command(void)
                        n2->nif.elsepart = list(0);
                else {
                        n2->nif.elsepart = NULL;
-                       tokpushback++;
+                       tokpushback = 1;
                }
                t = TFI;
                break;
@@ -9816,7 +9742,7 @@ parse_command(void)
                         * that the original Bourne shell only allowed NL).
                         */
                        if (lasttoken != TNL && lasttoken != TSEMI)
-                               tokpushback++;
+                               tokpushback = 1;
                }
                checkkwd = CHKNL | CHKKWD | CHKALIAS;
                if (readtoken() != TDO)
@@ -9889,7 +9815,7 @@ parse_command(void)
                break;
        case TWORD:
        case TREDIR:
-               tokpushback++;
+               tokpushback = 1;
                return simplecmd();
        }
 
@@ -9905,7 +9831,7 @@ parse_command(void)
                rpp = &n2->nfile.next;
                parsefname();
        }
-       tokpushback++;
+       tokpushback = 1;
        *rpp = NULL;
        if (redir) {
                if (n1->type != NSUBSHELL) {
@@ -9931,8 +9857,6 @@ parse_command(void)
  * will run code that appears at the end of readtoken1.
  */
 
-static int parsebackquote;             /* nonzero if we are inside backquotes */
-
 #define CHECKEND()      {goto checkend; checkend_return:;}
 #define PARSEREDIR()    {goto parseredir; parseredir_return:;}
 #define PARSESUB()      {goto parsesub; parsesub_return:;}
@@ -9943,19 +9867,24 @@ static int parsebackquote;             /* nonzero if we are inside backquotes */
 static int
 readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
 {
+       /* NB: syntax parameter fits into smallint */
        int c = firstc;
        char *out;
        int len;
        char line[EOFMARKLEN + 1];
-       struct nodelist *bqlist = 0;
-       int quotef = 0;
-       int dblquote = 0;
-       int varnest = 0;    /* levels of variables expansion */
-       int arinest = 0;    /* levels of arithmetic expansion */
-       int parenlevel = 0; /* levels of parens in arithmetic */
-       int dqvarnest = 0;  /* levels of variables expansion within double quotes */
-       int oldstyle = 0;
-       int prevsyntax = 0; /* syntax before arithmetic */
+       struct nodelist *bqlist;
+       smallint quotef;
+       smallint dblquote;
+       smallint oldstyle;
+       smallint prevsyntax; /* syntax before arithmetic */
+#if ENABLE_ASH_EXPAND_PRMT
+       smallint pssyntax;   /* we are expanding a prompt string */
+#endif
+       int varnest;         /* levels of variables expansion */
+       int arinest;         /* levels of arithmetic expansion */
+       int parenlevel;      /* levels of parens in arithmetic */
+       int dqvarnest;       /* levels of variables expansion within double quotes */
+
 #if __GNUC__
        /* Avoid longjmp clobbering */
        (void) &out;
@@ -9969,13 +9898,17 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
        (void) &prevsyntax;
        (void) &syntax;
 #endif
-
        startlinno = plinno;
-       dblquote = 0;
-       if (syntax == DQSYNTAX)
-               dblquote = 1;
-       quotef = 0;
        bqlist = NULL;
+       quotef = 0;
+       oldstyle = 0;
+       prevsyntax = 0;
+#if ENABLE_ASH_EXPAND_PRMT
+       pssyntax = (syntax == PSSYNTAX);
+       if (pssyntax)
+               syntax = DQSYNTAX;
+#endif
+       dblquote = (syntax == DQSYNTAX);
        varnest = 0;
        arinest = 0;
        parenlevel = 0;
@@ -10014,6 +9947,12 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
                                        if (doprompt)
                                                setprompt(2);
                                } else {
+#if ENABLE_ASH_EXPAND_PRMT
+                                       if (c == '$' && pssyntax) {
+                                               USTPUTC(CTLESC, out);
+                                               USTPUTC('\\', out);
+                                       }
+#endif
                                        if (dblquote &&
                                                c != '\\' && c != '`' &&
                                                c != '$' && (
@@ -10026,7 +9965,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
                                        if (SIT(c, SQSYNTAX) == CCTL)
                                                USTPUTC(CTLESC, out);
                                        USTPUTC(c, out);
-                                       quotef++;
+                                       quotef = 1;
                                }
                                break;
                        case CSQUOTE:
@@ -10050,7 +9989,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
                                                syntax = BASESYNTAX;
                                                dblquote = 0;
                                        }
-                                       quotef++;
+                                       quotef = 1;
                                        goto quotemark;
                                }
                                break;
@@ -10082,10 +10021,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
                                                if (--arinest == 0) {
                                                        USTPUTC(CTLENDARI, out);
                                                        syntax = prevsyntax;
-                                                       if (syntax == DQSYNTAX)
-                                                               dblquote = 1;
-                                                       else
-                                                               dblquote = 0;
+                                                       dblquote = (syntax == DQSYNTAX);
                                                } else
                                                        USTPUTC(')', out);
                                        } else {
@@ -10267,7 +10203,7 @@ parsesub: {
        int typeloc;
        int flags;
        char *p;
-       static const char types[] = "}-+?=";
+       static const char types[] ALIGN1 = "}-+?=";
 
        c = pgetc();
        if (
@@ -10371,21 +10307,20 @@ parsesub: {
  */
 parsebackq: {
        struct nodelist **nlpp;
-       int savepbq;
+       smallint savepbq;
        union node *n;
        char *volatile str;
        struct jmploc jmploc;
        struct jmploc *volatile savehandler;
        size_t savelen;
-       int saveprompt = 0;
+       smallint saveprompt = 0;
+
 #ifdef __GNUC__
        (void) &saveprompt;
 #endif
-
        savepbq = parsebackquote;
        if (setjmp(jmploc.loc)) {
-               if (str)
-                       free(str);
+               free(str);
                parsebackquote = 0;
                exception_handler = savehandler;
                longjmp(exception_handler->loc, 1);
@@ -10563,13 +10498,15 @@ parsearith: {
 #define NEW_xxreadtoken
 #ifdef NEW_xxreadtoken
 /* singles must be first! */
-static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
+static const char xxreadtoken_chars[7] ALIGN1 = {
+       '\n', '(', ')', '&', '|', ';', 0
+};
 
-static const char xxreadtoken_tokens[] = {
+static const char xxreadtoken_tokens[] ALIGN1 = {
        TNL, TLP, TRP,          /* only single occurrence allowed */
        TBACKGND, TPIPE, TSEMI, /* if single occurrence */
        TEOF,                   /* corresponds to trailing nul */
-       TAND, TOR, TENDCASE,    /* if double occurrence */
+       TAND, TOR, TENDCASE     /* if double occurrence */
 };
 
 #define xxreadtoken_doubles \
@@ -10714,7 +10651,7 @@ readtoken(void)
 {
        int t;
 #if DEBUG
-       int alreadyseen = tokpushback;
+       smallint alreadyseen = tokpushback;
 #endif
 
 #if ENABLE_ASH_ALIAS
@@ -10780,7 +10717,7 @@ peektoken(void)
        int t;
 
        t = readtoken();
-       tokpushback++;
+       tokpushback = 1;
        return tokname_array[t][0];
 }
 
@@ -10803,7 +10740,7 @@ parsecmd(int interact)
                return NEOF;
        if (t == TNL)
                return NULL;
-       tokpushback++;
+       tokpushback = 1;
        return list(1);
 }
 
@@ -10847,7 +10784,7 @@ expandstr(const char *ps)
 
        /* XXX Fix (char *) cast. */
        setinputstring((char *)ps);
-       readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
+       readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
        popfile();
 
        n.narg.type = NARG;
@@ -10959,7 +10896,8 @@ cmdloop(int top)
                        }
                        numeof++;
                } else if (nflag == 0) {
-                       job_warning = (job_warning == 2) ? 1 : 0;
+                       /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
+                       job_warning >>= 1;
                        numeof = 0;
                        evaltree(n, 0);
                }
@@ -11064,7 +11002,7 @@ echocmd(int argc, char **argv)
 static int
 testcmd(int argc, char **argv)
 {
-       return bb_test(argc, argv);
+       return test_main(argc, argv);
 }
 #endif
 
@@ -11115,13 +11053,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
                return;
        }
 
-#if ENABLE_FEATURE_SH_STANDALONE
-       if (find_applet_by_name(name)) {
-               entry->cmdtype = CMDNORMAL;
-               entry->u.index = -1;
-               return;
-       }
-#endif
+/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
 
        updatetbl = (path == pathval());
        if (!updatetbl) {
@@ -11171,6 +11103,14 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
                }
        }
 
+#if ENABLE_FEATURE_SH_STANDALONE
+       if (find_applet_by_name(name)) {
+               entry->cmdtype = CMDNORMAL;
+               entry->u.index = -1;
+               return;
+       }
+#endif
+
        /* We have to search path. */
        prev = -1;              /* where to start */
        if (cmdp && cmdp->rehash) {     /* doing a rehash */
@@ -11185,14 +11125,16 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
  loop:
        while ((fullname = padvance(&path, name)) != NULL) {
                stunalloc(fullname);
+               /* NB: code below will still use fullname
+                * despite it being "unallocated" */
                idx++;
                if (pathopt) {
                        if (prefix(pathopt, "builtin")) {
                                if (bcmd)
                                        goto builtin_success;
                                continue;
-                       } else if (!(act & DO_NOFUNC) &&
-                                  prefix(pathopt, "func")) {
+                       } else if (!(act & DO_NOFUNC)
+                        && prefix(pathopt, "func")) {
                                /* handled below */
                        } else {
                                /* ignore unimplemented options */
@@ -11220,6 +11162,9 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
                        continue;
                if (pathopt) {          /* this is a %func directory */
                        stalloc(strlen(fullname) + 1);
+                       /* NB: stalloc will return space pointed by fullname
+                        * (because we don't have any intervening allocations
+                        * between stunalloc above and this stalloc) */
                        readcmdfile(fullname);
                        cmdp = cmdlookup(name, 0);
                        if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
@@ -11308,8 +11253,7 @@ trapcmd(int argc, char **argv)
                        else
                                action = ckstrdup(action);
                }
-               if (trap[signo])
-                       free(trap[signo]);
+               free(trap[signo]);
                trap[signo] = action;
                if (signo != 0)
                        setsignal(signo);
@@ -11332,7 +11276,7 @@ helpcmd(int argc, char **argv)
        int col, i;
 
        out1fmt("\nBuilt-in commands:\n-------------------\n");
-       for (col = 0, i = 0; i < NUMBUILTINS; i++) {
+       for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
                col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
                                        builtintab[i].name + 1);
                if (col > 60) {
@@ -11438,7 +11382,7 @@ unsetcmd(int argc, char **argv)
 
 #include <sys/times.h>
 
-static const unsigned char timescmd_str[] = {
+static const unsigned char timescmd_str[] ALIGN1 = {
        ' ',  offsetof(struct tms, tms_utime),
        '\n', offsetof(struct tms, tms_stime),
        ' ',  offsetof(struct tms, tms_cutime),
@@ -11644,8 +11588,9 @@ readcmd(int argc, char **argv)
 #endif
 #if ENABLE_ASH_READ_TIMEOUT
        if (ts.tv_sec || ts.tv_usec) {
-               FD_ZERO (&set);
-               FD_SET (0, &set);
+// TODO: replace with poll, it is smaller
+               FD_ZERO(&set);
+               FD_SET(0, &set);
 
                i = select(FD_SETSIZE, &set, NULL, NULL, &ts);
                if (!i) {
@@ -11718,9 +11663,9 @@ readcmd(int argc, char **argv)
 static int
 umaskcmd(int argc, char **argv)
 {
-       static const char permuser[3] = "ugo";
-       static const char permmode[3] = "rwx";
-       static const short int permmask[] = {
+       static const char permuser[3] ALIGN1 = "ugo";
+       static const char permmode[3] ALIGN1 = "rwx";
+       static const short permmask[] ALIGN2 = {
                S_IRUSR, S_IWUSR, S_IXUSR,
                S_IRGRP, S_IWGRP, S_IXGRP,
                S_IROTH, S_IWOTH, S_IXOTH
@@ -12377,7 +12322,7 @@ arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
 }
 
 /* longest must be first */
-static const char op_tokens[] = {
+static const char op_tokens[] ALIGN1 = {
        '<','<','=',0, TOK_LSHIFT_ASSIGN,
        '>','>','=',0, TOK_RSHIFT_ASSIGN,
        '<','<',    0, TOK_LSHIFT,
@@ -12802,10 +12747,6 @@ int ash_main(int argc, char **argv)
        struct jmploc jmploc;
        struct stackmark smark;
 
-#ifdef __GLIBC__
-       dash_errno = __errno_location();
-#endif
-
 #if PROFILE
        monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
 #endif