Blame exp_event.c

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
}