about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Stephenson <pws@zsh.org>2016-02-17 10:40:55 +0000
committerPeter Stephenson <pws@zsh.org>2016-02-17 10:40:55 +0000
commitab74c86edb30fa04fda5fe7fa01e404334f7c2b0 (patch)
tree096002145a76bca90e09c2dc511008f33145acc2
parentf96a0167289d41d583b01b17482bba0641be2805 (diff)
downloadzsh-ab74c86edb30fa04fda5fe7fa01e404334f7c2b0.tar.gz
zsh-ab74c86edb30fa04fda5fe7fa01e404334f7c2b0.tar.xz
zsh-ab74c86edb30fa04fda5fe7fa01e404334f7c2b0.zip
37999: Sticky behaviour of EXIT traps.
They now have POSIX or non-POSIX behaviour based on the setting
of POSIX_TRAPS where the trap was defined, rather than where the
trap would (or would not) be executed.

Tweaks possible.
-rw-r--r--ChangeLog6
-rw-r--r--README14
-rw-r--r--Src/signals.c46
-rw-r--r--Test/C03traps.ztst20
4 files changed, 77 insertions, 9 deletions
diff --git a/ChangeLog b/ChangeLog
index 92c50cd46..fe1156f70 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2016-02-17  Peter Stephenson  <p.stephenson@samsung.com>
+
+	* 37999: README, Src/signals.c, Test/C03traps.ztst: Make
+	POSIX-style EXIT traps behave according to POSIX_TRAPS at
+	the point where the trap is set.
+
 2016-02-16  Daniel Shahaf  <d.s@daniel.shahaf.name>
 
 	* users/21256 + workers/37965: Doc/Zsh/contrib.yo,
diff --git a/README b/README
index 8b8ab5731..d5343db19 100644
--- a/README
+++ b/README
@@ -65,6 +65,20 @@ remainder of the value discarded.  This could lead to different behaviour
 if the argument contains non-numeric characters, or if the argument has
 leading zeroes and the OCTAL_ZEROES option is set.
 
+3) For some time the shell has had a POSIX_TRAPS option which determines
+whether the EXIT trap has POSIX behaviour (the trap is only run at shell
+exit) or traditional zsh behaviour (the trap is run once and discarded
+when the enclosing fuction or shell exits, whichever happens first).
+The use of this option has now been made "sticky" on the EXIT trap ---
+in other words, the setting of the option at the point where the trap is
+set now determines whether the trap has POSIX or traditional zsh
+behaviour.  This means that changing the option after the trap was set
+no longer has any effect.
+
+Other aspects of EXIT trap handling have not changed --- there is still
+only one EXIT trap at any point in a programme, so it is not generally
+useful to combine POSIX and non-POSIX behaviour in the same script.
+
 Incompatibilities between 5.0.8 and 5.2
 ---------------------------------------
 
diff --git a/Src/signals.c b/Src/signals.c
index aa0b5aaa7..32452ae4f 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -55,6 +55,15 @@ mod_export Eprog siglists[VSIGCOUNT];
 /**/
 mod_export int nsigtrapped;
 
+/*
+ * Flag that exit trap has been set in POSIX mode.
+ * The setter's expectation is therefore that it is run
+ * on programme exit, not function exit.
+ */
+
+/**/
+static int exit_trap_posix;
+
 /* Variables used by signal queueing */
 
 /**/
@@ -755,7 +764,7 @@ killjb(Job jn, int sig)
  * at once, so just use a linked list.
  */
 struct savetrap {
-    int sig, flags, local;
+    int sig, flags, local, posix;
     void *list;
 };
 
@@ -774,6 +783,7 @@ dosavetrap(int sig, int level)
     st = (struct savetrap *)zalloc(sizeof(*st));
     st->sig = sig;
     st->local = level;
+    st->posix = (sig == SIGEXIT) ? exit_trap_posix : 0;
     if ((st->flags = sigtrapped[sig]) & ZSIG_FUNC) {
 	/*
 	 * Get the old function: this assumes we haven't added
@@ -873,6 +883,10 @@ settrap(int sig, Eprog l, int flags)
      * works just the same.
      */
     sigtrapped[sig] |= (locallevel << ZSIG_SHIFT) | flags;
+    if (sig == SIGEXIT) {
+	/* Make POSIX behaviour of EXIT trap sticky */
+	exit_trap_posix = isset(POSIXTRAPS);
+    }
     unqueue_signals();
     return 0;
 }
