summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--Src/signals.c53
-rw-r--r--Test/C03traps.ztst16
3 files changed, 57 insertions, 18 deletions
diff --git a/ChangeLog b/ChangeLog
index c83bfeac3..c95caa86e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2004-03-10 Peter Stephenson <pws@csr.com>
+
+ * 19575: Src/signals.c, Test/C03traps.ztst: Fix the problem
+ that trap '...' EXIT overrode the exit status of the function
+ it was in.
+
2004-03-08 Clint Adams <clint@zsh.org>
* 19566: Doc/Zsh/params.yo: change associative array
diff --git a/Src/signals.c b/Src/signals.c
index 6863421fe..affb5379b 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -931,6 +931,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
char *name, num[4];
int trapret = 0;
int obreaks = breaks;
+ int isfunc;
/* if signal is being ignored or the trap function *
* is NULL, then return *
@@ -948,16 +949,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
*sigtr |= ZSIG_IGNORED;
lexsave();
- if (sig != SIGEXIT && sig != SIGDEBUG) {
- /*
- * SIGEXIT and SIGDEBUG are always run synchronously, so we don't
- * need to save and restore the state.
- *
- * Do we actually need this at all now we queue signals
- * for handling in places where they won't cause trouble?
- */
- execsave();
- }
+ execsave();
breaks = 0;
runhookdef(BEFORETRAPHOOK, NULL);
if (*sigtr & ZSIG_FUNC) {
@@ -970,27 +962,52 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
sprintf(num, "%d", sig);
zaddlinknode(args, num);
- trapreturn = -1;
+ trapreturn = -1; /* incremented by doshfunc */
sfcontext = SFC_SIGNAL;
doshfunc(name, sigfn, args, 0, 1);
sfcontext = osc;
freelinklist(args, (FreeFunc) NULL);
zsfree(name);
- } else
+
+ isfunc = 1;
+ } else {
+ trapreturn = -2; /* not incremented, used at current level */
+
execode(sigfn, 1, 0);
+
+ isfunc = 0;
+ }
runhookdef(AFTERTRAPHOOK, NULL);
- if (trapreturn > 0)
+ if (trapreturn > 0 && isfunc) {
+ /*
+ * Context was its own function. We propagate the return
+ * value specially. Return value zero means don't do
+ * anything special, so don't handle it.
+ */
trapret = trapreturn;
- else if (errflag)
+ } else if (trapreturn >= 0 && !isfunc) {
+ /*
+ * Context was an inline trap. If an explicit return value
+ * was used, we need to set `lastval'. Otherwise we use the
+ * value restored by execrestore. In this case, all return
+ * values indicate an explicit return from the current function,
+ * so always handle specially. trapreturn is always restored by
+ * execrestore.
+ */
+ trapret = trapreturn + 1;
+ } else if (errflag)
trapret = 1;
- if (sig != SIGEXIT && sig != SIGDEBUG)
- execrestore();
+ execrestore();
lexrestore();
if (trapret > 0) {
- breaks = loops;
- errflag = 1;
+ if (isfunc) {
+ breaks = loops;
+ errflag = 1;
+ } else {
+ lastval = trapret-1;
+ }
} else {
breaks += obreaks;
if (breaks > loops)
diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst
index 81ef38a30..26ba73da5 100644
--- a/Test/C03traps.ztst
+++ b/Test/C03traps.ztst
@@ -196,3 +196,19 @@
f
functions TRAPWINCH
1:Unsetting ordinary traps with localtraps.
+
+#
+# Returns from within traps are a perennial problem.
+# The following two apply to returns in and around standard
+# ksh-style traps. The intention is that a return value from
+# within the function is preserved (i.e. statuses set by the trap
+# are ignored) unless the trap explicitly executes `return', which makes
+# it return from the enclosing function.
+#
+ fn() { trap 'true' EXIT; return 1; }
+ fn
+1: ksh-style EXIT traps preserve return value
+
+ inner() { trap 'return 3' EXIT; return 2: }
+ outer() { inner; return 1; }
+3: ksh-style EXIT traps can force return status of enclosing function