hush: fix "unterminated last line loops forever" bug
[oweals/busybox.git] / shell / ash.c
index 371b5d9c3a3975ae1f98a5da2eedd98eaee43e01..4b37f403c73c171b4c9914d270f644b5d078e674 100644 (file)
@@ -183,7 +183,7 @@ static volatile sig_atomic_t intpending;
 /* do we generate EXSIG events */
 static int exsig;
 /* last pending signal */
-static volatile sig_atomic_t pendingsigs;
+static volatile sig_atomic_t pendingsig;
 
 /*
  * Sigmode records the current value of the signal handlers for the various
@@ -239,8 +239,15 @@ static void
 raise_interrupt(void)
 {
        int i;
+       sigset_t mask;
 
        intpending = 0;
+       /* Signal is not automatically re-enabled after it is raised,
+        * do it ourself */
+       sigemptyset(&mask);
+       sigprocmask(SIG_SETMASK, &mask, 0);
+       /* pendingsig = 0; - now done in onsig() */
+
        i = EXSIG;
        if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
                if (!(rootshell && iflag)) {
@@ -300,7 +307,7 @@ force_int_on(void)
        do { \
                exsig++; \
                xbarrier(); \
-               if (pendingsigs) \
+               if (pendingsig) \
                        raise_exception(EXSIG); \
        } while (0)
 /* EXSIG is turned off by evalbltin(). */
@@ -324,11 +331,13 @@ static void
 onsig(int signo)
 {
        gotsig[signo - 1] = 1;
-       pendingsigs = signo;
+       pendingsig = signo;
 
        if (exsig || (signo == SIGINT && !trap[SIGINT])) {
-               if (!suppressint)
+               if (!suppressint) {
+                       pendingsig = 0;
                        raise_interrupt();
+               }
                intpending = 1;
        }
 }
@@ -985,9 +994,10 @@ ash_vmsg(const char *msg, va_list ap)
 {
        fprintf(stderr, "%s: ", arg0);
        if (commandname) {
-               const char *fmt = (!iflag || parsefile->fd) ?
-                                       "%s: %d: " : "%s: ";
-               fprintf(stderr, fmt, commandname, startlinno);
+               if (strcmp(arg0, commandname))
+                       fprintf(stderr, "%s: ", commandname);
+               if (!iflag || parsefile->fd)
+                       fprintf(stderr, "line %d: ", startlinno);
        }
        vfprintf(stderr, msg, ap);
        outcslow('\n', stderr);
@@ -1215,6 +1225,9 @@ popstackmark(struct stackmark *mark)
 {
        struct stack_block *sp;
 
+       if (!mark->stackp)
+               return;
+
        INT_OFF;
        markp = mark->marknext;
        while (stackp != mark->stackp) {
@@ -1518,13 +1531,13 @@ nextopt(const char *optstring)
        c = *p++;
        for (q = optstring; *q != c; ) {
                if (*q == '\0')
-                       ash_msg_and_raise_error("Illegal option -%c", c);
+                       ash_msg_and_raise_error("illegal option -%c", c);
                if (*++q == ':')
                        q++;
        }
        if (*++q == ':') {
                if (*p == '\0' && (p = *argptr++) == NULL)
-                       ash_msg_and_raise_error("No arg for -%c option", c);
+                       ash_msg_and_raise_error("no arg for -%c option", c);
                optionarg = p;
                p = NULL;
        }
@@ -2280,7 +2293,8 @@ updatepwd(const char *dir)
                                                break;
                                }
                                break;
-                       } else if (p[1] == '\0')
+                       }
+                       if (p[1] == '\0')
                                break;
                        /* fall through */
                default:
@@ -3426,7 +3440,7 @@ static void
 xtcsetpgrp(int fd, pid_t pgrp)
 {
        if (tcsetpgrp(fd, pgrp))
-               ash_msg_and_raise_error("Cannot set tty process group (%m)");
+               ash_msg_and_raise_error("cannot set tty process group (%m)");
 }
 
 /*
@@ -3487,7 +3501,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);
@@ -3503,91 +3519,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
@@ -3626,7 +3573,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;
@@ -4472,7 +4420,7 @@ forkchild(struct job *jp, union node *n, int mode)
                if (jp->nprocs == 0) {
                        close(0);
                        if (open(bb_dev_null, O_RDONLY) != 0)
-                               ash_msg_and_raise_error("Can't open %s", bb_dev_null);
+                               ash_msg_and_raise_error("can't open %s", bb_dev_null);
                }
        }
        if (!oldlvl && iflag) {
@@ -4533,7 +4481,7 @@ forkshell(struct job *jp, union node *n, int mode)
                TRACE(("Fork failed, errno=%d", errno));
                if (jp)
                        freejob(jp);
-               ash_msg_and_raise_error("Cannot fork");
+               ash_msg_and_raise_error("cannot fork");
        }
        if (pid == 0)
                forkchild(jp, n, mode);
@@ -4699,7 +4647,7 @@ openhere(union node *redir)
        size_t len = 0;
 
        if (pipe(pip) < 0)
-               ash_msg_and_raise_error("Pipe call failed");
+               ash_msg_and_raise_error("pipe call failed");
        if (redir->type == NHERE) {
                len = strlen(redir->nhere.doc->narg.text);
                if (len <= PIPESIZE) {
@@ -4785,9 +4733,9 @@ openredirect(union node *redir)
 
        return f;
  ecreate:
-       ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "Directory nonexistent"));
+       ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "nonexistent directory"));
  eopen:
-       ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "No such file"));
+       ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "no such file"));
 }
 
 /*
@@ -5054,8 +5002,9 @@ esclen(const char *start, const char *p)
 static char *
 _rmescapes(char *str, int flag)
 {
+       static const char qchars[] = { CTLESC, CTLQUOTEMARK, '\0' };
+
        char *p, *q, *r;
-       static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 };
        unsigned inquotes;
        int notescaped;
        int globbing;
@@ -5312,7 +5261,7 @@ evalbackcmd(union node *n, struct backcmd *result)
                struct job *jp;
 
                if (pipe(pip) < 0)
-                       ash_msg_and_raise_error("Pipe call failed");
+                       ash_msg_and_raise_error("pipe call failed");
                jp = makejob(n, 1);
                if (forkshell(jp, n, FORK_NOJOB) == 0) {
                        FORCE_INT_ON;
@@ -6455,40 +6404,6 @@ casematch(union node *pattern, char *val)
 
 /* ============ find_command */
 
-static int is_safe_applet(char *name)
-{
-       /* It isn't a bug to have non-existent applet here... */
-       /* ...just a waste of space... */
-       static const char safe_applets[][8] = {
-               "["
-               USE_AWK    (, "awk"    )
-               USE_CAT    (, "cat"    )
-               USE_CHMOD  (, "chmod"  )
-               USE_CHOWN  (, "chown"  )
-               USE_CP     (, "cp"     )
-               USE_CUT    (, "cut"    )
-               USE_DD     (, "dd"     )
-               USE_ECHO   (, "echo"   )
-               USE_FIND   (, "find"   )
-               USE_HEXDUMP(, "hexdump")
-               USE_LN     (, "ln"     )
-               USE_LS     (, "ls"     )
-               USE_MKDIR  (, "mkdir"  )
-               USE_RM     (, "rm"     )
-               USE_SORT   (, "sort"   )
-               USE_TEST   (, "test"   )
-               USE_TOUCH  (, "touch"  )
-               USE_XARGS  (, "xargs"  )
-       };
-       int n = sizeof(safe_applets) / sizeof(safe_applets[0]);
-       int i;
-       for (i = 0; i < n; i++)
-               if (strcmp(safe_applets[i], name) == 0)
-                       return 1;
-
-       return 0;
-}
-
 struct builtincmd {
        const char *name;
        int (*builtin)(int, char **);
@@ -6551,27 +6466,22 @@ static void
 tryexec(char *cmd, char **argv, char **envp)
 {
        int repeated = 0;
-       struct BB_applet *a;
-       int argc = 0;
-       char **c;
 
-       if (strchr(cmd, '/') == NULL
-        && (a = find_applet_by_name(cmd)) != NULL
-        && is_safe_applet(cmd)
-       ) {
-               c = argv;
-               while (*c != NULL) {
-                       c++; argc++;
+#if ENABLE_FEATURE_SH_STANDALONE
+       if (strchr(cmd, '/') == NULL) {
+               const struct bb_applet *a;
+
+               a = find_applet_by_name(cmd);
+               if (a) {
+                       if (a->noexec) {
+                               current_applet = a;
+                               run_current_applet_and_exit(argv);
+                       }
+                       /* re-exec ourselves with the new arguments */
+                       execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp);
+                       /* If they called chroot or otherwise made the binary no longer
+                        * executable, fall through */
                }
-               applet_name = cmd;
-               exit(a->main(argc, argv));
-       }
-#if ENABLE_FEATURE_SH_STANDALONE_SHELL
-       if (find_applet_by_name(cmd) != NULL) {
-               /* re-exec ourselves with the new arguments */
-               execve(CONFIG_BUSYBOX_EXEC_PATH, argv, envp);
-               /* If they called chroot or otherwise made the binary no longer
-                * executable, fall through */
        }
 #endif
 
@@ -6593,7 +6503,7 @@ tryexec(char *cmd, char **argv, char **envp)
                        ;
                ap = new = ckmalloc((ap - argv + 2) * sizeof(char *));
                ap[1] = cmd;
-               *ap = cmd = (char *)DEFAULT_SHELL;
+               ap[0] = cmd = (char *)DEFAULT_SHELL;
                ap += 2;
                argv++;
                while ((*ap++ = *argv++))
@@ -6619,8 +6529,8 @@ shellexec(char **argv, const char *path, int idx)
 
        clearredir(1);
        envp = environment();
-       if (strchr(argv[0], '/') || is_safe_applet(argv[0])
-#if ENABLE_FEATURE_SH_STANDALONE_SHELL
+       if (strchr(argv[0], '/')
+#if ENABLE_FEATURE_SH_STANDALONE
         || find_applet_by_name(argv[0])
 #endif
        ) {
@@ -6952,6 +6862,11 @@ tokname(int tok)
 {
        static char buf[16];
 
+//try this:
+//if (tok < TSEMI) return tokname_array[tok] + 1;
+//sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
+//return buf;
+
        if (tok >= TSEMI)
                buf[0] = '"';
        sprintf(buf + (tok >= TSEMI), "%s%c",
@@ -6963,15 +6878,15 @@ tokname(int tok)
 static int
 pstrcmp(const void *a, const void *b)
 {
-       return strcmp((const char *) a, (*(const char *const *) b) + 1);
+       return strcmp((char*) a, (*(char**) b) + 1);
 }
 
 static const char *const *
 findkwd(const char *s)
 {
        return bsearch(s, tokname_array + KWDOFFSET,
-                       (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
-                       sizeof(const char *), pstrcmp);
+                       (sizeof(tokname_array) / sizeof(char *)) - KWDOFFSET,
+                       sizeof(char *), pstrcmp);
 }
 
 /*
@@ -7435,7 +7350,7 @@ dotrap(void)
        int skip = 0;
 
        savestatus = exitstatus;
-       pendingsigs = 0;
+       pendingsig = 0;
        xbarrier();
 
        for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
@@ -7574,7 +7489,7 @@ evaltree(union node *n, int flags)
  out:
        if ((checkexit & exitstatus))
                evalskip |= SKIPEVAL;
-       else if (pendingsigs && dotrap())
+       else if (pendingsig && dotrap())
                goto exexit;
 
        if (flags & EV_EXIT) {
@@ -7788,7 +7703,7 @@ evalpipe(union node *n, int flags)
                if (lp->next) {
                        if (pipe(pip) < 0) {
                                close(prevfd);
-                               ash_msg_and_raise_error("Pipe call failed");
+                               ash_msg_and_raise_error("pipe call failed");
                        }
                }
                if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
@@ -8441,7 +8356,7 @@ evalcommand(union node *cmd, int flags)
                        if (i == EXINT)
                                j = SIGINT;
                        if (i == EXSIG)
-                               j = pendingsigs;
+                               j = pendingsig;
                        if (j)
                                exit_status = j + 128;
                        exitstatus = exit_status;
@@ -8951,13 +8866,13 @@ setinputfile(const char *fname, int flags)
        if (fd < 0) {
                if (flags & INPUT_NOFILE_OK)
                        goto out;
-               ash_msg_and_raise_error("Can't open %s", fname);
+               ash_msg_and_raise_error("can't open %s", fname);
        }
        if (fd < 10) {
                fd2 = copyfd(fd, 10);
                close(fd);
                if (fd2 < 0)
-                       ash_msg_and_raise_error("Out of file descriptors");
+                       ash_msg_and_raise_error("out of file descriptors");
                fd = fd2;
        }
        setinputfd(fd, flags & INPUT_PUSH_FILE);
@@ -9095,7 +9010,7 @@ minus_o(char *name, int val)
                                return;
                        }
                }
-               ash_msg_and_raise_error("Illegal option -o %s", name);
+               ash_msg_and_raise_error("illegal option -o %s", name);
        }
        out1str("Current option settings\n");
        for (i = 0; i < NOPTS; i++)
@@ -9113,7 +9028,7 @@ setoption(int flag, int val)
                        return;
                }
        }
-       ash_msg_and_raise_error("Illegal option -%c", flag);
+       ash_msg_and_raise_error("illegal option -%c", flag);
        /* NOTREACHED */
 }
 static void
@@ -9366,7 +9281,7 @@ getoptscmd(int argc, char **argv)
        char **optbase;
 
        if (argc < 3)
-               ash_msg_and_raise_error("Usage: getopts optstring var [arg]");
+               ash_msg_and_raise_error("usage: getopts optstring var [arg]");
        if (argc == 3) {
                optbase = shellparam.p;
                if (shellparam.optind > shellparam.nparam + 1) {
@@ -9403,7 +9318,7 @@ static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
 static void
 raise_error_syntax(const char *msg)
 {
-       ash_msg_and_raise_error("Syntax error: %s", msg);
+       ash_msg_and_raise_error("syntax error: %s", msg);
        /* NOTREACHED */
 }
 
@@ -11135,19 +11050,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
                return;
        }
 
-#if ENABLE_FEATURE_SH_STANDALONE_SHELL
-       if (find_applet_by_name(name)) {
-               entry->cmdtype = CMDNORMAL;
-               entry->u.index = -1;
-               return;
-       }
-#endif
-
-       if (is_safe_applet(name)) {
-               entry->cmdtype = CMDNORMAL;
-               entry->u.index = -1;
-               return;
-       }
+/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
 
        updatetbl = (path == pathval());
        if (!updatetbl) {
@@ -11194,9 +11097,17 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
                                goto builtin_success;
                } else if (builtinloc <= 0) {
                        goto builtin_success;
-               }                       
+               }
        }
 
+#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 */
@@ -11366,7 +11277,7 @@ helpcmd(int argc, char **argv)
                        col = 0;
                }
        }
-#if ENABLE_FEATURE_SH_STANDALONE_SHELL
+#if ENABLE_FEATURE_SH_STANDALONE
        for (i = 0; i < NUM_APPLETS; i++) {
                col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
                if (col > 60) {
@@ -11801,7 +11712,7 @@ umaskcmd(int argc, char **argv)
                } else {
                        mask = ~mask & 0777;
                        if (!bb_parse_mode(ap, &mask)) {
-                               ash_msg_and_raise_error("Illegal mode: %s", ap);
+                               ash_msg_and_raise_error("illegal mode: %s", ap);
                        }
                        umask(~mask & 0777);
                }