1
0
Fork 0
mirror of git://git.code.sf.net/p/zsh/code synced 2024-05-31 12:26:03 +02:00

41012: Fix premature exit from nested function in EXIT trap.

Also add check so we don't delay an exit if we were already in
an EXIT trap for the main shell, as we should in that case leave
immediately.
This commit is contained in:
Peter Stephenson 2017-04-27 18:56:18 +01:00
parent 48b0daf3d4
commit d7110d8f01
5 changed files with 60 additions and 8 deletions

View File

@ -1,3 +1,11 @@
2017-04-27 Peter Stephenson <p.w.stephenson@ntlworld.com>
* 41012: Src/builtin.c, Src/exec.c, Src/signals.c,
Test/C03traps.ztst: Fix early exit from nested functions in EXIT
trap. Drive-by fix of testing for need to exit if exiting when
already in EXIT trap for main shell --- we should just leave
immediately.
2017-04-27 Peter Stephenson <p.stephenson@samsung.com>
* 41016: Test/A01grammar.ztst: test that quoted precommand

View File

@ -5500,7 +5500,7 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func)
}
/*FALLTHROUGH*/
case BIN_EXIT:
if (locallevel > forklevel) {
if (locallevel > forklevel && shell_exiting != -1) {
/*
* We don't exit directly from functions to allow tidying
* up, in particular EXIT traps. We still need to perform
@ -5509,6 +5509,9 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func)
*
* If we are forked, we exit the shell at the function depth
* at which we became a subshell, hence the comparison.
*
* If we are already exiting... give this all up as
* a bad job.
*/
if (stopmsg || (zexit(0,2), !stopmsg)) {
retflag = 1;
@ -5555,6 +5558,14 @@ checkjobs(void)
}
}
/*
* -1 if the shell is already committed to exit.
* positive if zexit() was already called.
*/
/**/
int shell_exiting;
/* exit the shell. val is the return value of the shell. *
* from_where is
* 1 if zexit is called because of a signal
@ -5566,10 +5577,8 @@ checkjobs(void)
mod_export void
zexit(int val, int from_where)
{
static int in_exit;
/* Don't do anything recursively: see below */
if (in_exit == -1)
if (shell_exiting == -1)
return;
if (isset(MONITOR) && !stopmsg && from_where != 1) {
@ -5582,14 +5591,14 @@ zexit(int val, int from_where)
}
}
/* Positive in_exit means we have been here before */
if (from_where == 2 || (in_exit++ && from_where))
if (from_where == 2 || (shell_exiting++ && from_where))
return;
/*
* We're now committed to exiting. Set in_exit to -1 to
* We're now committed to exiting. Set shell_exiting to -1 to
* indicate we shouldn't do any recursive processing.
*/
in_exit = -1;
shell_exiting = -1;
/*
* We want to do all remaining processing regardless of preceding
* errors, even user interrupts.

View File

@ -5688,8 +5688,11 @@ doshfunc(Shfunc shfunc, LinkList doshargs, int noreturnval)
* the only likely case where we need that second test is
* when we have an "always" block. The endparamscope() has
* already happened, hence the "+1" here.
*
* If we are in an exit trap, finish it first... we wouldn't set
* exit_pending if we were already in one.
*/
if (exit_pending && exit_level >= locallevel+1) {
if (exit_pending && exit_level >= locallevel+1 && !in_exit_trap) {
if (locallevel > forklevel) {
/* Still functions to return: force them to do so. */
retflag = 1;

View File

@ -55,6 +55,11 @@ mod_export Eprog siglists[VSIGCOUNT];
/**/
mod_export int nsigtrapped;
/* Running an exit trap? */
/**/
int in_exit_trap;
/*
* Flag that exit trap has been set in POSIX mode.
* The setter's expectation is therefore that it is run
@ -1435,7 +1440,13 @@ dotrap(int sig)
dont_queue_signals();
if (sig == SIGEXIT)
++in_exit_trap;
dotrapargs(sig, sigtrapped+sig, funcprog);
if (sig == SIGEXIT)
--in_exit_trap;
restore_queue_signals(q);
}

View File

@ -756,6 +756,27 @@ F:Must be tested with a top-level script rather than source or function
>''
>hello
$ZTST_testdir/../Src/zsh -f =(<<<"
trap handler EXIT
handler() {
echoa
echo b
}
echoa() {
echo a
}
exit0() {
exit
}
main() {
exit0
}
main
")
0:No early exit from nested function in EXIT trap.
>a
>b
%clean
rm -f TRAPEXIT