summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--Src/builtin.c23
-rw-r--r--Src/exec.c5
-rw-r--r--Src/signals.c11
-rw-r--r--Test/C03traps.ztst21
5 files changed, 60 insertions, 8 deletions
diff --git a/ChangeLog b/ChangeLog
index fb0d379e2..243957455 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/Src/builtin.c b/Src/builtin.c
index b2e552db7..063644efb 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -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.
diff --git a/Src/exec.c b/Src/exec.c
index 978a32d20..e0fc54445 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -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;
diff --git a/Src/signals.c b/Src/signals.c
index 68a7ae34d..cad40f4eb 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -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);
}
diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst
index 7bc0b486d..759401225 100644
--- a/Test/C03traps.ztst
+++ b/Test/C03traps.ztst
@@ -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