ash: cleanup part 6
authorDenis Vlasenko <vda.linux@googlemail.com>
Fri, 23 Feb 2007 01:05:15 +0000 (01:05 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Fri, 23 Feb 2007 01:05:15 +0000 (01:05 -0000)
shell/ash.c

index 54fc8ea48457d885b2d7046c03ca19defca422b3..69b8ab0b9467818dcdd26530266c0d51e50a0cfa 100644 (file)
@@ -3664,7 +3664,6 @@ printalias(const struct alias *ap)
 #define EV_TESTED 02            /* exit status is checked; ignore -e flag */
 #define EV_BACKCMD 04           /* command executing within back quotes */
 
-
 static void evalloop(union node *, int);
 static void evalfor(union node *, int);
 static void evalcase(union node *, int);
@@ -3677,13 +3676,11 @@ static int evalfun(struct funcnode *, int, char **, int);
 static void prehash(union node *);
 static int bltincmd(int, char **);
 
-
 static const struct builtincmd bltin = {
        "\0\0", bltincmd
 };
 
 
-
 /*
  * Evaluate a parse tree.  The value is left in the global variable
  * exitstatus.
@@ -7277,13 +7274,6 @@ static struct job *curjob;
 /* number of presumed living untracked jobs */
 static int jobless;
 
-#if JOBS
-static char *commandtext(union node *);
-static void cmdlist(union node *, int);
-static void cmdtxt(union node *);
-static void cmdputs(const char *);
-#endif
-
 static void
 set_curjob(struct job *jp, unsigned mode)
 {
@@ -7887,10 +7877,8 @@ showjob(FILE *out, struct job *jp, int mode)
                col += sizeof("Running") - 1;
        } else {
                int status = psend[-1].status;
-#if JOBS
                if (jp->state == JOBSTOPPED)
                        status = jp->stopstatus;
-#endif
                col += sprint_status(s + col, status, 0);
        }
 
@@ -8143,227 +8131,129 @@ makejob(union node *node, int nprocs)
        return jp;
 }
 
+#if JOBS
 /*
- * Fork off a subshell.  If we are doing job control, give the subshell its
- * own process group.  Jp is a job structure that the job is to be added to.
- * N is the command that will be evaluated by the child.  Both jp and n may
- * be NULL.  The mode parameter can be one of the following:
- *      FORK_FG - Fork off a foreground process.
- *      FORK_BG - Fork off a background process.
- *      FORK_NOJOB - Like FORK_FG, but don't give the process its own
- *                   process group even if job control is on.
- *
- * When job control is turned off, background processes have their standard
- * input redirected to /dev/null (except for the second and later processes
- * in a pipeline).
- *
- * Called with interrupts off.
+ * Return a string identifying a command (to be printed by the
+ * jobs command).
  */
+static char *cmdnextc;
+
 static void
