diff options
Diffstat (limited to 'exp_event.c')
-rw-r--r-- | exp_event.c | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/exp_event.c b/exp_event.c new file mode 100644 index 0000000..8ebe4b6 --- /dev/null +++ b/exp_event.c @@ -0,0 +1,349 @@ +/* exp_event.c - event interface for Expect + +Written by: Don Libes, NIST, 2/6/90 + +I hereby place this software in the public domain. However, the author and +NIST would appreciate credit if this program or parts of it are used. + +*/ + +#include "expect_cf.h" +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> + +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif + +#ifdef HAVE_PTYTRAP +# include <sys/ptyio.h> +#endif + +#include "tcl.h" +#include "exp_prog.h" +#include "exp_command.h" /* for ExpState defs */ +#include "exp_event.h" + +typedef struct ThreadSpecificData { + int rr; /* round robin ptr */ +} ThreadSpecificData; + +static Tcl_ThreadDataKey dataKey; + +void +exp_event_disarm_bg(esPtr) +ExpState *esPtr; +{ + Tcl_DeleteChannelHandler(esPtr->channel,exp_background_channelhandler,(ClientData)esPtr); +} + +static void +exp_arm_background_channelhandler_force(esPtr) +ExpState *esPtr; +{ + Tcl_CreateChannelHandler(esPtr->channel, + TCL_READABLE|TCL_EXCEPTION, + exp_background_channelhandler, + (ClientData)esPtr); + + esPtr->bg_status = armed; +} + +void +exp_arm_background_channelhandler(esPtr) +ExpState *esPtr; +{ + switch (esPtr->bg_status) { + case unarmed: + exp_arm_background_channelhandler_force(esPtr); + break; + case disarm_req_while_blocked: + esPtr->bg_status = blocked; /* forget request */ + break; + case armed: + case blocked: + /* do nothing */ + break; + } +} + +void +exp_disarm_background_channelhandler(esPtr) +ExpState *esPtr; +{ + switch (esPtr->bg_status) { + case blocked: + esPtr->bg_status = disarm_req_while_blocked; + break; + case armed: + esPtr->bg_status = unarmed; + exp_event_disarm_bg(esPtr); + break; + case disarm_req_while_blocked: + case unarmed: + /* do nothing */ + break; + } +} + +/* ignore block status and forcibly disarm handler - called from exp_close. */ +/* After exp_close returns, we will not have an opportunity to disarm */ +/* because the fd will be invalid, so we force it here. */ +void +exp_disarm_background_channelhandler_force(esPtr) +ExpState *esPtr; +{ + switch (esPtr->bg_status) { + case blocked: + case disarm_req_while_blocked: + case armed: + esPtr->bg_status = unarmed; + exp_event_disarm_bg(esPtr); + break; + case unarmed: + /* do nothing */ + break; + } +} + +/* this can only be called at the end of the bg handler in which */ +/* case we know the status is some kind of "blocked" */ +void +exp_unblock_background_channelhandler(esPtr) + ExpState *esPtr; +{ + switch (esPtr->bg_status) { + case blocked: + exp_arm_background_channelhandler_force(esPtr); + break; + case disarm_req_while_blocked: + exp_disarm_background_channelhandler_force(esPtr); + break; + } +} + +/* this can only be called at the beginning of the bg handler in which */ +/* case we know the status must be "armed" */ +void +exp_block_background_channelhandler(esPtr) +ExpState *esPtr; +{ + esPtr->bg_status = blocked; + exp_event_disarm_bg(esPtr); +} + + +/*ARGSUSED*/ +static void +exp_timehandler(clientData) +ClientData clientData; +{ + *(int *)clientData = TRUE; +} + +static void exp_channelhandler(clientData,mask) +ClientData clientData; +int mask; +{ + ExpState *esPtr = (ExpState *)clientData; + + esPtr->notified = TRUE; + esPtr->notifiedMask = mask; + + exp_event_disarm_fg(esPtr); +} + +void +exp_event_disarm_fg(esPtr) +ExpState *esPtr; +{ + /*printf("DeleteChannelHandler: %s\r\n",esPtr->name);*/ + Tcl_DeleteChannelHandler(esPtr->channel,exp_channelhandler,(ClientData)esPtr); + + /* remember that ChannelHandler has been disabled so that */ + /* it can be turned on for fg expect's as well as bg */ + esPtr->fg_armed = FALSE; +} + +/* returns status, one of EOF, TIMEOUT, ERROR or DATA */ +/* can now return RECONFIGURE, too */ +/*ARGSUSED*/ +int exp_get_next_event(interp,esPtrs,n,esPtrOut,timeout,key) +Tcl_Interp *interp; +ExpState *(esPtrs[]); +int n; /* # of esPtrs */ +ExpState **esPtrOut; /* 1st ready esPtr, not set if none */ +int timeout; /* seconds */ +int key; +{ + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + ExpState *esPtr; + int i; /* index into in-array */ +#ifdef HAVE_PTYTRAP + struct request_info ioctl_info; +#endif + + int old_configure_count = exp_configure_count; + + int timerFired = FALSE; + Tcl_TimerToken timerToken = 0;/* handle to Tcl timehandler descriptor */ + /* We must delete any timer before returning. Doing so throughout + * the code makes it unreadable; isolate the unreadable nonsense here. + */ +#define RETURN(x) { \ + if (timerToken) Tcl_DeleteTimerHandler(timerToken); \ + return(x); \ + } + + for (;;) { + /* if anything has been touched by someone else, report that */ + /* an event has been received */ + + for (i=0;i<n;i++) { + tsdPtr->rr++; + if (tsdPtr->rr >= n) tsdPtr->rr = 0; + + esPtr = esPtrs[tsdPtr->rr]; + + if (esPtr->key != key) { + esPtr->key = key; + esPtr->force_read = FALSE; + *esPtrOut = esPtr; + RETURN(EXP_DATA_OLD); + } else if ((!esPtr->force_read) && (!expSizeZero(esPtr))) { + *esPtrOut = esPtr; + RETURN(EXP_DATA_OLD); + } else if (esPtr->notified) { + /* this test of the mask should be redundant but SunOS */ + /* raises both READABLE and EXCEPTION (for no */ + /* apparent reason) when selecting on a plain file */ + if (esPtr->notifiedMask & TCL_READABLE) { + *esPtrOut = esPtr; + esPtr->notified = FALSE; + RETURN(EXP_DATA_NEW); + } + /* + * at this point we know that the event must be TCL_EXCEPTION + * indicating either EOF or HP ptytrap. + */ +#ifndef HAVE_PTYTRAP + RETURN(EXP_EOF); +#else + if (ioctl(esPtr->fdin,TIOCREQCHECK,&ioctl_info) < 0) { + expDiagLog("ioctl error on TIOCREQCHECK: %s", Tcl_PosixError(interp)); + RETURN(EXP_TCLERROR); + } + if (ioctl_info.request == TIOCCLOSE) { + RETURN(EXP_EOF); + } + if (ioctl(esPtr->fdin, TIOCREQSET, &ioctl_info) < 0) { + expDiagLog("ioctl error on TIOCREQSET after ioctl or open on slave: %s", Tcl_ErrnoMsg(errno)); + } + /* presumably, we trapped an open here */ + /* so simply continue by falling thru */ +#endif /* !HAVE_PTYTRAP */ + } + } + + if (!timerToken) { + if (timeout >= 0) { + timerToken = Tcl_CreateTimerHandler(1000*timeout, + exp_timehandler, + (ClientData)&timerFired); + } + } + + /* make sure that all fds that should be armed are */ + for (i=0;i<n;i++) { + esPtr = esPtrs[i]; + /*printf("CreateChannelHandler: %s\r\n",esPtr->name);*/ + Tcl_CreateChannelHandler( + esPtr->channel, + TCL_READABLE | TCL_EXCEPTION, + exp_channelhandler, + (ClientData)esPtr); + esPtr->fg_armed = TRUE; + } + + Tcl_DoOneEvent(0); /* do any event */ + + if (timerFired) return(EXP_TIMEOUT); + + if (old_configure_count != exp_configure_count) { + RETURN(EXP_RECONFIGURE); + } + } +} + +/* Having been told there was an event for a specific ExpState, get it */ +/* This returns status, one of EOF, TIMEOUT, ERROR or DATA */ +/*ARGSUSED*/ +int +exp_get_next_event_info(interp,esPtr) +Tcl_Interp *interp; +ExpState *esPtr; +{ +#ifdef HAVE_PTYTRAP + struct request_info ioctl_info; +#endif + + if (esPtr->notifiedMask & TCL_READABLE) return EXP_DATA_NEW; + + /* ready_mask must contain TCL_EXCEPTION */ +#ifndef HAVE_PTYTRAP + return(EXP_EOF); +#else + if (ioctl(esPtr->fdin,TIOCREQCHECK,&ioctl_info) < 0) { + expDiagLog("ioctl error on TIOCREQCHECK: %s", + Tcl_PosixError(interp)); + return(EXP_TCLERROR); + } + if (ioctl_info.request == TIOCCLOSE) { + return(EXP_EOF); + } + if (ioctl(esPtr->fdin, TIOCREQSET, &ioctl_info) < 0) { + expDiagLog("ioctl error on TIOCREQSET after ioctl or open on slave: %s", Tcl_ErrnoMsg(errno)); + } + /* presumably, we trapped an open here */ + /* call it an error for lack of anything more descriptive */ + /* it will be thrown away by caller anyway */ + return EXP_TCLERROR; +#endif +} + +/*ARGSUSED*/ +int /* returns TCL_XXX */ +exp_dsleep(interp,sec) +Tcl_Interp *interp; +double sec; +{ + int timerFired = FALSE; + + Tcl_CreateTimerHandler((int)(sec*1000),exp_timehandler,(ClientData)&timerFired); + + while (!timerFired) { + Tcl_DoOneEvent(0); + } + return TCL_OK; +} + +static char destroy_cmd[] = "destroy ."; + +static void +exp_event_exit_real(interp) +Tcl_Interp *interp; +{ + Tcl_Eval(interp,destroy_cmd); +} + +/* set things up for later calls to event handler */ +void +exp_init_event() +{ + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + tsdPtr->rr = 0; + + exp_event_exit = exp_event_exit_real; +} |