1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2025-11-29 03:31:01 +01:00

zsh-3.1.5-pws-19

This commit is contained in:
Tanaka Akira 1999-05-19 13:10:41 +00:00
parent ea0ddb0fc6
commit 4fec788fa5
52 changed files with 1568 additions and 340 deletions

View file

@ -213,6 +213,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;
@ -625,6 +697,13 @@ execlist(List list, int dont_change_job, int exiting)
static int donetrap;
int ret, cj;
int old_pline_level, old_list_pipe;
/*
* 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;
@ -644,6 +723,8 @@ execlist(List list, int dont_change_job, int exiting)
/* Loop through code followed by &&, ||, or end of sublist. */
while (slist) {
if (!oldnoerrexit)
noerrexit = (slist->type != END);
switch (slist->type) {
case END:
/* End of sublist; just execute, ignoring status. */
@ -687,6 +768,8 @@ execlist(List list, int dont_change_job, int exiting)
}
sublist_done:
noerrexit = oldnoerrexit;
if (sigtrapped[SIGDEBUG])
dotrap(SIGDEBUG);
@ -761,13 +844,17 @@ 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;
}
list_pipe_pid = lastwj = 0;
if (pline_level == 1)
simple_pline = (l->left->type == END);
}
lastwj = 0;
execpline2(l->left, how, opipe[0], ipipe[1], last1);
pline_level--;
if (how & Z_ASYNC) {
@ -800,6 +887,7 @@ 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);
for (pn = jobtab[jn->other].procs; pn; pn = pn->next)
@ -1588,7 +1676,7 @@ execcmd(Cmd cmd, int input, int output, int how, int last1)
closem(2);
#endif
if (how & Z_ASYNC) {
lastpid = (long) pid;
lastpid = (zlong) pid;
} else if (!jobtab[thisjob].stty_in_env && nonempty(cmd->vars)) {
/* search for STTY=... */
while (nonempty(cmd->vars))
@ -2548,7 +2636,7 @@ static int
execarith(Cmd cmd)
{
char *e;
long val = 0;
zlong val = 0;
if (isset(XTRACE))
fprintf(stderr, "%s((", prompt4 ? prompt4 : "");