diff -r 000000000000 -r 2e3d3ce01487 openenvutils/commandshell/shell/src/signals.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openenvutils/commandshell/shell/src/signals.c Tue Feb 02 10:12:00 2010 +0200 @@ -0,0 +1,1133 @@ +// signals.c - signals handling code +// +// © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved. +// +/* + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1992-1997 Paul Falstad + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Paul Falstad or the Zsh Development Group be liable + * to any party for direct, indirect, special, incidental, or consequential + * damages arising out of the use of this software and its documentation, + * even if Paul Falstad and the Zsh Development Group have been advised of + * the possibility of such damage. + * + * Paul Falstad and the Zsh Development Group specifically disclaim any + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose. The software + * provided hereunder is on an "as is" basis, and Paul Falstad and the + * Zsh Development Group have no obligation to provide maintenance, + * support, updates, enhancements, or modifications. + * + */ + +#include "zsh.mdh" +#include "signals.pro" + +#ifdef __SYMBIAN32__ +#include "dummy.h" +#endif //__SYMBIAN32__ + +#ifdef __SYMBIAN32__ +#ifdef __WINSCW__ +#pragma warn_unusedarg off +#endif//__WINSCW__ +#endif//__SYMBIAN32__ + +/* Array describing the state of each signal: an element contains * + * 0 for the default action or some ZSIG_* flags ored together. */ + +/**/ +mod_export int sigtrapped[VSIGCOUNT]; + +/* trap functions for each signal */ + +/**/ +mod_export Eprog sigfuncs[VSIGCOUNT]; + +/* Total count of trapped signals */ + +/**/ +mod_export int nsigtrapped; + +/* Variables used by signal queueing */ + +/**/ +mod_export int queueing_enabled, queue_front, queue_rear; +/**/ +mod_export int signal_queue[MAX_QUEUE_SIZE]; +/**/ +mod_export sigset_t signal_mask_queue[MAX_QUEUE_SIZE]; + +/* Variables used by trap queueing */ + +/**/ +mod_export int trap_queueing_enabled, trap_queue_front, trap_queue_rear; +/**/ +mod_export int trap_queue[MAX_QUEUE_SIZE]; + +/* This is only used on machines that don't understand signal sets. * + * On SYSV machines this will represent the signals that are blocked * + * (held) using sighold. On machines which can't block signals at * + * all, we will simulate this by ignoring them and remembering them * + * in this variable. */ +#if !defined(POSIX_SIGNALS) && !defined(BSD_SIGNALS) +static sigset_t blocked_set; +#endif + +#ifdef POSIX_SIGNALS +# define signal_jmp_buf sigjmp_buf +# define signal_setjmp(b) sigsetjmp((b),1) +# define signal_longjmp(b,n) siglongjmp((b),(n)) +#else +# define signal_jmp_buf jmp_buf +# define signal_setjmp(b) setjmp(b) +# define signal_longjmp(b,n) longjmp((b),(n)) +#endif + +#ifdef NO_SIGNAL_BLOCKING +# define signal_process(sig) signal_ignore(sig) +# define signal_reset(sig) install_handler(sig) +#else +# define signal_process(sig) ; +# define signal_reset(sig) ; +#endif + +/* Install signal handler for given signal. * + * If possible, we want to make sure that interrupted * + * system calls are not restarted. */ + +/**/ +mod_export void +install_handler(int sig) +{ +#ifdef POSIX_SIGNALS + struct sigaction act; + + act.sa_handler = (SIGNAL_HANDTYPE) zhandler; + sigemptyset(&act.sa_mask); /* only block sig while in handler */ + act.sa_flags = 0; +# ifdef SA_INTERRUPT /* SunOS 4.x */ + if (interact) + act.sa_flags |= SA_INTERRUPT; /* make sure system calls are not restarted */ +# endif +#ifndef __SYMBIAN32__ + sigaction(sig, &act, (struct sigaction *)NULL); +#endif +#else +# ifdef BSD_SIGNALS + struct sigvec vec; + + vec.sv_handler = (SIGNAL_HANDTYPE) zhandler; + vec.sv_mask = sigmask(sig); /* mask out this signal while in handler */ +# ifdef SV_INTERRUPT + vec.sv_flags = SV_INTERRUPT; /* make sure system calls are not restarted */ +# endif + sigvec(sig, &vec, (struct sigvec *)NULL); +# else +# ifdef SYSV_SIGNALS + /* we want sigset rather than signal because it will * + * block sig while in handler. signal usually doesn't */ + sigset(sig, zhandler); +# else /* NO_SIGNAL_BLOCKING (bummer) */ +#ifndef __SYMBIAN32__ + signal(sig, zhandler); +#endif + +# endif /* SYSV_SIGNALS */ +# endif /* BSD_SIGNALS */ +#endif /* POSIX_SIGNALS */ +} + +/* enable ^C interrupts */ + +/**/ +mod_export void +intr(void) +{ + if (interact) + install_handler(SIGINT); +} + +/* disable ^C interrupts */ + +#if 0 /**/ +void +nointr(void) +{ + if (interact) + signal_ignore(SIGINT); +} +#endif + +/* temporarily block ^C interrupts */ + +/**/ +mod_export void +holdintr(void) +{ + if (interact) + signal_block(signal_mask(SIGINT)); +} + +/* release ^C interrupts */ + +/**/ +mod_export void +noholdintr(void) +{ + if (interact) + signal_unblock(signal_mask(SIGINT)); +} + +/* create a signal mask containing * + * only the given signal */ + +/**/ +sigset_t +signal_mask(int sig) +{ + sigset_t set; + + sigemptyset(&set); + if (sig) + sigaddset(&set, sig); + return set; +} + +/* Block the signals in the given signal * + * set. Return the old signal set. */ + +/**/ +#ifdef POSIX_SIGNALS + +/**/ +mod_export sigset_t dummy_sigset1, dummy_sigset2; + +/**/ +#else + +/**/ +#ifndef BSD_SIGNALS + +sigset_t +signal_block(sigset_t set) +{ + sigset_t oset; + +#ifdef SYSV_SIGNALS + int i; + + oset = blocked_set; + for (i = 1; i <= NSIG; ++i) { + if (sigismember(&set, i) && !sigismember(&blocked_set, i)) { + sigaddset(&blocked_set, i); + sighold(i); + } + } +#else /* NO_SIGNAL_BLOCKING */ +/* We will just ignore signals if the system doesn't have * + * the ability to block them. */ + int i; + + oset = blocked_set; + for (i = 1; i <= NSIG; ++i) { + if (sigismember(&set, i) && !sigismember(&blocked_set, i)) { + sigaddset(&blocked_set, i); + signal_ignore(i); + } + } +#endif /* SYSV_SIGNALS */ + + return oset; +} + +/**/ +#endif /* BSD_SIGNALS */ + +/**/ +#endif /* POSIX_SIGNALS */ + +/* Unblock the signals in the given signal * + * set. Return the old signal set. */ + +#ifndef POSIX_SIGNALS + +sigset_t +signal_unblock(sigset_t set) +{ + + sigset_t oset; + +#ifndef __SYMBIAN32__ +# ifdef BSD_SIGNALS + sigfillset(&oset); + oset = sigsetmask(oset); + sigsetmask(oset & ~set); +# else +# ifdef SYSV_SIGNALS + int i; + + oset = blocked_set; + for (i = 1; i <= NSIG; ++i) { + if (sigismember(&set, i) && sigismember(&blocked_set, i)) { + sigdelset(&blocked_set, i); + sigrelse(i); + } + } +# else /* NO_SIGNAL_BLOCKING */ +/* On systems that can't block signals, we are just ignoring them. So * + * to unblock signals, we just reenable the signal handler for them. */ + int i; + + oset = blocked_set; + for (i = 1; i <= NSIG; ++i) { + if (sigismember(&set, i) && sigismember(&blocked_set, i)) { + sigdelset(&blocked_set, i); + install_handler(i); + } + } +# endif /* SYSV_SIGNALS */ +# endif /* BSD_SIGNALS */ + +#endif //__SYMBIAN32__ + return oset; +} + +#endif /* POSIX_SIGNALS */ + +/* set the process signal mask to * + * be the given signal mask */ + +/**/ +mod_export sigset_t +signal_setmask(sigset_t set) +{ + sigset_t oset; + +#ifdef POSIX_SIGNALS + sigprocmask(35, &set, &oset); +#else +# ifdef BSD_SIGNALS + oset = sigsetmask(set); +# else +# ifdef SYSV_SIGNALS + int i; + + oset = blocked_set; + for (i = 1; i <= NSIG; ++i) { + if (sigismember(&set, i) && !sigismember(&blocked_set, i)) { + sigaddset(&blocked_set, i); + sighold(i); + } else if (!sigismember(&set, i) && sigismember(&blocked_set, i)) { + sigdelset(&blocked_set, i); + sigrelse(i); + } + } +# else /* NO_SIGNAL_BLOCKING */ + int i; + + oset = blocked_set; + for (i = 1; i < NSIG; ++i) { + if (sigismember(&set, i) && !sigismember(&blocked_set, i)) { + sigaddset(&blocked_set, i); + signal_ignore(i); + } else if (!sigismember(&set, i) && sigismember(&blocked_set, i)) { + sigdelset(&blocked_set, i); + install_handler(i); + } + } +# endif /* SYSV_SIGNALS */ +# endif /* BSD_SIGNALS */ +#endif /* POSIX_SIGNALS */ + + return oset; +} + +#if defined(NO_SIGNAL_BLOCKING) +static int suspend_longjmp = 0; +static signal_jmp_buf suspend_jmp_buf; +#endif + +/**/ +int +signal_suspend(int sig, int sig2) +{ + int ret; + +#ifdef POSIX_SIGNALS + sigset_t set; +#ifdef BROKEN_POSIX_SIGSUSPEND + sigset_t oset; +#endif /* BROKEN_POSIX_SIGSUSPEND */ + + if (isset(TRAPSASYNC)) { + sigemptyset(&set); + } else { + sigfillset(&set); + sigdelset(&set, sig); + sigdelset(&set, SIGHUP); /* still don't know why we add this? */ + if (sig2) + sigdelset(&set, sig2); + } +#ifdef BROKEN_POSIX_SIGSUSPEND + sigprocmask(SIG_SETMASK, &set, &oset); + pause(); + sigprocmask(SIG_SETMASK, &oset, NULL); +#else /* not BROKEN_POSIX_SIGSUSPEND */ + ret = sigsuspend(&set); +#endif /* BROKEN_POSIX_SIGSUSPEND */ +#else /* not POSIX_SIGNALS */ +# ifdef BSD_SIGNALS + sigset_t set; + + if (isset(TRAPSASYNC)) { + sigemptyset(&set); + } else { + sigfillset(&set); + sigdelset(&set, sig); + if (sig2) + sigdelset(&set, sig2); + ret = sigpause(set); + } +# else +# ifdef SYSV_SIGNALS + ret = sigpause(sig); + +# else /* NO_SIGNAL_BLOCKING */ + /* need to use signal_longjmp to make this race-free * + * between the child_unblock() and pause() */ + if (signal_setjmp(suspend_jmp_buf) == 0) { + suspend_longjmp = 1; /* we want to signal_longjmp after catching signal */ + child_unblock(); /* do we need to unblock sig2 as well? */ + ret = pause(); + } + suspend_longjmp = 0; /* turn off using signal_longjmp since we are past * + * the pause() function. */ +# endif /* SYSV_SIGNALS */ +# endif /* BSD_SIGNALS */ +#endif /* POSIX_SIGNALS */ + + return ret; +} + +/* the signal handler */ + +/**/ +mod_export RETSIGTYPE +zhandler(int sig) +{ + sigset_t newmask, oldmask; + +#if defined(NO_SIGNAL_BLOCKING) + int do_jump; + signal_jmp_buf jump_to; +#endif + + signal_process(sig); + + sigfillset(&newmask); + oldmask = signal_block(newmask); /* Block all signals temporarily */ + +#if defined(NO_SIGNAL_BLOCKING) + do_jump = suspend_longjmp; /* do we need to longjmp to signal_suspend */ + suspend_longjmp = 0; /* In case a SIGCHLD somehow arrives */ + + if (sig == SIGCHLD) { /* Traps can cause nested signal_suspend() */ + if (do_jump) + jump_to = suspend_jmp_buf; /* Copy suspend_jmp_buf */ + } +#endif + + /* Are we queueing signals now? */ + if (queueing_enabled) { + int temp_rear = ++queue_rear % MAX_QUEUE_SIZE; + + DPUTS(temp_rear == queue_front, "BUG: signal queue full"); + if (temp_rear != queue_front) { /* Make sure it's not full (extremely unlikely) */ + queue_rear = temp_rear; /* ok, not full, so add to queue */ + signal_queue[queue_rear] = sig; /* save signal caught */ + signal_mask_queue[queue_rear] = oldmask; /* save current signal mask */ + } + signal_reset(sig); + return; + } + + signal_setmask(oldmask); /* Reset signal mask, signal traps ok now */ + + switch (sig) { + case SIGCHLD: + + /* keep WAITING until no more child processes to reap */ + for (;;) + cont: { + int old_errno = errno; /* save the errno, since WAIT may change it */ + int status; + Job jn; + Process pn; + pid_t pid; + pid_t *procsubpid = &cmdoutpid; + int *procsubval = &cmdoutval; + struct execstack *es = exstack; + + /* + * Reap the child process. + * If we want usage information, we need to use wait3. + */ +#ifdef HAVE_WAIT3 +# ifdef HAVE_GETRUSAGE + struct rusage ru; + + pid = wait3((void *)&status, WNOHANG|WUNTRACED, &ru); +# else + pid = wait3((void *)&status, WNOHANG|WUNTRACED, NULL); +# endif +#else +# ifdef HAVE_WAITPID + pid = waitpid(-1, &status, WNOHANG|WUNTRACED); +# else + pid = wait(&status); +# endif +#endif + + if (!pid) /* no more children to reap */ + break; + + /* check if child returned was from process substitution */ + for (;;) { + if (pid == *procsubpid) { + *procsubpid = 0; + if (WIFSIGNALED(status)) + *procsubval = (0200 | WTERMSIG(status)); + else + *procsubval = WEXITSTATUS(status); + get_usage(); + goto cont; + } + if (!es) + break; + procsubpid = &es->cmdoutpid; + procsubval = &es->cmdoutval; + es = es->next; + } + + /* check for WAIT error */ + if (pid == -1) { + if (errno != ECHILD) + zerr("wait failed: %e", NULL, errno); + errno = old_errno; /* WAIT changed errno, so restore the original */ + break; + } + + /* Find the process and job containing this pid and update it. */ + if (findproc(pid, &jn, &pn, 0)) { +#if defined(HAVE_WAIT3) && defined(HAVE_GETRUSAGE) + struct timezone dummy_tz; + gettimeofday(&pn->endtime, &dummy_tz); + pn->status = status; + pn->ti = ru; +#elif !defined(HAVE_GETRUSAGE) + update_process(pn, status); +#endif + update_job(jn); + } else if (findproc(pid, &jn, &pn, 1)) { + pn->status = status; + update_job(jn); + } else { + /* If not found, update the shell record of time spent by + * children in sub processes anyway: otherwise, this + * will get added on to the next found process that terminates. + */ + get_usage(); + } + } + break; + + case SIGHUP: + if (sigtrapped[SIGHUP]) + dotrap(SIGHUP); + else { + stopmsg = 1; + zexit(SIGHUP, 1); + } + break; + + case SIGINT: + if (sigtrapped[SIGINT]) + dotrap(SIGINT); + else { + if ((isset(PRIVILEGED) || isset(RESTRICTED)) && + isset(INTERACTIVE) && noerrexit < 0) + zexit(SIGINT, 1); + if (list_pipe || chline || simple_pline) { + breaks = loops; + errflag = 1; + inerrflush(); + } + } + break; + +#ifdef SIGWINCH + case SIGWINCH: + adjustwinsize(1); /* check window size and adjust */ + if (sigtrapped[SIGWINCH]) + dotrap(SIGWINCH); + break; +#endif + + case SIGALRM: + if (sigtrapped[SIGALRM]) { + int tmout; + dotrap(SIGALRM); + + if ((tmout = getiparam("TMOUT"))) + alarm(tmout); /* reset the alarm */ + } else { + int idle = ttyidlegetfn(NULL); + int tmout = getiparam("TMOUT"); + if (idle >= 0 && idle < tmout) + alarm(tmout - idle); + else { + errflag = noerrs = 0; + zwarn("timeout", NULL, 0); + stopmsg = 1; + zexit(SIGALRM, 1); + } + } + break; + + default: + dotrap(sig); + break; + } /* end of switch(sig) */ + + signal_reset(sig); + +/* This is used to make signal_suspend() race-free */ +#if defined(NO_SIGNAL_BLOCKING) + if (do_jump) + signal_longjmp(jump_to, 1); +#endif + +} /* handler */ + + +/* SIGHUP any jobs left running */ + +/**/ +void +killrunjobs(int from_signal) +{ + int i, killed = 0; + + if (unset(HUP)) + return; + for (i = 1; i <= maxjob; i++) + if ((from_signal || i != thisjob) && (jobtab[i].stat & STAT_LOCKED) && + !(jobtab[i].stat & STAT_NOPRINT) && + !(jobtab[i].stat & STAT_STOPPED)) { + if (jobtab[i].gleader != getpid() && + killpg(jobtab[i].gleader, SIGHUP) != -1) + killed++; + } + if (killed) + zwarn("warning: %d jobs SIGHUPed", NULL, killed); +} + + +/* send a signal to a job (simply involves kill if monitoring is on) */ + +/**/ +int +killjb(Job jn, int sig) +{ + Process pn; + int err = 0; + + if (jobbing) { + if (jn->stat & STAT_SUPERJOB) { + if (sig == SIGCONT) { + for (pn = jobtab[jn->other].procs; pn; pn = pn->next) + if (killpg(pn->pid, sig) == -1) + if (kill(pn->pid, sig) == -1 && errno != ESRCH) + err = -1; + + for (pn = jn->procs; pn->next; pn = pn->next) + if (kill(pn->pid, sig) == -1 && errno != ESRCH) + err = -1; + + if (!jobtab[jn->other].procs && pn) + if (kill(pn->pid, sig) == -1 && errno != ESRCH) + err = -1; + + return err; + } + if (killpg(jobtab[jn->other].gleader, sig) == -1 && errno != ESRCH) + err = -1; + + if (killpg(jn->gleader, sig) == -1 && errno != ESRCH) + err = -1; + + return err; + } + else + return killpg(jn->gleader, sig); + } + for (pn = jn->procs; pn; pn = pn->next) + if ((err = kill(pn->pid, sig)) == -1 && errno != ESRCH && sig != 0) + return -1; + 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; +static int dontsavetrap; + +/* + * Save the current trap by copying it. This does nothing to + * the existing value of sigtrapped or sigfuncs. + */ + +static void +dosavetrap(int sig, int level) +{ + struct savetrap *st; + st = (struct savetrap *)zalloc(sizeof(*st)); + st->sig = sig; + st->local = level; + if ((st->flags = sigtrapped[sig]) & ZSIG_FUNC) { + /* + * Get the old function: this assumes we haven't added + * the new one yet. + */ + Shfunc shf, newshf = NULL; + if ((shf = (Shfunc)gettrapnode(sig, 1))) { + /* Copy the node for saving */ + newshf = (Shfunc) zalloc(sizeof(*newshf)); + newshf->nam = ztrdup(shf->nam); + newshf->flags = shf->flags; + newshf->funcdef = dupeprog(shf->funcdef, 0); + if (shf->flags & PM_UNDEFINED) + newshf->funcdef->shf = newshf; + } +#ifdef DEBUG + else dputs("BUG: no function present with function trap flag set."); +#endif + st->list = newshf; + } else if (sigtrapped[sig]) { + st->list = sigfuncs[sig] ? dupeprog(sigfuncs[sig], 0) : NULL; + } else { + DPUTS(sigfuncs[sig], "BUG: sigfuncs not null for untrapped signal"); + st->list = NULL; + } + if (!savetraps) + savetraps = znewlinklist(); + /* + * Put this at the front of the list + */ + zinsertlinknode(savetraps, (LinkNode)savetraps, st); +} + +/**/ +mod_export int +settrap(int sig, Eprog l) +{ + if (sig == -1) + return 1; + if (jobbing && (sig == SIGTTOU || sig == SIGTSTP || sig == SIGTTIN)) { + zerr("can't trap SIG%s in interactive shells", sigs[sig], 0); + return 1; + } + + /* + * Call unsettrap() unconditionally, to make sure trap is saved + * if necessary. + */ + queue_signals(); + unsettrap(sig); + + sigfuncs[sig] = l; + if (empty_eprog(l)) { + sigtrapped[sig] = ZSIG_IGNORED; + if (sig && sig <= SIGCOUNT && +#ifdef SIGWINCH + sig != SIGWINCH && +#endif + sig != SIGCHLD) + signal_ignore(sig); + } else { + nsigtrapped++; + sigtrapped[sig] = ZSIG_TRAPPED; + if (sig && sig <= SIGCOUNT && +#ifdef SIGWINCH + sig != SIGWINCH && +#endif + 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); + unqueue_signals(); + return 0; +} + +/**/ +void +unsettrap(int sig) +{ + HashNode hn; + + queue_signals(); + hn = removetrap(sig); + if (hn) + shfunctab->freenode(hn); + unqueue_signals(); +} + +/**/ +HashNode +removetrap(int sig) +{ + int trapped; + + if (sig == -1 || + (jobbing && (sig == SIGTTOU || sig == SIGTSTP || sig == SIGTTIN))) + return NULL; + + queue_signals(); + 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 (!dontsavetrap && (isset(LOCALTRAPS) || sig == SIGEXIT) && + locallevel && + (!trapped || locallevel > (sigtrapped[sig] >> ZSIG_SHIFT))) + dosavetrap(sig, locallevel); + + if (!trapped) { + unqueue_signals(); + return NULL; + } + if (sigtrapped[sig] & ZSIG_TRAPPED) + nsigtrapped--; + sigtrapped[sig] = 0; + if (sig == SIGINT && interact) { + /* PWS 1995/05/16: added test for interactive, also noholdintr() * + * as subshells ignoring SIGINT have it blocked from delivery */ + intr(); + noholdintr(); + } else if (sig == SIGHUP) + install_handler(sig); + else if (sig && sig <= SIGCOUNT && +#ifdef SIGWINCH + sig != SIGWINCH && +#endif + sig != SIGCHLD) + signal_default(sig); + + /* + * At this point we free the appropriate structs. If we don't + * want that to happen then either the function should already have been + * removed from shfunctab, or the entry in sigfuncs should have been set + * to NULL. This is no longer necessary for saving traps as that + * copies the structures, so here we are remove the originals. + * That causes a little inefficiency, but a good deal more reliability. + */ + if (trapped & ZSIG_FUNC) { + HashNode node = gettrapnode(sig, 1); + + /* + * As in dosavetrap(), don't call removeshfuncnode() because + * that calls back into unsettrap(); + */ + sigfuncs[sig] = NULL; + if (node) + removehashnode(shfunctab, node->nam); + unqueue_signals(); + + return node; + } else if (sigfuncs[sig]) { + freeeprog(sigfuncs[sig]); + sigfuncs[sig] = NULL; + } + unqueue_signals(); + + return NULL; +} + +/**/ +void +starttrapscope(void) +{ + /* No special SIGEXIT behaviour inside another trap. */ + if (intrap) + return; + + /* + * SIGEXIT needs to be restored at the current locallevel, + * so give it the next higher one. dosavetrap() is called + * automatically where necessary. + */ + if (sigtrapped[SIGEXIT]) { + locallevel++; + unsettrap(SIGEXIT); + locallevel--; + } +} + +/* + * 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. + * Don't do this inside another trap. + */ + if (intrap) + exittr = 0; + else if ((exittr = sigtrapped[SIGEXIT])) { + if (exittr & ZSIG_FUNC) { + exitfn = removehashnode(shfunctab, "TRAPEXIT"); + } else { + exitfn = sigfuncs[SIGEXIT]; + } + sigfuncs[SIGEXIT] = NULL; + if (sigtrapped[SIGEXIT] & ZSIG_TRAPPED) + nsigtrapped--; + sigtrapped[SIGEXIT] = 0; + } + + if (savetraps) { + while ((ln = firstnode(savetraps)) && + (st = (struct savetrap *) ln->dat) && + st->local > locallevel) { + int sig = st->sig; + + remnode(savetraps, ln); + + if (st->flags && (st->list != NULL)) { + Eprog prog = (st->flags & ZSIG_FUNC) ? + ((Shfunc) st->list)->funcdef : (Eprog) st->list; + /* prevent settrap from saving this */ + dontsavetrap++; + settrap(sig, prog); + dontsavetrap--; + /* + * counting of nsigtrapped should presumably be handled + * in settrap... + */ + DPUTS((sigtrapped[sig] ^ st->flags) & ZSIG_TRAPPED, + "BUG: settrap didn't restore correct ZSIG_TRAPPED"); + if ((sigtrapped[sig] = st->flags) & ZSIG_FUNC) + shfunctab->addnode(shfunctab, ((Shfunc)st->list)->nam, + (Shfunc) st->list); + } else if (sigtrapped[sig]) + unsettrap(sig); + + zfree(st, sizeof(*st)); + } + } + + if (exittr) { + dotrapargs(SIGEXIT, &exittr, (exittr & ZSIG_FUNC) ? + ((Shfunc)exitfn)->funcdef : (Eprog) exitfn); + if (exittr & ZSIG_FUNC) + shfunctab->freenode((HashNode)exitfn); + else + freeeprog(exitfn); + } + DPUTS(!locallevel && savetraps && firstnode(savetraps), + "BUG: still saved traps outside all function scope"); +} + +/* Execute a trap function for a given signal, possibly + * with non-standard sigtrapped & sigfuncs values + */ + +/* Are we already executing a trap? */ +/**/ +int intrap; + +/* Is the current trap a function? */ + +/**/ +int trapisfunc; + +/**/ +void +dotrapargs(int sig, int *sigtr, void *sigfn) +{ + LinkList args; + 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 * + * * + * Also return if errflag is set. In fact, the code in the * + * function will test for this, but this way we keep status flags * + * intact without working too hard. Special cases (e.g. calling * + * a trap for SIGINT after the error flag was set) are handled * + * by the calling code. (PWS 1995/06/08). * + * * + * This test is now replicated in dotrap(). */ + if ((*sigtr & ZSIG_IGNORED) || !sigfn || errflag) + return; + + /* + * Never execute special (synchronous) traps inside other traps. + * This can cause unexpected code execution when more than one + * of these is set. + * + * The down side is that it's harder to debug traps. I don't think + * that's a big issue. + */ + if (intrap) { + switch (sig) { + case SIGEXIT: + case SIGDEBUG: + case SIGZERR: + return; + } + } + + intrap++; + *sigtr |= ZSIG_IGNORED; + + lexsave(); + execsave(); + breaks = 0; + runhookdef(BEFORETRAPHOOK, NULL); + if (*sigtr & ZSIG_FUNC) { + int osc = sfcontext; + HashNode hn = gettrapnode(sig, 0); + + args = znewlinklist(); + /* + * In case of multiple names, try to get + * a hint of the name in use from the function table. + * In special cases, e.g. EXIT traps, the function + * has already been removed. Then it's OK to + * use the standard name. + */ + if (hn) { + name = ztrdup(hn->nam); + } else { + name = (char *) zalloc(5 + strlen(sigs[sig])); + sprintf(name, "TRAP%s", sigs[sig]); + } + zaddlinknode(args, name); + sprintf(num, "%d", sig); + zaddlinknode(args, num); + + trapreturn = -1; /* incremented by doshfunc */ + trapisfunc = isfunc = 1; + + sfcontext = SFC_SIGNAL; + doshfunc(name, sigfn, args, 0, 1); + sfcontext = osc; + freelinklist(args, (FreeFunc) NULL); + zsfree(name); + + } else { + trapreturn = -2; /* not incremented, used at current level */ + trapisfunc = isfunc = 0; + + execode(sigfn, 1, 0); + } + runhookdef(AFTERTRAPHOOK, NULL); + + 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 (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; + execrestore(); + lexrestore(); + + if (trapret > 0) { + if (isfunc) { + breaks = loops; + errflag = 1; + } else { + lastval = trapret-1; + } + } else { + breaks += obreaks; + if (breaks > loops) + breaks = loops; + } + + /* + * If zle was running while the trap was executed, see if we + * need to restore the display. + */ + if (zleactive && resetneeded) + zrefresh(); + + if (*sigtr != ZSIG_IGNORED) + *sigtr &= ~ZSIG_IGNORED; + intrap--; +} + +/* Standard call to execute a trap for a given signal. */ + +/**/ +void +dotrap(int sig) +{ + /* Copied from dotrapargs(). */ + if ((sigtrapped[sig] & ZSIG_IGNORED) || !sigfuncs[sig] || errflag) + return; + + dotrapargs(sig, sigtrapped+sig, sigfuncs[sig]); +}