-forkchild(struct job *jp, union node *n, int mode)
+cmdputs(const char *s)
 {
-       int oldlvl;
-
-       TRACE(("Child shell %d\n", getpid()));
-       oldlvl = shlvl;
-       shlvl++;
-
-       closescript();
-       clear_traps();
-#if JOBS
-       /* do job control only in root shell */
-       jobctl = 0;
-       if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
-               pid_t pgrp;
+       const char *p, *str;
+       char c, cc[2] = " ";
+       char *nextc;
+       int subtype = 0;
+       int quoted = 0;
+       static const char vstype[VSTYPE + 1][4] = {
+               "", "}", "-", "+", "?", "=",
+               "%", "%%", "#", "##"
+       };
 
-               if (jp->nprocs == 0)
-                       pgrp = getpid();
-               else
-                       pgrp = jp->ps[0].pid;
-               /* This can fail because we are doing it in the parent also */
-               (void)setpgid(0, pgrp);
-               if (mode == FORK_FG)
-                       xtcsetpgrp(ttyfd, pgrp);
-               setsignal(SIGTSTP);
-               setsignal(SIGTTOU);
-       } else
+       nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
+       p = s;
+       while ((c = *p++) != 0) {
+               str = 0;
+               switch (c) {
+               case CTLESC:
+                       c = *p++;
+                       break;
+               case CTLVAR:
+                       subtype = *p++;
+                       if ((subtype & VSTYPE) == VSLENGTH)
+                               str = "${#";
+                       else
+                               str = "${";
+                       if (!(subtype & VSQUOTE) == !(quoted & 1))
+                               goto dostr;
+                       quoted ^= 1;
+                       c = '"';
+                       break;
+               case CTLENDVAR:
+                       str = "\"}" + !(quoted & 1);
+                       quoted >>= 1;
+                       subtype = 0;
+                       goto dostr;
+               case CTLBACKQ:
+                       str = "$(...)";
+                       goto dostr;
+               case CTLBACKQ+CTLQUOTE:
+                       str = "\"$(...)\"";
+                       goto dostr;
+#if ENABLE_ASH_MATH_SUPPORT
+               case CTLARI:
+                       str = "$((";
+                       goto dostr;
+               case CTLENDARI:
+                       str = "))";
+                       goto dostr;
 #endif
-       if (mode == FORK_BG) {
-               ignoresig(SIGINT);
-               ignoresig(SIGQUIT);
-               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);
+               case CTLQUOTEMARK:
+                       quoted ^= 1;
+                       c = '"';
+                       break;
+               case '=':
+                       if (subtype == 0)
+                               break;
+                       if ((subtype & VSTYPE) != VSNORMAL)
+                               quoted <<= 1;
+                       str = vstype[subtype & VSTYPE];
+                       if (subtype & VSNUL)
+                               c = ':';
+                       else
+                               goto checkstr;
+                       break;
+               case '\'':
+               case '\\':
+               case '"':
+               case '$':
+                       /* These can only happen inside quotes */
+                       cc[0] = c;
+                       str = cc;
+                       c = '\\';
+                       break;
+               default:
+                       break;
+               }
+               USTPUTC(c, nextc);
+ checkstr:
+               if (!str)
+                       continue;
+ dostr:
+               while ((c = *str++)) {
+                       USTPUTC(c, nextc);
                }
        }
-       if (!oldlvl && iflag) {
-               setsignal(SIGINT);
-               setsignal(SIGQUIT);
-               setsignal(SIGTERM);
+       if (quoted & 1) {
+               USTPUTC('"', nextc);
        }
-       for (jp = curjob; jp; jp = jp->prev_job)
-               freejob(jp);
-       jobless = 0;
+       *nextc = 0;
+       cmdnextc = nextc;
 }
 
-static void
-forkparent(struct job *jp, union node *n, int mode, pid_t pid)
-{
-       TRACE(("In parent shell: child = %d\n", pid));
-       if (!jp) {
-               while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
-               jobless++;
-               return;
-       }
-#if JOBS
-       if (mode != FORK_NOJOB && jp->jobctl) {
-               int pgrp;
-
-               if (jp->nprocs == 0)
-                       pgrp = pid;
-               else
-                       pgrp = jp->ps[0].pid;
-               /* This can fail because we are doing it in the child also */
-               setpgid(pid, pgrp);
-       }
-#endif
-       if (mode == FORK_BG) {
-               backgndpid = pid;               /* set $! */
-               set_curjob(jp, CUR_RUNNING);
-       }
-       if (jp) {
-               struct procstat *ps = &jp->ps[jp->nprocs++];
-               ps->pid = pid;
-               ps->status = -1;
-               ps->cmd = nullstr;
-#if JOBS
-               if (jobctl && n)
-                       ps->cmd = commandtext(n);
-#endif
-       }
-}
+/* cmdtxt() and cmdlist() call each other */
+static void cmdtxt(union node *n);
 
