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:
parent
ea0ddb0fc6
commit
4fec788fa5
52 changed files with 1568 additions and 340 deletions
98
Src/exec.c
98
Src/exec.c
|
|
@ -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 : "");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue