Blame exp_simple.c

Packit a69f91
/* 
Packit a69f91
 * tclUnixNotify.c --
Packit a69f91
 *
Packit a69f91
 *	This file contains Unix-specific procedures for the notifier,
Packit a69f91
 *	which is the lowest-level part of the Tcl event loop.  This file
Packit a69f91
 *	works together with ../generic/tclNotify.c.
Packit a69f91
 *
Packit a69f91
 * Copyright (c) 1995 Sun Microsystems, Inc.
Packit a69f91
 *
Packit a69f91
 * See the file "license.terms" for information on usage and redistribution
Packit a69f91
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
Packit a69f91
 */
Packit a69f91
Packit a69f91
static char sccsid[] = "@(#) tclUnixNotify.c 1.27 96/01/19 10:30:23";
Packit a69f91
Packit a69f91
#include "tclInt.h"
Packit a69f91
#include "tclPort.h"
Packit a69f91
#include <signal.h> 
Packit a69f91
Packit a69f91
/*
Packit a69f91
 * The information below is used to provide read, write, and
Packit a69f91
 * exception masks to select during calls to Tcl_DoOneEvent.
Packit a69f91
 */
Packit a69f91
Packit a69f91
static fd_mask checkMasks[3*MASK_SIZE];
Packit a69f91
				/* This array is used to build up the masks
Packit a69f91
				 * to be used in the next call to select.
Packit a69f91
				 * Bits are set in response to calls to
Packit a69f91
				 * Tcl_WatchFile. */
Packit a69f91
static fd_mask readyMasks[3*MASK_SIZE];
Packit a69f91
				/* This array reflects the readable/writable
Packit a69f91
				 * conditions that were found to exist by the
Packit a69f91
				 * last call to select. */
Packit a69f91
static int numFdBits;		/* Number of valid bits in checkMasks
Packit a69f91
				 * (one more than highest fd for which
Packit a69f91
				 * Tcl_WatchFile has been called). */
Packit a69f91

Packit a69f91
/*
Packit a69f91
 *----------------------------------------------------------------------
Packit a69f91
 *
Packit a69f91
 * Tcl_WatchFile --
Packit a69f91
 *
Packit a69f91
 *	Arrange for Tcl_DoOneEvent to include this file in the masks
Packit a69f91
 *	for the next call to select.  This procedure is invoked by
Packit a69f91
 *	event sources, which are in turn invoked by Tcl_DoOneEvent
Packit a69f91
 *	before it invokes select.
Packit a69f91
 *
Packit a69f91
 * Results:
Packit a69f91
 *	None.
Packit a69f91
 *
Packit a69f91
 * Side effects:
Packit a69f91
 *	
Packit a69f91
 *	The notifier will generate a file event when the I/O channel
Packit a69f91
 *	given by fd next becomes ready in the way indicated by mask.
Packit a69f91
 *	If fd is already registered then the old mask will be replaced
Packit a69f91
 *	with the new one.  Once the event is sent, the notifier will
Packit a69f91
 *	not send any more events about the fd until the next call to
Packit a69f91
 *	Tcl_NotifyFile. 
Packit a69f91
 *
Packit a69f91
 *----------------------------------------------------------------------
Packit a69f91
 */
Packit a69f91
Packit a69f91
void
Packit a69f91
Tcl_WatchFile(file, mask)
Packit a69f91
    Tcl_File file;	/* Generic file handle for a stream. */
Packit a69f91
    int mask;			/* OR'ed combination of TCL_READABLE,
Packit a69f91
				 * TCL_WRITABLE, and TCL_EXCEPTION:
Packit a69f91
				 * indicates conditions to wait for
Packit a69f91
				 * in select. */
Packit a69f91
{
Packit a69f91
    int fd, type, index;
Packit a69f91
    fd_mask bit;
Packit a69f91
Packit a69f91
    fd = (int) Tcl_GetFileInfo(file, &type);
Packit a69f91
Packit a69f91
    if (type != TCL_UNIX_FD) {
Packit a69f91
	panic("Tcl_WatchFile: unexpected file type");
Packit a69f91
    }
Packit a69f91
Packit a69f91
    if (fd >= FD_SETSIZE) {
Packit a69f91
	panic("Tcl_WatchFile can't handle file id %d", fd);
Packit a69f91
    }
Packit a69f91
Packit a69f91
    index = fd/(NBBY*sizeof(fd_mask));
Packit a69f91
    bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
Packit a69f91
    if (mask & TCL_READABLE) {
Packit a69f91
	checkMasks[index] |= bit;
Packit a69f91
    }
Packit a69f91
    if (mask & TCL_WRITABLE) {
Packit a69f91
	(checkMasks+MASK_SIZE)[index] |= bit;
Packit a69f91
    }
Packit a69f91
    if (mask & TCL_EXCEPTION) {
Packit a69f91
	(checkMasks+2*(MASK_SIZE))[index] |= bit;
Packit a69f91
    }
Packit a69f91
    if (numFdBits <= fd) {
Packit a69f91
	numFdBits = fd+1;
Packit a69f91
    }
Packit a69f91
}
Packit a69f91

