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