-static int
-forkshell(struct job *jp, union node *n, int mode)
+static void
+cmdlist(union node *np, int sep)
 {
-       int pid;
-
-       TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
-       pid = fork();
-       if (pid < 0) {
-               TRACE(("Fork failed, errno=%d", errno));
-               if (jp)
-                       freejob(jp);
-               ash_msg_and_raise_error("Cannot fork");
+       for (; np; np = np->narg.next) {
+               if (!sep)
+                       cmdputs(spcstr);
+               cmdtxt(np);
+               if (sep && np->narg.next)
+                       cmdputs(spcstr);
        }
-       if (pid == 0)
-               forkchild(jp, n, mode);
-       else
-               forkparent(jp, n, mode, pid);
-       return pid;
 }
 
-/*
- * Wait for job to finish.
- *
- * Under job control we have the problem that while a child process is
- * running interrupts generated by the user are sent to the child but not
- * to the shell.  This means that an infinite loop started by an inter-
- * active user may be hard to kill.  With job control turned off, an
- * interactive user may place an interactive program inside a loop.  If
- * the interactive program catches interrupts, the user doesn't want
- * these interrupts to also abort the loop.  The approach we take here
- * is to have the shell ignore interrupt signals while waiting for a
- * foreground process to terminate, and then send itself an interrupt
- * signal if the child process was terminated by an interrupt signal.
- * Unfortunately, some programs want to do a bit of cleanup and then
- * exit on interrupt; unless these processes terminate themselves by
- * sending a signal to themselves (instead of calling exit) they will
- * confuse this approach.
- *
- * Called with interrupts off.
- */
-static int
-waitforjob(struct job *jp)
+static void
+cmdtxt(union node *n)
 {
-       int st;
-
-       TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
-       while (jp->state == JOBRUNNING) {
-               dowait(DOWAIT_BLOCK, jp);
-       }
-       st = getstatus(jp);
-#if JOBS
-       if (jp->jobctl) {
-               xtcsetpgrp(ttyfd, rootpid);
-               /*
-                * This is truly gross.
-                * If we're doing job control, then we did a TIOCSPGRP which
-                * caused us (the shell) to no longer be in the controlling
-                * session -- so we wouldn't have seen any ^C/SIGINT.  So, we
-                * intuit from the subprocess exit status whether a SIGINT
-                * occurred, and if so interrupt ourselves.  Yuck.  - mycroft
-                */
-               if (jp->sigint)
-                       raise(SIGINT);
-       }
-       if (jp->state == JOBDONE)
-#endif
-               freejob(jp);
-       return st;
-}
-
-/*
- * return 1 if there are stopped jobs, otherwise 0
- */
-static int
-stoppedjobs(void)
-{
-       struct job *jp;
-       int retval;
-
-       retval = 0;
-       if (job_warning)
-               goto out;
-       jp = curjob;
-       if (jp && jp->state == JOBSTOPPED) {
-               out2str("You have stopped jobs.\n");
-               job_warning = 2;
-               retval++;
-       }
-
-out:
-       return retval;
-}
-
-/*
- * Return a string identifying a command (to be printed by the
- * jobs command).
- */
-#if JOBS
-static char *cmdnextc;
-
-static char *
-commandtext(union node *n)
-{
-       char *name;
-
-       STARTSTACKSTR(cmdnextc);
-       cmdtxt(n);
-       name = stackblock();
-       TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
-                       name, cmdnextc, cmdnextc));
-       return ckstrdup(name);
-}
-
-static void
-cmdtxt(union node *n)
-{
-       union node *np;
-       struct nodelist *lp;
-       const char *p;
-       char s[2];
+       union node *np;
+       struct nodelist *lp;
+       const char *p;
+       char s[2];
 
        if (!n)
                return;
@@ -8509,112 +8399,213 @@ cmdtxt(union node *n)
        }
 }
 
+static char *
+commandtext(union node *n)
+{
+       char *name;
+
+       STARTSTACKSTR(cmdnextc);
+       cmdtxt(n);
+       name = stackblock();
+       TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
+                       name, cmdnextc, cmdnextc));
+       return ckstrdup(name);
+}
+#endif /* JOBS */
+
+/*
+ * Fork off a subshell.  If we are doing job control, give the subshell its
+ * own process group.  Jp is a job structure that the job is to be added to.
+ * N is the command that will be evaluated by the child.  Both jp and n may
+ * be NULL.  The mode parameter can be one of the following:
+ *      FORK_FG - Fork off a foreground process.
+ *      FORK_BG - Fork off a background process.
+ *      FORK_NOJOB - Like FORK_FG, but don't give the process its own
+ *                   process group even if job control is on.
+ *
+ * When job control is turned off, background processes have their standard
+ * input redirected to /dev/null (except for the second and later processes
+ * in a pipeline).
+ *
+ * Called with interrupts off.
+ */
 static void
