#include "expect_cf.h"
#include <stdio.h>
#include <sys/types.h>
#ifdef HAVE_STROPTS_H
# include <sys/stropts.h>
#endif
#include <sys/ioctl.h>
#ifdef HAVE_SYS_FCNTL_H
# include <sys/fcntl.h>
#else
# include <fcntl.h>
#endif
#include <sys/file.h>
#include "exp_tty.h"
#include <errno.h>
#include <signal.h>
#if defined(SIGCLD) && !defined(SIGCHLD)
#define SIGCHLD SIGCLD
#endif
#ifdef HAVE_PTYTRAP
#include <sys/ptyio.h>
#endif
#ifdef CRAY
# ifndef TCSETCTTY
# if defined(HAVE_TERMIOS)
# include <termios.h>
# else
# include <termio.h>
# endif
# endif
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <math.h>
#include <ctype.h>
#include "tclInt.h"
#include "tcl.h"
#include "string.h"
#include "expect_tcl.h"
#include "exp_rename.h"
#include "exp_prog.h"
#include "exp_command.h"
#include "exp_log.h"
#include "exp_event.h"
#include "exp_pty.h"
#ifdef TCL_DEBUGGER
#include "tcldbg.h"
#endif
#define NULL_STRING "\300\200"
#define NULL_LENGTH 2
#define SPAWN_ID_VARNAME "spawn_id"
int exp_getptymaster();
int exp_getptyslave();
int exp_forked = FALSE;
static int sendCD_error = 2;
static int sendCD_user = 3;
static int sendCD_proc = 4;
static int sendCD_tty = 6;
int expect_key = 0;
int exp_configure_count = 0;
#ifdef HAVE_PTYTRAP
static Tcl_HashTable slaveNames;
#endif
typedef struct ThreadSpecificData {
ExpState *stdinout;
ExpState *stderrX;
ExpState *devtty;
ExpState *any;
Tcl_Channel *diagChannel;
Tcl_DString diagDString;
int diagEnabled;
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;
#ifdef FULLTRAPS
static void
init_traps(traps)
RETSIGTYPE (*traps[])();
{
int i;
for (i=1;i<NSIG;i++) {
traps[i] = SIG_ERR;
}
}
#endif
void
exp_error TCL_VARARGS_DEF(Tcl_Interp *,arg1)
{
Tcl_Interp *interp;
char *fmt;
va_list args;
char buffer[2000];
interp = TCL_VARARGS_START(Tcl_Interp *,arg1,args);
fmt = va_arg(args,char *);
vsprintf(buffer,fmt,args);
Tcl_SetResult(interp,buffer,TCL_VOLATILE);
va_end(args);
}
struct ExpState *
expStateCurrent(interp,opened,adjust,any)
Tcl_Interp *interp;
int opened;
int adjust;
int any;
{
static char *user_spawn_id = "exp0";
char *name = exp_get_var(interp,SPAWN_ID_VARNAME);
if (!name) name = user_spawn_id;
return expStateFromChannelName(interp,name,opened,adjust,any,SPAWN_ID_VARNAME);
}
ExpState *
expStateCheck(interp,esPtr,open,adjust,msg)
Tcl_Interp *interp;
ExpState *esPtr;
int open;
int adjust;
char *msg;
{
if (open && !esPtr->open) {
exp_error(interp,"%s: spawn id %s not open",msg,esPtr->name);
return(0);
}
if (adjust) expAdjust(esPtr);
return esPtr;
}
ExpState *
expStateFromChannelName(interp,name,open,adjust,any,msg)
Tcl_Interp *interp;
char *name;
int open;
int adjust;
char *msg;
{
ExpState *esPtr;
Tcl_Channel channel;
CONST char *chanName;
if (any) {
if (0 == strcmp(name,EXP_SPAWN_ID_ANY_LIT)) {
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
return tsdPtr->any;
}
}
channel = Tcl_GetChannel(interp,name,(int *)0);
if (!channel) return(0);
chanName = Tcl_GetChannelName(channel);
if (!isExpChannelName(chanName)) {
exp_error(interp,"%s: %s is not an expect channel - use spawn -open to convert",msg,chanName);
return(0);
}
esPtr = (ExpState *)Tcl_GetChannelInstanceData(channel);
return expStateCheck(interp,esPtr,open,adjust,msg);
}
static void
exp_wait_zero(status)
WAIT_STATUS_TYPE *status;
{
int i;
for (i=0;i<sizeof(WAIT_STATUS_TYPE);i++) {
((char *)status)[i] = 0;
}
}
void
exp_state_prep_for_invalidation(interp,esPtr)
Tcl_Interp *interp;
ExpState *esPtr;
{
exp_ecmd_remove_state_direct_and_indirect(interp,esPtr);
exp_configure_count++;
if (esPtr->fg_armed) {
exp_event_disarm_fg(esPtr);
}
}
void
exp_trap_on(master)
int master;
{
#ifdef HAVE_PTYTRAP
if (master == -1) return;
exp_slave_control(master,1);
#endif
}
int
exp_trap_off(name)
char *name;
{
#ifdef HAVE_PTYTRAP
ExpState *esPtr;
int enable = 0;
Tcl_HashEntry *entry = Tcl_FindHashEntry(&slaveNames,name);
if (!entry) {
expDiagLog("exp_trap_off: no entry found for %s\n",name);
return -1;
}
esPtr = (ExpState *)Tcl_GetHashValue(entry);
exp_slave_control(esPtr->fdin,0);
return esPtr->fdin;
#else
return name[0];
#endif
}
static
void
expBusy(esPtr)
ExpState *esPtr;
{
int x = open("/dev/null",0);
if (x != esPtr->fdin) {
fcntl(x,F_DUPFD,esPtr->fdin);
close(x);
}
expCloseOnExec(esPtr->fdin);
esPtr->fdBusy = TRUE;
}
int
exp_close(interp,esPtr)
Tcl_Interp *interp;
ExpState *esPtr;
{
if (0 == expStateCheck(interp,esPtr,1,0,"close")) return TCL_ERROR;
esPtr->open = FALSE;
close(esPtr->fdin);
if (esPtr->fd_slave != EXP_NOFD) close(esPtr->fd_slave);
if (esPtr->fdin != esPtr->fdout) close(esPtr->fdout);
if (esPtr->channel_orig && !esPtr->leaveopen) {
Tcl_VarEval(interp,"close ",Tcl_GetChannelName(esPtr->channel_orig),
(char *)0);
}
#ifdef HAVE_PTYTRAP
if (esPtr->slave_name) {
Tcl_HashEntry *entry;
entry = Tcl_FindHashEntry(&slaveNames,esPtr->slave_name);
Tcl_DeleteHashEntry(entry);
ckfree(esPtr->slave_name);
esPtr->slave_name = 0;
}
#endif
exp_state_prep_for_invalidation(interp,esPtr);
if (esPtr->user_waited) {
if (esPtr->registered) {
Tcl_UnregisterChannel(interp,esPtr->channel);
}
} else {
expBusy(esPtr);
}
return(TCL_OK);
}
expStateAnyIs(esPtr)
ExpState *esPtr;
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
return (esPtr == tsdPtr->any);
}
expDevttyIs(esPtr)
ExpState *esPtr;
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
return (esPtr == tsdPtr->devtty);
}
int
expStdinoutIs(esPtr)
ExpState *esPtr;
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
return (tsdPtr->stdinout == esPtr);
}
ExpState *
expStdinoutGet()
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
return tsdPtr->stdinout;
}
ExpState *
expDevttyGet()
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
return tsdPtr->devtty;
}
void
exp_init_spawn_id_vars(interp)
Tcl_Interp *interp;
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
Tcl_SetVar(interp, "user_spawn_id", tsdPtr->stdinout->name,0);
Tcl_SetVar(interp,"error_spawn_id", tsdPtr->stderrX->name,0);
Tcl_SetVar(interp, "any_spawn_id", EXP_SPAWN_ID_ANY_LIT,0);
if (exp_dev_tty != -1) {
Tcl_SetVar(interp,"tty_spawn_id",tsdPtr->devtty->name,0);
}
}
void
exp_init_spawn_ids(interp)
Tcl_Interp *interp;
{
static ExpState any_placeholder;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
tsdPtr->stdinout = expCreateChannel(interp,0,1,isatty(0)?exp_getpid:EXP_NOPID);
tsdPtr->stdinout->keepForever = 1;
tsdPtr->stderrX = expCreateChannel(interp,2,2,isatty(2)?exp_getpid:EXP_NOPID);
tsdPtr->stderrX->keepForever = 1;
if (exp_dev_tty != -1) {
tsdPtr->devtty = expCreateChannel(interp,exp_dev_tty,exp_dev_tty,exp_getpid);
tsdPtr->devtty->keepForever = 1;
}
tsdPtr->any = &any_placeholder;
}
void
expCloseOnExec(fd)
int fd;
{
(void) fcntl(fd,F_SETFD,1);
}
#define STTY_INIT "stty_init"
#if 0
static void
show_pgrp(fd,string)
int fd;
char *string;
{
int pgrp;
fprintf(stderr,"getting pgrp for %s\n",string);
if (-1 == ioctl(fd,TIOCGETPGRP,&pgrp)) perror("TIOCGETPGRP");
else fprintf(stderr,"%s pgrp = %d\n",string,pgrp);
if (-1 == ioctl(fd,TIOCGPGRP,&pgrp)) perror("TIOCGPGRP");
else fprintf(stderr,"%s pgrp = %d\n",string,pgrp);
if (-1 == tcgetpgrp(fd,pgrp)) perror("tcgetpgrp");
else fprintf(stderr,"%s pgrp = %d\n",string,pgrp);
}
static void
set_pgrp(fd)
int fd;
{
int pgrp = getpgrp(0);
if (-1 == ioctl(fd,TIOCSETPGRP,&pgrp)) perror("TIOCSETPGRP");
if (-1 == ioctl(fd,TIOCSPGRP,&pgrp)) perror("TIOCSPGRP");
if (-1 == tcsetpgrp(fd,pgrp)) perror("tcsetpgrp");
}
#endif
static
void
expSetpgrp()
{
#ifdef MIPS_BSD
# include <sysv/sys.s>
syscall(SYS_setpgrp);
#endif
#ifdef SETPGRP_VOID
(void) setpgrp();
#else
(void) setpgrp(0,0);
#endif
}
static void
set_slave_name(esPtr,name)
ExpState *esPtr;
char *name;
{
#ifdef HAVE_PTYTRAP
int newptr;
Tcl_HashEntry *entry;
esPtr->slave_name = ckalloc(strlen(exp_pty_slave_name)+1);
strcpy(esPtr->slave_name,exp_pty_slave_name);
entry = Tcl_CreateHashEntry(&slaveNames,exp_pty_slave_name,&newptr);
Tcl_SetHashValue(entry,(ClientData)esPtr);
#endif
}
static int
Exp_SpawnCmd(clientData,interp,argc,argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
ExpState *esPtr = 0;
int slave;
int pid;
char **a;
int errorfd;
int ttyfd;
int master;
int write_master;
int ttyinit = TRUE;
int ttycopy = TRUE;
int echo = TRUE;
int console = FALSE;
int pty_only = FALSE;
#ifdef FULLTRAPS
RETSIGTYPE (*traps[NSIG])();
#endif
int ignore[NSIG];
int i;
char *argv0 = argv[0];
char *chanName = 0;
int leaveopen = FALSE;
int rc, wc;
CONST char *stty_init;
int slave_write_ioctls = 1;
int slave_opens = 3;
int sync_fds[2];
int sync2_fds[2];
int status_pipe[2];
int child_errno;
char sync_byte;
Tcl_Channel channel;
Tcl_DString dstring;
Tcl_DStringInit(&dstring);
#ifdef FULLTRAPS
init_traps(&traps);
#endif
for (i=1;i<NSIG;i++) {
ignore[i] = FALSE;
}
argc--; argv++;
for (;argc>0;argc--,argv++) {
if (streq(*argv,"-nottyinit")) {
ttyinit = FALSE;
slave_write_ioctls--;
slave_opens--;
} else if (streq(*argv,"-nottycopy")) {
ttycopy = FALSE;
} else if (streq(*argv,"-noecho")) {
echo = FALSE;
} else if (streq(*argv,"-console")) {
console = TRUE;
} else if (streq(*argv,"-pty")) {
pty_only = TRUE;
} else if (streq(*argv,"-open")) {
if (argc < 2) {
exp_error(interp,"usage: -open file-identifier");
return TCL_ERROR;
}
chanName = argv[1];
argc--; argv++;
} else if (streq(*argv,"-leaveopen")) {
if (argc < 2) {
exp_error(interp,"usage: -open file-identifier");
return TCL_ERROR;
}
chanName = argv[1];
leaveopen = TRUE;
argc--; argv++;
} else if (streq(*argv,"-ignore")) {
int sig;
if (argc < 2) {
exp_error(interp,"usage: -ignore signal");
return TCL_ERROR;
}
sig = exp_string_to_signal(interp,argv[1]);
if (sig == -1) {
exp_error(interp,"usage: -ignore %s: unknown signal name",argv[1]);
return TCL_ERROR;
}
ignore[sig] = TRUE;
argc--; argv++;
#ifdef FULLTRAPS
} else if (streq(*argv,"-trap")) {
RETSIGTYPE (*sig_handler)();
int n;
char **list;
if (argc < 3) {
exp_error(interp,"usage: -trap siglist SIG_DFL or SIG_IGN");
return TCL_ERROR;
}
if (0 == strcmp(argv[2],"SIG_DFL")) {
sig_handler = SIG_DFL;
} else if (0 == strcmp(argv[2],"SIG_IGN")) {
sig_handler = SIG_IGN;
} else {
exp_error(interp,"usage: -trap siglist SIG_DFL or SIG_IGN");
return TCL_ERROR;
}
if (TCL_OK != Tcl_SplitList(interp,argv[1],&n,&list)) {
expErrorLogU(interp->result);
expErrorLogU("\r\n");
exp_error(interp,"usage: -trap {siglist} ...");
return TCL_ERROR;
}
for (i=0;i<n;i++) {
int sig = exp_string_to_signal(interp,list[i]);
if (sig == -1) {
ckfree((char *)&list);
return TCL_ERROR;
}
traps[sig] = sig_handler;
}
ckfree((char *)&list);
argc--; argv++;
argc--; argv++;
#endif
} else break;
}
if (chanName && (argc != 0)) {
exp_error(interp,"usage: -[leave]open [fileXX]");
return TCL_ERROR;
}
if (!pty_only && !chanName && (argc == 0)) {
exp_error(interp,"usage: spawn [spawn-args] program [program-args]");
return(TCL_ERROR);
}
stty_init = exp_get_var(interp,STTY_INIT);
if (stty_init) {
slave_write_ioctls++;
slave_opens++;
}
#if defined(TIOCSCTTY) && !defined(CIBAUD) && !defined(sun) && !defined(hp9000s300)
slave_write_ioctls++;
slave_opens++;
#endif
exp_pty_slave_name = 0;
Tcl_ReapDetachedProcs();
if (!chanName) {
if (echo) {
expStdoutLogU(argv0,0);
for (a = argv;*a;a++) {
expStdoutLogU(" ",0);
expStdoutLogU(*a,0);
}
expStdoutLogU("\r\n",0);
}
if (0 > (master = exp_getptymaster())) {
int testfd;
if (exp_pty_error) {
exp_error(interp,"%s",exp_pty_error);
return TCL_ERROR;
}
if (expChannelCountGet() > 10) {
exp_error(interp,"The system only has a finite number of ptys and you have many of them in use. The usual reason for this is that you forgot (or didn't know) to call \"wait\" after closing each of them.");
return TCL_ERROR;
}
testfd = open("/",0);
close(testfd);
if (testfd != -1) {
exp_error(interp,"The system has no more ptys. Ask your system administrator to create more.");
} else {
exp_error(interp,"- You have too many files are open. Close some files or increase your per-process descriptor limit.");
}
return(TCL_ERROR);
}
expCloseOnExec(master);
#define SPAWN_OUT "spawn_out"
Tcl_SetVar2(interp,SPAWN_OUT,"slave,name",exp_pty_slave_name,0);
if (pty_only) {
write_master = master;
}
} else {
int mode;
int rfd, wfd;
if (echo) {
expStdoutLogU(argv0,0);
expStdoutLogU(" [open ...]\r\n",0);
}
if (!(channel = Tcl_GetChannel(interp,chanName,&mode))) {
return TCL_ERROR;
}
if (!mode) {
exp_error(interp,"channel is neither readable nor writable");
return TCL_ERROR;
}
if (mode & TCL_READABLE) {
if (TCL_ERROR == Tcl_GetChannelHandle(channel, TCL_READABLE, (ClientData) &rfd)) {
return TCL_ERROR;
}
}
if (mode & TCL_WRITABLE) {
if (TCL_ERROR == Tcl_GetChannelHandle(channel, TCL_WRITABLE, (ClientData) &wfd)) {
return TCL_ERROR;
}
}
master = ((mode & TCL_READABLE)?rfd:wfd);
if (-1 == (write_master = master = dup(master))) {
exp_error(interp,"fdopen: %s",Tcl_PosixError(interp));
return TCL_ERROR;
}
if ((mode & TCL_READABLE) && (mode & TCL_WRITABLE) && (wfd != rfd)) {
if (-1 == (write_master = dup(wfd))) {
exp_error(interp,"fdopen: %s",Tcl_PosixError(interp));
return TCL_ERROR;
}
}
}
if (chanName || pty_only) {
esPtr = expCreateChannel(interp,master,write_master,EXP_NOPID);
if (chanName) {
esPtr->channel_orig = channel;
esPtr->leaveopen = leaveopen;
}
if (exp_pty_slave_name) set_slave_name(esPtr,exp_pty_slave_name);
esPtr->sys_waited = TRUE;
exp_wait_zero(&esPtr->wait);
Tcl_SetVar(interp,SPAWN_ID_VARNAME,esPtr->name,0);
if (!chanName) {
char value[20];
if (0 > (esPtr->fd_slave = exp_getptyslave(ttycopy,ttyinit,
stty_init))) {
exp_error(interp,"open(slave pty): %s\r\n",Tcl_PosixError(interp));
return TCL_ERROR;
}
exp_slave_control(master,1);
sprintf(value,"%d",esPtr->fd_slave);
Tcl_SetVar2(interp,SPAWN_OUT,"slave,fd",value,0);
}
sprintf(interp->result,"%d",EXP_NOPID);
expDiagLog("spawn: returns {%s}\r\n",interp->result);
return TCL_OK;
}
if (NULL == (argv[0] = Tcl_TranslateFileName(interp,argv[0],&dstring))) {
goto parent_error;
}
if (-1 == pipe(sync_fds)) {
exp_error(interp,"too many programs spawned? could not create pipe: %s",Tcl_PosixError(interp));
goto parent_error;
}
if (-1 == pipe(sync2_fds)) {
close(sync_fds[0]);
close(sync_fds[1]);
exp_error(interp,"too many programs spawned? could not create pipe: %s",Tcl_PosixError(interp));
goto parent_error;
}
if (-1 == pipe(status_pipe)) {
close(sync_fds[0]);
close(sync_fds[1]);
close(sync2_fds[0]);
close(sync2_fds[1]);
}
if ((pid = fork()) == -1) {
exp_error(interp,"fork: %s",Tcl_PosixError(interp));
goto parent_error;
}
if (pid) {
close(sync_fds[1]);
close(sync2_fds[0]);
close(status_pipe[1]);
esPtr = expCreateChannel(interp,master,master,pid);
if (exp_pty_slave_name) set_slave_name(esPtr,exp_pty_slave_name);
#ifdef CRAY
setptypid(pid);
#endif
expDiagLog("parent: waiting for sync byte\r\n");
while (((rc = read(sync_fds[0],&sync_byte,1)) < 0) && (errno == EINTR)) {
;
}
if (rc == -1) {
expErrorLogU("parent: sync byte read: ");
expErrorLogU(Tcl_ErrnoMsg(errno));
expErrorLogU("\r\n");
exit(-1);
}
exp_slave_control(master,1);
expDiagLog("parent: telling child to go ahead\r\n");
wc = write(sync2_fds[1]," ",1);
if (wc == -1) {
expErrorLog("parent: sync byte write: %s\r\n",Tcl_ErrnoMsg(errno));
exit(-1);
}
expDiagLog("parent: now unsynchronized from child\r\n");
close(sync_fds[0]);
close(sync2_fds[1]);
retry:
switch (read(status_pipe[0],&child_errno,sizeof child_errno)) {
case -1:
if (errno == EINTR) goto retry;
child_errno = errno;
break;
case 0:
child_errno = 0;
break;
default:
waitpid(pid, NULL, 0);
errno = child_errno;
exp_error(interp, "couldn't execute \"%s\": %s",
argv[0],Tcl_PosixError(interp));
goto parent_error;
}
close(status_pipe[0]);
Tcl_SetVar(interp,SPAWN_ID_VARNAME,esPtr->name,0);
sprintf(interp->result,"%d",pid);
expDiagLog("spawn: returns {%s}\r\n",interp->result);
Tcl_DStringFree(&dstring);
return(TCL_OK);
}
close(sync_fds[0]);
close(sync2_fds[1]);
close(status_pipe[0]);
expCloseOnExec(status_pipe[1]);
if (exp_dev_tty != -1) {
close(exp_dev_tty);
exp_dev_tty = -1;
}
#ifdef CRAY
(void) close(master);
#endif
#if defined(POSIX) && !defined(ultrix)
#define DO_SETSID
#endif
#ifdef __convex__
#define DO_SETSID
#endif
#ifdef DO_SETSID
setsid();
#else
#ifdef SYSV3
#ifndef CRAY
expSetpgrp();
#endif
#else
expSetpgrp();
#ifdef TIOCNOTTY
ttyfd = open("/dev/tty", O_RDWR);
if (ttyfd >= 0) {
(void) ioctl(ttyfd, TIOCNOTTY, (char *)0);
(void) close(ttyfd);
}
#endif
#endif
#endif
errorfd = fcntl(2,F_DUPFD,3);
#define restore_error_fd {close(2);fcntl(errorfd,F_DUPFD,2);}
close(0);
close(1);
close(2);
if (0 > (slave = exp_getptyslave(ttycopy,ttyinit,stty_init))) {
restore_error_fd
if (exp_pty_error) {
expErrorLog("open(slave pty): %s\r\n",exp_pty_error);
} else {
expErrorLog("open(slave pty): %s\r\n",Tcl_ErrnoMsg(errno));
}
exit(-1);
}
if (slave != 0) {
restore_error_fd
expErrorLog("exp_getptyslave: slave = %d but expected 0\n",slave);
exit(-1);
}
#if defined(TIOCSCTTY) && !defined(sun) && !defined(hpux)
#ifdef __QNX__
if (tcsetct(0, getpid()) == -1) {
restore_error_fd
expErrorLog("failed to get controlling terminal using TIOCSCTTY");
exit(-1);
}
#else
(void) ioctl(0,TIOCSCTTY,(char *)0);
#endif
#endif
#ifdef CRAY
(void) setsid();
(void) ioctl(0,TCSETCTTY,0);
(void) close(0);
if (open("/dev/tty", O_RDWR) < 0) {
restore_error_fd
expErrorLog("open(/dev/tty): %s\r\n",Tcl_ErrnoMsg(errno));
exit(-1);
}
(void) close(1);
(void) close(2);
(void) dup(0);
(void) dup(0);
setptyutmp();
#ifdef _CRAY2
if ((pid = fork()) == -1) {
restore_error_fd
expErrorLog("second fork: %s\r\n",Tcl_ErrnoMsg(errno));
exit(-1);
}
if (pid) {
int status;
int timeout;
char *t;
if (t = exp_get_var(interp,"pty_timeout"))
timeout = atoi(t);
else if (t = exp_get_var(interp,"timeout"))
timeout = atoi(t)/2;
else
timeout = 5;
while (wait(&status) < 0 && errno == EINTR)
;
sleep(timeout);
if (WIFSIGNALED(status))
kill(getpid(), WTERMSIG(status));
exit(WEXITSTATUS(status));
}
#endif
#endif
if (console) exp_console_set();
#ifdef FULLTRAPS
for (i=1;i<NSIG;i++) {
if (traps[i] != SIG_ERR) {
signal(i,traps[i]);
}
}
#endif
for (i=1;i<NSIG;i++) {
signal(i,ignore[i]?SIG_IGN:SIG_DFL);
}
wc = write(sync_fds[1]," ",1);
if (wc == -1) {
restore_error_fd
expErrorLog("child: sync byte write: %s\r\n",Tcl_ErrnoMsg(errno));
exit(-1);
}
close(sync_fds[1]);
while (((rc = read(sync2_fds[0],&sync_byte,1)) < 0) && (errno == EINTR)) {
;
}
if (rc == -1) {
restore_error_fd
expErrorLog("child: sync byte read: %s\r\n",Tcl_ErrnoMsg(errno));
exit(-1);
}
close(sync2_fds[0]);
(void) execvp(argv[0],argv);
write(status_pipe[1], &errno, sizeof errno);
exit(-1);
parent_error:
Tcl_DStringFree(&dstring);
if (esPtr) {
exp_close(interp,esPtr);
waitpid(esPtr->pid,&esPtr->wait,0);
if (esPtr->registered) {
Tcl_UnregisterChannel(interp,esPtr->channel);
}
}
return TCL_ERROR;
}
static int
Exp_ExpPidCmd(clientData,interp,argc,argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
char *chanName = 0;
ExpState *esPtr = 0;
argc--; argv++;
for (;argc>0;argc--,argv++) {
if (streq(*argv,"-i")) {
argc--; argv++;
if (!*argv) goto usage;
chanName = *argv;
} else goto usage;
}
if (chanName) {
if (!(esPtr = expStateFromChannelName(interp,chanName,0,0,0,"exp_pid"))) return TCL_ERROR;
} else {
if (!(esPtr = expStateCurrent(interp,0,0,0))) return TCL_ERROR;
}
sprintf(interp->result,"%d",esPtr->pid);
return TCL_OK;
usage:
exp_error(interp,"usage: -i spawn_id");
return TCL_ERROR;
}
static int
Exp_GetpidDeprecatedCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
expDiagLog("getpid is deprecated, use pid\r\n");
sprintf(interp->result,"%d",getpid());
return(TCL_OK);
}
static int
Exp_SleepCmd(clientData,interp,argc,argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
argc--; argv++;
if (argc != 1) {
exp_error(interp,"must have one arg: seconds");
return TCL_ERROR;
}
return(exp_dsleep(interp,(double)atof(*argv)));
}
static int
exact_write(esPtr,buffer,rembytes)
ExpState *esPtr;
char *buffer;
int rembytes;
{
Tcl_WriteChars(esPtr->channel,buffer,rembytes);
return(0);
}
struct slow_arg {
int size;
double time;
};
static int
get_slow_args(interp,x)
Tcl_Interp *interp;
struct slow_arg *x;
{
int sc;
CONST char *s = exp_get_var(interp,"send_slow");
if (!s) {
exp_error(interp,"send -s: send_slow has no value");
return(-1);
}
if (2 != (sc = sscanf(s,"%d %lf",&x->size,&x->time))) {
exp_error(interp,"send -s: found %d value(s) in send_slow but need 2",sc);
return(-1);
}
if (x->size <= 0) {
exp_error(interp,"send -s: size (%d) in send_slow must be positive", x->size);
return(-1);
}
if (x->time <= 0) {
exp_error(interp,"send -s: time (%f) in send_slow must be larger",x->time);
return(-1);
}
return(0);
}
static int
slow_write(interp,esPtr,buffer,rembytes,arg)
Tcl_Interp *interp;
ExpState *esPtr;
char *buffer;
int rembytes;
struct slow_arg *arg;
{
int rc;
char *p = buffer;
while (rembytes > 0) {
int bytelen;
int charlen;
char *p;
int i;
p = buffer;
charlen = (arg->size<rembytes?arg->size:rembytes);
for (i=0;i<charlen;i++) {
p = Tcl_UtfNext(p);
}
bytelen = p-buffer;
if (0 > exact_write(esPtr,buffer,bytelen)) return(-1);
rembytes -= bytelen;
buffer += bytelen;
if (rembytes > 0) {
rc = exp_dsleep(interp,arg->time);
if (rc>0) return rc;
}
}
return(0);
}
struct human_arg {
float alpha;
float alpha_eow;
float c;
float min, max;
};
static int
get_human_args(interp,x)
Tcl_Interp *interp;
struct human_arg *x;
{
int sc;
CONST char *s = exp_get_var(interp,"send_human");
if (!s) {
exp_error(interp,"send -h: send_human has no value");
return(-1);
}
if (5 != (sc = sscanf(s,"%f %f %f %f %f",
&x->alpha,&x->alpha_eow,&x->c,&x->min,&x->max))) {
if (sc == EOF) sc = 0;
exp_error(interp,"send -h: found %d value(s) in send_human but need 5",sc);
return(-1);
}
if (x->alpha < 0 || x->alpha_eow < 0) {
exp_error(interp,"send -h: average interarrival times (%f %f) must be non-negative in send_human", x->alpha,x->alpha_eow);
return(-1);
}
if (x->c <= 0) {
exp_error(interp,"send -h: variability (%f) in send_human must be positive",x->c);
return(-1);
}
x->c = 1/x->c;
if (x->min < 0) {
exp_error(interp,"send -h: minimum (%f) in send_human must be non-negative",x->min);
return(-1);
}
if (x->max < 0) {
exp_error(interp,"send -h: maximum (%f) in send_human must be non-negative",x->max);
return(-1);
}
if (x->max < x->min) {
exp_error(interp,"send -h: maximum (%f) must be >= minimum (%f) in send_human",x->max,x->min);
return(-1);
}
return(0);
}
static float
unit_random()
{
return((float)(1+(rand()%99991))/99991.0);
}
void
exp_init_unit_random()
{
srand(getpid());
}
static int
human_write(interp,esPtr,buffer,arg)
Tcl_Interp *interp;
ExpState *esPtr;
char *buffer;
struct human_arg *arg;
{
char *sp;
int size;
float t;
float alpha;
int wc;
int in_word = TRUE;
Tcl_UniChar ch;
expDiagLog("human_write: avg_arr=%f/%f 1/shape=%f min=%f max=%f\r\n",
arg->alpha,arg->alpha_eow,arg->c,arg->min,arg->max);
for (sp = buffer;*sp;sp += size) {
size = Tcl_UtfToUniChar(sp, &ch);
if (in_word && (Tcl_UniCharIsPunct(ch) || Tcl_UniCharIsSpace(ch)))
alpha = arg->alpha_eow;
else alpha = arg->alpha;
in_word = !(Tcl_UniCharIsPunct(ch) || Tcl_UniCharIsSpace(ch));
t = alpha * pow(-log((double)unit_random()),arg->c);
if (t<arg->min) t = arg->min;
else if (t>arg->max) t = arg->max;
if (sp != buffer) {
wc = exp_dsleep(interp,(double)t);
if (wc > 0) return wc;
}
wc = Tcl_WriteChars(esPtr->channel, sp, size);
if (0 > wc) return(wc);
}
return(0);
}
struct exp_i *exp_i_pool = 0;
struct exp_state_list *exp_state_list_pool = 0;
#define EXP_I_INIT_COUNT 10
#define EXP_FD_INIT_COUNT 10
struct exp_i *
exp_new_i()
{
int n;
struct exp_i *i;
if (!exp_i_pool) {
exp_i_pool = i = (struct exp_i *)ckalloc(
EXP_I_INIT_COUNT * sizeof(struct exp_i));
for (n=0;n<EXP_I_INIT_COUNT-1;n++,i++) {
i->next = i+1;
}
i->next = 0;
}
i = exp_i_pool;
exp_i_pool = exp_i_pool->next;
i->value = 0;
i->variable = 0;
i->state_list = 0;
i->ecount = 0;
i->next = 0;
return i;
}
struct exp_state_list *
exp_new_state(esPtr)
ExpState *esPtr;
{
int n;
struct exp_state_list *fd;
if (!exp_state_list_pool) {
exp_state_list_pool = fd = (struct exp_state_list *)ckalloc(
EXP_FD_INIT_COUNT * sizeof(struct exp_state_list));
for (n=0;n<EXP_FD_INIT_COUNT-1;n++,fd++) {
fd->next = fd+1;
}
fd->next = 0;
}
fd = exp_state_list_pool;
exp_state_list_pool = exp_state_list_pool->next;
fd->esPtr = esPtr;
return fd;
}
void
exp_free_state(fd_first)
struct exp_state_list *fd_first;
{
struct exp_state_list *fd, *penultimate;
if (!fd_first) return;
for (fd = fd_first;fd;fd=fd->next) {
penultimate = fd;
}
penultimate->next = exp_state_list_pool;
exp_state_list_pool = fd_first;
}
void
exp_free_state_single(fd)
struct exp_state_list *fd;
{
fd->next = exp_state_list_pool;
exp_state_list_pool = fd;
}
void
exp_free_i(interp,i,updateproc)
Tcl_Interp *interp;
struct exp_i *i;
Tcl_VarTraceProc *updateproc;
{
if (i->next) exp_free_i(interp,i->next,updateproc);
exp_free_state(i->state_list);
if (i->direct == EXP_INDIRECT) {
Tcl_UntraceVar(interp,i->variable,
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES,
updateproc,(ClientData)i);
}
if (i->value
&& (((i->direct == EXP_DIRECT) && (i->duration == EXP_PERMANENT))
|| ((i->direct == EXP_INDIRECT) && (i->duration == EXP_TEMPORARY)))) {
ckfree(i->value);
} else if (i->duration == EXP_PERMANENT) {
if (i->value) ckfree(i->value);
if (i->variable) ckfree(i->variable);
}
i->next = exp_i_pool;
exp_i_pool = i;
}
struct exp_i *
exp_new_i_complex(interp,arg,duration,updateproc)
Tcl_Interp *interp;
char *arg;
int duration;
Tcl_VarTraceProc *updateproc;
{
struct exp_i *i;
char **stringp;
i = exp_new_i();
i->direct = (isExpChannelName(arg)?EXP_DIRECT:EXP_INDIRECT);
#if OBSOLETE
i->direct = (isdigit(arg[0]) || (arg[0] == '-'))?EXP_DIRECT:EXP_INDIRECT;
#endif
if (i->direct == EXP_DIRECT) {
stringp = &i->value;
} else {
stringp = &i->variable;
}
i->duration = duration;
if (duration == EXP_PERMANENT) {
*stringp = ckalloc(strlen(arg)+1);
strcpy(*stringp,arg);
} else {
*stringp = arg;
}
i->state_list = 0;
if (TCL_ERROR == exp_i_update(interp,i)) {
exp_free_i(interp,i,(Tcl_VarTraceProc *)0);
return 0;
}
if (i->direct == EXP_INDIRECT) {
Tcl_TraceVar(interp, i->variable,
TCL_GLOBAL_ONLY|TCL_TRACE_WRITES,
updateproc, (ClientData) i);
}
return i;
}
void
exp_i_add_state(i,esPtr)
struct exp_i *i;
ExpState *esPtr;
{
struct exp_state_list *new_state;
new_state = exp_new_state(esPtr);
new_state->next = i->state_list;
i->state_list = new_state;
}
static int
exp_i_parse_states(interp,i)
Tcl_Interp *interp;
struct exp_i *i;
{
struct ExpState *esPtr;
char *p = i->value;
int argc;
char **argv;
int j;
if (Tcl_SplitList(NULL, p, &argc, &argv) != TCL_OK) goto error;
for (j = 0; j < argc; j++) {
esPtr = expStateFromChannelName(interp,argv[j],1,0,0,"");
if (!esPtr) goto error;
exp_i_add_state(i,esPtr);
}
ckfree((char*)argv);
return TCL_OK;
error:
expDiagLogU("exp_i_parse_states: ");
expDiagLogU(Tcl_GetStringResult(interp));
return TCL_ERROR;
}
int
exp_i_update(interp,i)
Tcl_Interp *interp;
struct exp_i *i;
{
char *p;
if (i->direct == EXP_INDIRECT) {
p = Tcl_GetVar(interp,i->variable,TCL_GLOBAL_ONLY);
if (!p) {
p = "";
expDiagLog("warning: indirect variable %s undefined",i->variable);
}
if (i->value) {
if (streq(p,i->value)) return TCL_OK;
ckfree(i->value);
}
i->value = ckalloc(strlen(p)+1);
strcpy(i->value,p);
exp_free_state(i->state_list);
i->state_list = 0;
} else {
i->state_list = 0;
}
return exp_i_parse_states(interp, i);
}
struct exp_i *
exp_new_i_simple(esPtr,duration)
ExpState *esPtr;
int duration;
{
struct exp_i *i;
i = exp_new_i();
i->direct = EXP_DIRECT;
i->duration = duration;
exp_i_add_state(i,esPtr);
return i;
}
static int
Exp_SendLogCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
argv++;
argc--;
if (argc) {
if (streq(*argv,"--")) {
argc--; argv++;
}
}
if (argc != 1) {
exp_error(interp,"usage: send [args] string");
return TCL_ERROR;
}
expLogDiagU(*argv);
return(TCL_OK);
}
static int
Exp_SendObjCmd(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
ExpState *esPtr = 0;
int rc;
struct human_arg human_args;
struct slow_arg slow_args;
#define SEND_STYLE_STRING_MASK 0x07
#define SEND_STYLE_PLAIN 0x01
#define SEND_STYLE_HUMAN 0x02
#define SEND_STYLE_SLOW 0x04
#define SEND_STYLE_ZERO 0x10
#define SEND_STYLE_BREAK 0x20
int send_style = SEND_STYLE_PLAIN;
int want_cooked = TRUE;
char *string;
int len = -1;
int zeros;
char *chanName = 0;
struct exp_state_list *state_list;
struct exp_i *i;
int j;
static char *options[] = {
"-i", "-h", "-s", "-null", "-0", "-raw", "-break", "--", (char *)0
};
enum options {
SEND_SPAWNID, SEND_HUMAN, SEND_SLOW, SEND_NULL, SEND_ZERO,
SEND_RAW, SEND_BREAK, SEND_LAST
};
for (j = 1; j < objc; j++) {
char *name;
int index;
name = Tcl_GetString(objv[j]);
if (name[0] != '-') {
break;
}
if (Tcl_GetIndexFromObj(interp, objv[j], options, "flag", 0,
&index) != TCL_OK) {
return TCL_ERROR;
}
switch ((enum options) index) {
case SEND_SPAWNID:
j++;
chanName = Tcl_GetString(objv[j]);
break;
case SEND_LAST:
j++;
goto getString;
case SEND_HUMAN:
if (-1 == get_human_args(interp,&human_args))
return(TCL_ERROR);
send_style = SEND_STYLE_HUMAN;
break;
case SEND_SLOW:
if (-1 == get_slow_args(interp,&slow_args))
return(TCL_ERROR);
send_style = SEND_STYLE_SLOW;
break;
case SEND_NULL:
case SEND_ZERO:
j++;
if (j >= objc) {
zeros = 1;
} else if (Tcl_GetIntFromObj(interp, objv[j], &zeros)
!= TCL_OK) {
return TCL_ERROR;
}
if (zeros < 1) return TCL_OK;
send_style = SEND_STYLE_ZERO;
string = "<zero(s)>";
break;
case SEND_RAW:
want_cooked = FALSE;
break;
case SEND_BREAK:
send_style = SEND_STYLE_BREAK;
string = "<break>";
break;
}
}
if (send_style & SEND_STYLE_STRING_MASK) {
if (j != objc-1) {
exp_error(interp,"usage: send [args] string");
return TCL_ERROR;
}
getString:
string = Tcl_GetStringFromObj(objv[j], &len);
} else {
len = strlen(string);
}
if (clientData == &sendCD_user) esPtr = tsdPtr->stdinout;
else if (clientData == &sendCD_error) esPtr = tsdPtr->stderrX;
else if (clientData == &sendCD_tty) esPtr = tsdPtr->devtty;
else if (!chanName) {
if (!(esPtr = expStateCurrent(interp,0,0,0))) return(TCL_ERROR);
}
if (esPtr) {
i = exp_new_i_simple(esPtr,EXP_TEMPORARY);
} else {
i = exp_new_i_complex(interp,chanName,FALSE,(Tcl_VarTraceProc *)0);
if (!i) return TCL_ERROR;
}
#define send_to_stderr (clientData == &sendCD_error)
#define send_to_proc (clientData == &sendCD_proc)
#define send_to_user ((clientData == &sendCD_user) || \
(clientData == &sendCD_tty))
if (send_to_proc) {
want_cooked = FALSE;
expDiagLogU("send: sending \"");
expDiagLogU(expPrintify(string));
expDiagLogU("\" to {");
} else {
expLogDiagU(string);
}
for (state_list=i->state_list;state_list;state_list=state_list->next) {
esPtr = state_list->esPtr;
if (send_to_proc) {
expDiagLog(" %s ",esPtr->name);
}
if (0 == expStateCheck(interp,esPtr,1,0,"send")) {
rc = TCL_ERROR;
goto finish;
}
if (want_cooked) string = exp_cook(string,&len);
switch (send_style) {
case SEND_STYLE_PLAIN:
rc = exact_write(esPtr,string,len);
break;
case SEND_STYLE_SLOW:
rc = slow_write(interp,esPtr,string,len,&slow_args);
break;
case SEND_STYLE_HUMAN:
rc = human_write(interp,esPtr,string,&human_args);
break;
case SEND_STYLE_ZERO:
for (;zeros>0;zeros--) {
rc = Tcl_WriteChars(esPtr->channel,
NULL_STRING, NULL_LENGTH);
}
rc = ((rc==1) ? 0 : -1);
break;
case SEND_STYLE_BREAK:
exp_tty_break(interp,esPtr->fdout);
rc = 0;
break;
}
if (rc != 0) {
if (rc == -1) {
exp_error(interp,"write(spawn_id=%d): %s",esPtr->fdout,Tcl_PosixError(interp));
rc = TCL_ERROR;
}
goto finish;
}
}
if (send_to_proc) expDiagLogU("}\r\n");
rc = TCL_OK;
finish:
exp_free_i(interp,i,(Tcl_VarTraceProc *)0);
return rc;
}
static int
Exp_LogFileCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
static char resultbuf[1000];
char *chanName = 0;
int leaveOpen = FALSE;
int logAll = FALSE;
int append = TRUE;
char *filename = 0;
argv++;
argc--;
for (;argc>0;argc--,argv++) {
if (streq(*argv,"-open")) {
if (!argv[1]) goto usage_error;
chanName = argv[1];
argc--; argv++;
} else if (streq(*argv,"-leaveopen")) {
if (!argv[1]) goto usage_error;
chanName = argv[1];
leaveOpen = TRUE;
argc--; argv++;
} else if (streq(*argv,"-a")) {
logAll = TRUE;
} else if (streq(*argv,"-info")) {
resultbuf[0] = '\0';
if (expLogChannelGet()) {
if (expLogAllGet()) strcat(resultbuf,"-a ");
if (!expLogAppendGet()) strcat(resultbuf,"-noappend ");
if (expLogFilenameGet()) {
strcat(resultbuf,expLogFilenameGet());
} else {
if (expLogLeaveOpenGet()) {
strcat(resultbuf,"-leaveopen ");
}
strcat(resultbuf,Tcl_GetChannelName(expLogChannelGet()));
}
Tcl_SetResult(interp,resultbuf,TCL_STATIC);
}
return TCL_OK;
} else if (streq(*argv,"-noappend")) {
append = FALSE;
} else break;
}
if (argc == 1) {
filename = argv[0];
} else if (argc > 1) {
goto usage_error;
}
if (chanName && filename) {
goto usage_error;
}
if (expLogChannelGet() && (chanName || filename)) {
if (filename && (0 == strcmp(filename,expLogFilenameGet()))) {
expLogAllSet(logAll);
return TCL_OK;
} else if (chanName && (0 == strcmp(filename,Tcl_GetChannelName(expLogChannelGet())))) {
expLogAllSet(logAll);
return TCL_OK;
} else {
exp_error(interp,"cannot start logging without first stopping logging");
return TCL_ERROR;
}
}
if (filename) {
if (TCL_ERROR == expLogChannelOpen(interp,filename,append)) {
return TCL_ERROR;
}
} else if (chanName) {
if (TCL_ERROR == expLogChannelSet(interp,chanName)) {
return TCL_ERROR;
}
} else {
expLogChannelClose(interp);
if (logAll) {
exp_error(interp,"cannot use -a without a file or channel");
return TCL_ERROR;
}
}
expLogAllSet(logAll);
expLogLeaveOpenSet(leaveOpen);
return TCL_OK;
usage_error:
exp_error(interp,"usage: log_file [-info] [-noappend] [[-a] file] [-[leave]open [open ...]]");
return TCL_ERROR;
}
static int
Exp_LogUserCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
int old_loguser = expLogUserGet();
if (argc == 0 || (argc == 2 && streq(argv[1],"-info"))) {
} else if (argc == 2) {
expLogUserSet(atoi(argv[1]));
} else {
exp_error(interp,"usage: [-info|1|0]");
}
sprintf(interp->result,"%d",old_loguser);
return(TCL_OK);
}
#ifdef TCL_DEBUGGER
static int
Exp_DebugCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
int now = FALSE;
int exp_tcl_debugger_was_available = exp_tcl_debugger_available;
if (argc > 3) goto usage;
if (argc == 1) {
sprintf(interp->result,"%d",exp_tcl_debugger_available);
return TCL_OK;
}
argv++;
while (*argv) {
if (streq(*argv,"-now")) {
now = TRUE;
argv++;
}
else break;
}
if (!*argv) {
if (now) {
Dbg_On(interp,1);
exp_tcl_debugger_available = 1;
} else {
goto usage;
}
} else if (streq(*argv,"0")) {
Dbg_Off(interp);
exp_tcl_debugger_available = 0;
} else {
Dbg_On(interp,now);
exp_tcl_debugger_available = 1;
}
sprintf(interp->result,"%d",exp_tcl_debugger_was_available);
return(TCL_OK);
usage:
exp_error(interp,"usage: [[-now] 1|0]");
return TCL_ERROR;
}
#endif
static int
Exp_ExpInternalCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
int newChannel = FALSE;
Tcl_Channel oldChannel;
static char resultbuf[1000];
if ((argc > 1) && streq(argv[1],"-info")) {
resultbuf[0] = '\0';
oldChannel = expDiagChannelGet();
if (oldChannel) {
sprintf(resultbuf,"-f %s ",expDiagFilename());
}
strcat(resultbuf,expDiagToStderrGet()?"1":"0");
Tcl_SetResult(interp,resultbuf,TCL_STATIC);
return TCL_OK;
}
argv++;
argc--;
while (argc) {
if (!streq(*argv,"-f")) break;
argc--;argv++;
if (argc < 1) goto usage;
expDiagChannelClose(interp);
if (TCL_OK != expDiagChannelOpen(interp,argv[0])) {
return TCL_ERROR;
}
newChannel = TRUE;
argc--;argv++;
}
if (argc != 1) goto usage;
if (!newChannel) {
expDiagChannelClose(interp);
}
expDiagToStderrSet(atoi(*argv));
return(TCL_OK);
usage:
exp_error(interp,"usage: [-f file] 0|1");
return TCL_ERROR;
}
char *exp_onexit_action = 0;
static int
Exp_ExitCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
int value = 0;
argv++;
if (*argv) {
if (exp_flageq(*argv,"-onexit",3)) {
argv++;
if (*argv) {
int len = strlen(*argv);
if (exp_onexit_action)
ckfree(exp_onexit_action);
exp_onexit_action = ckalloc(len + 1);
strcpy(exp_onexit_action,*argv);
} else if (exp_onexit_action) {
Tcl_AppendResult(interp,exp_onexit_action,(char *)0);
}
return TCL_OK;
} else if (exp_flageq(*argv,"-noexit",3)) {
argv++;
exp_exit_handlers((ClientData)interp);
return TCL_OK;
}
}
if (*argv) {
if (Tcl_GetInt(interp, *argv, &value) != TCL_OK) {
return TCL_ERROR;
}
}
Tcl_Exit(value);
}
static int
Exp_CloseObjCmd(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
int onexec_flag = FALSE;
int close_onexec;
int slave_flag = FALSE;
ExpState *esPtr = 0;
char *chanName = 0;
int objc_orig = objc;
Tcl_Obj *CONST *objv_orig = objv;
objc--; objv++;
for (;objc>0;objc--,objv++) {
if (streq("-i",Tcl_GetString(*objv))) {
objc--; objv++;
if (objc == 0) {
exp_error(interp,"usage: -i spawn_id");
return(TCL_ERROR);
}
chanName = Tcl_GetString(*objv);
} else if (streq(Tcl_GetString(*objv),"-slave")) {
slave_flag = TRUE;
} else if (streq(Tcl_GetString(*objv),"-onexec")) {
objc--; objv++;
if (objc == 0) {
exp_error(interp,"usage: -onexec 0|1");
return(TCL_ERROR);
}
onexec_flag = TRUE;
close_onexec = atoi(Tcl_GetString(*objv));
} else break;
}
if (objc) {
Tcl_CmdInfo info;
Tcl_ResetResult(interp);
if (0 == Tcl_GetCommandInfo(interp,"close",&info)) {
info.clientData = 0;
}
return(Tcl_CloseObjCmd(info.clientData,interp,objc_orig,objv_orig));
}
if (chanName) {
if (!(esPtr = expStateFromChannelName(interp,chanName,1,0,0,"close"))) return TCL_ERROR;
} else {
if (!(esPtr = expStateCurrent(interp,1,0,0))) return TCL_ERROR;
}
if (slave_flag) {
if (esPtr->fd_slave != EXP_NOFD) {
close(esPtr->fd_slave);
esPtr->fd_slave = EXP_NOFD;
exp_slave_control(esPtr->fdin,1);
return TCL_OK;
} else {
exp_error(interp,"no such slave");
return TCL_ERROR;
}
}
if (onexec_flag) {
fcntl(esPtr->fdin,F_SETFD,close_onexec);
return TCL_OK;
}
return(exp_close(interp,esPtr));
}
static void
tcl_tracer(clientData,interp,level,command,cmdProc,cmdClientData,argc,argv)
ClientData clientData;
Tcl_Interp *interp;
int level;
char *command;
int (*cmdProc)();
ClientData cmdClientData;
int argc;
char *argv[];
{
int i;
expErrorLog("%2d",level);
for (i = 0;i<level;i++) expErrorLogU(" ");
expErrorLogU(command);
expErrorLogU("\r\n");
}
static int
Exp_StraceCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
static int trace_level = 0;
static Tcl_Trace trace_handle;
if (argc > 1 && streq(argv[1],"-info")) {
sprintf(interp->result,"%d",trace_level);
return TCL_OK;
}
if (argc != 2) {
exp_error(interp,"usage: trace level");
return(TCL_ERROR);
}
if (trace_level > 0) Tcl_DeleteTrace(interp,trace_handle);
trace_level = atoi(argv[1]);
if (trace_level > 0)
trace_handle = Tcl_CreateTrace(interp,
trace_level,tcl_tracer,(ClientData)0);
return(TCL_OK);
}
#if 0
#ifndef NO_UNION_WAIT
# define WAIT_STATUS_TYPE union wait
#else
# define WAIT_STATUS_TYPE int
#endif
#endif
#if 0
#ifndef WIFEXITED
# define WIFEXITED(stat) (((*((int *) &(stat))) & 0xff) == 0)
#endif
#ifndef WEXITSTATUS
# define WEXITSTATUS(stat) (((*((int *) &(stat))) >> 8) & 0xff)
#endif
#ifndef WIFSIGNALED
# define WIFSIGNALED(stat) (((*((int *) &(stat)))) && ((*((int *) &(stat))) == ((*((int *) &(stat))) & 0x00ff)))
#endif
#ifndef WTERMSIG
# define WTERMSIG(stat) ((*((int *) &(stat))) & 0x7f)
#endif
#ifndef WIFSTOPPED
# define WIFSTOPPED(stat) (((*((int *) &(stat))) & 0xff) == 0177)
#endif
#ifndef WSTOPSIG
# define WSTOPSIG(stat) (((*((int *) &(stat))) >> 8) & 0xff)
#endif
#endif
struct forked_proc {
int pid;
WAIT_STATUS_TYPE wait_status;
enum {not_in_use, wait_done, wait_not_done} link_status;
struct forked_proc *next;
} *forked_proc_base = 0;
void
fork_clear_all()
{
struct forked_proc *f;
for (f=forked_proc_base;f;f=f->next) {
f->link_status = not_in_use;
}
}
void
fork_init(f,pid)
struct forked_proc *f;
int pid;
{
f->pid = pid;
f->link_status = wait_not_done;
}
void
fork_add(pid)
int pid;
{
struct forked_proc *f;
for (f=forked_proc_base;f;f=f->next) {
if (f->link_status == not_in_use) break;
}
if (!f) {
f = (struct forked_proc *)ckalloc(sizeof(struct forked_proc));
f->next = forked_proc_base;
forked_proc_base = f;
}
fork_init(f,pid);
}
#ifndef WNOHANG
#define WNOHANG WNOHANG_BACKUP_VALUE
#endif
static int
Exp_WaitCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
char *chanName = 0;
struct ExpState *esPtr;
struct forked_proc *fp = 0;
struct ExpState esTmp;
char spawn_id[20];
int nowait = FALSE;
int result = 0;
#define NO_CHILD -2
argv++;
argc--;
for (;argc>0;argc--,argv++) {
if (streq(*argv,"-i")) {
argc--; argv++;
if (argc==0) {
exp_error(interp,"usage: -i spawn_id");
return(TCL_ERROR);
}
chanName = *argv;
} else if (streq(*argv,"-nowait")) {
nowait = TRUE;
}
}
if (!chanName) {
if (!(esPtr = expStateCurrent(interp,0,0,1))) return TCL_ERROR;
} else {
if (!(esPtr = expStateFromChannelName(interp,chanName,0,0,1,"wait")))
return TCL_ERROR;
}
if (!expStateAnyIs(esPtr)) {
if (!esPtr->sys_waited) {
if (nowait) {
Tcl_DetachPids(1,(Tcl_Pid *)&esPtr->pid);
exp_wait_zero(&esPtr->wait);
} else {
while (1) {
if (Tcl_AsyncReady()) {
int rc = Tcl_AsyncInvoke(interp,TCL_OK);
if (rc != TCL_OK) return(rc);
}
result = waitpid(esPtr->pid,&esPtr->wait,0);
if (result == esPtr->pid) break;
if (result == -1) {
if (errno == EINTR) continue;
else break;
}
}
}
}
Tcl_ReapDetachedProcs();
exp_rearm_sigchld(interp);
strcpy(spawn_id,esPtr->name);
} else {
int waited_on_forked_process = 0;
esPtr = expWaitOnAny();
if (!esPtr) {
for (fp=forked_proc_base;fp;fp=fp->next) {
if (fp->link_status == not_in_use) continue;
restart:
result = waitpid(fp->pid,&fp->wait_status,WNOHANG);
if (result == fp->pid) {
waited_on_forked_process = 1;
break;
}
if (result == 0) continue;
if (result == -1) {
if (errno == EINTR) goto restart;
else break;
}
}
if (waited_on_forked_process) {
strcpy(spawn_id,"-1");
} else {
result = NO_CHILD;
Tcl_ReapDetachedProcs();
}
exp_rearm_sigchld(interp);
}
}
if (fp) {
esPtr = &esTmp;
esPtr->pid = fp->pid;
esPtr->wait = fp->wait_status;
}
if (result == -1) {
sprintf(interp->result,"%d %s -1 %d POSIX %s %s",
esPtr->pid,spawn_id,errno,Tcl_ErrnoId(),Tcl_ErrnoMsg(errno));
result = TCL_OK;
} else if (result == NO_CHILD) {
exp_error(interp,"no children");
return TCL_ERROR;
} else {
sprintf(interp->result,"%d %s 0 %d",
esPtr->pid,spawn_id,WEXITSTATUS(esPtr->wait));
if (WIFSIGNALED(esPtr->wait)) {
Tcl_AppendElement(interp,"CHILDKILLED");
Tcl_AppendElement(interp,Tcl_SignalId((int)(WTERMSIG(esPtr->wait))));
Tcl_AppendElement(interp,Tcl_SignalMsg((int) (WTERMSIG(esPtr->wait))));
} else if (WIFSTOPPED(esPtr->wait)) {
Tcl_AppendElement(interp,"CHILDSUSP");
Tcl_AppendElement(interp,Tcl_SignalId((int) (WSTOPSIG(esPtr->wait))));
Tcl_AppendElement(interp,Tcl_SignalMsg((int) (WSTOPSIG(esPtr->wait))));
}
}
if (fp) {
fp->link_status = not_in_use;
return ((result == -1)?TCL_ERROR:TCL_OK);
}
esPtr->sys_waited = TRUE;
esPtr->user_waited = TRUE;
if (!esPtr->open) {
if (esPtr->registered) {
Tcl_UnregisterChannel(interp,esPtr->channel);
}
}
return ((result == -1)?TCL_ERROR:TCL_OK);
}
static int
Exp_ForkCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
int rc;
if (argc > 1) {
exp_error(interp,"usage: fork");
return(TCL_ERROR);
}
rc = fork();
if (rc == -1) {
exp_error(interp,"fork: %s",Tcl_PosixError(interp));
return TCL_ERROR;
} else if (rc == 0) {
exp_forked = TRUE;
exp_getpid = getpid();
fork_clear_all();
} else {
fork_add(rc);
}
sprintf(interp->result,"%d",rc);
expDiagLog("fork: returns {%s}\r\n",interp->result);
return(TCL_OK);
}
static int
Exp_DisconnectCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
int ttyfd;
if (argc > 1) {
exp_error(interp,"usage: disconnect");
return(TCL_ERROR);
}
if (exp_disconnected) {
exp_error(interp,"already disconnected");
return(TCL_ERROR);
}
if (!exp_forked) {
exp_error(interp,"can only disconnect child process");
return(TCL_ERROR);
}
exp_disconnected = TRUE;
signal(SIGHUP,SIG_IGN);
if (isatty(0)) {
ExpState *stdinout = tsdPtr->stdinout;
if (stdinout->valid) {
exp_close(interp,stdinout);
if (stdinout->registered) {
Tcl_UnregisterChannel(interp,stdinout->channel);
}
}
open("/dev/null",0);
open("/dev/null",1);
}
if (isatty(2)) {
ExpState *devtty = tsdPtr->devtty;
if (devtty->valid) {
exp_close(interp,devtty);
if (devtty->registered) {
Tcl_UnregisterChannel(interp,devtty->channel);
}
}
open("/dev/null",1);
}
Tcl_UnsetVar(interp,"tty_spawn_id",TCL_GLOBAL_ONLY);
#ifdef DO_SETSID
setsid();
#else
#ifdef SYSV3
#ifdef sysV88
if (fork()) exit(0);
expSetpgrp();
#else
expSetpgrp();
if (fork()) exit(0);
#endif
#else
expSetpgrp();
#ifdef TIOCNOTTY
ttyfd = open("/dev/tty", O_RDWR);
if (ttyfd >= 0) {
(void) ioctl(ttyfd, TIOCNOTTY, (char *)0);
(void) close(ttyfd);
}
#endif
#endif
#endif
return(TCL_OK);
}
static int
Exp_OverlayCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
int newfd, oldfd;
int dash_name = 0;
char *command;
argc--; argv++;
while (argc) {
if (*argv[0] != '-') break;
if (streq(*argv,"-")) {
argc--; argv++;
dash_name = 1;
continue;
}
newfd = atoi(argv[0]+1);
argc--; argv++;
if (argc == 0) {
exp_error(interp,"overlay -# requires additional argument");
return(TCL_ERROR);
}
oldfd = atoi(argv[0]);
argc--; argv++;
expDiagLog("overlay: mapping fd %d to %d\r\n",oldfd,newfd);
if (oldfd != newfd) (void) dup2(oldfd,newfd);
else expDiagLog("warning: overlay: old fd == new fd (%d)\r\n",oldfd);
}
if (argc == 0) {
exp_error(interp,"need program name");
return(TCL_ERROR);
}
command = argv[0];
if (dash_name) {
argv[0] = ckalloc(1+strlen(command));
sprintf(argv[0],"-%s",command);
}
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
(void) execvp(command,argv);
exp_error(interp,"execvp(%s): %s\r\n",argv[0],Tcl_PosixError(interp));
return(TCL_ERROR);
}
int
Exp_InterpreterObjCmd(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
Tcl_Obj *eofObj = 0;
int i;
int index;
int rc;
static char *options[] = {
"-eof", (char *)0
};
enum options {
FLAG_EOF
};
for (i = 1; i < objc; i++) {
if (Tcl_GetIndexFromObj(interp, objv[i], options, "flag", 0,
&index) != TCL_OK) {
return TCL_ERROR;
}
switch ((enum options) index) {
case FLAG_EOF:
i++;
if (i >= objc) {
Tcl_WrongNumArgs(interp, 1, objv,"-eof cmd");
return TCL_ERROR;
}
eofObj = objv[i];
Tcl_IncrRefCount(eofObj);
break;
}
}
rc = exp_interpreter(interp,eofObj);
if (eofObj) Tcl_DecrRefCount(eofObj);
return rc;
}
int
Exp_ExpContinueCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
if (argc == 1) {
return EXP_CONTINUE;
} else if ((argc == 2) && (0 == strcmp(argv[1],"-continue_timer"))) {
return EXP_CONTINUE_TIMER;
}
exp_error(interp,"usage: exp_continue [-continue_timer]\n");
return(TCL_ERROR);
}
int
Exp_InterReturnObjCmd(clientData, interp, objc, objv)
ClientData clientData;
Tcl_Interp *interp;
int objc;
Tcl_Obj *CONST objv[];
{
int result = Tcl_ReturnObjCmd(clientData,interp,objc,objv);
if (result == TCL_RETURN)
result = EXP_TCL_RETURN;
return result;
}
int
Exp_OpenCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
ExpState *esPtr;
char *chanName = 0;
int newfd;
int leaveopen = FALSE;
Tcl_Channel channel;
argc--; argv++;
for (;argc>0;argc--,argv++) {
if (streq(*argv,"-i")) {
argc--; argv++;
if (!*argv) {
exp_error(interp,"usage: -i spawn_id");
return TCL_ERROR;
}
chanName = *argv;
} else if (streq(*argv,"-leaveopen")) {
leaveopen = TRUE;
argc--; argv++;
} else break;
}
if (!chanName) {
if (!(esPtr = expStateCurrent(interp,1,0,0))) return TCL_ERROR;
} else {
if (!(esPtr = expStateFromChannelName(interp,chanName,1,0,0,"exp_open")))
return TCL_ERROR;
}
if (-1 == (newfd = dup(esPtr->fdin))) {
exp_error(interp,"dup: %s",Tcl_PosixError(interp));
return TCL_ERROR;
}
if (!leaveopen) {
if (esPtr->pid != EXP_NOPID) {
Tcl_DetachPids(1,(Tcl_Pid *)&esPtr->pid);
esPtr->pid = EXP_NOPID;
esPtr->sys_waited = esPtr->user_waited = TRUE;
}
exp_close(interp,esPtr);
}
channel = Tcl_MakeFileChannel((ClientData)newfd,TCL_READABLE|TCL_WRITABLE);
Tcl_RegisterChannel(interp, channel);
Tcl_AppendResult(interp, Tcl_GetChannelName(channel), (char *) NULL);
return TCL_OK;
}
int
exp_flageq_code(flag,string,minlen)
char *flag;
char *string;
int minlen;
{
for (;*flag;flag++,string++,minlen--) {
if (*string == '\0') break;
if (*string != *flag) return 0;
}
if (*string == '\0' && minlen <= 0) return 1;
return 0;
}
void
exp_create_commands(interp,c)
Tcl_Interp *interp;
struct exp_cmd_data *c;
{
Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp);
char cmdnamebuf[80];
for (;c->name;c++) {
if ((c->flags & EXP_REDEFINE) ||
!(Tcl_FindHashEntry(&globalNsPtr->cmdTable,c->name) ||
Tcl_FindHashEntry(&currNsPtr->cmdTable,c->name))) {
if (c->objproc)
Tcl_CreateObjCommand(interp,c->name,
c->objproc,c->data,exp_deleteObjProc);
else
Tcl_CreateCommand(interp,c->name,c->proc,
c->data,exp_deleteProc);
}
if (!(c->name[0] == 'e' &&
c->name[1] == 'x' &&
c->name[2] == 'p')
&& !(c->flags & EXP_NOPREFIX)) {
sprintf(cmdnamebuf,"exp_%s",c->name);
if (c->objproc)
Tcl_CreateObjCommand(interp,cmdnamebuf,c->objproc,c->data,
exp_deleteObjProc);
else
Tcl_CreateCommand(interp,cmdnamebuf,c->proc,
c->data,exp_deleteProc);
}
}
}
static struct exp_cmd_data cmd_data[] = {
{"close", Exp_CloseObjCmd, 0, 0, EXP_REDEFINE},
#ifdef TCL_DEBUGGER
{"debug", exp_proc(Exp_DebugCmd), 0, 0},
#endif
{"exp_internal",exp_proc(Exp_ExpInternalCmd), 0, 0},
{"disconnect", exp_proc(Exp_DisconnectCmd), 0, 0},
{"exit", exp_proc(Exp_ExitCmd), 0, EXP_REDEFINE},
{"exp_continue",exp_proc(Exp_ExpContinueCmd),0, 0},
{"fork", exp_proc(Exp_ForkCmd), 0, 0},
{"exp_pid", exp_proc(Exp_ExpPidCmd), 0, 0},
{"getpid", exp_proc(Exp_GetpidDeprecatedCmd),0, 0},
{"interpreter", Exp_InterpreterObjCmd, 0, 0, 0},
{"log_file", exp_proc(Exp_LogFileCmd), 0, 0},
{"log_user", exp_proc(Exp_LogUserCmd), 0, 0},
{"exp_open", exp_proc(Exp_OpenCmd), 0, 0},
{"overlay", exp_proc(Exp_OverlayCmd), 0, 0},
{"inter_return",Exp_InterReturnObjCmd, 0, 0, 0},
{"send", Exp_SendObjCmd, 0, (ClientData)&sendCD_proc,0},
{"send_error", Exp_SendObjCmd, 0, (ClientData)&sendCD_error,0},
{"send_log", exp_proc(Exp_SendLogCmd), 0, 0},
{"send_tty", Exp_SendObjCmd, 0, (ClientData)&sendCD_tty,0},
{"send_user", Exp_SendObjCmd, 0, (ClientData)&sendCD_user,0},
{"sleep", exp_proc(Exp_SleepCmd), 0, 0},
{"spawn", exp_proc(Exp_SpawnCmd), 0, 0},
{"strace", exp_proc(Exp_StraceCmd), 0, 0},
{"wait", exp_proc(Exp_WaitCmd), 0, 0},
{0}};
void
exp_init_most_cmds(interp)
Tcl_Interp *interp;
{
exp_create_commands(interp,cmd_data);
#ifdef HAVE_PTYTRAP
Tcl_InitHashTable(&slaveNames,TCL_STRING_KEYS);
#endif
}