ash: cleanup part 6
No binary (build failed)
—
(log yok)
| @@ -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. | |
| */ | |