diff options
Diffstat (limited to 'Src/signals.c')
-rw-r--r-- | Src/signals.c | 159 |
1 files changed, 158 insertions, 1 deletions
diff --git a/Src/signals.c b/Src/signals.c index f69da0140..e9851b57a 100644 --- a/Src/signals.c +++ b/Src/signals.c @@ -602,6 +602,57 @@ killjb(Job jn, int sig) return err; } +/* + * List for saving traps. We don't usually have that many traps + * at once, so just use a linked list. + */ +struct savetrap { + int sig, flags, local; + void *list; +}; + +static LinkList savetraps; + +/* Flag to unsettrap not to free the structs, which we're keeping */ + +/**/ +int notrapfree; + +/* + * Save the current trap and unset it. + */ + +static void +dosavetrap(int sig, int level) +{ + struct savetrap *st; + 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 + * the new one yet. + */ + char func[20]; + sprintf(func, "TRAP%s", sigs[sig]); + st->list = shfunctab->removenode(shfunctab, func); + } else { + st->list = sigfuncs[sig]; + unsettrap(sig); + } + notrapfree--; + PERMALLOC { + if (!savetraps) + savetraps = newlinklist(); + /* + * Put this at the front of the list + */ + insertlinknode(savetraps, (LinkNode)savetraps, st); + } LASTALLOC; +} + /**/ int settrap(int sig, List l) @@ -612,7 +663,15 @@ settrap(int sig, List l) zerr("can't trap SIG%s in interactive shells", sigs[sig], 0); return 1; } - if (sigfuncs[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))) { + dosavetrap(sig, locallevel); + } else if (sigfuncs[sig]) unsettrap(sig); sigfuncs[sig] = l; if (!l) { @@ -632,6 +691,12 @@ settrap(int sig, List l) sig != SIGCHLD) install_handler(sig); } + /* + * Note that introducing the locallevel does not affect whether + * sigtrapped[sig] is zero or not, i.e. a test without a mask + * works just the same. + */ + sigtrapped[sig] |= (locallevel << ZSIG_SHIFT); return 0; } @@ -645,6 +710,19 @@ unsettrap(int sig) (jobbing && (sig == SIGTTOU || sig == SIGTSTP || sig == SIGTTIN))) { return; } + 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; + dosavetrap(sig, locallevel); + opts[LOCALTRAPS] = oldlt; + return; + } sigtrapped[sig] = 0; if (sig == SIGINT && interact) { /* PWS 1995/05/16: added test for interactive, also noholdintr() * @@ -659,6 +737,8 @@ unsettrap(int sig) #endif sig != SIGCHLD) signal_default(sig); + if (notrapfree) + return; if (trapped & ZSIG_FUNC) { char func[20]; HashNode hn; @@ -672,6 +752,83 @@ unsettrap(int sig) } } +/**/ +void +starttrapscope(void) +{ + /* + * SIGEXIT needs to be restored at the current locallevel, + * so give it the next higher one. + */ + if (sigtrapped[SIGEXIT]) + dosavetrap(SIGEXIT, locallevel+1); +} + +/* + * Reset traps after the end of a function: must be called after + * endparamscope() so that the locallevel has been decremented. + */ + +/**/ +void +endtrapscope(void) +{ + LinkNode ln; + struct savetrap *st; + int exittr; + void *exitfn = NULL; + + /* + * Remember the exit trap, but don't run it until + * after all the other traps have been put back. + */ + if ((exittr = sigtrapped[SIGEXIT])) { + notrapfree++; + if (exittr & ZSIG_FUNC) { + exitfn = shfunctab->removenode(shfunctab, "TRAPEXIT"); + } else { + exitfn = sigfuncs[SIGEXIT]; + unsettrap(SIGEXIT); + } + notrapfree--; + } + + if (savetraps) { + while ((ln = firstnode(savetraps)) && + (st = (struct savetrap *) ln->dat) && + st->local > locallevel) { + int sig = st->sig; + + remnode(savetraps, ln); + + if (sigtrapped[sig]) + unsettrap(sig); + if (st->flags) { + List list = (st->flags & ZSIG_FUNC) ? + ((Shfunc) st->list)->funcdef : (List) st->list; + /* prevent settrap from saving this */ + int oldlt = opts[LOCALTRAPS]; + opts[LOCALTRAPS] = 0; + settrap(sig, list); + opts[LOCALTRAPS] = oldlt; + if ((sigtrapped[sig] = st->flags) & ZSIG_FUNC) + shfunctab->addnode(shfunctab, ((Shfunc)st->list)->nam, + (Shfunc) st->list); + } + zfree(st, sizeof(*st)); + } + } + + if (exittr) { + dotrapargs(SIGEXIT, &exittr, (exittr & ZSIG_FUNC) ? + ((Shfunc)exitfn)->funcdef : (List) exitfn); + if (exittr & ZSIG_FUNC) + shfunctab->freenode((HashNode)exitfn); + else + freestruct(exitfn); + } +} + /* Execute a trap function for a given signal, possibly * with non-standard sigtrapped & sigfuncs values */ |