Packit a69f91
/*
Packit a69f91
 *----------------------------------------------------------------------
Packit a69f91
 *
Packit a69f91
 * Tcl_FileReady --
Packit a69f91
 *
Packit a69f91
 *	Indicates what conditions (readable, writable, etc.) were
Packit a69f91
 *	present on a file the last time the notifier invoked select.
Packit a69f91
 *	This procedure is typically invoked by event sources to see
Packit a69f91
 *	if they should queue events.
Packit a69f91
 *
Packit a69f91
 * Results:
Packit a69f91
 *	The return value is 0 if none of the conditions specified by mask
Packit a69f91
 *	was true for fd the last time the system checked.  If any of the
Packit a69f91
 *	conditions were true, then the return value is a mask of those
Packit a69f91
 *	that were true.
Packit a69f91
 *
Packit a69f91
 * Side effects:
Packit a69f91
 *	None.
Packit a69f91
 *
Packit a69f91
 *----------------------------------------------------------------------
Packit a69f91
 */
Packit a69f91
Packit a69f91
int
Packit a69f91
Tcl_FileReady(file, mask)
Packit a69f91
    Tcl_File file;	/* Generic file handle for a stream. */
Packit a69f91
    int mask;			/* OR'ed combination of TCL_READABLE,
Packit a69f91
				 * TCL_WRITABLE, and TCL_EXCEPTION:
Packit a69f91
				 * indicates conditions caller cares about. */
Packit a69f91
{
Packit a69f91
    int index, result, type, fd;
Packit a69f91
    fd_mask bit;
Packit a69f91
Packit a69f91
    fd = (int) Tcl_GetFileInfo(file, &type);
Packit a69f91
    if (type != TCL_UNIX_FD) {
Packit a69f91
	panic("Tcl_FileReady: unexpected file type");
Packit a69f91
    }
Packit a69f91
Packit a69f91
    index = fd/(NBBY*sizeof(fd_mask));
Packit a69f91
    bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
Packit a69f91
    result = 0;
Packit a69f91
    if ((mask & TCL_READABLE) && (readyMasks[index] & bit)) {
Packit a69f91
	result |= TCL_READABLE;
Packit a69f91
    }
Packit a69f91
    if ((mask & TCL_WRITABLE) && ((readyMasks+MASK_SIZE)[index] & bit)) {
Packit a69f91
	result |= TCL_WRITABLE;
Packit a69f91
    }
Packit a69f91
    if ((mask & TCL_EXCEPTION) && ((readyMasks+(2*MASK_SIZE))[index] & bit)) {
Packit a69f91
	result |= TCL_EXCEPTION;
Packit a69f91
    }
Packit a69f91
    return result;
Packit a69f91
}
Packit a69f91

Packit a69f91
/*
Packit a69f91
 *----------------------------------------------------------------------
Packit a69f91
 *
Packit a69f91
 * Tcl_WaitForEvent --
Packit a69f91
 *
Packit a69f91
 *	This procedure does the lowest level wait for events in a
Packit a69f91
 *	platform-specific manner.  It uses information provided by
Packit a69f91
 *	previous calls to Tcl_WatchFile, plus the timePtr argument,
Packit a69f91
 *	to determine what to wait for and how long to wait.
Packit a69f91
 *
Packit a69f91
 * Results:
Packit a69f91
 *	None.
Packit a69f91
 *
Packit a69f91
 * Side effects:
Packit a69f91
 *	May put the process to sleep for a while, depending on timePtr.
Packit a69f91
 *	When this procedure returns, an event of interest to the application
Packit a69f91
 *	has probably, but not necessarily, occurred.
Packit a69f91
 *
Packit a69f91
 *----------------------------------------------------------------------
Packit a69f91
 */
