#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include "expect_cf.h"
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include "tclInt.h"
#include "tcl.h"
#include "string.h"
#include "exp_rename.h"
#include "exp_prog.h"
#include "exp_command.h"
#include "exp_log.h"
static int ExpCloseProc _ANSI_ARGS_((ClientData instanceData,
Tcl_Interp *interp));
static int ExpInputProc _ANSI_ARGS_((ClientData instanceData,
char *buf, int toRead, int *errorCode));
static int ExpOutputProc _ANSI_ARGS_((
ClientData instanceData, char *buf, int toWrite,
int *errorCode));
static void ExpWatchProc _ANSI_ARGS_((ClientData instanceData,
int mask));
static int ExpGetHandleProc _ANSI_ARGS_((ClientData instanceData,
int direction, ClientData *handlePtr));
Tcl_ChannelType expChannelType = {
"exp",
NULL,
ExpCloseProc,
ExpInputProc,
ExpOutputProc,
NULL,
NULL,
NULL,
ExpWatchProc,
ExpGetHandleProc,
NULL,
};
typedef struct ThreadSpecificData {
ExpState *firstExpPtr;
int channelCount;
} ThreadSpecificData;
static Tcl_ThreadDataKey dataKey;
static int
ExpInputProc(instanceData, buf, toRead, errorCodePtr)
ClientData instanceData;
char *buf;
int toRead;
int *errorCodePtr;
{
ExpState *esPtr = (ExpState *) instanceData;
int bytesRead;
*errorCodePtr = 0;
bytesRead = read(esPtr->fdin, buf, (size_t) toRead);
if (bytesRead > -1) {
if (esPtr->parity == 0) {
char *end = buf+bytesRead;
for (;buf < end;buf++) {
*buf &= 0x7f;
}
}
return bytesRead;
}
*errorCodePtr = errno;
return -1;
}
static int
ExpOutputProc(instanceData, buf, toWrite, errorCodePtr)
ClientData instanceData;
char *buf;
int toWrite;
int *errorCodePtr;
{
ExpState *esPtr = (ExpState *) instanceData;
int written = 0;
*errorCodePtr = 0;
if (toWrite < 0) Tcl_Panic("ExpOutputProc: called with negative char count");
while (toWrite > 0) {
written = write(esPtr->fdout, buf, (size_t) toWrite);
if (written == 0) {
sleep(1);
expDiagLogU("write() failed to write anything - will sleep(1) and retry...\n");
} else if (written < 0) {
*errorCodePtr = errno;
return -1;
}
buf += written;
toWrite -= written;
}
return written;
}
static int
ExpCloseProc(instanceData, interp)
ClientData instanceData;
Tcl_Interp *interp;
{
ExpState *esPtr = (ExpState *) instanceData;
ExpState **nextPtrPtr;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
esPtr->registered = FALSE;
#if 0
Tcl_DeleteFileHandler(esPtr->fdin);
#endif
Tcl_DecrRefCount(esPtr->buffer);
for (nextPtrPtr = &(tsdPtr->firstExpPtr); (*nextPtrPtr) != NULL;
nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
if ((*nextPtrPtr) == esPtr) {
(*nextPtrPtr) = esPtr->nextPtr;
break;
}
}
tsdPtr->channelCount--;
if (esPtr->bg_status == blocked ||
esPtr->bg_status == disarm_req_while_blocked) {
esPtr->freeWhenBgHandlerUnblocked = 1;
} else {
expStateFree(esPtr);
}
return 0;
}
static void
ExpWatchProc(instanceData, mask)
ClientData instanceData;
int mask;
{
ExpState *esPtr = (ExpState *) instanceData;
mask &= esPtr->validMask;
if (mask) {
Tcl_CreateFileHandler(esPtr->fdin, mask,
(Tcl_FileProc *) Tcl_NotifyChannel,
(ClientData) esPtr->channel);
} else {
Tcl_DeleteFileHandler(esPtr->fdin);
}
}
static int
ExpGetHandleProc(instanceData, direction, handlePtr)
ClientData instanceData;
int direction;
ClientData *handlePtr;
{
ExpState *esPtr = (ExpState *) instanceData;
if (direction & TCL_WRITABLE) {
*handlePtr = (ClientData) esPtr->fdin;
}
if (direction & TCL_READABLE) {
*handlePtr = (ClientData) esPtr->fdin;
} else {
return TCL_ERROR;
}
return TCL_OK;
}
int
expChannelCountGet()
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
return tsdPtr->channelCount;
}
int
expSizeGet(esPtr)
ExpState *esPtr;
{
int len;
Tcl_GetStringFromObj(esPtr->buffer,&len);
return len;
}
int
expSizeZero(esPtr)
ExpState *esPtr;
{
int len;
Tcl_GetStringFromObj(esPtr->buffer,&len);
return (len == 0);
}
void
expStateFree(esPtr)
ExpState *esPtr;
{
if (esPtr->fdBusy) {
close(esPtr->fdin);
}
esPtr->valid = FALSE;
if (!esPtr->keepForever) {
ckfree((char *)esPtr);
}
}
void
exp_close_all(interp)
Tcl_Interp *interp;
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
ExpState *esPtr;
for (esPtr = tsdPtr->firstExpPtr;esPtr;esPtr = esPtr->nextPtr) {
exp_close(interp,esPtr);
}
}
ExpState *
expWaitOnAny()
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
int result;
ExpState *esPtr;
for (esPtr = tsdPtr->firstExpPtr;esPtr;esPtr = esPtr->nextPtr) {
if (esPtr->pid == exp_getpid) continue;
if (esPtr->user_waited) continue;
if (esPtr->sys_waited) break;
restart:
result = waitpid(esPtr->pid,&esPtr->wait,WNOHANG);
if (result == esPtr->pid) break;
if (result == 0) continue;
if (result == -1) {
if (errno == EINTR) goto restart;
else break;
}
}
return esPtr;
}
ExpState *
expWaitOnOne() {
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
ExpState *esPtr;
int pid;
WAIT_STATUS_TYPE status;
pid = wait(&status);
for (esPtr = tsdPtr->firstExpPtr;esPtr;esPtr = esPtr->nextPtr) {
if (esPtr->pid == pid) {
esPtr->sys_waited = TRUE;
esPtr->wait = status;
return esPtr;
}
}
}
void
exp_background_channelhandlers_run_all()
{
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
ExpState *esPtr;
for (esPtr = tsdPtr->firstExpPtr;esPtr;esPtr = esPtr->nextPtr) {
if (esPtr->bg_interp && !expSizeZero(esPtr)) {
exp_background_channelhandler((ClientData)esPtr,0);
}
}
}
ExpState *
expCreateChannel(interp,fdin,fdout,pid)
Tcl_Interp *interp;
int fdin;
int fdout;
int pid;
{
ExpState *esPtr;
int mask;
Tcl_ChannelType *channelTypePtr;
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
channelTypePtr = &expChannelType;
esPtr = (ExpState *) ckalloc((unsigned) sizeof(ExpState));
esPtr->nextPtr = tsdPtr->firstExpPtr;
tsdPtr->firstExpPtr = esPtr;
sprintf(esPtr->name,"exp%d",fdin);
mask = TCL_READABLE | TCL_WRITABLE;
esPtr->validMask = mask | TCL_EXCEPTION;
esPtr->fdin = fdin;
esPtr->fdout = fdout;
if (fdin != 0 && fdin != 2) {
expCloseOnExec(fdin);
if (fdin != fdout) expCloseOnExec(fdout);
}
esPtr->fdBusy = FALSE;
esPtr->channel = Tcl_CreateChannel(channelTypePtr, esPtr->name,
(ClientData) esPtr, mask);
Tcl_RegisterChannel(interp,esPtr->channel);
esPtr->registered = TRUE;
Tcl_SetChannelOption(interp,esPtr->channel,"-buffering","none");
Tcl_SetChannelOption(interp,esPtr->channel,"-blocking","0");
Tcl_SetChannelOption(interp,esPtr->channel,"-translation","lf");
esPtr->pid = pid;
esPtr->msize = 0;
esPtr->buffer = Tcl_NewStringObj("",0);
Tcl_IncrRefCount(esPtr->buffer);
esPtr->umsize = exp_default_match_max;
expAdjust(esPtr);
esPtr->printed = 0;
esPtr->echoed = 0;
esPtr->rm_nulls = exp_default_rm_nulls;
esPtr->parity = exp_default_parity;
esPtr->key = expect_key++;
esPtr->force_read = FALSE;
esPtr->fg_armed = FALSE;
esPtr->channel_orig = 0;
esPtr->fd_slave = EXP_NOFD;
#ifdef HAVE_PTYTRAP
esPtr->slave_name = 0;
#endif
esPtr->open = TRUE;
esPtr->notified = FALSE;
esPtr->user_waited = FALSE;
esPtr->sys_waited = FALSE;
esPtr->bg_interp = 0;
esPtr->bg_status = unarmed;
esPtr->bg_ecount = 0;
esPtr->freeWhenBgHandlerUnblocked = FALSE;
esPtr->keepForever = FALSE;
esPtr->valid = TRUE;
tsdPtr->channelCount++;
return esPtr;
}
void
expChannelInit() {
ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
tsdPtr->channelCount = 0;
}