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