#include "expect_cf.h"
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifndef NSIG
#ifdef _NSIG
#define NSIG _NSIG
#endif
#endif
#if defined(SIGCLD) && !defined(SIGCHLD)
#define SIGCHLD SIGCLD
#endif
#include "tcl.h"
#include "exp_rename.h"
#include "exp_prog.h"
#include "exp_command.h"
#include "exp_log.h"
#ifdef TCL_DEBUGGER
#include "Dbg.h"
#endif
#define NO_SIG 0
static struct trap {
char *action;
int mark;
Tcl_Interp *interp;
int code;
char *name;
int reserved;
} traps[NSIG];
int sigchld_count = 0;
static int eval_trap_action();
static int got_sig;
static Tcl_AsyncHandler async_handler;
static char *
signal_to_string(sig)
int sig;
{
if (sig <= 0 || sig > NSIG) return("SIGNAL OUT OF RANGE");
return(traps[sig].name);
}
static int current_sig = NO_SIG;
int exp_nostack_dump = FALSE;
static int
tophalf(clientData,interp,code)
ClientData clientData;
Tcl_Interp *interp;
int code;
{
struct trap *trap;
int rc;
int i;
Tcl_Interp *sig_interp;
exp_debuglog("sighandler: handling signal(%d)\r\n",got_sig);
if (got_sig <= 0 || got_sig >= NSIG) {
errorlog("caught impossible signal %d\r\n",got_sig);
abort();
}
current_sig = got_sig;
trap = &traps[current_sig];
trap->mark = FALSE;
if (current_sig == SIGCHLD) {
sigchld_count--;
exp_debuglog("sigchld_count-- == %d\n",sigchld_count);
}
if (!trap->action) {
if (current_sig == 0) return code;
errorlog("caught unexpected signal: %s (%d)\r\n",
signal_to_string(current_sig),current_sig);
abort();
}
if (trap->interp) {
sig_interp = trap->interp;
} else if (!interp) {
sig_interp = interp;
} else {
sig_interp = exp_interp;
}
rc = eval_trap_action(sig_interp,current_sig,trap,code);
current_sig = NO_SIG;
if (sigchld_count) {
got_sig = SIGCHLD;
traps[SIGCHLD].mark = TRUE;
Tcl_AsyncMark(async_handler);
} else {
got_sig = -1;
for (i=1;i<NSIG;i++) {
if (traps[i].mark) {
got_sig = i;
Tcl_AsyncMark(async_handler);
break;
}
}
}
return rc;
}
#ifdef REARM_SIG
int sigchld_sleep;
static int rearm_sigchld = FALSE;
static int rearming_sigchld = FALSE;
#endif
static void
bottomhalf(sig)
int sig;
{
#ifdef REARM_SIG
if (sig != SIGCHLD) {
signal(sig,bottomhalf);
} else {
rearm_sigchld = TRUE;
if (rearming_sigchld) sigchld_sleep = TRUE;
}
#endif
traps[sig].mark = TRUE;
got_sig = sig;
Tcl_AsyncMark(async_handler);
if (sig == SIGCHLD) {
sigchld_count++;
}
#if 0
if (env_valid && (sig != 0)) longjmp(env,2);
#endif
}
void
exp_rearm_sigchld(interp)
Tcl_Interp *interp;
{
#ifdef REARM_SIG
if (rearm_sigchld) {
rearm_sigchld = FALSE;
rearming_sigchld = TRUE;
signal(SIGCHLD,bottomhalf);
}
rearming_sigchld = FALSE;
if (sigchld_sleep) {
exp_dsleep(interp,0.2);
sigchld_sleep = FALSE;
}
#endif
}
void
exp_init_trap()
{
int i;
for (i=1;i<NSIG;i++) {
traps[i].name = Tcl_SignalId(i);
traps[i].action = 0;
traps[i].reserved = FALSE;
}
#if defined(SIGCLD)
traps[SIGCLD].name = "SIGCHLD";
#endif
#if defined(SIGALRM)
traps[SIGALRM].reserved = TRUE;
#endif
#if defined(SIGKILL)
traps[SIGKILL].reserved = TRUE;
#endif
#if defined(SIGSTOP)
traps[SIGSTOP].reserved = TRUE;
#endif
async_handler = Tcl_AsyncCreate(tophalf,(ClientData)0);
}
int
exp_string_to_signal(interp,s)
Tcl_Interp *interp;
char *s;
{
int sig;
char *name;
if (1 == sscanf(s,"%d",&sig)) {
if (sig > 0 && sig < NSIG) return sig;
} else {
for (sig=1;sig<NSIG;sig++) {
name = traps[sig].name;
if (streq(s,name) || streq(s,name+3)) return(sig);
}
}
exp_error(interp,"invalid signal %s",s);
return -1;
}
int
Exp_TrapCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
char *action = 0;
int n;
char **list;
int len;
int i;
int show_name = FALSE;
int show_number = FALSE;
int show_max = FALSE;
int rc = TCL_OK;
int new_code = FALSE;
Tcl_Interp *new_interp = interp;
argc--; argv++;
while (*argv) {
if (streq(*argv,"-code")) {
argc--; argv++;
new_code = TRUE;
} else if (streq(*argv,"-interp")) {
argc--; argv++;
new_interp = 0;
} else if (streq(*argv,"-name")) {
argc--; argv++;
show_name = TRUE;
} else if (streq(*argv,"-number")) {
argc--; argv++;
show_number = TRUE;
} else if (streq(*argv,"-max")) {
argc--; argv++;
show_max = TRUE;
} else break;
}
if (show_name || show_number || show_max) {
if (argc > 0) goto usage_error;
if (show_max) {
sprintf(interp->result,"%d",NSIG-1);
return TCL_OK;
}
if (current_sig == NO_SIG) {
exp_error(interp,"no signal in progress");
return TCL_ERROR;
}
if (show_name) {
interp->result = signal_to_string(current_sig) + 3;
} else {
sprintf(interp->result,"%d",current_sig);
}
return TCL_OK;
}
if (argc == 0 || argc > 2) goto usage_error;
if (argc == 1) {
int sig = exp_string_to_signal(interp,*argv);
if (sig == -1) return TCL_ERROR;
if (traps[sig].action) {
Tcl_AppendResult(interp,traps[sig].action,(char *)0);
} else {
interp->result = "SIG_DFL";
}
return TCL_OK;
}
action = *argv;
if (TCL_OK != Tcl_SplitList(interp,argv[1],&n,&list)) {
errorlog("%s\r\n",interp->result);
goto usage_error;
}
for (i=0;i<n;i++) {
int sig = exp_string_to_signal(interp,list[i]);
if (sig == -1) {
rc = TCL_ERROR;
break;
}
if (traps[sig].reserved) {
exp_error(interp,"cannot trap %s",signal_to_string(sig));
rc = TCL_ERROR;
break;
}
#if 0
#ifdef TCL_DEBUGGER
if (sig == SIGINT && exp_tcl_debugger_available) {
exp_debuglog("trap: cannot trap SIGINT while using debugger\r\n");
continue;
}
#endif
#endif
exp_debuglog("trap: setting up signal %d (\"%s\")\r\n",sig,list[i]);
if (traps[sig].action) ckfree(traps[sig].action);
if (streq(action,"SIG_DFL")) {
traps[sig].action = 0;
signal(sig,SIG_DFL);
#ifdef REARM_SIG
if (sig == SIGCHLD)
rearm_sigchld = FALSE;
#endif
} else {
len = 1 + strlen(action);
traps[sig].action = ckalloc(len);
memcpy(traps[sig].action,action,len);
traps[sig].interp = new_interp;
traps[sig].code = new_code;
if (streq(action,"SIG_IGN")) {
signal(sig,SIG_IGN);
} else signal(sig,bottomhalf);
}
}
ckfree((char *)list);
return(rc);
usage_error:
exp_error(interp,"usage: trap [command or SIG_DFL or SIG_IGN] {list of signals}");
return TCL_ERROR;
}
static int
eval_trap_action(interp,sig,trap,oldcode)
Tcl_Interp *interp;
int sig;
struct trap *trap;
int oldcode;
{
int code_flag;
int newcode;
Tcl_DString ei;
char *eip;
Tcl_DString ec;
char *ecp;
Tcl_DString ir;
exp_debuglog("async event handler: Tcl_Eval(%s)\r\n",trap->action);
code_flag = trap->code;
if (!code_flag) {
eip = Tcl_GetVar(interp,"errorInfo",TCL_GLOBAL_ONLY);
if (eip) {
Tcl_DStringInit(&ei);
eip = Tcl_DStringAppend(&ei,eip,-1);
}
ecp = Tcl_GetVar(interp,"errorCode",TCL_GLOBAL_ONLY);
if (ecp) {
Tcl_DStringInit(&ec);
ecp = Tcl_DStringAppend(&ec,ecp,-1);
}
Tcl_DStringInit(&ir);
Tcl_DStringAppend(&ir,interp->result,-1);
}
newcode = Tcl_GlobalEval(interp,trap->action);
if (code_flag) {
exp_debuglog("return value = %d for trap %s, action %s\r\n",
newcode,signal_to_string(sig),trap->action);
if (*interp->result != 0) {
errorlog("%s\r\n",interp->result);
eip = Tcl_GetVar(interp,"errorInfo",TCL_GLOBAL_ONLY);
exp_nostack_dump =
(eip && (0 == strncmp("-nostack",eip,8)));
}
} else if (newcode != TCL_OK && newcode != TCL_RETURN) {
if (newcode != TCL_ERROR) {
exp_error(interp,"return value = %d for trap %s, action %s\r\n",newcode,signal_to_string(sig),trap->action);
}
Tcl_BackgroundError(interp);
}
if (!code_flag) {
Tcl_ResetResult(interp);
if (eip) {
Tcl_AddErrorInfo(interp,eip);
Tcl_DStringFree(&ei);
} else {
Tcl_UnsetVar(interp,"errorInfo",0);
}
if (ecp) {
if (!streq("NONE",ecp))
Tcl_SetErrorCode(interp,ecp,(char *)0);
Tcl_DStringFree(&ec);
} else {
Tcl_UnsetVar(interp,"errorCode",0);
}
Tcl_DStringResult(interp,&ir);
Tcl_DStringFree(&ir);
newcode = oldcode;
}
return newcode;
}
static struct exp_cmd_data
cmd_data[] = {
{"trap", exp_proc(Exp_TrapCmd), (ClientData)EXP_SPAWN_ID_BAD, 0},
{0}};
void
exp_init_trap_cmds(interp)
Tcl_Interp *interp;
{
exp_create_commands(interp,cmd_data);
}