-cmdlist(union node *np, int sep)
+forkchild(struct job *jp, union node *n, int mode)
 {
-       for (; np; np = np->narg.next) {
-               if (!sep)
-                       cmdputs(spcstr);
-               cmdtxt(np);
-               if (sep && np->narg.next)
-                       cmdputs(spcstr);
+       int oldlvl;
+
+       TRACE(("Child shell %d\n", getpid()));
+       oldlvl = shlvl;
+       shlvl++;
+
+       closescript();
+       clear_traps();
+#if JOBS
+       /* do job control only in root shell */
+       jobctl = 0;
+       if (mode != FORK_NOJOB && jp->jobctl && !oldlvl) {
+               pid_t pgrp;
+
+               if (jp->nprocs == 0)
+                       pgrp = getpid();
+               else
+                       pgrp = jp->ps[0].pid;
+               /* This can fail because we are doing it in the parent also */
+               (void)setpgid(0, pgrp);
+               if (mode == FORK_FG)
+                       xtcsetpgrp(ttyfd, pgrp);
+               setsignal(SIGTSTP);
+               setsignal(SIGTTOU);
+       } else
+#endif
+       if (mode == FORK_BG) {
+               ignoresig(SIGINT);
+               ignoresig(SIGQUIT);
+               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);
+               }
        }
+       if (!oldlvl && iflag) {
+               setsignal(SIGINT);
+               setsignal(SIGQUIT);
+               setsignal(SIGTERM);
+       }
+       for (jp = curjob; jp; jp = jp->prev_job)
+               freejob(jp);
+       jobless = 0;
 }
 
 static void
