about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTanaka Akira <akr@users.sourceforge.net>2000-01-07 22:31:15 +0000
committerTanaka Akira <akr@users.sourceforge.net>2000-01-07 22:31:15 +0000
commitb43fb116b89dd6e54fa17d5685351f8d03426bb9 (patch)
tree33ce240ff50eb51263bb40cbe6ea03a8d36c6f07
parent5446d2d9c3686ee58f578fa2a5198dda90a722e2 (diff)
downloadzsh-b43fb116b89dd6e54fa17d5685351f8d03426bb9.tar.gz
zsh-b43fb116b89dd6e54fa17d5685351f8d03426bb9.tar.xz
zsh-b43fb116b89dd6e54fa17d5685351f8d03426bb9.zip
zsh-workers/9267
-rw-r--r--Src/signals.c85
-rw-r--r--Test/08traps.ztst128
-rw-r--r--Test/50cd.ztst9
3 files changed, 175 insertions, 47 deletions
diff --git a/Src/signals.c b/Src/signals.c
index 1a28bf6cd..58d21e82d 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -635,11 +635,6 @@ struct savetrap {
 
 static LinkList savetraps;
 
-/* Flag to unsettrap not to free the structs, which we're keeping */
-
-/**/
-int notrapfree;
-
 /*
  * Save the current trap and unset it.
  */
@@ -651,7 +646,6 @@ dosavetrap(int sig, int level)
     st = (struct savetrap *)zalloc(sizeof(*st));
     st->sig = sig;
     st->local = level;
-    notrapfree++;
     if ((st->flags = sigtrapped[sig]) & ZSIG_FUNC) {
 	/*
 	 * Get the old function: this assumes we haven't added
@@ -667,10 +661,7 @@ dosavetrap(int sig, int level)
     } else {
 	st->list = sigfuncs[sig];
 	sigfuncs[sig] = NULL;
-	unsettrap(sig);
     }
-    sigtrapped[sig] = 0;
-    notrapfree--;
     PERMALLOC {
 	if (!savetraps)
 	    savetraps = newlinklist();
@@ -691,16 +682,13 @@ settrap(int sig, List l)
         zerr("can't trap SIG%s in interactive shells", sigs[sig], 0);
         return 1;
     }
+
     /*
-     * Note that we save the trap here even if there isn't an existing
-     * one, to aid in removing this one.  However, if there's
-     * already one at the current locallevel we just overwrite it.
+     * Call unsettrap() unconditionally, to make sure trap is saved
+     * if necessary.
      */
-    if (isset(LOCALTRAPS) && locallevel &&
-	(!sigtrapped[sig] || locallevel > (sigtrapped[sig] >> ZSIG_SHIFT))) {
-	dosavetrap(sig, locallevel);
-    } else if (sigfuncs[sig])
-	unsettrap(sig);
+    unsettrap(sig);
+
     sigfuncs[sig] = l;
     if (!l) {
 	sigtrapped[sig] = ZSIG_IGNORED;
@@ -734,23 +722,23 @@ unsettrap(int sig)
 {
     int trapped;
 
-    if (sig == -1 || !(trapped = sigtrapped[sig]) ||
-	(jobbing && (sig == SIGTTOU || sig == SIGTSTP || sig == SIGTTIN))) {
-        return;
-    }
+    if (sig == -1 ||
+	(jobbing && (sig == SIGTTOU || sig == SIGTSTP || sig == SIGTTIN)))
+	return;
+
+    trapped = sigtrapped[sig];
+    /*
+     * Note that we save the trap here even if there isn't an existing
+     * one, to aid in removing this one.  However, if there's
+     * already one at the current locallevel we just overwrite it.
+     */
     if (isset(LOCALTRAPS) && locallevel &&
-	sigtrapped[sig] && locallevel > (sigtrapped[sig] >> ZSIG_SHIFT)) {
-	/*
-	 * This calls unsettrap recursively to do any dirty work, so
-	 * make sure this bit doesn't happen:  a bit messy, but hard
-	 * to avoid.
-	 */
-	int oldlt = opts[LOCALTRAPS];
-	opts[LOCALTRAPS] = 0;
+	(!trapped || locallevel > (sigtrapped[sig] >> ZSIG_SHIFT)))
 	dosavetrap(sig, locallevel);
-	opts[LOCALTRAPS] = oldlt;
-	return;
-    }
+
+    if (!trapped)
+        return;
+
     sigtrapped[sig] = 0;
     if (sig == SIGINT && interact) {
 	/* PWS 1995/05/16:  added test for interactive, also noholdintr() *
@@ -765,14 +753,24 @@ unsettrap(int sig)
 #endif
              sig != SIGCHLD)
         signal_default(sig);
-    if (notrapfree)
-	return;
+
+    /*
+     * At this point we free the appropriate structs.  If we don't
+     * want that to happen (e.g. we are saving the trap), then
+     * either the function should already have been removed from shfunctab,
+     * or the entry in sigfuncs should have been set to NULL, and then
+     * we're laughing, in a sort of vague virtual sense.
+     */
     if (trapped & ZSIG_FUNC) {
 	char func[20];
 	HashNode hn;
 
 	sprintf(func, "TRAP%s", sigs[sig]);
-	if ((hn = shfunctab->removenode(shfunctab, func)))
+	/*
+	 * As in dosavetrap(), don't call removeshfuncnode() because
+	 * that calls back into unsettrap();
+	 */
+	if ((hn = removehashnode(shfunctab, func)))
 	    shfunctab->freenode(hn);
     } else if (sigfuncs[sig]) {
 	freestruct(sigfuncs[sig]);
@@ -786,10 +784,14 @@ starttrapscope(void)
 {
     /*
      * SIGEXIT needs to be restored at the current locallevel,
-     * so give it the next higher one.
+     * so give it the next higher one. dosavetrap() is called
+     * automatically where necessary.
      */
-    if (sigtrapped[SIGEXIT])
-	dosavetrap(SIGEXIT, locallevel+1);
+    if (sigtrapped[SIGEXIT]) {
+	locallevel++;
+	unsettrap(SIGEXIT);
+	locallevel--;
+    }
 }
 
 /*
@@ -811,14 +813,13 @@ endtrapscope(void)
      * after all the other traps have been put back.
      */
     if ((exittr = sigtrapped[SIGEXIT])) {
-	notrapfree++;
 	if (exittr & ZSIG_FUNC) {
-	    exitfn = shfunctab->removenode(shfunctab, "TRAPEXIT");
+	    exitfn = removehashnode(shfunctab, "TRAPEXIT");
 	} else {
 	    exitfn = sigfuncs[SIGEXIT];
-	    unsettrap(SIGEXIT);
+	    sigfuncs[SIGEXIT] = NULL;
 	}
-	notrapfree--;
+	unsettrap(SIGEXIT);
     }
 
     if (savetraps) {
diff --git a/Test/08traps.ztst b/Test/08traps.ztst
new file mode 100644
index 000000000..10eb48ba9
--- /dev/null
+++ b/Test/08traps.ztst
@@ -0,0 +1,128 @@
+# Tests for both trap builtin and TRAP* functions.
+
+%prep
+
+  setopt localtraps
+  mkdir traps.tmp && cd traps.tmp
+
+%test
+
+  fn1() {
+    trap 'print EXIT1' EXIT
+    fn2() { trap 'print EXIT2' EXIT; }
+    fn2
+  }
+  fn1
+0:Nested `trap ... EXIT'
+>EXIT2
+>EXIT1
+
+  fn1() {
+    TRAPEXIT() { print EXIT1; }
+    fn2() { TRAPEXIT() { print EXIT2; }; }
+    fn2
+  }
+  fn1
+0: Nested TRAPEXIT
+>EXIT2
+>EXIT1
+
+  fn1() {
+    trap 'print EXIT1' EXIT
+    fn2() { trap - EXIT; }
+    fn2
+  }
+  fn1
+0:Nested `trap - EXIT' on `trap ... EXIT'
+>EXIT1
+
+  fn1() {
+    TRAPEXIT() { print EXIT1; }
+    fn2() { trap - EXIT; }
+    fn2
+  }
+  fn1
+0:Nested `trap - EXIT' on `TRAPEXIT'
+>EXIT1
+
+  fn1() {
+    trap
+    trap 'print INT1' INT
+    fn2() { trap 'print INT2' INT; trap; }
+    trap
+    fn2
+    trap
+  }
+  fn1
+0: Nested `trap ... INT', not triggered
+>trap -- 'print INT1' INT
+>trap -- 'print INT2' INT
+>trap -- 'print INT1' INT
+
+   fn1() {
+    trap
+    TRAPINT() { print INT1; }
+    fn2() { TRAPINT() { print INT2; }; trap; }
+    trap
+    fn2
+    trap
+  }
+  fn1
+0: Nested `trap ... INT', not triggered
+>TRAPINT () {
+>	print INT1
+>}
+>TRAPINT () {
+>	print INT2
+>}
+>TRAPINT () {
+>	print INT1
+>}
+
+  fn1() {
+    trap 'print INT1' INT
+    fn2() { trap - INT; trap; }
+    trap
+    fn2
+    trap
+  }
+  fn1
+0: Nested `trap - INT' on untriggered `trap ... INT'
+>trap -- 'print INT1' INT
+>trap -- 'print INT1' INT
+
+# Testing the triggering of traps here is very unpleasant.
+# The delays are attempts to avoid race conditions, though there is
+# no guarantee that they will work.  Note the subtlety that the
+# `sleep' in the function which receives the trap does *not* get the
+# signal, only the parent shell, which is waiting for a SIGCHILD.
+# (At least, that's what I think is happening.) Thus we have to wait at
+# least the full two seconds to make sure we have got the output from the
+# execution of the trap.
+
+  print 'This test takes at least three seconds...' >&8
+  fn1() {
+    trap 'print TERM1' TERM
+    fn2() { trap 'print TERM2; return 1' TERM; sleep 2; }
+    fn2 &
+    sleep 1
+    kill -TERM $!
+    sleep 2
+  }
+  fn1
+0: Nested `trap ... TERM', triggered on inner loop
+>TERM2
+
+  print 'This test, too, takes at least three seconds...' >&8
+  fn1() {
+    trap 'print TERM1; return 1' TERM
+    fn2() { trap 'print TERM2; return 1' TERM; }
+    fn2
+    sleep 2
+  }
+  fn1 &
+  sleep 1
+  kill -TERM $!
+  sleep 2
+0: Nested `trap ... TERM', triggered on outer loop
+>TERM1
diff --git a/Test/50cd.ztst b/Test/50cd.ztst
index a7053d0a0..d5836ec94 100644
--- a/Test/50cd.ztst
+++ b/Test/50cd.ztst
@@ -17,11 +17,10 @@
 # of spaces and/or tabs, to differentiate it from tags with a special
 # meaning to the test harness.  Note that this is true even in sections
 # where there are no such tags.  Also note that file descriptor 9
-# is reserved for input from the test script; if ZTST_verbose is set,
-# output is sent to the original stdout via fd 8.  Option settings
-# are preserved between the execution of different code chunks;
-# initially, all standard zsh options (the effect of `emulate -R zsh')
-# are set.
+# is reserved for input from the test script, and file descriptor 8
+# preserves the original stdout.  Option settings are preserved between the
+# execution of different code chunks; initially, all standard zsh options
+# (the effect of `emulate -R zsh') are set.
 
 %prep
 # This optional section prepares the test, creating directories and files