Packit a69f91
Packit a69f91
void
Packit a69f91
Tcl_WaitForEvent(timePtr)
Packit a69f91
    Tcl_Time *timePtr;		/* Specifies the maximum amount of time
Packit a69f91
				 * that this procedure should block before
Packit a69f91
				 * returning.  The time is given as an
Packit a69f91
				 * interval, not an absolute wakeup time.
Packit a69f91
				 * NULL means block forever. */
Packit a69f91
{
Packit a69f91
    struct timeval timeout, *timeoutPtr;
Packit a69f91
    int numFound;
Packit a69f91
Packit a69f91
    memcpy((VOID *) readyMasks, (VOID *) checkMasks,
Packit a69f91
	    3*MASK_SIZE*sizeof(fd_mask));
Packit a69f91
    if (timePtr == NULL) {
Packit a69f91
	timeoutPtr = NULL;
Packit a69f91
    } else {
Packit a69f91
	timeoutPtr = &timeout;
Packit a69f91
	timeout.tv_sec = timePtr->sec;
Packit a69f91
	timeout.tv_usec = timePtr->usec;
Packit a69f91
    }
Packit a69f91
    numFound = select(numFdBits, (SELECT_MASK *) &readyMasks[0],
Packit a69f91
	    (SELECT_MASK *) &readyMasks[MASK_SIZE],
Packit a69f91
	    (SELECT_MASK *) &readyMasks[2*MASK_SIZE], timeoutPtr);
Packit a69f91
Packit a69f91
    /*
Packit a69f91
     * Some systems don't clear the masks after an error, so
Packit a69f91
     * we have to do it here.
Packit a69f91
     */
Packit a69f91
Packit a69f91
    if (numFound == -1) {
Packit a69f91
	memset((VOID *) readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
Packit a69f91
    }
Packit a69f91
Packit a69f91
    /*
Packit a69f91
     * Reset the check masks in preparation for the next call to
Packit a69f91
     * select.
Packit a69f91
     */
Packit a69f91
Packit a69f91
    numFdBits = 0;
Packit a69f91
    memset((VOID *) checkMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
Packit a69f91
}
Packit a69f91

Packit a69f91
/*
Packit a69f91
 *----------------------------------------------------------------------
Packit a69f91
 *
Packit a69f91
 * Tcl_Sleep --
Packit a69f91
 *
Packit a69f91
 *	Delay execution for the specified number of milliseconds.
Packit a69f91
 *
Packit a69f91
 * Results:
Packit a69f91
 *	None.
Packit a69f91
 *
Packit a69f91
 * Side effects:
Packit a69f91
 *	Time passes.
Packit a69f91
 *
Packit a69f91
 *----------------------------------------------------------------------
Packit a69f91
 */
Packit a69f91
Packit a69f91
void
Packit a69f91
Tcl_Sleep(ms)
Packit a69f91
    int ms;			/* Number of milliseconds to sleep. */
Packit a69f91
{
Packit a69f91
    static struct timeval delay;
Packit a69f91
    Tcl_Time before, after;
Packit a69f91
Packit a69f91
    /*
Packit a69f91
     * The only trick here is that select appears to return early
Packit a69f91
     * under some conditions, so we have to check to make sure that
Packit a69f91
     * the right amount of time really has elapsed.  If it's too
Packit a69f91
     * early, go back to sleep again.
Packit a69f91
     */
Packit a69f91
Packit a69f91
    TclGetTime(&before);
Packit a69f91
    after = before;
Packit a69f91
    after.sec += ms/1000;
Packit a69f91
    after.usec += (ms%1000)*1000;
Packit a69f91
    if (after.usec > 1000000) {
Packit a69f91
	after.usec -= 1000000;
Packit a69f91
	after.sec += 1;
Packit a69f91
    }
Packit a69f91
    while (1) {
Packit a69f91
	delay.tv_sec = after.sec - before.sec;
Packit a69f91
	delay.tv_usec = after.usec - before.usec;
Packit a69f91
	if (delay.tv_usec < 0) {
Packit a69f91
	    delay.tv_usec += 1000000;
Packit a69f91
	    delay.tv_sec -= 1;
Packit a69f91
	}
Packit a69f91
Packit a69f91
	/*
Packit a69f91
	 * Special note:  must convert delay.tv_sec to int before comparing
Packit a69f91
	 * to zero, since delay.tv_usec is unsigned on some platforms.
Packit a69f91
	 */
Packit a69f91
Packit a69f91
	if ((((int) delay.tv_sec) < 0)
Packit a69f91
		|| ((delay.tv_usec == 0) && (delay.tv_sec == 0))) {
Packit a69f91
	    break;
Packit a69f91
	}
Packit a69f91
	(void) select(0, (SELECT_MASK *) 0, (SELECT_MASK *) 0,
Packit a69f91
		(SELECT_MASK *) 0, &delay);
Packit a69f91
	TclGetTime(&before);
Packit a69f91
    }
Packit a69f91
}
Packit a69f91
Packit a69f91
Packit a69f91
Packit a69f91
Packit a69f91
Packit a69f91
Packit a69f91
Packit a69f91
#if 0 /* WHOLE FILE */
Packit a69f91
Packit a69f91
Packit a69f91
Packit a69f91
/* interact (with only one process) - give user keyboard control
Packit a69f91
Packit a69f91
Written by: Don Libes, NIST, 2/6/90
Packit a69f91
Packit a69f91
Design and implementation of this program was paid for by U.S. tax
Packit a69f91
dollars.  Therefore it is public domain.  However, the author and NIST
Packit a69f91
would appreciate credit if this program or parts of it are used.
Packit a69f91
*/
Packit a69f91
Packit a69f91
/* This file exists for deficient versions of UNIX that lack select,
Packit a69f91
poll, or some other multiplexing hook.  Instead, this code uses two
Packit a69f91
processes per spawned process.  One sends characters from the spawnee
Packit a69f91
to the spawner; a second send chars the other way.
Packit a69f91
Packit a69f91
This will work on any UNIX system.  The only sacrifice is that it
Packit a69f91
doesn't support multiple processes.  Eventually, it should catch
Packit a69f91
SIGCHLD on dead processes and do the right thing.  But it is pretty
Packit a69f91
gruesome to imagine so many processes to do all this.  If you change
Packit a69f91
it successfully, please mail back the changes to me.  - Don
Packit a69f91
*/
Packit a69f91
Packit a69f91
#include "expect_cf.h"
Packit a69f91
#include <stdio.h>
Packit a69f91
#include <sys/types.h>
Packit a69f91
#include <sys/time.h>
Packit a69f91
Packit a69f91
#ifdef HAVE_SYS_WAIT_H
Packit a69f91
#include <sys/wait.h>
Packit a69f91
#endif
Packit a69f91
Packit a69f91
#include "tcl.h"
Packit a69f91
#include "exp_prog.h"
Packit a69f91
#include "exp_command.h"	/* for struct ExpState defs */
Packit a69f91
#include "exp_event.h"
Packit a69f91
Packit a69f91
/*ARGSUSED*/
Packit a69f91
void
Packit a69f91
exp_arm_background_channelhandler(esPtr)
Packit a69f91
ExpState *esPtr;
Packit a69f91
{
Packit a69f91
}
Packit a69f91
Packit a69f91
/*ARGSUSED*/
Packit a69f91
void
Packit a69f91
exp_disarm_background_channelhandler(esPtr)
Packit a69f91
ExpState *esPtr;
Packit a69f91
{
Packit a69f91
}
Packit a69f91
Packit a69f91
/*ARGSUSED*/
Packit a69f91
void
Packit a69f91
exp_disarm_background_channelhandler_force(esPtr)
Packit a69f91
ExpState *esPtr;
Packit a69f91
{
Packit a69f91
}
Packit a69f91
Packit a69f91
/*ARGSUSED*/
Packit a69f91
void
Packit a69f91
exp_unblock_background_channelhandler(esPtr)
Packit a69f91
ExpState *esPtr;
Packit a69f91
{
Packit a69f91
}
Packit a69f91
Packit a69f91
/*ARGSUSED*/
Packit a69f91
void
Packit a69f91
exp_block_background_channelhandler(esPtr)
Packit a69f91
ExpState *esPtr;
Packit a69f91
{
Packit a69f91
}
Packit a69f91
Packit a69f91
/*ARGSUSED*/
Packit a69f91
void
Packit a69f91
exp_event_disarm(fd)
Packit a69f91
int fd;
Packit a69f91
{
Packit a69f91
}
Packit a69f91
Packit a69f91
/* returns status, one of EOF, TIMEOUT, ERROR or DATA */
Packit a69f91
/*ARGSUSED*/
Packit a69f91
int
Packit a69f91
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 event master, not set if none */
Packit a69f91
int timeout;		/* seconds */
Packit a69f91
int key;
Packit a69f91
{
Packit a69f91
    ExpState *esPtr;
Packit a69f91
Packit a69f91
    if (n > 1) {
Packit a69f91
	exp_error(interp,"expect not compiled with multiprocess support");
Packit a69f91
	/* select a different INTERACT_TYPE in Makefile */
Packit a69f91
	return(TCL_ERROR);
Packit a69f91
    }
Packit a69f91
Packit a69f91
    esPtr = *esPtrOut = esPtrs[0];
Packit a69f91
Packit a69f91
    if (esPtr->key != key) {
Packit a69f91
	esPtr->key = key;
Packit a69f91
	esPtr->force_read = FALSE;
Packit a69f91
	return(EXP_DATA_OLD);
Packit a69f91
    } else if ((!esPtr->force_read) && (esPtr->size != 0)) {
Packit a69f91
	return(EXP_DATA_OLD);
Packit a69f91
    }
Packit a69f91
Packit a69f91
    return(EXP_DATA_NEW);
Packit a69f91
}
Packit a69f91
Packit a69f91
/*ARGSUSED*/
Packit a69f91
int
Packit a69f91
exp_get_next_event_info(interp,esPtr,ready_mask)
Packit a69f91
Tcl_Interp *interp;
Packit a69f91
ExpState *esPtr;
Packit a69f91
int ready_mask;
Packit a69f91
{
Packit a69f91
}
Packit a69f91
Packit a69f91
/* There is no portable way to do sub-second sleeps on such a system, so */
Packit a69f91
/* do the next best thing (without a busy loop) and fake it: sleep the right */
Packit a69f91
/* amount of time over the long run.  Note that while "subtotal" isn't */
Packit a69f91
/* reinitialized, it really doesn't matter for such a gross hack as random */
Packit a69f91
/* scheduling pauses will easily introduce occasional one second delays. */
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
	static double subtotal = 0;
Packit a69f91
	int seconds;
Packit a69f91
Packit a69f91
	subtotal += sec;
Packit a69f91
	if (subtotal < 1) return TCL_OK;
Packit a69f91
	seconds = subtotal;
Packit a69f91
	subtotal -= seconds;
Packit a69f91
 restart:
Packit a69f91
	if (Tcl_AsyncReady()) {
Packit a69f91
		int rc = Tcl_AsyncInvoke(interp,TCL_OK);
Packit a69f91
		if (rc != TCL_OK) return(rc);
Packit a69f91
	}
Packit a69f91
	sleep(seconds);
Packit a69f91
	return TCL_OK;
Packit a69f91
}
Packit a69f91
Packit a69f91
#if 0
Packit a69f91
/* There is no portable way to do sub-second sleeps on such a system, so */
Packit a69f91
/* do the next best thing (without a busy loop) and fake it: sleep the right */
Packit a69f91
/* amount of time over the long run.  Note that while "subtotal" isn't */
Packit a69f91
/* reinitialized, it really doesn't matter for such a gross hack as random */
Packit a69f91
/* scheduling pauses will easily introduce occasional one second delays. */
Packit a69f91
int	/* returns TCL_XXX */
Packit a69f91
exp_usleep(interp,usec)
Packit a69f91
Tcl_Interp *interp;
Packit a69f91
long usec;		/* microseconds */
Packit a69f91
{
Packit a69f91
	static subtotal = 0;
Packit a69f91
	int seconds;
Packit a69f91
Packit a69f91
	subtotal += usec;
Packit a69f91
	if (subtotal < 1000000) return TCL_OK;
Packit a69f91
	seconds = subtotal/1000000;
Packit a69f91
	subtotal = subtotal%1000000;
Packit a69f91
 restart:
Packit a69f91
	if (Tcl_AsyncReady()) {
Packit a69f91
		int rc = Tcl_AsyncInvoke(interp,TCL_OK);
Packit a69f91
		if (rc != TCL_OK) return(exp_tcl2_returnvalue(rc));
Packit a69f91
	}
Packit a69f91
	sleep(seconds);
Packit a69f91
	return TCL_OK;
Packit a69f91
}
Packit a69f91
#endif /*0*/
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
	exp_event_exit = 0;
Packit a69f91
}
Packit a69f91
Packit a69f91
#endif /* WHOLE FILE! */