/* exp_trap.c - Expect's trap command
|
/* exp_trap.c - Expect's trap command
|
|
|
Written by: Don Libes, NIST, 9/1/93
|
Written by: Don Libes, NIST, 9/1/93
|
|
|
Design and implementation of this program was paid for by U.S. tax
|
Design and implementation of this program was paid for by U.S. tax
|
dollars. Therefore it is public domain. However, the author and NIST
|
dollars. Therefore it is public domain. However, the author and NIST
|
would appreciate credit if this program or parts of it are used.
|
would appreciate credit if this program or parts of it are used.
|
|
|
*/
|
*/
|
|
|
#include "expect_cf.h"
|
#include "expect_cf.h"
|
|
|
#include <stdio.h>
|
#include <stdio.h>
|
#include <signal.h>
|
#include <signal.h>
|
#include <sys/types.h>
|
#include <sys/types.h>
|
|
|
#ifdef HAVE_SYS_WAIT_H
|
#ifdef HAVE_SYS_WAIT_H
|
#include <sys/wait.h>
|
#include <sys/wait.h>
|
#endif
|
#endif
|
|
|
/* Use _NSIG if NSIG not present */
|
/* Use _NSIG if NSIG not present */
|
#ifndef NSIG
|
#ifndef NSIG
|
#ifdef _NSIG
|
#ifdef _NSIG
|
#define NSIG _NSIG
|
#define NSIG _NSIG
|
#endif
|
#endif
|
#endif
|
#endif
|
|
|
#if defined(SIGCLD) && !defined(SIGCHLD)
|
#if defined(SIGCLD) && !defined(SIGCHLD)
|
#define SIGCHLD SIGCLD
|
#define SIGCHLD SIGCLD
|
#endif
|
#endif
|
|
|
#include "tcl.h"
|
#include "tcl.h"
|
|
|
#include "exp_rename.h"
|
#include "exp_rename.h"
|
#include "exp_prog.h"
|
#include "exp_prog.h"
|
#include "exp_command.h"
|
#include "exp_command.h"
|
#include "exp_log.h"
|
#include "exp_log.h"
|
|
|
#ifdef TCL_DEBUGGER
|
#ifdef TCL_DEBUGGER
|
#include "Dbg.h"
|
#include "Dbg.h"
|
#endif
|
#endif
|
|
|
#define NO_SIG 0
|
#define NO_SIG 0
|
|
|
static struct trap {
|
static struct trap {
|
char *action; /* Tcl command to execute upon sig */
|
char *action; /* Tcl command to execute upon sig */
|
/* Each is handled by the eval_trap_action */
|
/* Each is handled by the eval_trap_action */
|
int mark; /* TRUE if signal has occurred */
|
int mark; /* TRUE if signal has occurred */
|
Tcl_Interp *interp; /* interp to use or 0 if we should use the */
|
Tcl_Interp *interp; /* interp to use or 0 if we should use the */
|
/* interpreter active at the time the sig */
|
/* interpreter active at the time the sig */
|
/* is processed */
|
/* is processed */
|
int code; /* return our new code instead of code */
|
int code; /* return our new code instead of code */
|
/* available when signal is processed */
|
/* available when signal is processed */
|
char *name; /* name of signal */
|
char *name; /* name of signal */
|
int reserved; /* if unavailable for trapping */
|
int reserved; /* if unavailable for trapping */
|
} traps[NSIG];
|
} traps[NSIG];
|
|
|
int sigchld_count = 0; /* # of sigchlds caught but not yet processed */
|
int sigchld_count = 0; /* # of sigchlds caught but not yet processed */
|
|
|
static int eval_trap_action();
|
static int eval_trap_action();
|
|
|
static int got_sig; /* this records the last signal received */
|
static int got_sig; /* this records the last signal received */
|
/* it is only a hint and can be wiped out */
|
/* it is only a hint and can be wiped out */
|
/* by multiple signals, but it will always */
|
/* by multiple signals, but it will always */
|
/* be left with a valid signal that is */
|
/* be left with a valid signal that is */
|
/* pending */
|
/* pending */
|
|
|
static Tcl_AsyncHandler async_handler;
|
static Tcl_AsyncHandler async_handler;
|
|
|
static char *
|
static char *
|
signal_to_string(sig)
|
signal_to_string(sig)
|
int sig;
|
int sig;
|
{
|
{
|
if (sig <= 0 || sig > NSIG) return("SIGNAL OUT OF RANGE");
|
if (sig <= 0 || sig > NSIG) return("SIGNAL OUT OF RANGE");
|
return(traps[sig].name);
|
return(traps[sig].name);
|
}
|
}
|
|
|
/* current sig being processed by user sig handler */
|
/* current sig being processed by user sig handler */
|
static int current_sig = NO_SIG;
|
static int current_sig = NO_SIG;
|
|
|
int exp_nostack_dump = FALSE; /* TRUE if user has requested unrolling of */
|
int exp_nostack_dump = FALSE; /* TRUE if user has requested unrolling of */
|
/* stack with no trace */
|
/* stack with no trace */
|
|
|
|
|
|
|
/*ARGSUSED*/
|
/*ARGSUSED*/
|
static int
|
static int
|
tophalf(clientData,interp,code)
|
tophalf(clientData,interp,code)
|
ClientData clientData;
|
ClientData clientData;
|
Tcl_Interp *interp;
|
Tcl_Interp *interp;
|
int code;
|
int code;
|
{
|
{
|
struct trap *trap; /* last trap processed */
|
struct trap *trap; /* last trap processed */
|
int rc;
|
int rc;
|
int i;
|
int i;
|
Tcl_Interp *sig_interp;
|
Tcl_Interp *sig_interp;
|
/* extern Tcl_Interp *exp_interp;*/
|
/* extern Tcl_Interp *exp_interp;*/
|
|
|
exp_debuglog("sighandler: handling signal(%d)\r\n",got_sig);
|
exp_debuglog("sighandler: handling signal(%d)\r\n",got_sig);
|
|
|
if (got_sig <= 0 || got_sig >= NSIG) {
|
if (got_sig <= 0 || got_sig >= NSIG) {
|
errorlog("caught impossible signal %d\r\n",got_sig);
|
errorlog("caught impossible signal %d\r\n",got_sig);
|
abort();
|
abort();
|
}
|
}
|
|
|
/* start to work on this sig. got_sig can now be overwritten */
|
/* start to work on this sig. got_sig can now be overwritten */
|
/* and it won't cause a problem */
|
/* and it won't cause a problem */
|
current_sig = got_sig;
|
current_sig = got_sig;
|
trap = &traps[current_sig];
|
trap = &traps[current_sig];
|
|
|
trap->mark = FALSE;
|
trap->mark = FALSE;
|
|
|
/* decrement below looks dangerous */
|
/* decrement below looks dangerous */
|
/* Don't we need to temporarily block bottomhalf? */
|
/* Don't we need to temporarily block bottomhalf? */
|
if (current_sig == SIGCHLD) {
|
if (current_sig == SIGCHLD) {
|
sigchld_count--;
|
sigchld_count--;
|
exp_debuglog("sigchld_count-- == %d\n",sigchld_count);
|
exp_debuglog("sigchld_count-- == %d\n",sigchld_count);
|
}
|
}
|
|
|
if (!trap->action) {
|
if (!trap->action) {
|
/* In this one case, we let ourselves be called when no */
|
/* In this one case, we let ourselves be called when no */
|
/* signaler predefined, since we are calling explicitly */
|
/* signaler predefined, since we are calling explicitly */
|
/* from another part of the program, and it is just simpler */
|
/* from another part of the program, and it is just simpler */
|
if (current_sig == 0) return code;
|
if (current_sig == 0) return code;
|
errorlog("caught unexpected signal: %s (%d)\r\n",
|
errorlog("caught unexpected signal: %s (%d)\r\n",
|
signal_to_string(current_sig),current_sig);
|
signal_to_string(current_sig),current_sig);
|
abort();
|
abort();
|
}
|
}
|
|
|
if (trap->interp) {
|
if (trap->interp) {
|
/* if trap requested original interp, use it */
|
/* if trap requested original interp, use it */
|
sig_interp = trap->interp;
|
sig_interp = trap->interp;
|
} else if (!interp) {
|
} else if (!interp) {
|
/* else if another interp is available, use it */
|
/* else if another interp is available, use it */
|
sig_interp = interp;
|
sig_interp = interp;
|
} else {
|
} else {
|
/* fall back to exp_interp */
|
/* fall back to exp_interp */
|
sig_interp = exp_interp;
|
sig_interp = exp_interp;
|
}
|
}
|
|
|
rc = eval_trap_action(sig_interp,current_sig,trap,code);
|
rc = eval_trap_action(sig_interp,current_sig,trap,code);
|
current_sig = NO_SIG;
|
current_sig = NO_SIG;
|
|
|
/*
|
/*
|
* scan for more signals to process
|
* scan for more signals to process
|
*/
|
*/
|
|
|
/* first check for additional SIGCHLDs */
|
/* first check for additional SIGCHLDs */
|
if (sigchld_count) {
|
if (sigchld_count) {
|
got_sig = SIGCHLD;
|
got_sig = SIGCHLD;
|
traps[SIGCHLD].mark = TRUE;
|
traps[SIGCHLD].mark = TRUE;
|
Tcl_AsyncMark(async_handler);
|
Tcl_AsyncMark(async_handler);
|
} else {
|
} else {
|
got_sig = -1;
|
got_sig = -1;
|
for (i=1;i<NSIG;i++) {
|
for (i=1;i<NSIG;i++) {
|
if (traps[i].mark) {
|
if (traps[i].mark) {
|
got_sig = i;
|
got_sig = i;
|
Tcl_AsyncMark(async_handler);
|
Tcl_AsyncMark(async_handler);
|
break;
|
break;
|
}
|
}
|
}
|
}
|
}
|
}
|
return rc;
|
return rc;
|
}
|
}
|
|
|
#ifdef REARM_SIG
|
#ifdef REARM_SIG
|
int sigchld_sleep;
|
int sigchld_sleep;
|
static int rearm_sigchld = FALSE; /* TRUE if sigchld needs to be */
|
static int rearm_sigchld = FALSE; /* TRUE if sigchld needs to be */
|
/* rearmed (i.e., because it has
|
/* rearmed (i.e., because it has
|
/* just gone off) */
|
/* just gone off) */
|
static int rearming_sigchld = FALSE;
|
static int rearming_sigchld = FALSE;
|
#endif
|
#endif
|
|
|
/* called upon receipt of a user-declared signal */
|
/* called upon receipt of a user-declared signal */
|
static void
|
static void
|
bottomhalf(sig)
|
bottomhalf(sig)
|
int sig;
|
int sig;
|
{
|
{
|
#ifdef REARM_SIG
|
#ifdef REARM_SIG
|
/*
|
/*
|
* tiny window of death if same signal should arrive here
|
* tiny window of death if same signal should arrive here
|
* before we've reinstalled it
|
* before we've reinstalled it
|
*/
|
*/
|
|
|
/* In SV, sigchld must be rearmed after wait to avoid recursion */
|
/* In SV, sigchld must be rearmed after wait to avoid recursion */
|
if (sig != SIGCHLD) {
|
if (sig != SIGCHLD) {
|
signal(sig,bottomhalf);
|
signal(sig,bottomhalf);
|
} else {
|
} else {
|
/* request rearm */
|
/* request rearm */
|
rearm_sigchld = TRUE;
|
rearm_sigchld = TRUE;
|
if (rearming_sigchld) sigchld_sleep = TRUE;
|
if (rearming_sigchld) sigchld_sleep = TRUE;
|
}
|
}
|
#endif
|
#endif
|
|
|
traps[sig].mark = TRUE;
|
traps[sig].mark = TRUE;
|
got_sig = sig; /* just a hint - can be wiped out by another */
|
got_sig = sig; /* just a hint - can be wiped out by another */
|
Tcl_AsyncMark(async_handler);
|
Tcl_AsyncMark(async_handler);
|
|
|
/* if we are called while this particular async is being processed */
|
/* if we are called while this particular async is being processed */
|
/* original async_proc will turn off "mark" so that when async_proc */
|
/* original async_proc will turn off "mark" so that when async_proc */
|
/* is recalled, it will see that nothing was left to do */
|
/* is recalled, it will see that nothing was left to do */
|
|
|
/* In case of SIGCHLD though, we must recall it as many times as
|
/* In case of SIGCHLD though, we must recall it as many times as
|
* we have received it.
|
* we have received it.
|
*/
|
*/
|
if (sig == SIGCHLD) {
|
if (sig == SIGCHLD) {
|
sigchld_count++;
|
sigchld_count++;
|
/* exp_debuglog(stderr,"sigchld_count++ == %d\n",sigchld_count);*/
|
/* exp_debuglog(stderr,"sigchld_count++ == %d\n",sigchld_count);*/
|
}
|
}
|
#if 0
|
#if 0
|
/* if we are doing an i_read, restart it */
|
/* if we are doing an i_read, restart it */
|
if (env_valid && (sig != 0)) longjmp(env,2);
|
if (env_valid && (sig != 0)) longjmp(env,2);
|
#endif
|
#endif
|
}
|
}
|
|
|
/*ARGSUSED*/
|
/*ARGSUSED*/
|
void
|
void
|
exp_rearm_sigchld(interp)
|
exp_rearm_sigchld(interp)
|
Tcl_Interp *interp;
|
Tcl_Interp *interp;
|
{
|
{
|
#ifdef REARM_SIG
|
#ifdef REARM_SIG
|
if (rearm_sigchld) {
|
if (rearm_sigchld) {
|
rearm_sigchld = FALSE;
|
rearm_sigchld = FALSE;
|
rearming_sigchld = TRUE;
|
rearming_sigchld = TRUE;
|
signal(SIGCHLD,bottomhalf);
|
signal(SIGCHLD,bottomhalf);
|
}
|
}
|
|
|
rearming_sigchld = FALSE;
|
rearming_sigchld = FALSE;
|
|
|
/* if the rearming immediately caused another SIGCHLD, slow down */
|
/* if the rearming immediately caused another SIGCHLD, slow down */
|
/* It's probably one of Tcl's intermediary pipeline processes that */
|
/* It's probably one of Tcl's intermediary pipeline processes that */
|
/* Tcl hasn't caught up with yet. */
|
/* Tcl hasn't caught up with yet. */
|
if (sigchld_sleep) {
|
if (sigchld_sleep) {
|
exp_dsleep(interp,0.2);
|
exp_dsleep(interp,0.2);
|
sigchld_sleep = FALSE;
|
sigchld_sleep = FALSE;
|
}
|
}
|
#endif
|
#endif
|
}
|
}
|
|
|
|
|
void
|
void
|
exp_init_trap()
|
exp_init_trap()
|
{
|
{
|
int i;
|
int i;
|
|
|
for (i=1;i<NSIG;i++) {
|
for (i=1;i<NSIG;i++) {
|
traps[i].name = Tcl_SignalId(i);
|
traps[i].name = Tcl_SignalId(i);
|
traps[i].action = 0;
|
traps[i].action = 0;
|
traps[i].reserved = FALSE;
|
traps[i].reserved = FALSE;
|
}
|
}
|
|
|
/*
|
/*
|
* fix up any special cases
|
* fix up any special cases
|
*/
|
*/
|
|
|
#if defined(SIGCLD)
|
#if defined(SIGCLD)
|
/* Tcl names it SIGCLD, not good for portable scripts */
|
/* Tcl names it SIGCLD, not good for portable scripts */
|
traps[SIGCLD].name = "SIGCHLD";
|
traps[SIGCLD].name = "SIGCHLD";
|
#endif
|
#endif
|
#if defined(SIGALRM)
|
#if defined(SIGALRM)
|
traps[SIGALRM].reserved = TRUE;
|
traps[SIGALRM].reserved = TRUE;
|
#endif
|
#endif
|
#if defined(SIGKILL)
|
#if defined(SIGKILL)
|
traps[SIGKILL].reserved = TRUE;
|
traps[SIGKILL].reserved = TRUE;
|
#endif
|
#endif
|
#if defined(SIGSTOP)
|
#if defined(SIGSTOP)
|
traps[SIGSTOP].reserved = TRUE;
|
traps[SIGSTOP].reserved = TRUE;
|
#endif
|
#endif
|
|
|
async_handler = Tcl_AsyncCreate(tophalf,(ClientData)0);
|
async_handler = Tcl_AsyncCreate(tophalf,(ClientData)0);
|
|
|
}
|
}
|
|
|
/* given signal index or name as string, */
|
/* given signal index or name as string, */
|
/* returns signal index or -1 if bad arg */
|
/* returns signal index or -1 if bad arg */
|
int
|
int
|
exp_string_to_signal(interp,s)
|
exp_string_to_signal(interp,s)
|
Tcl_Interp *interp;
|
Tcl_Interp *interp;
|
char *s;
|
char *s;
|
{
|
{
|
int sig;
|
int sig;
|
char *name;
|
char *name;
|
|
|
/* try interpreting as an integer */
|
/* try interpreting as an integer */
|
if (1 == sscanf(s,"%d",&sig)) {
|
if (1 == sscanf(s,"%d",&sig)) {
|
if (sig > 0 && sig < NSIG) return sig;
|
if (sig > 0 && sig < NSIG) return sig;
|
} else {
|
} else {
|
/* try interpreting as a string */
|
/* try interpreting as a string */
|
for (sig=1;sig<NSIG;sig++) {
|
for (sig=1;sig<NSIG;sig++) {
|
name = traps[sig].name;
|
name = traps[sig].name;
|
if (streq(s,name) || streq(s,name+3)) return(sig);
|
if (streq(s,name) || streq(s,name+3)) return(sig);
|
}
|
}
|
}
|
}
|
|
|
exp_error(interp,"invalid signal %s",s);
|
exp_error(interp,"invalid signal %s",s);
|
|
|
return -1;
|
return -1;
|
}
|
}
|
|
|
/*ARGSUSED*/
|
/*ARGSUSED*/
|
int
|
int
|
Exp_TrapCmd(clientData, interp, argc, argv)
|
Exp_TrapCmd(clientData, interp, argc, argv)
|
ClientData clientData;
|
ClientData clientData;
|
Tcl_Interp *interp;
|
Tcl_Interp *interp;
|
int argc;
|
int argc;
|
char **argv;
|
char **argv;
|
{
|
{
|
char *action = 0;
|
char *action = 0;
|
int n; /* number of signals in list */
|
int n; /* number of signals in list */
|
char **list; /* list of signals */
|
char **list; /* list of signals */
|
int len; /* length of action */
|
int len; /* length of action */
|
int i;
|
int i;
|
int show_name = FALSE; /* if user asked for current sig by name */
|
int show_name = FALSE; /* if user asked for current sig by name */
|
int show_number = FALSE;/* if user asked for current sig by number */
|
int show_number = FALSE;/* if user asked for current sig by number */
|
int show_max = FALSE; /* if user asked for NSIG-1 */
|
int show_max = FALSE; /* if user asked for NSIG-1 */
|
int rc = TCL_OK;
|
int rc = TCL_OK;
|
int new_code = FALSE; /* if action result should overwrite orig */
|
int new_code = FALSE; /* if action result should overwrite orig */
|
Tcl_Interp *new_interp = interp;/* interp in which to evaluate */
|
Tcl_Interp *new_interp = interp;/* interp in which to evaluate */
|
/* action when signal occurs */
|
/* action when signal occurs */
|
|
|
argc--; argv++;
|
argc--; argv++;
|
|
|
while (*argv) {
|
while (*argv) {
|
if (streq(*argv,"-code")) {
|
if (streq(*argv,"-code")) {
|
argc--; argv++;
|
argc--; argv++;
|
new_code = TRUE;
|
new_code = TRUE;
|
} else if (streq(*argv,"-interp")) {
|
} else if (streq(*argv,"-interp")) {
|
argc--; argv++;
|
argc--; argv++;
|
new_interp = 0;
|
new_interp = 0;
|
} else if (streq(*argv,"-name")) {
|
} else if (streq(*argv,"-name")) {
|
argc--; argv++;
|
argc--; argv++;
|
show_name = TRUE;
|
show_name = TRUE;
|
} else if (streq(*argv,"-number")) {
|
} else if (streq(*argv,"-number")) {
|
argc--; argv++;
|
argc--; argv++;
|
show_number = TRUE;
|
show_number = TRUE;
|
} else if (streq(*argv,"-max")) {
|
} else if (streq(*argv,"-max")) {
|
argc--; argv++;
|
argc--; argv++;
|
show_max = TRUE;
|
show_max = TRUE;
|
} else break;
|
} else break;
|
}
|
}
|
|
|
if (show_name || show_number || show_max) {
|
if (show_name || show_number || show_max) {
|
if (argc > 0) goto usage_error;
|
if (argc > 0) goto usage_error;
|
if (show_max) {
|
if (show_max) {
|
sprintf(interp->result,"%d",NSIG-1);
|
sprintf(interp->result,"%d",NSIG-1);
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
|
|
if (current_sig == NO_SIG) {
|
if (current_sig == NO_SIG) {
|
exp_error(interp,"no signal in progress");
|
exp_error(interp,"no signal in progress");
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
if (show_name) {
|
if (show_name) {
|
/* skip over "SIG" */
|
/* skip over "SIG" */
|
interp->result = signal_to_string(current_sig) + 3;
|
interp->result = signal_to_string(current_sig) + 3;
|
} else {
|
} else {
|
sprintf(interp->result,"%d",current_sig);
|
sprintf(interp->result,"%d",current_sig);
|
}
|
}
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
|
|
if (argc == 0 || argc > 2) goto usage_error;
|
if (argc == 0 || argc > 2) goto usage_error;
|
|
|
if (argc == 1) {
|
if (argc == 1) {
|
int sig = exp_string_to_signal(interp,*argv);
|
int sig = exp_string_to_signal(interp,*argv);
|
if (sig == -1) return TCL_ERROR;
|
if (sig == -1) return TCL_ERROR;
|
|
|
if (traps[sig].action) {
|
if (traps[sig].action) {
|
Tcl_AppendResult(interp,traps[sig].action,(char *)0);
|
Tcl_AppendResult(interp,traps[sig].action,(char *)0);
|
} else {
|
} else {
|
interp->result = "SIG_DFL";
|
interp->result = "SIG_DFL";
|
}
|
}
|
return TCL_OK;
|
return TCL_OK;
|
}
|
}
|
|
|
action = *argv;
|
action = *argv;
|
|
|
/* argv[1] is the list of signals - crack it open */
|
/* argv[1] is the list of signals - crack it open */
|
if (TCL_OK != Tcl_SplitList(interp,argv[1],&n,&list)) {
|
if (TCL_OK != Tcl_SplitList(interp,argv[1],&n,&list)) {
|
errorlog("%s\r\n",interp->result);
|
errorlog("%s\r\n",interp->result);
|
goto usage_error;
|
goto usage_error;
|
}
|
}
|
|
|
for (i=0;i<n;i++) {
|
for (i=0;i<n;i++) {
|
int sig = exp_string_to_signal(interp,list[i]);
|
int sig = exp_string_to_signal(interp,list[i]);
|
if (sig == -1) {
|
if (sig == -1) {
|
rc = TCL_ERROR;
|
rc = TCL_ERROR;
|
break;
|
break;
|
}
|
}
|
|
|
if (traps[sig].reserved) {
|
if (traps[sig].reserved) {
|
exp_error(interp,"cannot trap %s",signal_to_string(sig));
|
exp_error(interp,"cannot trap %s",signal_to_string(sig));
|
rc = TCL_ERROR;
|
rc = TCL_ERROR;
|
break;
|
break;
|
}
|
}
|
|
|
#if 0
|
#if 0
|
#ifdef TCL_DEBUGGER
|
#ifdef TCL_DEBUGGER
|
if (sig == SIGINT && exp_tcl_debugger_available) {
|
if (sig == SIGINT && exp_tcl_debugger_available) {
|
exp_debuglog("trap: cannot trap SIGINT while using debugger\r\n");
|
exp_debuglog("trap: cannot trap SIGINT while using debugger\r\n");
|
continue;
|
continue;
|
}
|
}
|
#endif /* TCL_DEBUGGER */
|
#endif /* TCL_DEBUGGER */
|
#endif
|
#endif
|
|
|
exp_debuglog("trap: setting up signal %d (\"%s\")\r\n",sig,list[i]);
|
exp_debuglog("trap: setting up signal %d (\"%s\")\r\n",sig,list[i]);
|
|
|
if (traps[sig].action) ckfree(traps[sig].action);
|
if (traps[sig].action) ckfree(traps[sig].action);
|
|
|
if (streq(action,"SIG_DFL")) {
|
if (streq(action,"SIG_DFL")) {
|
/* should've been free'd by now if nec. */
|
/* should've been free'd by now if nec. */
|
traps[sig].action = 0;
|
traps[sig].action = 0;
|
signal(sig,SIG_DFL);
|
signal(sig,SIG_DFL);
|
#ifdef REARM_SIG
|
#ifdef REARM_SIG
|
if (sig == SIGCHLD)
|
if (sig == SIGCHLD)
|
rearm_sigchld = FALSE;
|
rearm_sigchld = FALSE;
|
#endif /*REARM_SIG*/
|
#endif /*REARM_SIG*/
|
} else {
|
} else {
|
len = 1 + strlen(action);
|
len = 1 + strlen(action);
|
traps[sig].action = ckalloc(len);
|
traps[sig].action = ckalloc(len);
|
memcpy(traps[sig].action,action,len);
|
memcpy(traps[sig].action,action,len);
|
traps[sig].interp = new_interp;
|
traps[sig].interp = new_interp;
|
traps[sig].code = new_code;
|
traps[sig].code = new_code;
|
if (streq(action,"SIG_IGN")) {
|
if (streq(action,"SIG_IGN")) {
|
signal(sig,SIG_IGN);
|
signal(sig,SIG_IGN);
|
} else signal(sig,bottomhalf);
|
} else signal(sig,bottomhalf);
|
}
|
}
|
}
|
}
|
ckfree((char *)list);
|
ckfree((char *)list);
|
return(rc);
|
return(rc);
|
usage_error:
|
usage_error:
|
exp_error(interp,"usage: trap [command or SIG_DFL or SIG_IGN] {list of signals}");
|
exp_error(interp,"usage: trap [command or SIG_DFL or SIG_IGN] {list of signals}");
|
return TCL_ERROR;
|
return TCL_ERROR;
|
}
|
}
|
|
|
/* called by tophalf() to process the given signal */
|
/* called by tophalf() to process the given signal */
|
static int
|
static int
|
eval_trap_action(interp,sig,trap,oldcode)
|
eval_trap_action(interp,sig,trap,oldcode)
|
Tcl_Interp *interp;
|
Tcl_Interp *interp;
|
int sig;
|
int sig;
|
struct trap *trap;
|
struct trap *trap;
|
int oldcode;
|
int oldcode;
|
{
|
{
|
int code_flag;
|
int code_flag;
|
int newcode;
|
int newcode;
|
Tcl_DString ei; /* errorInfo */
|
Tcl_DString ei; /* errorInfo */
|
char *eip;
|
char *eip;
|
Tcl_DString ec; /* errorCode */
|
Tcl_DString ec; /* errorCode */
|
char *ecp;
|
char *ecp;
|
Tcl_DString ir; /* interp->result */
|
Tcl_DString ir; /* interp->result */
|
|
|
exp_debuglog("async event handler: Tcl_Eval(%s)\r\n",trap->action);
|
exp_debuglog("async event handler: Tcl_Eval(%s)\r\n",trap->action);
|
|
|
/* save to prevent user from redefining trap->code while trap */
|
/* save to prevent user from redefining trap->code while trap */
|
/* is executing */
|
/* is executing */
|
code_flag = trap->code;
|
code_flag = trap->code;
|
|
|
if (!code_flag) {
|
if (!code_flag) {
|
/*
|
/*
|
* save return values
|
* save return values
|
*/
|
*/
|
eip = Tcl_GetVar(interp,"errorInfo",TCL_GLOBAL_ONLY);
|
eip = Tcl_GetVar(interp,"errorInfo",TCL_GLOBAL_ONLY);
|
if (eip) {
|
if (eip) {
|
Tcl_DStringInit(&ei);
|
Tcl_DStringInit(&ei);
|
eip = Tcl_DStringAppend(&ei,eip,-1);
|
eip = Tcl_DStringAppend(&ei,eip,-1);
|
}
|
}
|
ecp = Tcl_GetVar(interp,"errorCode",TCL_GLOBAL_ONLY);
|
ecp = Tcl_GetVar(interp,"errorCode",TCL_GLOBAL_ONLY);
|
if (ecp) {
|
if (ecp) {
|
Tcl_DStringInit(&ec);
|
Tcl_DStringInit(&ec);
|
ecp = Tcl_DStringAppend(&ec,ecp,-1);
|
ecp = Tcl_DStringAppend(&ec,ecp,-1);
|
}
|
}
|
/* I assume interp->result is always non-zero, right? */
|
/* I assume interp->result is always non-zero, right? */
|
Tcl_DStringInit(&ir);
|
Tcl_DStringInit(&ir);
|
Tcl_DStringAppend(&ir,interp->result,-1);
|
Tcl_DStringAppend(&ir,interp->result,-1);
|
}
|
}
|
|
|
newcode = Tcl_GlobalEval(interp,trap->action);
|
newcode = Tcl_GlobalEval(interp,trap->action);
|
|
|
/*
|
/*
|
* if new code is to be ignored (usual case - see "else" below)
|
* if new code is to be ignored (usual case - see "else" below)
|
* allow only OK/RETURN from trap, otherwise complain
|
* allow only OK/RETURN from trap, otherwise complain
|
*/
|
*/
|
|
|
if (code_flag) {
|
if (code_flag) {
|
exp_debuglog("return value = %d for trap %s, action %s\r\n",
|
exp_debuglog("return value = %d for trap %s, action %s\r\n",
|
newcode,signal_to_string(sig),trap->action);
|
newcode,signal_to_string(sig),trap->action);
|
if (*interp->result != 0) {
|
if (*interp->result != 0) {
|
errorlog("%s\r\n",interp->result);
|
errorlog("%s\r\n",interp->result);
|
|
|
/*
|
/*
|
* Check errorinfo and see if it contains -nostack.
|
* Check errorinfo and see if it contains -nostack.
|
* This shouldn't be necessary, but John changed the
|
* This shouldn't be necessary, but John changed the
|
* top level interp so that it distorts arbitrary
|
* top level interp so that it distorts arbitrary
|
* return values into TCL_ERROR, so by the time we
|
* return values into TCL_ERROR, so by the time we
|
* get back, we'll have lost the value of errorInfo
|
* get back, we'll have lost the value of errorInfo
|
*/
|
*/
|
|
|
eip = Tcl_GetVar(interp,"errorInfo",TCL_GLOBAL_ONLY);
|
eip = Tcl_GetVar(interp,"errorInfo",TCL_GLOBAL_ONLY);
|
exp_nostack_dump =
|
exp_nostack_dump =
|
(eip && (0 == strncmp("-nostack",eip,8)));
|
(eip && (0 == strncmp("-nostack",eip,8)));
|
}
|
}
|
} else if (newcode != TCL_OK && newcode != TCL_RETURN) {
|
} else if (newcode != TCL_OK && newcode != TCL_RETURN) {
|
if (newcode != TCL_ERROR) {
|
if (newcode != TCL_ERROR) {
|
exp_error(interp,"return value = %d for trap %s, action %s\r\n",newcode,signal_to_string(sig),trap->action);
|
exp_error(interp,"return value = %d for trap %s, action %s\r\n",newcode,signal_to_string(sig),trap->action);
|
}
|
}
|
Tcl_BackgroundError(interp);
|
Tcl_BackgroundError(interp);
|
}
|
}
|
|
|
if (!code_flag) {
|
if (!code_flag) {
|
/*
|
/*
|
* restore values
|
* restore values
|
*/
|
*/
|
Tcl_ResetResult(interp); /* turns off Tcl's internal */
|
Tcl_ResetResult(interp); /* turns off Tcl's internal */
|
/* flags: ERR_IN_PROGRESS, ERROR_CODE_SET */
|
/* flags: ERR_IN_PROGRESS, ERROR_CODE_SET */
|
|
|
if (eip) {
|
if (eip) {
|
Tcl_AddErrorInfo(interp,eip);
|
Tcl_AddErrorInfo(interp,eip);
|
Tcl_DStringFree(&ei);
|
Tcl_DStringFree(&ei);
|
} else {
|
} else {
|
Tcl_UnsetVar(interp,"errorInfo",0);
|
Tcl_UnsetVar(interp,"errorInfo",0);
|
}
|
}
|
|
|
/* restore errorCode. Note that Tcl_AddErrorInfo (above) */
|
/* restore errorCode. Note that Tcl_AddErrorInfo (above) */
|
/* resets it to NONE. If the previous value is NONE, it's */
|
/* resets it to NONE. If the previous value is NONE, it's */
|
/* important to avoid calling Tcl_SetErrorCode since this */
|
/* important to avoid calling Tcl_SetErrorCode since this */
|
/* with cause Tcl to set its internal ERROR_CODE_SET flag. */
|
/* with cause Tcl to set its internal ERROR_CODE_SET flag. */
|
if (ecp) {
|
if (ecp) {
|
if (!streq("NONE",ecp))
|
if (!streq("NONE",ecp))
|
Tcl_SetErrorCode(interp,ecp,(char *)0);
|
Tcl_SetErrorCode(interp,ecp,(char *)0);
|
Tcl_DStringFree(&ec);
|
Tcl_DStringFree(&ec);
|
} else {
|
} else {
|
Tcl_UnsetVar(interp,"errorCode",0);
|
Tcl_UnsetVar(interp,"errorCode",0);
|
}
|
}
|
|
|
Tcl_DStringResult(interp,&ir);
|
Tcl_DStringResult(interp,&ir);
|
Tcl_DStringFree(&ir);
|
Tcl_DStringFree(&ir);
|
|
|
newcode = oldcode;
|
newcode = oldcode;
|
|
|
/* note that since newcode gets overwritten here by old code */
|
/* note that since newcode gets overwritten here by old code */
|
/* it is possible to return in the middle of a trap by using */
|
/* it is possible to return in the middle of a trap by using */
|
/* "return" (or "continue" for that matter)! */
|
/* "return" (or "continue" for that matter)! */
|
}
|
}
|
return newcode;
|
return newcode;
|
}
|
}
|
|
|
static struct exp_cmd_data
|
static struct exp_cmd_data
|
cmd_data[] = {
|
cmd_data[] = {
|
{"trap", exp_proc(Exp_TrapCmd), (ClientData)EXP_SPAWN_ID_BAD, 0},
|
{"trap", exp_proc(Exp_TrapCmd), (ClientData)EXP_SPAWN_ID_BAD, 0},
|
{0}};
|
{0}};
|
|
|
void
|
void
|
exp_init_trap_cmds(interp)
|
exp_init_trap_cmds(interp)
|
Tcl_Interp *interp;
|
Tcl_Interp *interp;
|
{
|
{
|
exp_create_commands(interp,cmd_data);
|
exp_create_commands(interp,cmd_data);
|
}
|
}
|
|
|
|
|