#include "expect_cf.h"
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#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 "tcldbg.h"
#endif
#define NO_SIG 0
static struct trap {
char *action;
int mark;
Tcl_Interp *interp;
int code;
CONST 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 CONST 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;
expDiagLog("sighandler: handling signal(%d)\r\n",got_sig);
if (got_sig <= 0 || got_sig >= NSIG) {
expErrorLog("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--;
expDiagLog("sigchld_count-- == %d\n",sigchld_count);
}
if (!trap->action) {
if (current_sig == 0) return code;
expErrorLog("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
#ifdef HAVE_SIGLONGJMP
if (env_valid && (sig != 0)) siglongjmp(env,2);
#else
if (env_valid && (sig != 0)) longjmp(env,2);
#endif
#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;
CONST 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_TrapObjCmd(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
char *action = 0;
int n;
Tcl_Obj **list;
char *arg;
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;
objc--; objv++;
while (objc) {
arg = Tcl_GetString(*objv);
if (streq(arg,"-code")) {
objc--; objv++;
new_code = TRUE;
} else if (streq(arg,"-interp")) {
objc--; objv++;
new_interp = 0;
} else if (streq(arg,"-name")) {
objc--; objv++;
show_name = TRUE;
} else if (streq(arg,"-number")) {
objc--; objv++;
show_number = TRUE;
} else if (streq(arg,"-max")) {
objc--; objv++;
show_max = TRUE;
} else break;
}
if (show_name || show_number || show_max) {
if (objc > 0) goto usage_error;
if (show_max) {
Tcl_SetObjResult(interp,Tcl_NewIntObj(NSIG-1));
}
if (current_sig == NO_SIG) {
Tcl_SetResult(interp,"no signal in progress",TCL_STATIC);
return TCL_ERROR;
}
if (show_name) {
Tcl_SetResult(interp,(char*)signal_to_string(current_sig) + 3,TCL_STATIC);
} else {
Tcl_SetObjResult(interp,Tcl_NewIntObj(current_sig));
}
return TCL_OK;
}
if (objc == 0 || objc > 2) goto usage_error;
if (objc == 1) {
int sig = exp_string_to_signal(interp,arg);
if (sig == -1) return TCL_ERROR;
if (traps[sig].action) {
Tcl_SetResult(interp,traps[sig].action,TCL_STATIC);
} else {
Tcl_SetResult(interp,"SIG_DFL",TCL_STATIC);
}
return TCL_OK;
}
action = arg;
if (TCL_OK != Tcl_ListObjGetElements(interp,objv[1],&n,&list)) {
return TCL_ERROR;
}
for (i=0;i<n;i++) {
char *s;
int sig;
s = Tcl_GetString(list[i]);
sig = exp_string_to_signal(interp,s);
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;
}
expDiagLog("trap: setting up signal %d (\"%s\")\r\n",sig,s);
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);
}
}
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_Obj *eip;
Tcl_Obj *ecp;
Tcl_Obj *irp;
expDiagLogU("async event handler: Tcl_Eval(");
expDiagLogU(trap->action);
expDiagLogU(")\r\n");
code_flag = trap->code;
if (!code_flag) {
eip = Tcl_GetVar2Ex(interp,"errorInfo","",TCL_GLOBAL_ONLY);
if (eip) eip = Tcl_DuplicateObj(eip);
ecp = Tcl_GetVar2Ex(interp,"errorCode","",TCL_GLOBAL_ONLY);
if (ecp) ecp = Tcl_DuplicateObj(ecp);
irp = Tcl_GetObjResult(interp);
if (irp) irp = Tcl_DuplicateObj(irp);
}
newcode = Tcl_GlobalEval(interp,trap->action);
if (code_flag) {
expDiagLog("return value = %d for trap %s, action ",newcode,signal_to_string(sig));
expDiagLogU(trap->action);
expDiagLogU("\r\n");
if (0 != strcmp(Tcl_GetStringResult(interp),"")) {
eip = Tcl_GetVar2Ex(interp,"errorInfo","",TCL_GLOBAL_ONLY);
if (eip) {
exp_nostack_dump = (0 == strncmp("-nostack",Tcl_GetString(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) {
int len;
char *s = Tcl_GetStringFromObj(eip,&len);
Tcl_AddObjErrorInfo(interp,s,len);
Tcl_DecrRefCount(eip);
} else {
Tcl_UnsetVar(interp,"errorInfo",0);
}
if (ecp) {
if (!streq("NONE",Tcl_GetString(ecp)))
Tcl_SetErrorCode(interp,ecp);
} else {
Tcl_UnsetVar(interp,"errorCode",0);
}
newcode = oldcode;
}
return newcode;
}
static struct exp_cmd_data
cmd_data[] = {
{"trap", Exp_TrapObjCmd, 0, (ClientData)EXP_SPAWN_ID_BAD, 0},
{0}};
void
exp_init_trap_cmds(interp)
Tcl_Interp *interp;
{
exp_create_commands(interp,cmd_data);
}