diff options
Diffstat (limited to 'Src/exec.c')
-rw-r--r-- | Src/exec.c | 1882 |
1 files changed, 1275 insertions, 607 deletions
diff --git a/Src/exec.c b/Src/exec.c index 1b355d028..0dffaf4e2 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -38,7 +38,7 @@ int noerrexit; /* suppress error messages */ /**/ -int noerrs; +mod_export int noerrs; /* do not save history on exec and exit */ @@ -48,7 +48,7 @@ int nohistsave; /* error/break flag */ /**/ -int errflag; +mod_export int errflag; /* Status of return from a trap */ @@ -63,7 +63,7 @@ int subsh; /* != 0 if we have a return pending */ /**/ -int retflag; +mod_export int retflag; /**/ long lastval2; @@ -92,17 +92,17 @@ int max_zsh_fd; /* input fd from the coprocess */ /**/ -int coprocin; +mod_export int coprocin; /* output fd from the coprocess */ /**/ -int coprocout; +mod_export int coprocout; /* != 0 if the line editor is active */ /**/ -int zleactive; +mod_export int zleactive; /* pid of process undergoing 'process substitution' */ @@ -113,45 +113,66 @@ pid_t cmdoutpid; /**/ int cmdoutval; - + +/* The context in which a shell function is called, see SFC_* in zsh.h. */ + +/**/ +mod_export int sfcontext; + /* Stack to save some variables before executing a signal handler function */ /**/ struct execstack *exstack; -#define execerr() if (!forked) { lastval = 1; return; } else _exit(1) +/* Stack with names of functions currently active. */ + +/**/ +mod_export Funcstack funcstack; + +#define execerr() if (!forked) { lastval = 1; goto done; } else _exit(1) static LinkList args; static int doneps4; +/* Execution functions. */ + +static int (*execfuncs[]) _((Estate, int)) = { + execcursh, exectime, execfuncdef, execfor, execselect, + execwhile, execrepeat, execcase, execif, execcond, + execarith, execautofn +}; + /* parse string into a list */ /**/ -List -parse_string(char *s) +mod_export Eprog +parse_string(char *s, int ln) { - List l; + Eprog p; + int oldlineno = lineno; lexsave(); - inpush(s, 0, NULL); - strinbeg(); - stophist = 2; - l = parse_list(); + inpush(s, (ln ? INP_LINENO : 0), NULL); + strinbeg(0); + lineno = ln ? 1 : -1; + p = parse_list(); + lineno = oldlineno; strinend(); inpop(); lexrestore(); - return l; + return p; } +/**/ #ifdef HAVE_GETRLIMIT /* the resource limits for the shell and its children */ /**/ -struct rlimit current_limits[RLIM_NLIMITS], limits[RLIM_NLIMITS]; +mod_export struct rlimit current_limits[RLIM_NLIMITS], limits[RLIM_NLIMITS]; /**/ -int +mod_export int zsetlimit(int limnum, char *nam) { if (limits[limnum].rlim_max != current_limits[limnum].rlim_max || @@ -167,7 +188,7 @@ zsetlimit(int limnum, char *nam) } /**/ -int +mod_export int setlimits(char *nam) { int limnum; @@ -179,6 +200,7 @@ setlimits(char *nam) return ret; } +/**/ #endif /* HAVE_GETRLIMIT */ /* fork and set limits */ @@ -206,6 +228,78 @@ zfork(void) return pid; } +/* + * Allen Edeln gebiet ich Andacht, + * Hohen und Niedern von Heimdalls Geschlecht; + * Ich will list_pipe's Wirken kuenden + * Die aeltesten Sagen, der ich mich entsinne... + * + * In most shells, if you do something like: + * + * cat foo | while read a; do grep $a bar; done + * + * the shell forks and executes the loop in the sub-shell thus created. + * In zsh this traditionally executes the loop in the current shell, which + * is nice to have if the loop does something to change the shell, like + * setting parameters or calling builtins. + * Putting the loop in a sub-shell makes live easy, because the shell only + * has to put it into the job-structure and then treats it as a normal + * process. Suspending and interrupting is no problem then. + * Some years ago, zsh either couldn't suspend such things at all, or + * it got really messed up when users tried to do it. As a solution, we + * implemented the list_pipe-stuff, which has since then become a reason + * for many nightmares. + * Pipelines like the one above are executed by the functions in this file + * which call each other (and sometimes recursively). The one above, for + * example would lead to a function call stack roughly like: + * + * execlist->execpline->execcmd->execwhile->execlist->execpline + * + * (when waiting for the grep, ignoring execpline2 for now). At this time, + * zsh has build two job-table entries for it: one for the cat and one for + * the grep. If the user hits ^Z at this point (and jobbing is used), the + * shell is notified that the grep was suspended. The list_pipe flag is + * used to tell the execpline where it was waiting that it was in a pipeline + * with a shell construct at the end (which may also be a shell function or + * several other things). When zsh sees the suspended grep, it forks to let + * the sub-shell execute the rest of the while loop. The parent shell walks + * up in the function call stack to the first execpline. There it has to find + * out that it has just forked and then has to add information about the sub- + * shell (its pid and the text for it) in the job entry of the cat. The pid + * is passed down in the list_pipe_pid variable. + * But there is a problem: the suspended grep is a child of the parent shell + * and can't be adopted by the sub-shell. So the parent shell also has to + * keep the information about this process (more precisely: this pipeline) + * by keeping the job table entry it created for it. The fact that there + * are two jobs which have to be treated together is remembered by setting + * the STAT_SUPERJOB flag in the entry for the cat-job (which now also + * contains a process-entry for the whole loop -- the sub-shell) and by + * setting STAT_SUBJOB in the job of the grep-job. With that we can keep + * sub-jobs from being displayed and we can handle an fg/bg on the super- + * job correctly. When the super-job is continued, the shell also wakes up + * the sub-job. But then, the grep will exit sometime. Now the parent shell + * has to remember not to try to wake it up again (in case of another ^Z). + * It also has to wake up the sub-shell (which suspended itself immediately + * after creation), so that the rest of the loop is executed by it. + * But there is more: when the sub-shell is created, the cat may already + * have exited, so we can't put the sub-shell in the process group of it. + * In this case, we put the sub-shell in the process group of the parent + * shell and in any case, the sub-shell has to put all commands executed + * by it into its own process group, because only this way the parent + * shell can control them since it only knows the process group of the sub- + * shell. Of course, this information is also important when putting a job + * in the foreground, where we have to attach its process group to the + * controlling tty. + * All this is made more difficult because we have to handle return values + * correctly. If the grep is signaled, its exit status has to be propagated + * back to the parent shell which needs it to set the exit status of the + * super-job. And of course, when the grep is signaled (including ^C), the + * loop has to be stopped, etc. + * The code for all this is distributed over three files (exec.c, jobs.c, + * and signals.c) and none of them is a simple one. So, all in all, there + * may still be bugs, but considering the complexity (with race conditions, + * signal handling, and all that), this should probably be expected. + */ /**/ int list_pipe = 0, simple_pline = 0; @@ -219,12 +313,18 @@ static char list_pipe_text[JOBTEXTSIZE]; /**/ static int -execcursh(Cmd cmd) +execcursh(Estate state, int do_exec) { - if (!list_pipe) + Wordcode end = state->pc + WC_CURSH_SKIP(state->pc[-1]); + + if (!list_pipe && thisjob != list_pipe_job) deletejob(jobtab + thisjob); - execlist(cmd->u.list, 1, cmd->flags & CFLAG_EXEC); - cmd->u.list = NULL; + cmdpush(CS_CURSH); + execlist(state, 1, do_exec); + cmdpop(); + + state->pc = end; + return lastval; } @@ -276,7 +376,9 @@ zexecve(char *pth, char **argv) if (execvebuf[1] == '!') { for (t0 = 0; t0 != ct; t0++) if (execvebuf[t0] == '\n') - execvebuf[t0] = '\0'; + break; + while (inblank(execvebuf[t0])) + execvebuf[t0--] = '\0'; execvebuf[POUNDBANGLIMIT] = '\0'; for (ptr = execvebuf + 2; *ptr && *ptr == ' '; ptr++); for (ptr2 = ptr; *ptr && *ptr != ' '; ptr++); @@ -383,6 +485,7 @@ execute(Cmdnam not_used_yet, int dash) } argv = makecline(args); + closem(3); child_unblock(); if ((int) strlen(arg0) >= PATH_MAX) { zerr("command too long: %s", arg0, 0); @@ -452,13 +555,17 @@ execute(Cmdnam not_used_yet, int dash) _exit(1); } -#define try(X) { if (iscom(X)) return ztrdup(X); } +#define RET_IF_COM(X) { if (iscom(X)) return docopy ? dupstring(X) : arg0; } -/* get the full pathname of an external command */ +/* + * Get the full pathname of an external command. + * If the second argument is zero, return the first argument if found; + * if non-zero, return the path using heap memory. (RET_IF_COM(X), above). + */ /**/ -char * -findcmd(char *arg0) +mod_export char * +findcmd(char *arg0, int docopy) { char **pp; char *z, *s, buf[MAXCMDLEN]; @@ -471,7 +578,7 @@ findcmd(char *arg0) return NULL; for (s = arg0; *s; s++) if (*s == '/') { - try(arg0); + RET_IF_COM(arg0); if (arg0 == s || unset(PATHDIRS)) { return NULL; } @@ -491,13 +598,13 @@ findcmd(char *arg0) *z++ = '/'; } strcpy(z, arg0); - try(buf); + RET_IF_COM(buf); } strcpy(nn, cn->u.name ? *(cn->u.name) : ""); strcat(nn, "/"); strcat(nn, cn->nam); } - try(nn); + RET_IF_COM(nn); } for (pp = path; *pp; pp++) { z = buf; @@ -506,7 +613,7 @@ findcmd(char *arg0) *z++ = '/'; } strcpy(z, arg0); - try(buf); + RET_IF_COM(buf); } return NULL; } @@ -528,9 +635,15 @@ isreallycom(Cmdnam cn) { char fullnam[MAXCMDLEN]; - strcpy(fullnam, cn->u.name ? *(cn->u.name) : ""); - strcat(fullnam, "/"); - strcat(fullnam, cn->nam); + if (cn->flags & HASHED) + strcpy(fullnam, cn->u.cmd); + else if (!cn->u.name) + return 0; + else { + strcpy(fullnam, *(cn->u.name)); + strcat(fullnam, "/"); + strcat(fullnam, cn->nam); + } return iscom(fullnam); } @@ -549,7 +662,7 @@ isrelative(char *s) } /**/ -Cmdnam +mod_export Cmdnam hashcmd(char *arg0, char **pp) { Cmdnam cn; @@ -588,17 +701,60 @@ hashcmd(char *arg0, char **pp) /* execute a string */ /**/ -void +mod_export void execstring(char *s, int dont_change_job, int exiting) { - List list; + Eprog prog; pushheap(); - if ((list = parse_string(s))) - execlist(list, dont_change_job, exiting); + if ((prog = parse_string(s, 0))) + execode(prog, dont_change_job, exiting); popheap(); } +/**/ +void +execode(Eprog p, int dont_change_job, int exiting) +{ + struct estate s; + + s.prog = p; + s.pc = p->prog; + s.strs = p->strs; + + execlist(&s, dont_change_job, exiting); +} + +/* Execute a simplified command. This is used to execute things that + * will run completely in the shell, so that we can by-pass all that + * nasty job-handling and redirection stuff in execpline and execcmd. */ + +/**/ +static int +execsimple(Estate state) +{ + wordcode code = *state->pc++; + + if (errflag) + return (lastval = 1); + + if (code) + lineno = code - 1; + + code = wc_code(*state->pc++); + + if (code == WC_ASSIGN) { + cmdoutval = 0; + addvars(state, state->pc - 1, 0); + if (isset(XTRACE)) { + fputc('\n', xtrerr); + fflush(xtrerr); + } + return (lastval = (errflag ? errflag : cmdoutval)); + } else + return (lastval = (execfuncs[code - WC_CURSH])(state, 0)); +} + /* Main routine for executing a list. * * exiting means that the (sub)shell we are in is a definite goner * * after the current list is finished, so we may be able to exec the * @@ -608,45 +764,78 @@ execstring(char *s, int dont_change_job, int exiting) /**/ void -execlist(List list, int dont_change_job, int exiting) +execlist(Estate state, int dont_change_job, int exiting) { - Sublist slist; static int donetrap; - int ret, cj; - int old_pline_level, old_list_pipe; + Wordcode next; + wordcode code; + int ret, cj, csp, ltype; + int old_pline_level, old_list_pipe, oldlineno; + /* + * ERREXIT only forces the shell to exit if the last command in a && + * or || fails. This is the case even if an earlier command is a + * shell function or other current shell structure, so we have to set + * noerrexit here if the sublist is not of type END. + */ + int oldnoerrexit = noerrexit; cj = thisjob; old_pline_level = pline_level; old_list_pipe = list_pipe; + oldlineno = lineno; if (sourcelevel && unset(SHINSTDIN)) pline_level = list_pipe = 0; /* Loop over all sets of comands separated by newline, * * semi-colon or ampersand (`sublists'). */ - while (list && list != &dummy_list && !breaks && !retflag) { + code = *state->pc++; + while (wc_code(code) == WC_LIST && !breaks && !retflag) { + ltype = WC_LIST_TYPE(code); + csp = cmdsp; + + if (ltype & Z_SIMPLE) { + next = state->pc + WC_LIST_SKIP(code); + execsimple(state); + state->pc = next; + goto sublist_done; + } /* Reset donetrap: this ensures that a trap is only * * called once for each sublist that fails. */ donetrap = 0; - simplifyright(list); - slist = list->left; /* Loop through code followed by &&, ||, or end of sublist. */ - while (slist) { - switch (slist->type) { - case END: + code = *state->pc++; + while (wc_code(code) == WC_SUBLIST) { + next = state->pc + WC_SUBLIST_SKIP(code); + if (!oldnoerrexit) + noerrexit = (WC_SUBLIST_TYPE(code) != WC_SUBLIST_END); + switch (WC_SUBLIST_TYPE(code)) { + case WC_SUBLIST_END: /* End of sublist; just execute, ignoring status. */ - execpline(slist, list->type, !list->right && exiting); + if (WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) + execsimple(state); + else + execpline(state, code, ltype, (ltype & Z_END) && exiting); + state->pc = next; goto sublist_done; break; - case ANDNEXT: + case WC_SUBLIST_AND: /* If the return code is non-zero, we skip pipelines until * * we find a sublist followed by ORNEXT. */ - if ((ret = execpline(slist, Z_SYNC, 0))) { - while ((slist = slist->right)) - if (slist->type == ORNEXT) - break; - if (!slist) { + if ((ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ? + execsimple(state) : + execpline(state, code, Z_SYNC, 0)))) { + state->pc = next; + code = *state->pc++; + next = state->pc + WC_SUBLIST_SKIP(code); + while (wc_code(code) == WC_SUBLIST && + WC_SUBLIST_TYPE(code) == WC_SUBLIST_AND) { + state->pc = next; + code = *state->pc++; + next = state->pc + WC_SUBLIST_SKIP(code); + } + if (wc_code(code) != WC_SUBLIST) { /* We've skipped to the end of the list, not executing * * the final pipeline, so don't perform error handling * * for this sublist. */ @@ -654,28 +843,43 @@ execlist(List list, int dont_change_job, int exiting) goto sublist_done; } } + cmdpush(CS_CMDAND); break; - case ORNEXT: + case WC_SUBLIST_OR: /* If the return code is zero, we skip pipelines until * * we find a sublist followed by ANDNEXT. */ - if (!(ret = execpline(slist, Z_SYNC, 0))) { - while ((slist = slist->right)) - if (slist->type == ANDNEXT) - break; - if (!slist) { + if (!(ret = ((WC_SUBLIST_FLAGS(code) & WC_SUBLIST_SIMPLE) ? + execsimple(state) : + execpline(state, code, Z_SYNC, 0)))) { + state->pc = next; + code = *state->pc++; + next = state->pc + WC_SUBLIST_SKIP(code); + while (wc_code(code) == WC_SUBLIST && + WC_SUBLIST_TYPE(code) == WC_SUBLIST_OR) { + state->pc = next; + code = *state->pc++; + next = state->pc + WC_SUBLIST_SKIP(code); + } + if (wc_code(code) != WC_SUBLIST) { /* We've skipped to the end of the list, not executing * * the final pipeline, so don't perform error handling * * for this sublist. */ donetrap = 1; goto sublist_done; - } + } } + cmdpush(CS_CMDOR); break; } - slist = slist->right; + state->pc = next; + code = *state->pc++; } + state->pc--; sublist_done: + cmdsp = csp; + noerrexit = oldnoerrexit; + if (sigtrapped[SIGDEBUG]) dotrap(SIGDEBUG); @@ -696,12 +900,13 @@ sublist_done: exit(lastval); } } - - list = list->right; + if (ltype & Z_END) + break; + code = *state->pc++; } - pline_level = old_pline_level; list_pipe = old_list_pipe; + lineno = oldlineno; if (dont_change_job) thisjob = cj; } @@ -718,15 +923,17 @@ sublist_done: /**/ static int -execpline(Sublist l, int how, int last1) +execpline(Estate state, wordcode slcode, int how, int last1) { int ipipe[2], opipe[2]; int pj, newjob; int old_simple_pline = simple_pline; - static int lastwj; + int slflags = WC_SUBLIST_FLAGS(slcode); + wordcode code = *state->pc++; + static int lastwj, lpforked; - if (!l->left) - return lastval = (l->flags & PFLAG_NOT) != 0; + if (wc_code(code) != WC_PIPE) + return lastval = (slflags & WC_SUBLIST_NOT) != 0; pj = thisjob; ipipe[0] = ipipe[1] = opipe[0] = opipe[1] = 0; @@ -735,10 +942,11 @@ execpline(Sublist l, int how, int last1) /* get free entry in job table and initialize it */ if ((thisjob = newjob = initjob()) == -1) return 1; + if (how & Z_TIMED) jobtab[thisjob].stat |= STAT_TIMED; - if (l->flags & PFLAG_COPROC) { + if (slflags & WC_SUBLIST_COPROC) { how = Z_ASYNC; if (coprocin >= 0) { zclose(coprocin); @@ -750,20 +958,26 @@ execpline(Sublist l, int how, int last1) coprocout = opipe[1]; fdtable[coprocin] = fdtable[coprocout] = 0; } + /* This used to set list_pipe_pid=0 unconditionally, but in things + * like `ls|if true; then sleep 20; cat; fi' where the sleep was + * stopped, the top-level execpline() didn't get the pid for the + * sub-shell because it was overwritten. */ if (!pline_level++) { list_pipe_job = newjob; + list_pipe_pid = 0; nowait = 0; + simple_pline = (WC_PIPE_TYPE(code) == WC_PIPE_END); } - list_pipe_pid = lastwj = 0; - if (pline_level == 1) - simple_pline = (l->left->type == END); - execpline2(l->left, how, opipe[0], ipipe[1], last1); + lastwj = lpforked = 0; + execpline2(state, code, how, opipe[0], ipipe[1], last1); pline_level--; if (how & Z_ASYNC) { lastwj = newjob; jobtab[thisjob].stat |= STAT_NOSTTY; - if (l->flags & PFLAG_COPROC) + if (slflags & WC_SUBLIST_COPROC) { zclose(ipipe[1]); + zclose(opipe[0]); + } if (how & Z_DISOWN) { deletejob(jobtab + thisjob); thisjob = -1; @@ -775,13 +989,14 @@ execpline(Sublist l, int how, int last1) } else { if (newjob != lastwj) { Job jn = jobtab + newjob; + int updated; if (newjob == list_pipe_job && list_pipe_child) _exit(0); lastwj = thisjob = newjob; - if (list_pipe) + if (list_pipe || (pline_level && !(how & Z_TIMED))) jn->stat |= STAT_NOPRINT; if (nowait) { @@ -789,8 +1004,15 @@ execpline(Sublist l, int how, int last1) struct process *pn, *qn; curjob = newjob; + DPUTS(!list_pipe_pid, "invalid list_pipe_pid"); addproc(list_pipe_pid, list_pipe_text); + /* If the super-job contains only the sub-shell, the + sub-shell is the group leader. */ + if (!jn->procs->next || lpforked == 2) { + jn->gleader = list_pipe_pid; + jn->stat |= STAT_SUBLEADER; + } for (pn = jobtab[jn->other].procs; pn; pn = pn->next) if (WIFSTOPPED(pn->status)) break; @@ -801,27 +1023,42 @@ execpline(Sublist l, int how, int last1) } jn->stat &= ~(STAT_DONE | STAT_NOPRINT); - jn->stat |= STAT_STOPPED | STAT_CHANGED; + jn->stat |= STAT_STOPPED | STAT_CHANGED | STAT_LOCKED; printjob(jn, !!isset(LONGLISTJOBS), 1); } - else + else if (newjob != list_pipe_job) deletejob(jn); + else + lastwj = -1; } + errbrk_saved = 0; for (; !nowait;) { if (list_pipe_child) { jn->stat |= STAT_NOPRINT; makerunning(jn); } - if (!(jn->stat & STAT_LOCKED)) + if (!(jn->stat & STAT_LOCKED)) { + updated = !!jobtab[thisjob].procs; waitjobs(); - + child_block(); + } else + updated = 0; + if (!updated && + list_pipe_job && jobtab[list_pipe_job].procs && + !(jobtab[list_pipe_job].stat & STAT_STOPPED)) { + child_unblock(); + child_block(); + } if (list_pipe_child && jn->stat & STAT_DONE && lastval2 & 0200) killpg(mypgrp, lastval2 & ~0200); - if ((list_pipe || last1) && !list_pipe_child && - jn->stat & STAT_STOPPED) { + if (!list_pipe_child && !lpforked && !subsh && jobbing && + (list_pipe || last1 || pline_level) && + ((jn->stat & STAT_STOPPED) || + (list_pipe_job && pline_level && + (jobtab[list_pipe_job].stat & STAT_STOPPED)))) { pid_t pid; int synch[2]; @@ -841,27 +1078,45 @@ execpline(Sublist l, int how, int last1) else if (pid) { char dummy; + lpforked = + (killpg(jobtab[list_pipe_job].gleader, 0) == -1 ? 2 : 1); list_pipe_pid = pid; nowait = errflag = 1; breaks = loops; close(synch[1]); read(synch[0], &dummy, 1); close(synch[0]); - jobtab[list_pipe_job].other = newjob; - jobtab[list_pipe_job].stat |= STAT_SUPERJOB; - jn->stat |= STAT_SUBJOB | STAT_NOPRINT; - jn->other = pid; - killpg(jobtab[list_pipe_job].gleader, SIGSTOP); + /* If this job has finished, we leave it as a + * normal (non-super-) job. */ + if (!(jn->stat & STAT_DONE)) { + jobtab[list_pipe_job].other = newjob; + jobtab[list_pipe_job].stat |= STAT_SUPERJOB; + jn->stat |= STAT_SUBJOB | STAT_NOPRINT; + jn->other = pid; + } + if ((list_pipe || last1) && jobtab[list_pipe_job].procs) + killpg(jobtab[list_pipe_job].gleader, SIGSTOP); break; } else { close(synch[0]); entersubsh(Z_ASYNC, 0, 0); - setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader); + if (jobtab[list_pipe_job].procs) { + if (setpgrp(0L, mypgrp = jobtab[list_pipe_job].gleader) + == -1) { + setpgrp(0L, mypgrp = getpid()); + } + } else + setpgrp(0L, mypgrp = getpid()); close(synch[1]); kill(getpid(), SIGSTOP); list_pipe = 0; list_pipe_child = 1; + opts[INTERACTIVE] = 0; + if (errbrk_saved) { + errflag = prev_errflag; + breaks = prev_breaks; + } break; } } @@ -874,16 +1129,18 @@ execpline(Sublist l, int how, int last1) if (list_pipe && (lastval & 0200) && pj >= 0 && (!(jn->stat & STAT_INUSE) || (jn->stat & STAT_DONE))) { + deletejob(jn); jn = jobtab + pj; - jn->stat |= STAT_NOPRINT; - killjb(jobtab + pj, lastval & ~0200); + killjb(jn, lastval & ~0200); } - if (list_pipe_child || (list_pipe && (jn->stat & STAT_DONE))) + if (list_pipe_child || + ((jn->stat & STAT_DONE) && + (list_pipe || (pline_level && !(jn->stat & STAT_SUBJOB))))) deletejob(jn); thisjob = pj; } - if (l->flags & PFLAG_NOT) + if (slflags & WC_SUBLIST_NOT) lastval = !lastval; } if (!pline_level) @@ -897,32 +1154,43 @@ static int subsh_close = -1; /**/ static void -execpline2(Pline pline, int how, int input, int output, int last1) +execpline2(Estate state, wordcode pcode, + int how, int input, int output, int last1) { pid_t pid; int pipes[2]; - int oldlineno; if (breaks || retflag) return; - oldlineno = lineno; - lineno = pline->left->lineno; + if (WC_PIPE_LINENO(pcode)) + lineno = WC_PIPE_LINENO(pcode) - 1; - if (pline_level == 1) - strcpy(list_pipe_text, getjobtext((void *) pline->left)); - if (pline->type == END) { - execcmd(pline->left, input, output, how, last1 ? 1 : 2); - pline->left = NULL; - } else { + if (pline_level == 1) { + if ((how & Z_ASYNC) || (!sfcontext && !sourcelevel)) + strcpy(list_pipe_text, + getjobtext(state->prog, + state->pc + (WC_PIPE_TYPE(pcode) == WC_PIPE_END ? + 0 : 1))); + else + list_pipe_text[0] = '\0'; + } + if (WC_PIPE_TYPE(pcode) == WC_PIPE_END) + execcmd(state, input, output, how, last1 ? 1 : 2); + else { int old_list_pipe = list_pipe; + Wordcode next = state->pc + (*state->pc); + wordcode code; + + state->pc++; + code = *state->pc; mpipe(pipes); /* if we are doing "foo | bar" where foo is a current * * shell command, do foo in a subshell and do the * * rest of the pipeline in the current shell. */ - if (pline->left->type >= CURSH && (how & Z_SYNC)) { + if (wc_code(code) >= WC_CURSH && (how & Z_SYNC)) { int synch[2]; pipe(synch); @@ -933,7 +1201,7 @@ execpline2(Pline pline, int how, int input, int output, int last1) } else if (pid) { char dummy, *text; - text = getjobtext((void *) pline->left); + text = getjobtext(state->prog, state->pc); addproc(pid, text); close(synch[1]); read(synch[0], &dummy, 1); @@ -943,28 +1211,27 @@ execpline2(Pline pline, int how, int input, int output, int last1) close(synch[0]); entersubsh(how, 2, 0); close(synch[1]); - execcmd(pline->left, input, pipes[1], how, 0); + execcmd(state, input, pipes[1], how, 0); _exit(lastval); } } else { - /* otherwise just do the pipeline normally. */ + /* otherwise just do the pipeline normally. */ subsh_close = pipes[0]; - execcmd(pline->left, input, pipes[1], how, 0); + execcmd(state, input, pipes[1], how, 0); } - pline->left = NULL; zclose(pipes[1]); - if (pline->right) { - /* if another execpline() is invoked because the command is * - * a list it must know that we're already in a pipeline */ - list_pipe = 1; - execpline2(pline->right, how, pipes[0], output, last1); - list_pipe = old_list_pipe; - zclose(pipes[0]); - subsh_close = -1; - } + state->pc = next; + + /* if another execpline() is invoked because the command is * + * a list it must know that we're already in a pipeline */ + cmdpush(CS_PIPE); + list_pipe = 1; + execpline2(state, *state->pc++, how, pipes[0], output, last1); + list_pipe = old_list_pipe; + cmdpop(); + zclose(pipes[0]); + subsh_close = -1; } - - lineno = oldlineno; } /* make the argv array */ @@ -977,20 +1244,21 @@ makecline(LinkList list) char **argv, **ptr; /* A bigger argv is necessary for executing scripts */ - ptr = - argv = 2 + (char **) ncalloc((countlinknodes(list) + 4) * sizeof(char *)); + ptr = argv = 2 + (char **) hcalloc((countlinknodes(list) + 4) * + sizeof(char *)); + if (isset(XTRACE)) { if (!doneps4) - fprintf(stderr, "%s", (prompt4) ? prompt4 : ""); + printprompt4(); for (node = firstnode(list); node; incnode(node)) { *ptr++ = (char *)getdata(node); - zputs(getdata(node), stderr); + zputs(getdata(node), xtrerr); if (nextnode(node)) - fputc(' ', stderr); + fputc(' ', xtrerr); } - fputc('\n', stderr); - fflush(stderr); + fputc('\n', xtrerr); + fflush(xtrerr); } else { for (node = firstnode(list); node; incnode(node)) *ptr++ = (char *)getdata(node); @@ -1000,18 +1268,33 @@ makecline(LinkList list) } /**/ -void +mod_export void untokenize(char *s) { - for (; *s; s++) - if (itok(*s)) - if (*s == Nularg) - chuck(s--); - else - *s = ztokens[*s - Pound]; + if (*s) { + int c; + + while ((c = *s++)) + if (itok(c)) { + char *p = s - 1; + + if (c != Nularg) + *p++ = ztokens[c - Pound]; + + while ((c = *s++)) { + if (itok(c)) { + if (c != Nularg) + *p++ = ztokens[c - Pound]; + } else + *p++ = c; + } + *p = '\0'; + break; + } + } } -/* Open a file for writing redicection */ +/* Open a file for writing redirection */ /**/ static int @@ -1052,34 +1335,34 @@ clobber_open(struct redir *f) static void closemn(struct multio **mfds, int fd) { - struct multio *mn = mfds[fd]; - char buf[TCBUFSIZE]; - int len, i; + if (fd >= 0 && mfds[fd] && mfds[fd]->ct >= 2) { + struct multio *mn = mfds[fd]; + char buf[TCBUFSIZE]; + int len, i; - if (fd < 0 || !mfds[fd] || mfds[fd]->ct < 2) - return; - if (zfork()) { - for (i = 0; i < mn->ct; i++) - zclose(mn->fds[i]); - zclose(mn->pipe); - mn->ct = 1; - mn->fds[0] = fd; - return; - } - /* pid == 0 */ - closeallelse(mn); - if (mn->rflag) { - /* tee process */ - while ((len = read(mn->pipe, buf, TCBUFSIZE)) > 0) + if (zfork()) { for (i = 0; i < mn->ct; i++) - write(mn->fds[i], buf, len); - } else { - /* cat process */ - for (i = 0; i < mn->ct; i++) - while ((len = read(mn->fds[i], buf, TCBUFSIZE)) > 0) - write(mn->pipe, buf, len); + zclose(mn->fds[i]); + zclose(mn->pipe); + mn->ct = 1; + mn->fds[0] = fd; + return; + } + /* pid == 0 */ + closeallelse(mn); + if (mn->rflag) { + /* tee process */ + while ((len = read(mn->pipe, buf, TCBUFSIZE)) > 0) + for (i = 0; i < mn->ct; i++) + write(mn->fds[i], buf, len); + } else { + /* cat process */ + for (i = 0; i < mn->ct; i++) + while ((len = read(mn->fds[i], buf, TCBUFSIZE)) > 0) + write(mn->pipe, buf, len); + } + _exit(0); } - _exit(0); } /* close all the mnodes (failure) */ @@ -1135,7 +1418,7 @@ addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag) if (!mfds[fd1] || unset(MULTIOS)) { if(!mfds[fd1]) { /* starting a new multio */ - mfds[fd1] = (struct multio *) alloc(sizeof(struct multio)); + mfds[fd1] = (struct multio *) zhalloc(sizeof(struct multio)); if (!forked && save[fd1] == -2) save[fd1] = (fd1 == fd2) ? -1 : movefd(fd1); } @@ -1170,40 +1453,48 @@ addfd(int forked, int *save, struct multio **mfds, int fd1, int fd2, int rflag) /**/ static void -addvars(LinkList l, int export) +addvars(Estate state, Wordcode pc, int export) { - Varasg v; LinkList vl; - int xtr; - char **arr, **ptr; + int xtr, isstr, htok = 0; + char **arr, **ptr, *name; + Wordcode opc = state->pc; + wordcode ac; + local_list1(svl); xtr = isset(XTRACE); - if (xtr && nonempty(l)) { - fprintf(stderr, "%s", prompt4 ? prompt4 : ""); + if (xtr) { + printprompt4(); doneps4 = 1; } - - while (nonempty(l)) { - v = (Varasg) ugetnode(l); - singsub(&v->name); - if (errflag) - return; - untokenize(v->name); + state->pc = pc; + while (wc_code(ac = *state->pc++) == WC_ASSIGN) { + name = ecgetstr(state, EC_DUPTOK, &htok); + if (htok) + untokenize(name); if (xtr) - fprintf(stderr, "%s=", v->name); - if (v->type == PM_SCALAR) { - vl = newlinklist(); - addlinknode(vl, v->str); + fprintf(xtrerr, "%s=", name); + if ((isstr = (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR))) { + init_list1(svl, ecgetstr(state, EC_DUPTOK, &htok)); + vl = &svl; } else - vl = v->arr; - prefork(vl, v->type == PM_SCALAR ? 7 : 3); - if (errflag) - return; - if (isset(GLOBASSIGN) || v->type != PM_SCALAR) - globlist(vl); - if (errflag) - return; - if (v->type == PM_SCALAR && (empty(vl) || !nextnode(firstnode(vl)))) { + vl = ecgetlist(state, WC_ASSIGN_NUM(ac), EC_DUPTOK, &htok); + + if (vl && htok) { + prefork(vl, (isstr ? (PF_SINGLE|PF_ASSIGN) : + PF_ASSIGN)); + if (errflag) { + state->pc = opc; + return; + } + if (isset(GLOBASSIGN) || !isstr) + globlist(vl, 0); + if (errflag) { + state->pc = opc; + return; + } + } + if (isstr && (empty(vl) || !nextnode(firstnode(vl)))) { Param pm; char *val; int allexp; @@ -1215,48 +1506,100 @@ addvars(LinkList l, int export) val = ztrdup(ugetnode(vl)); } if (xtr) - fprintf(stderr, "%s ", val); - if (export) { - if (export < 0) { - /* We are going to fork so do not bother freeing this */ - pm = (Param) paramtab->removenode(paramtab, v->name); - if (isset(RESTRICTED) && (pm->flags & PM_RESTRICTED)) { - zerr("%s: restricted", pm->nam, 0); - zsfree(val); - return; - } + fprintf(xtrerr, "%s ", val); + if (export && !strchr(name, '[')) { + if (export < 0 && isset(RESTRICTED) && + (pm = (Param) paramtab->removenode(paramtab, name)) && + (pm->flags & PM_RESTRICTED)) { + zerr("%s: restricted", pm->nam, 0); + zsfree(val); + state->pc = opc; + return; } allexp = opts[ALLEXPORT]; opts[ALLEXPORT] = 1; - pm = setsparam(v->name, val); + pm = setsparam(name, val); opts[ALLEXPORT] = allexp; } else - pm = setsparam(v->name, val); - if (errflag) + pm = setsparam(name, val); + if (errflag) { + state->pc = opc; return; + } continue; } - ptr = arr = (char **) zalloc(sizeof(char **) * (countlinknodes(vl) + 1)); + if (vl) { + ptr = arr = (char **) zalloc(sizeof(char **) * + (countlinknodes(vl) + 1)); - while (nonempty(vl)) - *ptr++ = ztrdup((char *) ugetnode(vl)); + while (nonempty(vl)) + *ptr++ = ztrdup((char *) ugetnode(vl)); + } else + ptr = arr = (char **) zalloc(sizeof(char **)); *ptr = NULL; if (xtr) { - fprintf(stderr, "( "); + fprintf(xtrerr, "( "); for (ptr = arr; *ptr; ptr++) - fprintf(stderr, "%s ", *ptr); - fprintf(stderr, ") "); + fprintf(xtrerr, "%s ", *ptr); + fprintf(xtrerr, ") "); } - setaparam(v->name, arr); - if (errflag) + setaparam(name, arr); + if (errflag) { + state->pc = opc; return; + } + } + state->pc = opc; +} + +/**/ +void +setunderscore(char *str) +{ + if (str && *str) { + int l = strlen(str) + 1, nl = (l + 31) & ~31; + + if (nl > underscorelen || (underscorelen - nl) > 64) { + zfree(underscore, underscorelen); + underscore = (char *) zalloc(underscorelen = nl); + } + strcpy(underscore, str); + underscoreused = l; + } else { + if (underscorelen > 128) { + zfree(underscore, underscorelen); + underscore = (char *) zalloc(underscorelen = 32); + } + *underscore = '\0'; + underscoreused = 1; + } +} + +/* These describe the type of espansions that need to be done on the words + * used in the thing we are about to execute. They are set in execcmd() and + * used in execsubst() which might be called from one of the functions + * called from execcmd() (like execfor() and so on). */ + +static int esprefork, esglob = 1; + +/**/ +void +execsubst(LinkList strs) +{ + if (strs) { + prefork(strs, esprefork); + if (esglob) { + LinkList ostrs = strs; + globlist(strs, 0); + strs = ostrs; + } } } /**/ static void -execcmd(Cmd cmd, int input, int output, int how, int last1) +execcmd(Estate state, int input, int output, int how, int last1) { HashNode hn = NULL; LinkNode node; @@ -1264,15 +1607,35 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) struct multio *mfds[10]; char *text; int save[10]; - int fil, dfil, is_cursh, type, i; + int fil, dfil, is_cursh, type, do_exec = 0, i, htok = 0; int nullexec = 0, assign = 0, forked = 0; int is_shfunc = 0, is_builtin = 0, is_exec = 0; /* Various flags to the command. */ int cflags = 0, checked = 0; + LinkList redir; + wordcode code; + Wordcode beg = state->pc, varspc; + FILE *oxtrerr = xtrerr; doneps4 = 0; - args = cmd->args; - type = cmd->type; + redir = (wc_code(*state->pc) == WC_REDIR ? ecgetredirs(state) : NULL); + if (wc_code(*state->pc) == WC_ASSIGN) { + varspc = state->pc; + while (wc_code((code = *state->pc)) == WC_ASSIGN) + state->pc += (WC_ASSIGN_TYPE(code) == WC_ASSIGN_SCALAR ? + 3 : WC_ASSIGN_NUM(code) + 2); + } else + varspc = NULL; + + code = *state->pc++; + + type = wc_code(code); + + /* It would be nice if we could use EC_DUPTOK instead of EC_DUP here. + * But for that we would need to check/change all builtins so that + * they don't modify their argument strings. */ + args = (type == WC_SIMPLE ? + ecgetlist(state, WC_SIMPLE_ARGC(code), EC_DUP, &htok) : NULL); for (i = 0; i < 10; i++) { save[i] = -2; @@ -1281,7 +1644,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) /* If the command begins with `%', then assume it is a * * reference to a job in the job table. */ - if (type == SIMPLE && nonempty(args) && + if (type == WC_SIMPLE && args && nonempty(args) && *(char *)peekfirst(args) == '%') { pushnode(args, dupstring((how & Z_DISOWN) ? "disown" : (how & Z_ASYNC) ? "bg" : "fg")); @@ -1292,8 +1655,8 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) * any redirections, then check if it matches as a prefix of a * * job currently in the job table. If it does, then we treat it * * as a command to resume this job. */ - if (isset(AUTORESUME) && type == SIMPLE && (how & Z_SYNC) && - nonempty(args) && empty(cmd->redir) && !input && + if (isset(AUTORESUME) && type == WC_SIMPLE && (how & Z_SYNC) && + args && nonempty(args) && (!redir || empty(redir)) && !input && !nextnode(firstnode(args))) { if (unset(NOTIFY)) scanjobs(); @@ -1306,8 +1669,8 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) * command if it contains some tokens (e.g. x=ex; ${x}port), so this * * only works in simple cases. has_token() is called to make sure * * this really is a simple case. */ - if (type == SIMPLE) { - while (nonempty(args)) { + if (type == WC_SIMPLE) { + while (args && nonempty(args)) { char *cmdarg = (char *) peekfirst(args); checked = !has_token(cmdarg); if (!checked) @@ -1323,13 +1686,12 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) } if (!(hn->flags & BINF_PREFIX)) { is_builtin = 1; -#ifdef DYNAMIC + /* autoload the builtin if necessary */ if (!((Builtin) hn)->handlerfunc) { load_module(((Builtin) hn)->optstr); hn = builtintab->getnode(builtintab, cmdarg); } -#endif assign = (hn->flags & BINF_MAGICEQUALS); break; } @@ -1344,18 +1706,20 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) } /* Do prefork substitutions */ - prefork(args, assign ? 2 : isset(MAGICEQUALSUBST)); + esprefork = (assign || isset(MAGICEQUALSUBST)) ? PF_TYPESET : 0; + if (args && htok) + prefork(args, esprefork); - if (type == SIMPLE) { + if (type == WC_SIMPLE) { int unglobbed = 0; for (;;) { char *cmdarg; if (!(cflags & BINF_NOGLOB)) - while (!checked && !errflag && nonempty(args) && + while (!checked && !errflag && args && nonempty(args) && has_token((char *) peekfirst(args))) - glob(args, firstnode(args)); + glob(args, firstnode(args), 0); else if (!unglobbed) { for (node = firstnode(args); node; incnode(node)) untokenize((char *) getdata(node)); @@ -1364,46 +1728,59 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) /* Current shell should not fork unless the * * exec occurs at the end of a pipeline. */ - if ((cflags & BINF_EXEC) && last1 == 2) - cmd->flags |= CFLAG_EXEC; + if ((cflags & BINF_EXEC) && last1) + do_exec = 1; /* Empty command */ - if (empty(args)) { - if (nonempty(cmd->redir)) { - if (cmd->flags & CFLAG_EXEC) { + if (!args || empty(args)) { + if (redir && nonempty(redir)) { + if (do_exec) { /* Was this "exec < foobar"? */ nullexec = 1; break; - } else if (!nullcmd || !*nullcmd || + } else if (varspc) { + nullexec = 2; + break; + } else if (!nullcmd || !*nullcmd || opts[CSHNULLCMD] || (cflags & BINF_PREFIX)) { zerr("redirection with no command", NULL, 0); errflag = lastval = 1; return; + } else if (!nullcmd || !*nullcmd || opts[SHNULLCMD]) { + if (!args) + args = newlinklist(); + addlinknode(args, dupstring(":")); } else if (readnullcmd && *readnullcmd && - ((Redir) peekfirst(cmd->redir))->type == READ && - !nextnode(firstnode(cmd->redir))) { + ((Redir) peekfirst(redir))->type == READ && + !nextnode(firstnode(redir))) { + if (!args) + args = newlinklist(); addlinknode(args, dupstring(readnullcmd)); - } else + } else { + if (!args) + args = newlinklist(); addlinknode(args, dupstring(nullcmd)); + } } else if ((cflags & BINF_PREFIX) && (cflags & BINF_COMMAND)) { lastval = 0; return; } else { cmdoutval = 0; - addvars(cmd->vars, 0); + if (varspc) + addvars(state, varspc, 0); if (errflag) lastval = errflag; else lastval = cmdoutval; if (isset(XTRACE)) { - fputc('\n', stderr); - fflush(stderr); + fputc('\n', xtrerr); + fflush(xtrerr); } return; } - } else if (isset(RESTRICTED) && (cflags & BINF_EXEC) && - (cmd->flags & CFLAG_EXEC)) { - zerrnam("exec", "%s: restricted", (char *) getdata(firstnode(args)), 0); + } else if (isset(RESTRICTED) && (cflags & BINF_EXEC) && do_exec) { + zerrnam("exec", "%s: restricted", + (char *) getdata(firstnode(args)), 0); lastval = 1; return; } @@ -1428,11 +1805,12 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) } if (!(hn->flags & BINF_PREFIX)) { is_builtin = 1; -#ifdef DYNAMIC + /* autoload the builtin if necessary */ - if (!((Builtin) hn)->handlerfunc) + if (!((Builtin) hn)->handlerfunc) { load_module(((Builtin) hn)->optstr); -#endif + hn = builtintab->getnode(builtintab, cmdarg); + } break; } cflags &= ~BINF_BUILTIN & ~BINF_COMMAND; @@ -1448,23 +1826,20 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) } /* Get the text associated with this command. */ - if (jobbing || (how & Z_TIMED)) - text = getjobtext((void *) cmd); + if ((how & Z_ASYNC) || + (!sfcontext && !sourcelevel && (jobbing || (how & Z_TIMED)))) + text = getjobtext(state->prog, beg); else text = NULL; /* Set up special parameter $_ */ - zsfree(underscore); - if (nonempty(args) - && (underscore = ztrdup((char *) getdata(lastnode(args))))) - untokenize(underscore); - else - underscore = ztrdup(""); + + setunderscore((args && nonempty(args)) ? ((char *) getdata(lastnode(args))) : ""); /* Warn about "rm *" */ - if (type == SIMPLE && interact && unset(RMSTARSILENT) - && isset(SHINSTDIN) && nonempty(args) && nextnode(firstnode(args)) - && !strcmp(peekfirst(args), "rm")) { + if (type == WC_SIMPLE && interact && unset(RMSTARSILENT) && + isset(SHINSTDIN) && args && nonempty(args) && + nextnode(firstnode(args)) && !strcmp(peekfirst(args), "rm")) { LinkNode node, next; for (node = nextnode(firstnode(args)); node && !errflag; node = next) { @@ -1493,28 +1868,33 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) return; } - if (type == SIMPLE && !nullexec) { + if (type == WC_SIMPLE && !nullexec) { char *s; - char trycd = (isset(AUTOCD) && isset(SHINSTDIN) - && empty(cmd->redir) && !empty(args) - && !nextnode(firstnode(args)) - && *(char *)peekfirst(args)); + char trycd = (isset(AUTOCD) && isset(SHINSTDIN) && + (!redir || empty(redir)) && args && !empty(args) && + !nextnode(firstnode(args)) && *(char *)peekfirst(args)); - DPUTS(empty(args), "BUG: empty(args) in exec.c"); + DPUTS((!args || empty(args)), "BUG: empty(args) in exec.c"); if (!hn) { /* Resolve external commands */ char *cmdarg = (char *) peekfirst(args); + char **checkpath = pathchecked; + int dohashcmd = isset(HASHCMDS); hn = cmdnamtab->getnode(cmdnamtab, cmdarg); if (hn && trycd && !isreallycom((Cmdnam)hn)) { + if (!(((Cmdnam)hn)->flags & HASHED)) { + checkpath = path; + dohashcmd = 1; + } cmdnamtab->removenode(cmdnamtab, cmdarg); cmdnamtab->freenode(hn); hn = NULL; } - if (!hn && isset(HASHCMDS) && strcmp(cmdarg, "..")) { + if (!hn && dohashcmd && strcmp(cmdarg, "..")) { for (s = cmdarg; *s && *s != '/'; s++); if (!*s) - hn = (HashNode) hashcmd(cmdarg, pathchecked); + hn = (HashNode) hashcmd(cmdarg, checkpath); } } @@ -1529,7 +1909,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) } /* This is nonzero if the command is a current shell procedure? */ - is_cursh = (is_builtin || is_shfunc || (type >= CURSH) || nullexec); + is_cursh = (is_builtin || is_shfunc || nullexec || type >= WC_CURSH); /************************************************************************** * Do we need to fork? We need to fork if: * @@ -1552,10 +1932,11 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) * current shell. * **************************************************************************/ - if ((how & Z_ASYNC) || (!(cmd->flags & CFLAG_EXEC) && - (((is_builtin || is_shfunc) && output) || - (!is_cursh && (last1 != 1 || sigtrapped[SIGZERR] || - sigtrapped[SIGEXIT] || havefiles()))))) { + if ((how & Z_ASYNC) || + (!do_exec && + (((is_builtin || is_shfunc) && output) || + (!is_cursh && (last1 != 1 || sigtrapped[SIGZERR] || + sigtrapped[SIGEXIT] || havefiles()))))) { pid_t pid; int synch[2]; @@ -1576,21 +1957,27 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) closem(2); #endif if (how & Z_ASYNC) { - lastpid = (long) pid; - } else if (!jobtab[thisjob].stty_in_env && nonempty(cmd->vars)) { + lastpid = (zlong) pid; + } else if (!jobtab[thisjob].stty_in_env && varspc) { /* search for STTY=... */ - while (nonempty(cmd->vars)) - if (!strcmp(((Varasg) ugetnode(cmd->vars))->name, "STTY")) { + Wordcode p = varspc; + wordcode ac; + + while (wc_code(ac = *p) == WC_ASSIGN) { + if (!strcmp(ecrawstr(state->prog, p + 1, NULL), "STTY")) { jobtab[thisjob].stty_in_env = 1; break; } + p += (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR ? + 3 : WC_ASSIGN_NUM(ac) + 2); + } } addproc(pid, text); return; } /* pid == 0 */ close(synch[0]); - entersubsh(how, type != SUBSH && !(how & Z_ASYNC) ? 2 : 1, 0); + entersubsh(how, (type != WC_SUBSH) && !(how & Z_ASYNC) ? 2 : 1, 0); close(synch[1]); forked = 1; if (sigtrapped[SIGINT] & ZSIG_IGNORED) @@ -1613,13 +2000,26 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) is_exec = 1; } - if (!(cflags & BINF_NOGLOB)) - globlist(args); + if ((esglob = !(cflags & BINF_NOGLOB)) && args && htok) { + LinkList oargs = args; + globlist(args, 0); + args = oargs; + } if (errflag) { lastval = 1; goto err; } + /* Make a copy of stderr for xtrace output before redirecting */ + fflush(xtrerr); + if (isset(XTRACE) && xtrerr == stderr && + (type < WC_SUBSH || type == WC_TIMED)) { + if (!(xtrerr = fdopen(movefd(dup(fileno(stderr))), "w"))) + xtrerr = stderr; + else + fdtable[fileno(xtrerr)] = 3; + } + /* Add pipeline input/output to mnodes */ if (input) addfd(forked, save, mfds, 0, input, 0); @@ -1627,11 +2027,12 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) addfd(forked, save, mfds, 1, output, 1); /* Do process substitutions */ - spawnpipes(cmd->redir); + if (redir) + spawnpipes(redir); /* Do io redirections */ - while (nonempty(cmd->redir)) { - fn = (Redir) ugetnode(cmd->redir); + while (redir && nonempty(redir)) { + fn = (Redir) ugetnode(redir); DPUTS(fn->type == HEREDOC || fn->type == HEREDOCDASH, "BUG: unexpanded here document"); if (fn->type == INPIPE) { @@ -1649,7 +2050,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) } addfd(forked, save, mfds, fn->fd1, fn->fd2, 1); } else { - if (fn->type != HERESTR && xpandredir(fn, cmd->redir)) + if (fn->type != HERESTR && xpandredir(fn, redir)) continue; if (errflag) { closemnodes(mfds); @@ -1691,7 +2092,8 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) addfd(forked, save, mfds, fn->fd1, fil, 0); /* If this is 'exec < file', read from stdin, * * not terminal, unless `file' is a terminal. */ - if (nullexec && fn->fd1 == 0 && isset(SHINSTDIN) && interact) + if (nullexec == 1 && fn->fd1 == 0 && + isset(SHINSTDIN) && interact) init_io(); break; case CLOSE: @@ -1702,16 +2104,28 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) break; case MERGEIN: case MERGEOUT: - if(fn->fd2 < 10) + if (fn->fd2 < 10) closemn(mfds, fn->fd2); - fil = dup(fn->fd2); + if (fn->fd2 > 9 && + (fdtable[fn->fd2] || + fn->fd2 == coprocin || + fn->fd2 == coprocout)) { + fil = -1; + errno = EBADF; + } else { + int fd = fn->fd2; + if(fd == -2) + fd = (fn->type == MERGEOUT) ? coprocout : coprocin; + fil = dup(fd); + } if (fil == -1) { char fdstr[4]; closemnodes(mfds); fixfds(save); - sprintf(fdstr, "%d", fn->fd2); - zerr("%s: %e", fdstr, errno); + if (fn->fd2 != -2) + sprintf(fdstr, "%d", fn->fd2); + zerr("%s: %e", fn->fd2 == -2 ? "coprocess" : fdstr, errno); execerr(); } addfd(forked, save, mfds, fn->fd1, fil, fn->type == MERGEOUT); @@ -1748,37 +2162,43 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) /* We are done with redirection. close the mnodes, * * spawning tee/cat processes as necessary. */ for (i = 0; i < 10; i++) - closemn(mfds, i); + if (mfds[i] && mfds[i]->ct >= 2) + closemn(mfds, i); if (nullexec) { - for (i = 0; i < 10; i++) - if (save[i] != -2) - zclose(save[i]); + if (nullexec == 1) { + /* + * If nullexec is 1 we specifically *don't* restore the original + * fd's before returning. + */ + for (i = 0; i < 10; i++) + if (save[i] != -2) + zclose(save[i]); + goto done; + } /* - * Here we specifically *don't* restore the original fd's - * before returning. + * If nullexec is 2, we have variables to add with the redirections + * in place. */ - return; - } - - if (isset(EXECOPT) && !errflag) { + if (varspc) + addvars(state, varspc, 0); + lastval = errflag ? errflag : cmdoutval; + if (isset(XTRACE)) { + fputc('\n', xtrerr); + fflush(xtrerr); + } + } else if (isset(EXECOPT) && !errflag) { /* * We delay the entersubsh() to here when we are exec'ing * the current shell (including a fake exec to run a builtin then * exit) in case there is an error return. */ if (is_exec) - entersubsh(how, type != SUBSH ? 2 : 1, 1); - if (type >= CURSH) { - static int (*func[]) _((Cmd)) = { - execcursh, exectime, execfuncdef, execfor, execwhile, - execrepeat, execif, execcase, execselect, execcond, - execarith, execautofn - }; - + entersubsh(how, (type != WC_SUBSH) ? 2 : 1, 1); + if (type >= WC_CURSH) { if (last1 == 1) - cmd->flags |= CFLAG_EXEC; - lastval = (func[type - CURSH]) (cmd); + do_exec = 1; + lastval = (execfuncs[type - WC_CURSH])(state, do_exec); } else if (is_builtin || is_shfunc) { LinkList restorelist = 0, removelist = 0; /* builtin or shell function */ @@ -1786,24 +2206,29 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) if (!forked && ((cflags & BINF_COMMAND) || (unset(POSIXBUILTINS) && !assign) || (isset(POSIXBUILTINS) && !is_shfunc && - !(hn->flags & BINF_PSPECIAL)))) - save_params(cmd, &restorelist, &removelist); - - if (cmd->vars) { + !(hn->flags & BINF_PSPECIAL)))) { + if (varspc) + save_params(state, varspc, &restorelist, &removelist); + else + restorelist = removelist = NULL; + } + if (varspc) { /* Export this if the command is a shell function, * but not if it's a builtin. */ - addvars(cmd->vars, is_shfunc); + addvars(state, varspc, is_shfunc); if (errflag) { - restore_params(restorelist, removelist); + if (restorelist) + restore_params(restorelist, removelist); lastval = 1; fixfds(save); - return; + goto done; } } if (is_shfunc) { /* It's a shell function */ + #ifdef PATH_DEV_FD int i; @@ -1814,7 +2239,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) if (subsh_close >= 0) zclose(subsh_close); subsh_close = -1; - execshfunc(cmd, (Shfunc) hn); + execshfunc((Shfunc) hn, args); #ifdef PATH_DEV_FD for (i = 10; i <= max_zsh_fd; i++) if (fdtable[i] > 1) @@ -1843,30 +2268,31 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) clearerr(stdout); } - if (cmd->flags & CFLAG_EXEC) { + if (do_exec) { if (subsh) _exit(lastval); /* If we are exec'ing a command, and we are not in a subshell, * * then check if we should save the history file. */ if (isset(RCS) && interact && !nohistsave) - savehistfile(getsparam("HISTFILE"), 1, isset(APPENDHISTORY) ? 3 : 0); + savehistfile(NULL, 1, HFILE_USE_OPTIONS); exit(lastval); } - - restore_params(restorelist, removelist); + if (restorelist) + restore_params(restorelist, removelist); } else { - if (cmd->flags & CFLAG_EXEC) { + if (!forked) setiparam("SHLVL", --shlvl); + if (do_exec) { /* If we are exec'ing a command, and we are not * * in a subshell, then save the history file. */ if (!subsh && isset(RCS) && interact && !nohistsave) - savehistfile(getsparam("HISTFILE"), 1, isset(APPENDHISTORY) ? 3 : 0); + savehistfile(NULL, 1, HFILE_USE_OPTIONS); } - if (type == SIMPLE) { - if (cmd->vars) { - addvars(cmd->vars, -1); + if (type == WC_SIMPLE) { + if (varspc) { + addvars(state, varspc, -1); if (errflag) _exit(1); } @@ -1881,7 +2307,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) #endif execute((Cmdnam) hn, cflags & BINF_DASH); } else { /* ( ... ) */ - DPUTS(cmd->vars && nonempty(cmd->vars), + DPUTS(varspc, "BUG: assigment before complex command"); list_pipe = 0; if (subsh_close >= 0) @@ -1889,7 +2315,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) subsh_close = -1; /* If we're forked (and we should be), no need to return */ DPUTS(last1 != 1 && !forked, "BUG: not exiting?"); - execlist(cmd->u.list, 0, 1); + execlist(state, 0, 1); } } } @@ -1898,54 +2324,48 @@ execcmd(Cmd cmd, int input, int output, int how, int last1) if (forked) _exit(lastval); fixfds(save); + + done: + if (xtrerr != oxtrerr) { + fil = fileno(xtrerr); + fclose(xtrerr); + xtrerr = oxtrerr; + zclose(fil); + } } /* Arrange to have variables restored. */ /**/ static void -save_params(Cmd cmd, LinkList *restore_p, LinkList *remove_p) +save_params(Estate state, Wordcode pc, LinkList *restore_p, LinkList *remove_p) { Param pm; - LinkNode node; char *s; - - MUSTUSEHEAP("save_params()"); + wordcode ac; *restore_p = newlinklist(); *remove_p = newlinklist(); - for (node = firstnode(cmd->vars); node; incnode(node)) { - s = ((Varasg) getdata(node))->name; + while (wc_code(ac = *pc) == WC_ASSIGN) { + s = ecrawstr(state->prog, pc + 1, NULL); if ((pm = (Param) paramtab->getnode(paramtab, s))) { if (!(pm->flags & PM_SPECIAL)) { paramtab->removenode(paramtab, s); } else if (!(pm->flags & PM_READONLY) && (unset(RESTRICTED) || !(pm->flags & PM_RESTRICTED))) { - Param tpm = (Param) alloc(sizeof *tpm); - + Param tpm = (Param) zhalloc(sizeof *tpm); tpm->nam = s; - tpm->flags = pm->flags; - switch (PM_TYPE(pm->flags)) { - case PM_SCALAR: - tpm->u.str = ztrdup(pm->gets.cfn(pm)); - break; - case PM_INTEGER: - tpm->u.val = pm->gets.ifn(pm); - break; - case PM_ARRAY: - PERMALLOC { - tpm->u.arr = arrdup(pm->gets.afn(pm)); - } LASTALLOC; - break; - } + copyparam(tpm, pm, 1); pm = tpm; } addlinknode(*remove_p, s); addlinknode(*restore_p, pm); - } else { + } else addlinknode(*remove_p, s); - } + + pc += (WC_ASSIGN_TYPE(ac) == WC_ASSIGN_SCALAR ? + 3 : WC_ASSIGN_NUM(ac) + 2); } } @@ -1958,14 +2378,12 @@ restore_params(LinkList restorelist, LinkList removelist) Param pm; char *s; - if (removelist) { - /* remove temporary parameters */ - while ((s = (char *) ugetnode(removelist))) { - if ((pm = (Param) paramtab->getnode(paramtab, s)) && - !(pm->flags & PM_SPECIAL)) { - pm->flags &= ~PM_READONLY; - unsetparam_pm(pm, 0, 0); - } + /* remove temporary parameters */ + while ((s = (char *) ugetnode(removelist))) { + if ((pm = (Param) paramtab->getnode(paramtab, s)) && + !(pm->flags & PM_SPECIAL)) { + pm->flags &= ~PM_READONLY; + unsetparam_pm(pm, 0, 0); } } @@ -1986,14 +2404,21 @@ restore_params(LinkList restorelist, LinkList removelist) case PM_INTEGER: tpm->sets.ifn(tpm, pm->u.val); break; + case PM_EFLOAT: + case PM_FFLOAT: + tpm->sets.ffn(tpm, pm->u.dval); + break; case PM_ARRAY: tpm->sets.afn(tpm, pm->u.arr); break; + case PM_HASHED: + tpm->sets.hfn(tpm, pm->u.hash); + break; } } else paramtab->addnode(paramtab, pm->nam, pm); - if (pm->flags & PM_EXPORTED) - pm->env = addenv(pm->nam, getsparam(pm->nam)); + if ((pm->flags & PM_EXPORTED) && ((s = getsparam(pm->nam)))) + pm->env = addenv(pm->nam, s, pm->flags); } } } @@ -2037,18 +2462,17 @@ entersubsh(int how, int cl, int fake) } } else if (thisjob != -1 && cl) { if (jobtab[list_pipe_job].gleader && (list_pipe || list_pipe_child)) { - if (kill(jobtab[list_pipe_job].gleader, 0) == -1 || + if (killpg(jobtab[list_pipe_job].gleader, 0) == -1 || setpgrp(0L, jobtab[list_pipe_job].gleader) == -1) { jobtab[list_pipe_job].gleader = - jobtab[thisjob].gleader = mypgrp; - setpgrp(0L, mypgrp); - + jobtab[thisjob].gleader = (list_pipe_child ? mypgrp : getpid()); + setpgrp(0L, jobtab[list_pipe_job].gleader); if (how & Z_SYNC) attachtty(jobtab[thisjob].gleader); } } else if (!jobtab[thisjob].gleader || - (setpgrp(0L, jobtab[thisjob].gleader) == -1)) { + setpgrp(0L, jobtab[thisjob].gleader) == -1) { jobtab[thisjob].gleader = getpid(); if (list_pipe_job != thisjob && !jobtab[list_pipe_job].gleader) @@ -2086,7 +2510,7 @@ entersubsh(int how, int cl, int fake) /* close internal shell fds */ /**/ -void +mod_export void closem(int how) { int i; @@ -2147,8 +2571,14 @@ gethere(char *str, int typ) if (t > buf && t[-1] == '\n') t--; *t = '\0'; - if (!qt) + if (!qt) { + int ef = errflag; + parsestr(buf); + + if (!errflag) + errflag = ef; + } s = dupstring(buf); zfree(buf, bsiz); return s; @@ -2184,24 +2614,26 @@ getherestr(struct redir *fn) LinkList getoutput(char *cmd, int qt) { - List list; + Eprog prog; int pipes[2]; pid_t pid; - Cmd c; - Redir r; + Wordcode pc; - if (!(list = parse_string(cmd))) + if (!(prog = parse_string(cmd, 0))) return NULL; - if (list != &dummy_list && !list->right && !list->left->flags && - list->left->type == END && list->left->left->type == END && - (c = list->left->left->left)->type == SIMPLE && empty(c->args) && - empty(c->vars) && nonempty(c->redir) && - !nextnode(firstnode(c->redir)) && - (r = (Redir) getdata(firstnode(c->redir)))->fd1 == 0 && - r->type == READ) { + + pc = prog->prog; + if (prog != &dummy_eprog && + wc_code(pc[0]) == WC_LIST && (WC_LIST_TYPE(pc[0]) & Z_END) && + wc_code(pc[1]) == WC_SUBLIST && !WC_SUBLIST_FLAGS(pc[1]) && + WC_SUBLIST_TYPE(pc[1]) == WC_SUBLIST_END && + wc_code(pc[2]) == WC_PIPE && WC_PIPE_TYPE(pc[2]) == WC_PIPE_END && + wc_code(pc[3]) == WC_REDIR && WC_REDIR_TYPE(pc[3]) == READ && + !pc[4] && + wc_code(pc[6]) == WC_SIMPLE && !WC_SIMPLE_ARGC(pc[6])) { /* $(< word) */ int stream; - char *s = r->name; + char *s = dupstring(ecrawstr(prog, pc + 5, NULL)); singsub(&s); if (errflag) @@ -2213,7 +2645,6 @@ getoutput(char *cmd, int qt) } return readoutput(stream, qt); } - mpipe(pipes); child_block(); cmdoutval = 0; @@ -2231,18 +2662,19 @@ getoutput(char *cmd, int qt) zclose(pipes[1]); retval = readoutput(pipes[0], qt); fdtable[pipes[0]] = 0; - child_suspend(0); /* unblocks */ + waitforpid(pid); /* unblocks */ lastval = cmdoutval; return retval; } - /* pid == 0 */ child_unblock(); zclose(pipes[0]); redup(pipes[1], 1); opts[MONITOR] = 0; entersubsh(Z_SYNC, 1, 0); - execlist(list, 0, 1); + cmdpush(CS_CMDSUBST); + execode(prog, 0, 1); + cmdpop(); close(1); _exit(lastval); zerr("exit returned in child!!", NULL, 0); @@ -2253,7 +2685,7 @@ getoutput(char *cmd, int qt) /* read output of command substitution */ /**/ -static LinkList +mod_export LinkList readoutput(int in, int qt) { LinkList ret; @@ -2263,7 +2695,7 @@ readoutput(int in, int qt) fin = fdopen(in, "r"); ret = newlinklist(); - ptr = buf = (char *) ncalloc(bsiz = 64); + ptr = buf = (char *) hcalloc(bsiz = 64); while ((c = fgetc(fin)) != EOF || errno == EINTR) { if (c == EOF) { errno = 0; @@ -2276,7 +2708,7 @@ readoutput(int in, int qt) cnt++; } if (++cnt >= bsiz) { - char *pp = (char *) ncalloc(bsiz *= 2); + char *pp = (char *) hcalloc(bsiz *= 2); memcpy(pp, buf, cnt - 1); ptr = (buf = pp) + cnt - 1; @@ -2294,7 +2726,7 @@ readoutput(int in, int qt) } addlinknode(ret, buf); } else { - char **words = spacesplit(buf, 0); + char **words = spacesplit(buf, 0, 1); while (*words) { if (isset(GLOBSUBST)) @@ -2306,11 +2738,11 @@ readoutput(int in, int qt) } /**/ -static List +static Eprog parsecmd(char *cmd) { char *str; - List list; + Eprog prog; for (str = cmd + 2; *str && *str != Outpar; str++); if (!*str || cmd[1] != Inpar) { @@ -2318,11 +2750,11 @@ parsecmd(char *cmd) return NULL; } *str = '\0'; - if (str[1] || !(list = parse_string(cmd + 2))) { + if (str[1] || !(prog = parse_string(cmd + 2, 0))) { zerr("parse error in process substitution", NULL, 0); return NULL; } - return list; + return prog; } /* =(...) */ @@ -2333,22 +2765,22 @@ getoutputfile(char *cmd) { pid_t pid; char *nam; - List list; + Eprog prog; int fd; if (thisjob == -1) return NULL; - if (!(list = parsecmd(cmd))) + if (!(prog = parsecmd(cmd))) return NULL; if (!(nam = gettempname())) return NULL; nam = ztrdup(nam); - PERMALLOC { - if (!jobtab[thisjob].filelist) - jobtab[thisjob].filelist = newlinklist(); - addlinknode(jobtab[thisjob].filelist, nam); - } LASTALLOC; + + if (!jobtab[thisjob].filelist) + jobtab[thisjob].filelist = znewlinklist(); + zaddlinknode(jobtab[thisjob].filelist, nam); + child_block(); fd = open(nam, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0600); @@ -2371,7 +2803,9 @@ getoutputfile(char *cmd) redup(fd, 1); opts[MONITOR] = 0; entersubsh(Z_SYNC, 1, 0); - execlist(list, 0, 1); + cmdpush(CS_CMDSUBST); + execode(prog, 0, 1); + cmdpop(); close(1); _exit(lastval); zerr("exit returned in child!!", NULL, 0); @@ -2407,7 +2841,7 @@ getproc(char *cmd) zerr("doesn't look like your system supports FIFOs.", NULL, 0); return NULL; #else - List list; + Eprog prog; int out = *cmd == Inang; char *pnam; #ifndef PATH_DEV_FD @@ -2422,16 +2856,15 @@ getproc(char *cmd) if (!(pnam = namedpipe())) return NULL; #else - pnam = ncalloc(strlen(PATH_DEV_FD) + 6); + pnam = hcalloc(strlen(PATH_DEV_FD) + 6); #endif - if (!(list = parsecmd(cmd))) + if (!(prog = parsecmd(cmd))) return NULL; #ifndef PATH_DEV_FD - PERMALLOC { - if (!jobtab[thisjob].filelist) - jobtab[thisjob].filelist = newlinklist(); - addlinknode(jobtab[thisjob].filelist, ztrdup(pnam)); - } LASTALLOC; + if (!jobtab[thisjob].filelist) + jobtab[thisjob].filelist = znewlinklist(); + zaddlinknode(jobtab[thisjob].filelist, ztrdup(pnam)); + if (zfork()) { #else mpipe(pipes); @@ -2456,7 +2889,9 @@ getproc(char *cmd) redup(pipes[out], out); closem(0); /* this closes pipes[!out] as well */ #endif - execlist(list, 0, 1); + cmdpush(CS_CMDSUBST); + execode(prog, 0, 1); + cmdpop(); zclose(out); _exit(lastval); return NULL; @@ -2469,10 +2904,10 @@ getproc(char *cmd) static int getpipe(char *cmd) { - List list; + Eprog prog; int pipes[2], out = *cmd == Inang; - if (!(list = parsecmd(cmd))) + if (!(prog = parsecmd(cmd))) return -1; mpipe(pipes); if (zfork()) { @@ -2482,7 +2917,9 @@ getpipe(char *cmd) entersubsh(Z_ASYNC, 1, 0); redup(pipes[out], out); closem(0); /* this closes pipes[!out] as well */ - execlist(list, 0, 1); + cmdpush(CS_CMDSUBST); + execode(prog, 0, 1); + cmdpop(); _exit(lastval); return 0; } @@ -2518,26 +2955,62 @@ spawnpipes(LinkList l) } } +extern int tracingcond; + /* evaluate a [[ ... ]] */ /**/ static int -execcond(Cmd cmd) +execcond(Estate state, int do_exec) { - return !evalcond(cmd->u.cond); + int stat; + + state->pc--; + if (isset(XTRACE)) { + printprompt4(); + fprintf(xtrerr, "[["); + tracingcond++; + } + cmdpush(CS_COND); + stat = !evalcond(state); + cmdpop(); + if (isset(XTRACE)) { + fprintf(xtrerr, " ]]\n"); + fflush(xtrerr); + tracingcond--; + } + return stat; } /* evaluate a ((...)) arithmetic command */ /**/ static int -execarith(Cmd cmd) +execarith(Estate state, int do_exec) { char *e; - long val = 0; + zlong val = 0; + int htok = 0; + + if (isset(XTRACE)) { + printprompt4(); + fprintf(xtrerr, "(("); + } + cmdpush(CS_MATH); + e = ecgetstr(state, EC_DUPTOK, &htok); + if (htok) + singsub(&e); + if (isset(XTRACE)) + fprintf(xtrerr, " %s", e); + + val = mathevali(e); - while ((e = (char *) ugetnode(cmd->args))) - val = matheval(e); + cmdpop(); + + if (isset(XTRACE)) { + fprintf(xtrerr, " ))\n"); + fflush(xtrerr); + } errflag = 0; return !val; } @@ -2546,16 +3019,16 @@ execarith(Cmd cmd) /**/ static int -exectime(Cmd cmd) +exectime(Estate state, int do_exec) { int jb; jb = thisjob; - if (!cmd->u.pline) { + if (WC_TIMED_TYPE(state->pc[-1]) == WC_TIMED_EMPTY) { shelltime(); return 0; } - execpline(cmd->u.pline, Z_TIMED|Z_SYNC, 0); + execpline(state, *state->pc++, Z_TIMED|Z_SYNC, 0); thisjob = jb; return lastval; } @@ -2564,32 +3037,74 @@ exectime(Cmd cmd) /**/ static int -execfuncdef(Cmd cmd) +execfuncdef(Estate state, int do_exec) { Shfunc shf; char *s; - int signum; - - PERMALLOC { - while ((s = (char *) ugetnode(cmd->args))) { - shf = (Shfunc) zalloc(sizeof *shf); - shf->funcdef = (List) dupstruct(cmd->u.list); - shf->flags = 0; - - /* is this shell function a signal trap? */ - if (!strncmp(s, "TRAP", 4) && (signum = getsignum(s + 4)) != -1) { - if (settrap(signum, shf->funcdef)) { - freestruct(shf->funcdef); - zfree(shf, sizeof *shf); - LASTALLOC_RETURN 1; - } - sigtrapped[signum] |= ZSIG_FUNC; + int signum, nprg, sbeg, nstrs, npats, len, plen, i, htok = 0; + Wordcode beg = state->pc, end; + Eprog prog; + Patprog *pp; + LinkList names; + + end = beg + WC_FUNCDEF_SKIP(state->pc[-1]); + names = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok); + nprg = end - beg; + sbeg = *state->pc++; + nstrs = *state->pc++; + npats = *state->pc++; + + nprg = (end - state->pc); + plen = nprg * sizeof(wordcode); + len = plen + (npats * sizeof(Patprog)) + nstrs; + + if (htok) + execsubst(names); + + while ((s = (char *) ugetnode(names))) { + prog = (Eprog) zalloc(sizeof(*prog)); + prog->npats = npats; + prog->len = len; + if (state->prog->dump) { + prog->flags = EF_MAP; + incrdumpcount(state->prog->dump); + prog->pats = pp = (Patprog *) zalloc(npats * sizeof(Patprog)); + prog->prog = state->pc; + prog->strs = state->strs + sbeg; + prog->dump = state->prog->dump; + } else { + prog->flags = EF_REAL; + prog->pats = pp = (Patprog *) zalloc(len); + prog->prog = (Wordcode) (prog->pats + npats); + prog->strs = (char *) (prog->prog + nprg); + prog->dump = NULL; + memcpy(prog->prog, state->pc, plen); + memcpy(prog->strs, state->strs + sbeg, nstrs); + } + for (i = npats; i--; pp++) + *pp = dummy_patprog1; + prog->shf = NULL; + + shf = (Shfunc) zalloc(sizeof(*shf)); + shf->funcdef = prog; + shf->flags = 0; + + /* is this shell function a signal trap? */ + if (!strncmp(s, "TRAP", 4) && + (signum = getsignum(s + 4)) != -1) { + if (settrap(signum, shf->funcdef)) { + freeeprog(shf->funcdef); + zfree(shf, sizeof(*shf)); + state->pc = end; + return 1; } - shfunctab->addnode(shfunctab, ztrdup(s), shf); + sigtrapped[signum] |= ZSIG_FUNC; } - } LASTALLOC; - if(isset(HISTNOFUNCTIONS)) + shfunctab->addnode(shfunctab, ztrdup(s), shf); + } + if (isset(HISTNOFUNCTIONS)) remhist(); + state->pc = end; return 0; } @@ -2597,22 +3112,45 @@ execfuncdef(Cmd cmd) /**/ static void -execshfunc(Cmd cmd, Shfunc shf) +execshfunc(Shfunc shf, LinkList args) { LinkList last_file_list = NULL; + unsigned char *ocs; + int ocsp, osfc; if (errflag) return; - if (!list_pipe) { + if (!list_pipe && thisjob != list_pipe_job) { /* Without this deletejob the process table * * would be filled by a recursive function. */ last_file_list = jobtab[thisjob].filelist; jobtab[thisjob].filelist = NULL; deletejob(jobtab + thisjob); } - - doshfunc(shf->funcdef, cmd->args, shf->flags, 0); + if (isset(XTRACE)) { + LinkNode lptr; + printprompt4(); + if (args) + for (lptr = firstnode(args); lptr; incnode(lptr)) { + if (lptr != firstnode(args)) + fputc(' ', xtrerr); + fprintf(xtrerr, "%s", (char *)getdata(lptr)); + } + fputc('\n', xtrerr); + fflush(xtrerr); + } + ocs = cmdstack; + ocsp = cmdsp; + cmdstack = (unsigned char *) zalloc(CMDSTACKSZ); + cmdsp = 0; + if ((osfc = sfcontext) == SFC_NONE) + sfcontext = SFC_DIRECT; + doshfunc(shf->nam, shf->funcdef, args, shf->flags, 0); + sfcontext = osfc; + free(cmdstack); + cmdstack = ocs; + cmdsp = ocsp; if (!list_pipe) deletefilelist(last_file_list); @@ -2626,168 +3164,244 @@ execshfunc(Cmd cmd, Shfunc shf) /**/ static int -execautofn(Cmd cmd) +execautofn(Estate state, int do_exec) { - Shfunc shf = cmd->u.autofn->shf; - List l = getfpfunc(shf->nam); - if(l == &dummy_list) { - zerr("%s: function definition file not found", shf->nam, 0); + Shfunc shf; + char *oldscriptname; + + if (!(shf = loadautofn(state->prog->shf, 1, 0))) return 1; + + oldscriptname = scriptname; + scriptname = dupstring(state->prog->shf->nam); + execode(shf->funcdef, 1, 0); + scriptname = oldscriptname; + + return lastval; +} + +/**/ +Shfunc +loadautofn(Shfunc shf, int fksh, int autol) +{ + int noalias = noaliases, ksh = 1; + Eprog prog; + + pushheap(); + + noaliases = (shf->flags & PM_UNALIASED); + prog = getfpfunc(shf->nam, &ksh); + noaliases = noalias; + + if (ksh == 1) + ksh = fksh; + + if (prog == &dummy_eprog) { + zerr("%s: function definition file not found", shf->nam, 0); + popheap(); + return NULL; } - if(isset(KSHAUTOLOAD)) { - VARARR(char, n, strlen(shf->nam) + 1); - strcpy(n, shf->nam); - execlist(l, 1, 0); - shf = (Shfunc) shfunctab->getnode(shfunctab, n); - if(!shf || (shf->flags & PM_UNDEFINED)) { - zerr("%s: function not defined by file", n, 0); - return 1; + if (!prog) + prog = &dummy_eprog; + if (ksh == 2 || (ksh == 1 && isset(KSHAUTOLOAD))) { + if (autol) { + prog->flags |= EF_RUN; + + freeeprog(shf->funcdef); + if (prog->flags & EF_MAP) + shf->funcdef = prog; + else + shf->funcdef = dupeprog(prog, 0); + shf->flags &= ~PM_UNDEFINED; + } else { + VARARR(char, n, strlen(shf->nam) + 1); + strcpy(n, shf->nam); + execode(prog, 1, 0); + shf = (Shfunc) shfunctab->getnode(shfunctab, n); + if (!shf || (shf->flags & PM_UNDEFINED)) { + zerr("%s: function not defined by file", n, 0); + popheap(); + return NULL; + } } } else { - freestruct(shf->funcdef); - PERMALLOC { - shf->funcdef = dupstruct(stripkshdef(l, shf->nam)); - } LASTALLOC; + freeeprog(shf->funcdef); + if (prog->flags & EF_MAP) + shf->funcdef = stripkshdef(prog, shf->nam); + else + shf->funcdef = dupeprog(stripkshdef(prog, shf->nam), 0); shf->flags &= ~PM_UNDEFINED; } - HEAPALLOC { - execlist(dupstruct(shf->funcdef), 1, 0); - } LASTALLOC; - return lastval; + popheap(); + + return shf; } /* execute a shell function */ /**/ -void -doshfunc(List list, LinkList doshargs, int flags, int noreturnval) +mod_export void +doshfunc(char *name, Eprog prog, LinkList doshargs, int flags, int noreturnval) /* If noreturnval is nonzero, then reset the current return * * value (lastval) to its value before the shell function * * was executed. */ { - char **tab, **x, *oargv0 = NULL; - int xexittr, newexittr, oldzoptind, oldlastval; - char *ou; - void *xexitfn, *newexitfn; - char saveopts[OPT_SIZE]; - int obreaks = breaks; - - HEAPALLOC { - pushheap(); - if (trapreturn < 0) - trapreturn--; - oldlastval = lastval; - xexittr = sigtrapped[SIGEXIT]; - if (xexittr & ZSIG_FUNC) - xexitfn = shfunctab->removenode(shfunctab, "TRAPEXIT"); - else - xexitfn = sigfuncs[SIGEXIT]; - sigtrapped[SIGEXIT] = 0; - sigfuncs[SIGEXIT] = NULL; - tab = pparams; - oldzoptind = zoptind; - zoptind = 1; - - /* We need to save the current options even if LOCALOPTIONS is * - * not currently set. That's because if it gets set in the * - * function we need to restore the original options on exit. */ - memcpy(saveopts, opts, sizeof(opts)); - - if (flags & PM_TAGGED) - opts[XTRACE] = 1; - opts[PRINTEXITVALUE] = 0; - if (doshargs) { - LinkNode node; - - node = doshargs->first; - pparams = x = (char **) zcalloc(((sizeof *x) * (1 + countlinknodes(doshargs)))); - if (isset(FUNCTIONARGZERO)) { - oargv0 = argzero; - argzero = ztrdup((char *) node->dat); - } - node = node->next; - for (; node; node = node->next, x++) - *x = ztrdup((char *) node->dat); - } else { - pparams = (char **) zcalloc(sizeof *pparams); - if (isset(FUNCTIONARGZERO)) { - oargv0 = argzero; - argzero = ztrdup(argzero); - } + char **tab, **x, *oargv0; + int oldzoptind, oldlastval, oldoptcind; + char saveopts[OPT_SIZE], *oldscriptname = NULL, *fname = dupstring(name); + int obreaks; + struct funcstack fstack; + + pushheap(); + + oargv0 = NULL; + obreaks = breaks;; + if (trapreturn < 0) + trapreturn--; + oldlastval = lastval; + + starttrapscope(); + + tab = pparams; + if (!(flags & PM_UNDEFINED)) { + oldscriptname = scriptname; + scriptname = dupstring(name); + } + oldzoptind = zoptind; + zoptind = 1; + oldoptcind = optcind; + optcind = 0; + + /* We need to save the current options even if LOCALOPTIONS is * + * not currently set. That's because if it gets set in the * + * function we need to restore the original options on exit. */ + memcpy(saveopts, opts, sizeof(opts)); + + if (flags & PM_TAGGED) + opts[XTRACE] = 1; + opts[PRINTEXITVALUE] = 0; + if (doshargs) { + LinkNode node; + + node = doshargs->first; + pparams = x = (char **) zcalloc(((sizeof *x) * + (1 + countlinknodes(doshargs)))); + if (isset(FUNCTIONARGZERO)) { + oargv0 = argzero; + argzero = ztrdup((char *) node->dat); } - startparamscope(); - ou = underscore; - underscore = ztrdup(underscore); - execlist(dupstruct(list), 1, 0); - zsfree(underscore); - underscore = ou; - endparamscope(); - - if (retflag) { - retflag = 0; - breaks = obreaks; - } - freearray(pparams); - if (oargv0) { - zsfree(argzero); - argzero = oargv0; - } - zoptind = oldzoptind; - pparams = tab; - - if (isset(LOCALOPTIONS)) { - /* restore all shell options except PRIVILEGED and RESTRICTED */ - saveopts[PRIVILEGED] = opts[PRIVILEGED]; - saveopts[RESTRICTED] = opts[RESTRICTED]; - memcpy(opts, saveopts, sizeof(opts)); - } else { - /* just restore a couple. */ - opts[XTRACE] = saveopts[XTRACE]; - opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE]; - opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS]; + node = node->next; + for (; node; node = node->next, x++) + *x = ztrdup((char *) node->dat); + } else { + pparams = (char **) zcalloc(sizeof *pparams); + if (isset(FUNCTIONARGZERO)) { + oargv0 = argzero; + argzero = ztrdup(argzero); } + } + fstack.name = dupstring(name); + fstack.prev = funcstack; + funcstack = &fstack; - /* - * The trap '...' EXIT runs in the environment of the caller, - * so remember it here but run it after resetting the - * traps for the parent. - */ - newexittr = sigtrapped[SIGEXIT]; - newexitfn = sigfuncs[SIGEXIT]; - if (newexittr & ZSIG_FUNC) - shfunctab->removenode(shfunctab, "TRAPEXIT"); - - sigtrapped[SIGEXIT] = xexittr; - if (xexittr & ZSIG_FUNC) { - shfunctab->addnode(shfunctab, ztrdup("TRAPEXIT"), xexitfn); - sigfuncs[SIGEXIT] = ((Shfunc) xexitfn)->funcdef; - } else - sigfuncs[SIGEXIT] = (List) xexitfn; + if (prog->flags & EF_RUN) { + Shfunc shf; + + runshfunc(prog, NULL, fstack.name); - if (newexitfn) { - dotrapargs(SIGEXIT, &newexittr, newexitfn); - freestruct(newexitfn); + prog->flags &= ~EF_RUN; + + if (!(shf = (Shfunc) shfunctab->getnode(shfunctab, + (name = fname)))) { + zerr("%s: function not defined by file", name, 0); + if (!noreturnval) + lastval = 1; + popheap(); + return; } + prog = shf->funcdef; + } + runshfunc(prog, wrappers, fstack.name); + funcstack = fstack.prev; + if (retflag) { + retflag = 0; + breaks = obreaks; + } + freearray(pparams); + if (oargv0) { + zsfree(argzero); + argzero = oargv0; + } + pparams = tab; + optcind = oldoptcind; + zoptind = oldzoptind; + if (oldscriptname) + scriptname = oldscriptname; + + if (isset(LOCALOPTIONS)) { + /* restore all shell options except PRIVILEGED and RESTRICTED */ + saveopts[PRIVILEGED] = opts[PRIVILEGED]; + saveopts[RESTRICTED] = opts[RESTRICTED]; + memcpy(opts, saveopts, sizeof(opts)); + } else { + /* just restore a couple. */ + opts[XTRACE] = saveopts[XTRACE]; + opts[PRINTEXITVALUE] = saveopts[PRINTEXITVALUE]; + opts[LOCALOPTIONS] = saveopts[LOCALOPTIONS]; + } - if (trapreturn < -1) - trapreturn++; - if (noreturnval) - lastval = oldlastval; - popheap(); - } LASTALLOC; + endtrapscope(); + + if (trapreturn < -1) + trapreturn++; + if (noreturnval) + lastval = oldlastval; + popheap(); +} + +/* This finally executes a shell function and any function wrappers * + * defined by modules. This works by calling the wrapper function which * + * in turn has to call back this function with the arguments it gets. */ + +/**/ +mod_export void +runshfunc(Eprog prog, FuncWrap wrap, char *name) +{ + int cont; + VARARR(char, ou, underscoreused); + + memcpy(ou, underscore, underscoreused); + + while (wrap) { + wrap->module->wrapper++; + cont = wrap->handler(prog, wrap->next, name); + wrap->module->wrapper--; + + if (!wrap->module->wrapper && + (wrap->module->flags & MOD_UNLOAD)) + unload_module(wrap->module, NULL); + + if (!cont) + return; + wrap = wrap->next; + } + startparamscope(); + execode(prog, 1, 0); + setunderscore(ou); + endparamscope(); } /* Search fpath for an undefined function. Finds the file, and returns the * * list of its contents. */ /**/ -static List -getfpfunc(char *s) +Eprog +getfpfunc(char *s, int *ksh) { char **pp, buf[PATH_MAX]; off_t len; char *d; - List r; + Eprog r; int fd; pp = fpath; @@ -2798,29 +3412,36 @@ getfpfunc(char *s) sprintf(buf, "%s/%s", *pp, s); else strcpy(buf, s); + if ((r = try_dump_file(*pp, s, buf, ksh))) + return r; unmetafy(buf, NULL); if (!access(buf, R_OK) && (fd = open(buf, O_RDONLY | O_NOCTTY)) != -1) { if ((len = lseek(fd, 0, 2)) != -1) { + d = (char *) zalloc(len + 1); lseek(fd, 0, 0); - d = (char *) zcalloc(len + 1); if (read(fd, d, len) == len) { + char *oldscriptname = scriptname; + close(fd); + d[len] = '\0'; d = metafy(d, len, META_REALLOC); - HEAPALLOC { - r = parse_string(d); - } LASTALLOC; + + scriptname = dupstring(s); + r = parse_string(d, 1); + scriptname = oldscriptname; + zfree(d, len + 1); + return r; - } else { - zfree(d, len + 1); + } else close(fd); - } - } else { + + zfree(d, len + 1); + } else close(fd); - } } } - return &dummy_list; + return &dummy_eprog; } /* Handle the most common type of ksh-style autoloading, when doing a * @@ -2830,29 +3451,60 @@ getfpfunc(char *s) * contents of that definition. Otherwise, use the entire file. */ /**/ -static List -stripkshdef(List l, char *name) +Eprog +stripkshdef(Eprog prog, char *name) { - Sublist s; - Pline p; - Cmd c; - if(!l) + Wordcode pc = prog->prog; + wordcode code; + + if (!prog) return NULL; - if(l->type != Z_SYNC || l->right) - return l; - s = l->left; - if(s->flags || s->right) - return l; - p = s->left; - if(p->right) - return l; - c = p->left; - if(c->type != FUNCDEF || c->flags || - nonempty(c->redir) || nonempty(c->vars) || - empty(c->args) || lastnode(c->args) != firstnode(c->args) || - strcmp(name, peekfirst(c->args))) - return l; - return c->u.list; + code = *pc++; + if (wc_code(code) != WC_LIST || + (WC_LIST_TYPE(code) & (Z_SYNC|Z_END|Z_SIMPLE)) != (Z_SYNC|Z_END|Z_SIMPLE)) + return prog; + pc++; + code = *pc++; + if (wc_code(code) != WC_FUNCDEF || + *pc != 1 || strcmp(name, ecrawstr(prog, pc + 1, NULL))) + return prog; + + { + Eprog ret; + Wordcode end = pc + WC_FUNCDEF_SKIP(code); + int sbeg = pc[2], nstrs = pc[3], nprg, npats = pc[4], plen, len, i; + Patprog *pp; + + pc += 5; + + nprg = end - pc; + plen = nprg * sizeof(wordcode); + len = plen + (npats * sizeof(Patprog)) + nstrs; + + if (prog->flags & EF_MAP) { + ret = prog; + free(prog->pats); + ret->pats = pp = (Patprog *) zalloc(npats * sizeof(Patprog)); + ret->prog = pc; + ret->strs = prog->strs + sbeg; + } else { + ret = (Eprog) zhalloc(sizeof(*ret)); + ret->flags = EF_HEAP; + ret->pats = pp = (Patprog *) zhalloc(len); + ret->prog = (Wordcode) (ret->pats + npats); + ret->strs = (char *) (ret->prog + nprg); + memcpy(ret->prog, pc, plen); + memcpy(ret->strs, prog->strs + sbeg, nstrs); + ret->dump = NULL; + } + ret->len = len; + ret->npats = npats; + for (i = npats; i--; pp++) + *pp = dummy_patprog1; + ret->shf = NULL; + + return ret; + } } /* check to see if AUTOCD applies here */ @@ -2901,9 +3553,26 @@ static int cancd2(char *s) { struct stat buf; - char *us = unmeta(s); + char *us, *us2 = NULL; + int ret; - return !(access(us, X_OK) || stat(us, &buf) || !S_ISDIR(buf.st_mode)); + /* + * If CHASEDOTS and CHASELINKS are not set, we want to rationalize the + * path by removing foo/.. combinations in the logical rather than + * the physical path. If either is set, we test the physical path. + */ + if (!isset(CHASEDOTS) && !isset(CHASELINKS)) { + if (*s != '/') + us = tricat(pwd[1] ? pwd : "", "/", s); + else + us = ztrdup(s); + fixdir(us2 = us); + } else + us = unmeta(s); + ret = !(access(us, X_OK) || stat(us, &buf) || !S_ISDIR(buf.st_mode)); + if (us2) + free(us2); + return ret; } /**/ @@ -2928,8 +3597,7 @@ execsave(void) es->trapreturn = trapreturn; es->noerrs = noerrs; es->subsh_close = subsh_close; - es->underscore = underscore; - underscore = ztrdup(underscore); + es->underscore = ztrdup(underscore); es->next = exstack; exstack = es; noerrs = cmdoutpid = 0; @@ -2957,8 +3625,8 @@ execrestore(void) trapreturn = exstack->trapreturn; noerrs = exstack->noerrs; subsh_close = exstack->subsh_close; - zsfree(underscore); - underscore = exstack->underscore; + setunderscore(exstack->underscore); + zsfree(exstack->underscore); en = exstack->next; free(exstack); exstack = en; |