From a6704933dde751bfccf85b632361034367ad6ec5 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Fri, 23 Feb 2007 01:05:15 +0000 Subject: [PATCH] ash: cleanup part 6 --- shell/ash.c | 613 ++++++++++++++++++++++++++-------------------------- 1 file changed, 301 insertions(+), 312 deletions(-) diff --git a/shell/ash.c b/shell/ash.c index 54fc8ea48..69b8ab0b9 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -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. */ -- 2.25.1