-cmdputs(const char *s)
+forkparent(struct job *jp, union node *n, int mode, pid_t pid)
 {
-       const char *p, *str;
-       char c, cc[2] = " ";
-       char *nextc;
-       int subtype = 0;
-       int quoted = 0;
-       static const char vstype[VSTYPE + 1][4] = {
-               "", "}", "-", "+", "?", "=",
-               "%", "%%", "#", "##"
-       };
+       TRACE(("In parent shell: child = %d\n", pid));
+       if (!jp) {
+               while (jobless && dowait(DOWAIT_NORMAL, 0) > 0);
+               jobless++;
+               return;
+       }
+#if JOBS
+       if (mode != FORK_NOJOB && jp->jobctl) {
+               int pgrp;
 
-       nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
-       p = s;
-       while ((c = *p++) != 0) {
-               str = 0;
-               switch (c) {
-               case CTLESC:
-                       c = *p++;
-                       break;
-               case CTLVAR:
-                       subtype = *p++;
-                       if ((subtype & VSTYPE) == VSLENGTH)
-                               str = "${#";
-                       else
-                               str = "${";
-                       if (!(subtype & VSQUOTE) == !(quoted & 1))
-                               goto dostr;
-                       quoted ^= 1;
-                       c = '"';
-                       break;
-               case CTLENDVAR:
-                       str = "\"}" + !(quoted & 1);
-                       quoted >>= 1;
-                       subtype = 0;
-                       goto dostr;
-               case CTLBACKQ:
-                       str = "$(...)";
-                       goto dostr;
-               case CTLBACKQ+CTLQUOTE:
-                       str = "\"$(...)\"";
-                       goto dostr;
-#if ENABLE_ASH_MATH_SUPPORT
-               case CTLARI:
-                       str = "$((";
-                       goto dostr;
-               case CTLENDARI:
-                       str = "))";
-                       goto dostr;
+               if (jp->nprocs == 0)
+                       pgrp = pid;
+               else
+                       pgrp = jp->ps[0].pid;
+               /* This can fail because we are doing it in the child also */
+               setpgid(pid, pgrp);
+       }
 #endif
-               case CTLQUOTEMARK:
-                       quoted ^= 1;
-                       c = '"';
-                       break;
-               case '=':
-                       if (subtype == 0)
-                               break;
-                       if ((subtype & VSTYPE) != VSNORMAL)
-                               quoted <<= 1;
-                       str = vstype[subtype & VSTYPE];
-                       if (subtype & VSNUL)
-                               c = ':';
-                       else
-                               goto checkstr;
-                       break;
-               case '\'':
-               case '\\':
-               case '"':
-               case '$':
-                       /* These can only happen inside quotes */
-                       cc[0] = c;
-                       str = cc;
-                       c = '\\';
-                       break;
-               default:
-                       break;
-               }
-               USTPUTC(c, nextc);
- checkstr:
-               if (!str)
-                       continue;
- dostr:
-               while ((c = *str++)) {
-                       USTPUTC(c, nextc);
-               }
+       if (mode == FORK_BG) {
+               backgndpid = pid;               /* set $! */
+               set_curjob(jp, CUR_RUNNING);
        }
-       if (quoted & 1) {
-               USTPUTC('"', nextc);
+       if (jp) {
+               struct procstat *ps = &jp->ps[jp->nprocs++];
+               ps->pid = pid;
+               ps->status = -1;
+               ps->cmd = nullstr;
+#if JOBS
+               if (jobctl && n)
+                       ps->cmd = commandtext(n);
+#endif
        }
-       *nextc = 0;
-       cmdnextc = nextc;
 }
-#endif /* JOBS */
+
+static int
+forkshell(struct job *jp, union node *n, int mode)
+{
+       int pid;
+
+       TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
+       pid = fork();
+       if (pid < 0) {
+               TRACE(("Fork failed, errno=%d", errno));
+               if (jp)
+                       freejob(jp);
+               ash_msg_and_raise_error("Cannot fork");
+       }
+       if (pid == 0)
+               forkchild(jp, n, mode);
+       else
+               forkparent(jp, n, mode, pid);
+       return pid;
+}
+
+/*
+ * Wait for job to finish.
+ *
+ * Under job control we have the problem that while a child process is
+ * running interrupts generated by the user are sent to the child but not
+ * to the shell.  This means that an infinite loop started by an inter-
+ * active user may be hard to kill.  With job control turned off, an
+ * interactive user may place an interactive program inside a loop.  If
+ * the interactive program catches interrupts, the user doesn't want
+ * these interrupts to also abort the loop.  The approach we take here
+ * is to have the shell ignore interrupt signals while waiting for a
+ * foreground process to terminate, and then send itself an interrupt
+ * signal if the child process was terminated by an interrupt signal.
+ * Unfortunately, some programs want to do a bit of cleanup and then
+ * exit on interrupt; unless these processes terminate themselves by
+ * sending a signal to themselves (instead of calling exit) they will
+ * confuse this approach.
+ *
+ * Called with interrupts off.
+ */
+static int
+waitforjob(struct job *jp)
+{
+       int st;
+
+       TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
+       while (jp->state == JOBRUNNING) {
+               dowait(DOWAIT_BLOCK, jp);
+       }
+       st = getstatus(jp);
+#if JOBS
+       if (jp->jobctl) {
+               xtcsetpgrp(ttyfd, rootpid);
+               /*
+                * This is truly gross.
+                * If we're doing job control, then we did a TIOCSPGRP which
+                * caused us (the shell) to no longer be in the controlling
+                * session -- so we wouldn't have seen any ^C/SIGINT.  So, we
+                * intuit from the subprocess exit status whether a SIGINT
+                * occurred, and if so interrupt ourselves.  Yuck.  - mycroft
+                */
+               if (jp->sigint)
+                       raise(SIGINT);
+       }
+       if (jp->state == JOBDONE)
+#endif
+               freejob(jp);
+       return st;
+}
+
+/*
+ * return 1 if there are stopped jobs, otherwise 0
+ */
+static int
+stoppedjobs(void)
+{
+       struct job *jp;
+       int retval;
+
+       retval = 0;
+       if (job_warning)
+               goto out;
+       jp = curjob;
+       if (jp && jp->state == JOBSTOPPED) {
+               out2str("You have stopped jobs.\n");
+               job_warning = 2;
+               retval++;
+       }
+ out:
+       return retval;
+}
+
 
 #if ENABLE_ASH_MAIL
 /*      mail.c       */
@@ -11348,8 +11339,6 @@ redirectsafe(union node *redir, int flags)
 
 /*      trap.c       */
 
-
-
 /*
  * The trap builtin.
  */