@@ -908,7 +922,7 @@ removetrap(int sig)
      * already one at the current locallevel we just overwrite it.
      */
     if (!dontsavetrap &&
-	(sig == SIGEXIT ? !isset(POSIXTRAPS) : isset(LOCALTRAPS)) &&
+	(sig == SIGEXIT ? !exit_trap_posix : isset(LOCALTRAPS)) &&
 	locallevel &&
 	(!trapped || locallevel > (sigtrapped[sig] >> ZSIG_SHIFT)))
 	dosavetrap(sig, locallevel);
@@ -935,6 +949,8 @@ removetrap(int sig)
 #endif
              sig != SIGCHLD)
         signal_default(sig);
+    if (sig == SIGEXIT)
+	exit_trap_posix = 0;
 
     /*
      * At this point we free the appropriate structs.  If we don't
@@ -978,7 +994,7 @@ starttrapscope(void)
      * so give it the next higher one. dosavetrap() is called
      * automatically where necessary.
      */
-    if (sigtrapped[SIGEXIT] && !isset(POSIXTRAPS)) {
+    if (sigtrapped[SIGEXIT] && !exit_trap_posix) {
 	locallevel++;
 	unsettrap(SIGEXIT);
 	locallevel--;
@@ -1005,7 +1021,7 @@ endtrapscope(void)
      * Don't do this inside another trap.
      */
     if (!intrap &&
-	!isset(POSIXTRAPS) && (exittr = sigtrapped[SIGEXIT])) {
+	!exit_trap_posix && (exittr = sigtrapped[SIGEXIT])) {
 	if (exittr & ZSIG_FUNC) {
 	    exitfn = removehashnode(shfunctab, "TRAPEXIT");
 	} else {
@@ -1031,7 +1047,9 @@ endtrapscope(void)
 		if (st->flags & ZSIG_FUNC)
 		    settrap(sig, NULL, ZSIG_FUNC);
 		else
-		    settrap(sig, (Eprog) st->list, 0);
+			settrap(sig, (Eprog) st->list, 0);
+		if (sig == SIGEXIT)
+		    exit_trap_posix = st->posix;
 		dontsavetrap--;
 		/*
 		 * counting of nsigtrapped should presumably be handled
@@ -1042,16 +1060,26 @@ endtrapscope(void)
 		if ((sigtrapped[sig] = st->flags) & ZSIG_FUNC)
 		    shfunctab->addnode(shfunctab, ((Shfunc)st->list)->node.nam,
 				       (Shfunc) st->list);
-	    } else if (sigtrapped[sig])
-		unsettrap(sig);
+	    } else if (sigtrapped[sig]) {
+		/*
+		 * Don't restore the old state if someone has set a
+		 * POSIX-style exit trap --- allow this to propagate.
+		 */
+		if (sig != SIGEXIT || !exit_trap_posix)
+		    unsettrap(sig);
+	    }
 
 	    zfree(st, sizeof(*st));
 	}
     }
 
     if (exittr) {
-	if (!isset(POSIXTRAPS))
-	    dotrapargs(SIGEXIT, &exittr, exitfn);
+	/*
+	 * We already made sure this wasn't set as a POSIX exit trap.
+	 * We respect the user's intention when the trap in question
+	 * was set.
+	 */
+	dotrapargs(SIGEXIT, &exittr, exitfn);
 	if (exittr & ZSIG_FUNC)
 	    shfunctab->freenode((HashNode)exitfn);
 	else
diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst
index 4b2843a47..d8183a428 100644
--- a/Test/C03traps.ztst
+++ b/Test/C03traps.ztst
@@ -399,6 +399,26 @@
 >}
 >No, really exited
 
+   (cd ..; $ZTST_exe -fc 'unsetopt posixtraps;
+   echo start program
+   emulate sh -c '\''testfn() {
+     echo start function
+     set -o | grep posixtraps
+     trap "echo EXIT TRAP TRIGGERED" EXIT
+     echo end function
+   }'\''
+   testfn
+   echo program continuing
+   echo end of program')
+0:POSIX_TRAPS effect on EXIT trap is sticky
+>start program
+>start function
+>noposixtraps          off
+>end function
+>program continuing
+>end of program
+>EXIT TRAP TRIGGERED
+
    (set -e
     printf "a\nb\n" | while read line
     do