#include "tclInt.h"
#include "tclPort.h"
#if ((!defined(EWOULDBLOCK)) && (defined(EAGAIN)))
# define EWOULDBLOCK EAGAIN
#endif
#if ((!defined(EAGAIN)) && (defined(EWOULDBLOCK)))
# define EAGAIN EWOULDBLOCK
#endif
#if ((!defined(EAGAIN)) && (!defined(EWOULDBLOCK)))
error one of EWOULDBLOCK or EAGAIN must be defined
#endif
typedef struct CopyState {
struct Channel *readPtr;
struct Channel *writePtr;
int readFlags;
int writeFlags;
int toRead;
int total;
Tcl_Interp *interp;
Tcl_Obj *cmdPtr;
int bufSize;
char buffer[1];
} CopyState;
typedef struct ChannelBuffer {
int nextAdded;
int nextRemoved;
int bufSize;
struct ChannelBuffer *nextPtr;
char buf[4];
} ChannelBuffer;
#define CHANNELBUFFER_HEADER_SIZE (sizeof(ChannelBuffer) - 4)
#define CHANNELBUFFER_DEFAULT_SIZE (1024 * 4)
typedef struct CloseCallback {
Tcl_CloseProc *proc;
ClientData clientData;
struct CloseCallback *nextPtr;
} CloseCallback;
typedef struct EventScriptRecord {
struct Channel *chanPtr;
char *script;
Tcl_Interp *interp;
int mask;
struct EventScriptRecord *nextPtr;
} EventScriptRecord;
typedef struct Channel {
char *channelName;
int flags;
Tcl_EolTranslation inputTranslation;
Tcl_EolTranslation outputTranslation;
int inEofChar;
int outEofChar;
int unreportedError;
ClientData instanceData;
Tcl_ChannelType *typePtr;
int refCount;
CloseCallback *closeCbPtr;
ChannelBuffer *curOutPtr;
ChannelBuffer *outQueueHead;
ChannelBuffer *outQueueTail;
ChannelBuffer *saveInBufPtr;
ChannelBuffer *inQueueHead;
ChannelBuffer *inQueueTail;
struct ChannelHandler *chPtr;
int interestMask;
struct Channel *nextChanPtr;
EventScriptRecord *scriptRecordPtr;
int bufSize;
Tcl_TimerToken timer;
CopyState *csPtr;
} Channel;
#define CHANNEL_NONBLOCKING (1<<3)
#define CHANNEL_LINEBUFFERED (1<<4)
#define CHANNEL_UNBUFFERED (1<<5)
#define BUFFER_READY (1<<6)
#define BG_FLUSH_SCHEDULED (1<<7)
#define CHANNEL_CLOSED (1<<8)
#define CHANNEL_EOF (1<<9)
#define CHANNEL_STICKY_EOF (1<<10)
#define CHANNEL_BLOCKED (1<<11)
#define INPUT_SAW_CR (1<<12)
#define CHANNEL_DEAD (1<<13)
#define CHANNEL_GETS_BLOCKED (1<<14)
typedef struct ChannelHandler {
Channel *chanPtr;
int mask;
Tcl_ChannelProc *proc;
ClientData clientData;
struct ChannelHandler *nextPtr;
} ChannelHandler;
typedef struct NextChannelHandler {
ChannelHandler *nextHandlerPtr;
struct NextChannelHandler *nestedHandlerPtr;
} NextChannelHandler;
static NextChannelHandler *nestedHandlerPtr = (NextChannelHandler *) NULL;
static Channel *firstChanPtr = (Channel *) NULL;
static int channelExitHandlerCreated = 0;
typedef struct ChannelHandlerEvent {
Tcl_Event header;
Channel *chanPtr;
int readyMask;
} ChannelHandlerEvent;
static Tcl_Channel stdinChannel = NULL;
static int stdinInitialized = 0;
static Tcl_Channel stdoutChannel = NULL;
static int stdoutInitialized = 0;
static Tcl_Channel stderrChannel = NULL;
static int stderrInitialized = 0;
static void ChannelEventScriptInvoker _ANSI_ARGS_((
ClientData clientData, int flags));
static void ChannelTimerProc _ANSI_ARGS_((
ClientData clientData));
static void CheckForStdChannelsBeingClosed _ANSI_ARGS_((
Tcl_Channel chan));
static void CleanupChannelHandlers _ANSI_ARGS_((
Tcl_Interp *interp, Channel *chanPtr));
static int CloseChannel _ANSI_ARGS_((Tcl_Interp *interp,
Channel *chanPtr, int errorCode));
static void CloseChannelsOnExit _ANSI_ARGS_((ClientData data));
static int CopyAndTranslateBuffer _ANSI_ARGS_((
Channel *chanPtr, char *result, int space));
static int CopyData _ANSI_ARGS_((CopyState *csPtr, int mask));
static void CopyEventProc _ANSI_ARGS_((ClientData clientData,
int mask));
static void CreateScriptRecord _ANSI_ARGS_((
Tcl_Interp *interp, Channel *chanPtr,
int mask, char *script));
static void DeleteChannelTable _ANSI_ARGS_((
ClientData clientData, Tcl_Interp *interp));
static void DeleteScriptRecord _ANSI_ARGS_((Tcl_Interp *interp,
Channel *chanPtr, int mask));
static void DiscardInputQueued _ANSI_ARGS_((
Channel *chanPtr, int discardSavedBuffers));
static void DiscardOutputQueued _ANSI_ARGS_((
Channel *chanPtr));
static int DoRead _ANSI_ARGS_((Channel *chanPtr, char *srcPtr,
int slen));
static int DoWrite _ANSI_ARGS_((Channel *chanPtr, char *srcPtr,
int slen));
static int FlushChannel _ANSI_ARGS_((Tcl_Interp *interp,
Channel *chanPtr, int calledFromAsyncFlush));
static Tcl_HashTable *GetChannelTable _ANSI_ARGS_((Tcl_Interp *interp));
static int GetEOL _ANSI_ARGS_((Channel *chanPtr));
static int GetInput _ANSI_ARGS_((Channel *chanPtr));
static void RecycleBuffer _ANSI_ARGS_((Channel *chanPtr,
ChannelBuffer *bufPtr, int mustDiscard));
static int ScanBufferForEOL _ANSI_ARGS_((Channel *chanPtr,
ChannelBuffer *bufPtr,
Tcl_EolTranslation translation, int eofChar,
int *bytesToEOLPtr, int *crSeenPtr));
static int ScanInputForEOL _ANSI_ARGS_((Channel *chanPtr,
int *bytesQueuedPtr));
static int SetBlockMode _ANSI_ARGS_((Tcl_Interp *interp,
Channel *chanPtr, int mode));
static void StopCopy _ANSI_ARGS_((CopyState *csPtr));
static void UpdateInterest _ANSI_ARGS_((Channel *chanPtr));
static int CheckForDeadChannel _ANSI_ARGS_((Tcl_Interp *interp,
Channel *chan));
static int
SetBlockMode(interp, chanPtr, mode)
Tcl_Interp *interp;
Channel *chanPtr;
int mode;
{
int result = 0;
if (chanPtr->typePtr->blockModeProc != NULL) {
result = (chanPtr->typePtr->blockModeProc) (chanPtr->instanceData,
mode);
}
if (result != 0) {
Tcl_SetErrno(result);
if (interp != (Tcl_Interp *) NULL) {
Tcl_AppendResult(interp, "error setting blocking mode: ",
Tcl_PosixError(interp), (char *) NULL);
}
return TCL_ERROR;
}
if (mode == TCL_MODE_BLOCKING) {
chanPtr->flags &= (~(CHANNEL_NONBLOCKING | BG_FLUSH_SCHEDULED));
} else {
chanPtr->flags |= CHANNEL_NONBLOCKING;
}
return TCL_OK;
}
void
Tcl_SetStdChannel(channel, type)
Tcl_Channel channel;
int type;
{
switch (type) {
case TCL_STDIN:
stdinInitialized = 1;
stdinChannel = channel;
break;
case TCL_STDOUT:
stdoutInitialized = 1;
stdoutChannel = channel;
break;
case TCL_STDERR:
stderrInitialized = 1;
stderrChannel = channel;
break;
}
}
Tcl_Channel
Tcl_GetStdChannel(type)
int type;
{
Tcl_Channel channel = NULL;
switch (type) {
case TCL_STDIN:
if (!stdinInitialized) {
stdinChannel = TclGetDefaultStdChannel(TCL_STDIN);
stdinInitialized = 1;
if (stdinChannel != (Tcl_Channel) NULL) {
(void) Tcl_RegisterChannel((Tcl_Interp *) NULL,
stdinChannel);
}
}
channel = stdinChannel;
break;
case TCL_STDOUT:
if (!stdoutInitialized) {
stdoutChannel = TclGetDefaultStdChannel(TCL_STDOUT);
stdoutInitialized = 1;
if (stdoutChannel != (Tcl_Channel) NULL) {
(void) Tcl_RegisterChannel((Tcl_Interp *) NULL,
stdoutChannel);
}
}
channel = stdoutChannel;
break;
case TCL_STDERR:
if (!stderrInitialized) {
stderrChannel = TclGetDefaultStdChannel(TCL_STDERR);
stderrInitialized = 1;
if (stderrChannel != (Tcl_Channel) NULL) {
(void) Tcl_RegisterChannel((Tcl_Interp *) NULL,
stderrChannel);
}
}
channel = stderrChannel;
break;
}
return channel;
}
void
Tcl_CreateCloseHandler(chan, proc, clientData)
Tcl_Channel chan;
Tcl_CloseProc *proc;
ClientData clientData;
{
Channel *chanPtr;
CloseCallback *cbPtr;
chanPtr = (Channel *) chan;
cbPtr = (CloseCallback *) ckalloc((unsigned) sizeof(CloseCallback));
cbPtr->proc = proc;
cbPtr->clientData = clientData;
cbPtr->nextPtr = chanPtr->closeCbPtr;
chanPtr->closeCbPtr = cbPtr;
}
void
Tcl_DeleteCloseHandler(chan, proc, clientData)
Tcl_Channel chan;
Tcl_CloseProc *proc;
ClientData clientData;
{
Channel *chanPtr;
CloseCallback *cbPtr, *cbPrevPtr;
chanPtr = (Channel *) chan;
for (cbPtr = chanPtr->closeCbPtr, cbPrevPtr = (CloseCallback *) NULL;
cbPtr != (CloseCallback *) NULL;
cbPtr = cbPtr->nextPtr) {
if ((cbPtr->proc == proc) && (cbPtr->clientData == clientData)) {
if (cbPrevPtr == (CloseCallback *) NULL) {
chanPtr->closeCbPtr = cbPtr->nextPtr;
}
ckfree((char *) cbPtr);
break;
} else {
cbPrevPtr = cbPtr;
}
}
}
static void
CloseChannelsOnExit(clientData)
ClientData clientData;
{
Channel *chanPtr;
Channel *nextChanPtr;
for (chanPtr = firstChanPtr; chanPtr != (Channel *) NULL;
chanPtr = nextChanPtr) {
nextChanPtr = chanPtr->nextChanPtr;
(void) Tcl_SetChannelOption(NULL, (Tcl_Channel) chanPtr,
"-blocking", "on");
if ((chanPtr == (Channel *) stdinChannel) ||
(chanPtr == (Channel *) stdoutChannel) ||
(chanPtr == (Channel *) stderrChannel)) {
chanPtr->refCount--;
}
if (chanPtr->refCount <= 0) {
(void) Tcl_Close((Tcl_Interp *) NULL, (Tcl_Channel) chanPtr);
} else {
Tcl_Flush((Tcl_Channel) chanPtr);
(chanPtr->typePtr->closeProc) (chanPtr->instanceData,
(Tcl_Interp *) NULL);
chanPtr->instanceData = (ClientData) NULL;
chanPtr->flags |= CHANNEL_DEAD;
}
}
firstChanPtr = (Channel *) NULL;
nestedHandlerPtr = (NextChannelHandler *) NULL;
channelExitHandlerCreated = 0;
stdinChannel = NULL;
stdinInitialized = 0;
stdoutChannel = NULL;
stdoutInitialized = 0;
stderrChannel = NULL;
stderrInitialized = 0;
}
static Tcl_HashTable *
GetChannelTable(interp)
Tcl_Interp *interp;
{
Tcl_HashTable *hTblPtr;
Tcl_Channel stdinChan, stdoutChan, stderrChan;
hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
if (hTblPtr == (Tcl_HashTable *) NULL) {
hTblPtr = (Tcl_HashTable *) ckalloc((unsigned) sizeof(Tcl_HashTable));
Tcl_InitHashTable(hTblPtr, TCL_STRING_KEYS);
(void) Tcl_SetAssocData(interp, "tclIO",
(Tcl_InterpDeleteProc *) DeleteChannelTable,
(ClientData) hTblPtr);
if (Tcl_IsSafe(interp) == 0) {
stdinChan = Tcl_GetStdChannel(TCL_STDIN);
if (stdinChan != NULL) {
Tcl_RegisterChannel(interp, stdinChan);
}
stdoutChan = Tcl_GetStdChannel(TCL_STDOUT);
if (stdoutChan != NULL) {
Tcl_RegisterChannel(interp, stdoutChan);
}
stderrChan = Tcl_GetStdChannel(TCL_STDERR);
if (stderrChan != NULL) {
Tcl_RegisterChannel(interp, stderrChan);
}
}
}
return hTblPtr;
}
static void
DeleteChannelTable(clientData, interp)
ClientData clientData;
Tcl_Interp *interp;
{
Tcl_HashTable *hTblPtr;
Tcl_HashSearch hSearch;
Tcl_HashEntry *hPtr;
Channel *chanPtr;
EventScriptRecord *sPtr, *prevPtr, *nextPtr;
hTblPtr = (Tcl_HashTable *) clientData;
for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
hPtr != (Tcl_HashEntry *) NULL;
hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch)) {
chanPtr = (Channel *) Tcl_GetHashValue(hPtr);
for (sPtr = chanPtr->scriptRecordPtr,
prevPtr = (EventScriptRecord *) NULL;
sPtr != (EventScriptRecord *) NULL;
sPtr = nextPtr) {
nextPtr = sPtr->nextPtr;
if (sPtr->interp == interp) {
if (prevPtr == (EventScriptRecord *) NULL) {
chanPtr->scriptRecordPtr = nextPtr;
} else {
prevPtr->nextPtr = nextPtr;
}
Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
ChannelEventScriptInvoker, (ClientData) sPtr);
ckfree(sPtr->script);
ckfree((char *) sPtr);
} else {
prevPtr = sPtr;
}
}
Tcl_DeleteHashEntry(hPtr);
chanPtr->refCount--;
if (chanPtr->refCount <= 0) {
if (!(chanPtr->flags & BG_FLUSH_SCHEDULED)) {
(void) Tcl_Close(interp, (Tcl_Channel) chanPtr);
}
}
}
Tcl_DeleteHashTable(hTblPtr);
ckfree((char *) hTblPtr);
}
static void
CheckForStdChannelsBeingClosed(chan)
Tcl_Channel chan;
{
Channel *chanPtr = (Channel *) chan;
if ((chan == stdinChannel) && (stdinInitialized)) {
if (chanPtr->refCount < 2) {
chanPtr->refCount = 0;
stdinChannel = NULL;
return;
}
} else if ((chan == stdoutChannel) && (stdoutInitialized)) {
if (chanPtr->refCount < 2) {
chanPtr->refCount = 0;
stdoutChannel = NULL;
return;
}
} else if ((chan == stderrChannel) && (stderrInitialized)) {
if (chanPtr->refCount < 2) {
chanPtr->refCount = 0;
stderrChannel = NULL;
return;
}
}
}
int
Tcl_UnregisterChannel(interp, chan)
Tcl_Interp *interp;
Tcl_Channel chan;
{
Tcl_HashTable *hTblPtr;
Tcl_HashEntry *hPtr;
Channel *chanPtr;
chanPtr = (Channel *) chan;
if (interp != (Tcl_Interp *) NULL) {
hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
if (hTblPtr == (Tcl_HashTable *) NULL) {
return TCL_OK;
}
hPtr = Tcl_FindHashEntry(hTblPtr, chanPtr->channelName);
if (hPtr == (Tcl_HashEntry *) NULL) {
return TCL_OK;
}
if ((Channel *) Tcl_GetHashValue(hPtr) != chanPtr) {
return TCL_OK;
}
Tcl_DeleteHashEntry(hPtr);
CleanupChannelHandlers(interp, chanPtr);
}
chanPtr->refCount--;
CheckForStdChannelsBeingClosed(chan);
if (chanPtr->refCount <= 0) {
if ((chanPtr->curOutPtr != NULL) &&
(chanPtr->curOutPtr->nextAdded >
chanPtr->curOutPtr->nextRemoved)) {
chanPtr->flags |= BUFFER_READY;
}
chanPtr->flags |= CHANNEL_CLOSED;
if (!(chanPtr->flags & BG_FLUSH_SCHEDULED)) {
if (Tcl_Close(interp, chan) != TCL_OK) {
return TCL_ERROR;
}
}
}
return TCL_OK;
}
void
Tcl_RegisterChannel(interp, chan)
Tcl_Interp *interp;
Tcl_Channel chan;
{
Tcl_HashTable *hTblPtr;
Tcl_HashEntry *hPtr;
int new;
Channel *chanPtr;
chanPtr = (Channel *) chan;
if (chanPtr->channelName == (char *) NULL) {
panic("Tcl_RegisterChannel: channel without name");
}
if (interp != (Tcl_Interp *) NULL) {
hTblPtr = GetChannelTable(interp);
hPtr = Tcl_CreateHashEntry(hTblPtr, chanPtr->channelName, &new);
if (new == 0) {
if (chan == (Tcl_Channel) Tcl_GetHashValue(hPtr)) {
return;
}
panic("Tcl_RegisterChannel: duplicate channel names");
}
Tcl_SetHashValue(hPtr, (ClientData) chanPtr);
}
chanPtr->refCount++;
}
Tcl_Channel
Tcl_GetChannel(interp, chanName, modePtr)
Tcl_Interp *interp;
char *chanName;
int *modePtr;
{
Channel *chanPtr;
Tcl_HashTable *hTblPtr;
Tcl_HashEntry *hPtr;
char *name;
name = chanName;
if ((chanName[0] == 's') && (chanName[1] == 't')) {
chanPtr = NULL;
if (strcmp(chanName, "stdin") == 0) {
chanPtr = (Channel *)Tcl_GetStdChannel(TCL_STDIN);
} else if (strcmp(chanName, "stdout") == 0) {
chanPtr = (Channel *)Tcl_GetStdChannel(TCL_STDOUT);
} else if (strcmp(chanName, "stderr") == 0) {
chanPtr = (Channel *)Tcl_GetStdChannel(TCL_STDERR);
}
if (chanPtr != NULL) {
name = chanPtr->channelName;
}
}
hTblPtr = GetChannelTable(interp);
hPtr = Tcl_FindHashEntry(hTblPtr, name);
if (hPtr == (Tcl_HashEntry *) NULL) {
Tcl_AppendResult(interp, "can not find channel named \"",
chanName, "\"", (char *) NULL);
return NULL;
}
chanPtr = (Channel *) Tcl_GetHashValue(hPtr);
if (modePtr != NULL) {
*modePtr = (chanPtr->flags & (TCL_READABLE|TCL_WRITABLE));
}
return (Tcl_Channel) chanPtr;
}
Tcl_Channel
Tcl_CreateChannel(typePtr, chanName, instanceData, mask)
Tcl_ChannelType *typePtr;
char *chanName;
ClientData instanceData;
int mask;
{
Channel *chanPtr;
chanPtr = (Channel *) ckalloc((unsigned) sizeof(Channel));
if (chanName != (char *) NULL) {
chanPtr->channelName = ckalloc((unsigned) (strlen(chanName) + 1));
strcpy(chanPtr->channelName, chanName);
} else {
panic("Tcl_CreateChannel: NULL channel name");
}
chanPtr->flags = mask;
chanPtr->inputTranslation = TCL_TRANSLATE_AUTO;
chanPtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
chanPtr->inEofChar = 0;
chanPtr->outEofChar = 0;
chanPtr->unreportedError = 0;
chanPtr->instanceData = instanceData;
chanPtr->typePtr = typePtr;
chanPtr->refCount = 0;
chanPtr->closeCbPtr = (CloseCallback *) NULL;
chanPtr->curOutPtr = (ChannelBuffer *) NULL;
chanPtr->outQueueHead = (ChannelBuffer *) NULL;
chanPtr->outQueueTail = (ChannelBuffer *) NULL;
chanPtr->saveInBufPtr = (ChannelBuffer *) NULL;
chanPtr->inQueueHead = (ChannelBuffer *) NULL;
chanPtr->inQueueTail = (ChannelBuffer *) NULL;
chanPtr->chPtr = (ChannelHandler *) NULL;
chanPtr->interestMask = 0;
chanPtr->scriptRecordPtr = (EventScriptRecord *) NULL;
chanPtr->bufSize = CHANNELBUFFER_DEFAULT_SIZE;
chanPtr->timer = NULL;
chanPtr->csPtr = NULL;
chanPtr->nextChanPtr = firstChanPtr;
firstChanPtr = chanPtr;
if (!channelExitHandlerCreated) {
channelExitHandlerCreated = 1;
Tcl_CreateExitHandler(CloseChannelsOnExit, (ClientData) NULL);
}
if ((stdinChannel == NULL) && (stdinInitialized == 1)) {
Tcl_SetStdChannel((Tcl_Channel)chanPtr, TCL_STDIN);
Tcl_RegisterChannel((Tcl_Interp *) NULL, (Tcl_Channel) chanPtr);
} else if ((stdoutChannel == NULL) && (stdoutInitialized == 1)) {
Tcl_SetStdChannel((Tcl_Channel)chanPtr, TCL_STDOUT);
Tcl_RegisterChannel((Tcl_Interp *) NULL, (Tcl_Channel) chanPtr);
} else if ((stderrChannel == NULL) && (stderrInitialized == 1)) {
Tcl_SetStdChannel((Tcl_Channel)chanPtr, TCL_STDERR);
Tcl_RegisterChannel((Tcl_Interp *) NULL, (Tcl_Channel) chanPtr);
}
return (Tcl_Channel) chanPtr;
}
int
Tcl_GetChannelMode(chan)
Tcl_Channel chan;
{
Channel *chanPtr;
chanPtr = (Channel *) chan;
return (chanPtr->flags & (TCL_READABLE | TCL_WRITABLE));
}
char *
Tcl_GetChannelName(chan)
Tcl_Channel chan;
{
Channel *chanPtr;
chanPtr = (Channel *) chan;
return chanPtr->channelName;
}
Tcl_ChannelType *
Tcl_GetChannelType(chan)
Tcl_Channel chan;
{
Channel *chanPtr;
chanPtr = (Channel *) chan;
return chanPtr->typePtr;
}
int
Tcl_GetChannelHandle(chan, direction, handlePtr)
Tcl_Channel chan;
int direction;
ClientData *handlePtr;
{
Channel *chanPtr;
ClientData handle;
int result;
chanPtr = (Channel *) chan;
result = (chanPtr->typePtr->getHandleProc)(chanPtr->instanceData,
direction, &handle);
if (handlePtr) {
*handlePtr = handle;
}
return result;
}
ClientData
Tcl_GetChannelInstanceData(chan)
Tcl_Channel chan;
{
Channel *chanPtr;
chanPtr = (Channel *) chan;
return chanPtr->instanceData;
}
static void
RecycleBuffer(chanPtr, bufPtr, mustDiscard)
Channel *chanPtr;
ChannelBuffer *bufPtr;
int mustDiscard;
{
if (mustDiscard) {
ckfree((char *) bufPtr);
return;
}
if (chanPtr->flags & TCL_READABLE) {
if (chanPtr->inQueueHead == (ChannelBuffer *) NULL) {
chanPtr->inQueueHead = bufPtr;
chanPtr->inQueueTail = bufPtr;
goto keepit;
}
if (chanPtr->saveInBufPtr == (ChannelBuffer *) NULL) {
chanPtr->saveInBufPtr = bufPtr;
goto keepit;
}
}
if (chanPtr->flags & TCL_WRITABLE) {
if (chanPtr->curOutPtr == (ChannelBuffer *) NULL) {
chanPtr->curOutPtr = bufPtr;
goto keepit;
}
}
ckfree((char *) bufPtr);
return;
keepit:
bufPtr->nextRemoved = 0;
bufPtr->nextAdded = 0;
bufPtr->nextPtr = (ChannelBuffer *) NULL;
}
static void
DiscardOutputQueued(chanPtr)
Channel *chanPtr;
{
ChannelBuffer *bufPtr;
while (chanPtr->outQueueHead != (ChannelBuffer *) NULL) {
bufPtr = chanPtr->outQueueHead;
chanPtr->outQueueHead = bufPtr->nextPtr;
RecycleBuffer(chanPtr, bufPtr, 0);
}
chanPtr->outQueueHead = (ChannelBuffer *) NULL;
chanPtr->outQueueTail = (ChannelBuffer *) NULL;
}
static int
CheckForDeadChannel(interp, chanPtr)
Tcl_Interp *interp;
Channel *chanPtr;
{
if (chanPtr->flags & CHANNEL_DEAD) {
Tcl_SetErrno(EINVAL);
if (interp) {
Tcl_AppendResult(interp,
"unable to access channel: invalid channel",
(char *) NULL);
}
return 1;
}
return 0;
}
static int
FlushChannel(interp, chanPtr, calledFromAsyncFlush)
Tcl_Interp *interp;
Channel *chanPtr;
int calledFromAsyncFlush;
{
ChannelBuffer *bufPtr;
int toWrite;
int written;
int errorCode;
errorCode = 0;
if (CheckForDeadChannel(interp,chanPtr)) return -1;
while (1) {
if (((chanPtr->curOutPtr != (ChannelBuffer *) NULL) &&
(chanPtr->curOutPtr->nextAdded == chanPtr->curOutPtr->bufSize))
|| ((chanPtr->flags & BUFFER_READY) &&
(chanPtr->outQueueHead == (ChannelBuffer *) NULL))) {
chanPtr->flags &= (~(BUFFER_READY));
chanPtr->curOutPtr->nextPtr = (ChannelBuffer *) NULL;
if (chanPtr->outQueueHead == (ChannelBuffer *) NULL) {
chanPtr->outQueueHead = chanPtr->curOutPtr;
} else {
chanPtr->outQueueTail->nextPtr = chanPtr->curOutPtr;
}
chanPtr->outQueueTail = chanPtr->curOutPtr;
chanPtr->curOutPtr = (ChannelBuffer *) NULL;
}
bufPtr = chanPtr->outQueueHead;
if ((!calledFromAsyncFlush) &&
(chanPtr->flags & BG_FLUSH_SCHEDULED)) {
return 0;
}
if (bufPtr == (ChannelBuffer *) NULL) {
break;
}
toWrite = bufPtr->nextAdded - bufPtr->nextRemoved;
written = (chanPtr->typePtr->outputProc) (chanPtr->instanceData,
bufPtr->buf + bufPtr->nextRemoved, toWrite, &errorCode);
if (written < 0) {
if (errorCode == EINTR) {
errorCode = 0;
continue;
}
if ((errorCode == EWOULDBLOCK) || (errorCode == EAGAIN)) {
if (chanPtr->flags & CHANNEL_NONBLOCKING) {
if (!(chanPtr->flags & BG_FLUSH_SCHEDULED)) {
chanPtr->flags |= BG_FLUSH_SCHEDULED;
UpdateInterest(chanPtr);
}
errorCode = 0;
break;
} else {
panic("Blocking channel driver did not block on output");
}
}
if (calledFromAsyncFlush) {
if (chanPtr->unreportedError == 0) {
chanPtr->unreportedError = errorCode;
}
} else {
Tcl_SetErrno(errorCode);
if (interp != NULL) {
Tcl_SetResult(interp,
Tcl_PosixError(interp), TCL_VOLATILE);
}
}
DiscardOutputQueued(chanPtr);
continue;
}
bufPtr->nextRemoved += written;
if (bufPtr->nextRemoved == bufPtr->nextAdded) {
chanPtr->outQueueHead = bufPtr->nextPtr;
if (chanPtr->outQueueHead == (ChannelBuffer *) NULL) {
chanPtr->outQueueTail = (ChannelBuffer *) NULL;
}
RecycleBuffer(chanPtr, bufPtr, 0);
}
}
if ((chanPtr->outQueueHead == (ChannelBuffer *) NULL) &&
(chanPtr->flags & BG_FLUSH_SCHEDULED)) {
chanPtr->flags &= (~(BG_FLUSH_SCHEDULED));
(chanPtr->typePtr->watchProc)(chanPtr->instanceData,
chanPtr->interestMask);
}
if ((chanPtr->flags & CHANNEL_CLOSED) && (chanPtr->refCount <= 0) &&
(chanPtr->outQueueHead == (ChannelBuffer *) NULL) &&
((chanPtr->curOutPtr == (ChannelBuffer *) NULL) ||
(chanPtr->curOutPtr->nextAdded ==
chanPtr->curOutPtr->nextRemoved))) {
return CloseChannel(interp, chanPtr, errorCode);
}
return errorCode;
}
static int
CloseChannel(interp, chanPtr, errorCode)
Tcl_Interp *interp;
Channel *chanPtr;
int errorCode;
{
int result = 0;
Channel *prevChanPtr;
if (chanPtr == NULL) {
return result;
}
DiscardInputQueued(chanPtr, 1);
if (chanPtr->curOutPtr != (ChannelBuffer *) NULL) {
ckfree((char *) chanPtr->curOutPtr);
chanPtr->curOutPtr = (ChannelBuffer *) NULL;
}
if (chanPtr->outQueueHead != (ChannelBuffer *) NULL) {
panic("TclFlush, closed channel: queued output left");
}
if ((chanPtr->outEofChar != 0) && (chanPtr->flags & TCL_WRITABLE)) {
int dummy;
char c;
c = (char) chanPtr->outEofChar;
(chanPtr->typePtr->outputProc) (chanPtr->instanceData, &c, 1, &dummy);
}
chanPtr->flags &= (~(TCL_READABLE|TCL_WRITABLE));
if (chanPtr == firstChanPtr) {
firstChanPtr = chanPtr->nextChanPtr;
} else {
for (prevChanPtr = firstChanPtr;
(prevChanPtr != (Channel *) NULL) &&
(prevChanPtr->nextChanPtr != chanPtr);
prevChanPtr = prevChanPtr->nextChanPtr) {
}
if (prevChanPtr == (Channel *) NULL) {
panic("FlushChannel: damaged channel list");
}
prevChanPtr->nextChanPtr = chanPtr->nextChanPtr;
}
result = (chanPtr->typePtr->closeProc) (chanPtr->instanceData, interp);
if (chanPtr->channelName != (char *) NULL) {
ckfree(chanPtr->channelName);
}
if (chanPtr->unreportedError != 0) {
errorCode = chanPtr->unreportedError;
}
if (errorCode == 0) {
errorCode = result;
if (errorCode != 0) {
Tcl_SetErrno(errorCode);
}
}
Tcl_DeleteTimerHandler(chanPtr->timer);
chanPtr->typePtr = NULL;
Tcl_EventuallyFree((ClientData) chanPtr, TCL_DYNAMIC);
return errorCode;
}
int
Tcl_Close(interp, chan)
Tcl_Interp *interp;
Tcl_Channel chan;
{
ChannelHandler *chPtr, *chNext;
CloseCallback *cbPtr;
EventScriptRecord *ePtr, *eNextPtr;
Channel *chanPtr;
int result;
NextChannelHandler *nhPtr;
if (chan == (Tcl_Channel) NULL) {
return TCL_OK;
}
CheckForStdChannelsBeingClosed(chan);
chanPtr = (Channel *) chan;
if (chanPtr->refCount > 0) {
panic("called Tcl_Close on channel with refCount > 0");
}
for (nhPtr = nestedHandlerPtr;
nhPtr != (NextChannelHandler *) NULL;
nhPtr = nhPtr->nestedHandlerPtr) {
if (nhPtr->nextHandlerPtr &&
(nhPtr->nextHandlerPtr->chanPtr == chanPtr)) {
nhPtr->nextHandlerPtr = NULL;
}
}
for (chPtr = chanPtr->chPtr;
chPtr != (ChannelHandler *) NULL;
chPtr = chNext) {
chNext = chPtr->nextPtr;
ckfree((char *) chPtr);
}
chanPtr->chPtr = (ChannelHandler *) NULL;
StopCopy(chanPtr->csPtr);
chanPtr->interestMask = 0;
for (ePtr = chanPtr->scriptRecordPtr;
ePtr != (EventScriptRecord *) NULL;
ePtr = eNextPtr) {
eNextPtr = ePtr->nextPtr;
ckfree(ePtr->script);
ckfree((char *) ePtr);
}
chanPtr->scriptRecordPtr = (EventScriptRecord *) NULL;
while (chanPtr->closeCbPtr != (CloseCallback *) NULL) {
cbPtr = chanPtr->closeCbPtr;
chanPtr->closeCbPtr = cbPtr->nextPtr;
(cbPtr->proc) (cbPtr->clientData);
ckfree((char *) cbPtr);
}
if ((chanPtr->curOutPtr != (ChannelBuffer *) NULL) &&
(chanPtr->curOutPtr->nextAdded > chanPtr->curOutPtr->nextRemoved)) {
chanPtr->flags |= BUFFER_READY;
}
chanPtr->flags |= CHANNEL_CLOSED;
result = FlushChannel(interp, chanPtr, 0);
if (result != 0) {
return TCL_ERROR;
}
return TCL_OK;
}
int
Tcl_Write(chan, srcPtr, slen)
Tcl_Channel chan;
char *srcPtr;
int slen;
{
Channel *chanPtr = (Channel *) chan;
if (chanPtr->unreportedError != 0) {
Tcl_SetErrno(chanPtr->unreportedError);
chanPtr->unreportedError = 0;
return -1;
}
if (!(chanPtr->flags & TCL_WRITABLE)) {
Tcl_SetErrno(EACCES);
return -1;
}
if (chanPtr->csPtr) {
Tcl_SetErrno(EBUSY);
return -1;
}
if (slen < 0) {
slen = strlen(srcPtr);
}
return DoWrite(chanPtr, srcPtr, slen);
}
static int
DoWrite(chanPtr, srcPtr, slen)
Channel *chanPtr;
char *srcPtr;
int slen;
{
ChannelBuffer *outBufPtr;
int foundNewline;
char *dPtr, *sPtr;
int crsent;
int i;
int destCopied;
int totalDestCopied;
int srcCopied;
char *destPtr;
crsent = 0;
srcCopied = 0;
totalDestCopied = 0;
while (slen > 0) {
if (chanPtr->curOutPtr == (ChannelBuffer *) NULL) {
chanPtr->curOutPtr = (ChannelBuffer *) ckalloc((unsigned)
(CHANNELBUFFER_HEADER_SIZE + chanPtr->bufSize));
chanPtr->curOutPtr->nextAdded = 0;
chanPtr->curOutPtr->nextRemoved = 0;
chanPtr->curOutPtr->bufSize = chanPtr->bufSize;
chanPtr->curOutPtr->nextPtr = (ChannelBuffer *) NULL;
}
outBufPtr = chanPtr->curOutPtr;
destCopied = outBufPtr->bufSize - outBufPtr->nextAdded;
if (destCopied > slen) {
destCopied = slen;
}
destPtr = outBufPtr->buf + outBufPtr->nextAdded;
switch (chanPtr->outputTranslation) {
case TCL_TRANSLATE_LF:
srcCopied = destCopied;
memcpy((VOID *) destPtr, (VOID *) srcPtr, (size_t) destCopied);
break;
case TCL_TRANSLATE_CR:
srcCopied = destCopied;
memcpy((VOID *) destPtr, (VOID *) srcPtr, (size_t) destCopied);
for (dPtr = destPtr; dPtr < destPtr + destCopied; dPtr++) {
if (*dPtr == '\n') {
*dPtr = '\r';
}
}
break;
case TCL_TRANSLATE_CRLF:
for (srcCopied = 0, dPtr = destPtr, sPtr = srcPtr;
dPtr < destPtr + destCopied;
dPtr++, sPtr++, srcCopied++) {
if (*sPtr == '\n') {
if (crsent) {
*dPtr = '\n';
crsent = 0;
} else {
*dPtr = '\r';
crsent = 1;
sPtr--, srcCopied--;
}
} else {
*dPtr = *sPtr;
}
}
break;
case TCL_TRANSLATE_AUTO:
panic("Tcl_Write: AUTO output translation mode not supported");
default:
panic("Tcl_Write: unknown output translation mode");
}
outBufPtr->nextAdded += destCopied;
if (!(chanPtr->flags & BUFFER_READY)) {
if (outBufPtr->nextAdded == outBufPtr->bufSize) {
chanPtr->flags |= BUFFER_READY;
} else if (chanPtr->flags & CHANNEL_LINEBUFFERED) {
for (sPtr = srcPtr, i = 0, foundNewline = 0;
(i < srcCopied) && (!foundNewline);
i++, sPtr++) {
if (*sPtr == '\n') {
foundNewline = 1;
break;
}
}
if (foundNewline) {
chanPtr->flags |= BUFFER_READY;
}
} else if (chanPtr->flags & CHANNEL_UNBUFFERED) {
chanPtr->flags |= BUFFER_READY;
}
}
totalDestCopied += srcCopied;
srcPtr += srcCopied;
slen -= srcCopied;
if (chanPtr->flags & BUFFER_READY) {
if (FlushChannel(NULL, chanPtr, 0) != 0) {
return -1;
}
}
}
return totalDestCopied;
}
int
Tcl_Flush(chan)
Tcl_Channel chan;
{
int result;
Channel *chanPtr;
chanPtr = (Channel *) chan;
if (chanPtr->unreportedError != 0) {
Tcl_SetErrno(chanPtr->unreportedError);
chanPtr->unreportedError = 0;
return TCL_ERROR;
}
if (!(chanPtr->flags & TCL_WRITABLE)) {
Tcl_SetErrno(EACCES);
return TCL_ERROR;
}
if (chanPtr->csPtr) {
Tcl_SetErrno(EBUSY);
return -1;
}
if ((chanPtr->curOutPtr != (ChannelBuffer *) NULL) &&
(chanPtr->curOutPtr->nextAdded > 0)) {
chanPtr->flags |= BUFFER_READY;
}
result = FlushChannel(NULL, chanPtr, 0);
if (result != 0) {
return TCL_ERROR;
}
return TCL_OK;
}
static void
DiscardInputQueued(chanPtr, discardSavedBuffers)
Channel *chanPtr;
int discardSavedBuffers;
{
ChannelBuffer *bufPtr, *nxtPtr;
bufPtr = chanPtr->inQueueHead;
chanPtr->inQueueHead = (ChannelBuffer *) NULL;
chanPtr->inQueueTail = (ChannelBuffer *) NULL;
for (; bufPtr != (ChannelBuffer *) NULL; bufPtr = nxtPtr) {
nxtPtr = bufPtr->nextPtr;
RecycleBuffer(chanPtr, bufPtr, discardSavedBuffers);
}
if (discardSavedBuffers) {
if (chanPtr->saveInBufPtr != (ChannelBuffer *) NULL) {
ckfree((char *) chanPtr->saveInBufPtr);
chanPtr->saveInBufPtr = (ChannelBuffer *) NULL;
}
}
}
static int
GetInput(chanPtr)
Channel *chanPtr;
{
int toRead;
int result;
int nread;
ChannelBuffer *bufPtr;
if (CheckForDeadChannel(NULL,chanPtr)) return EINVAL;
if ((chanPtr->inQueueTail != (ChannelBuffer *) NULL) &&
(chanPtr->inQueueTail->nextAdded < chanPtr->inQueueTail->bufSize)) {
bufPtr = chanPtr->inQueueTail;
toRead = bufPtr->bufSize - bufPtr->nextAdded;
} else {
if (chanPtr->saveInBufPtr != (ChannelBuffer *) NULL) {
bufPtr = chanPtr->saveInBufPtr;
chanPtr->saveInBufPtr = (ChannelBuffer *) NULL;
} else {
bufPtr = (ChannelBuffer *) ckalloc(
((unsigned) CHANNELBUFFER_HEADER_SIZE + chanPtr->bufSize));
bufPtr->bufSize = chanPtr->bufSize;
}
bufPtr->nextRemoved = 0;
bufPtr->nextAdded = 0;
toRead = bufPtr->bufSize;
if (chanPtr->inQueueTail == (ChannelBuffer *) NULL) {
chanPtr->inQueueHead = bufPtr;
} else {
chanPtr->inQueueTail->nextPtr = bufPtr;
}
chanPtr->inQueueTail = bufPtr;
bufPtr->nextPtr = (ChannelBuffer *) NULL;
}
if (chanPtr->flags & CHANNEL_EOF) {
return 0;
}
nread = (chanPtr->typePtr->inputProc) (chanPtr->instanceData,
bufPtr->buf + bufPtr->nextAdded, toRead, &result);
if (nread == 0) {
chanPtr->flags |= CHANNEL_EOF;
} else if (nread < 0) {
if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
chanPtr->flags |= CHANNEL_BLOCKED;
result = EAGAIN;
if (chanPtr->flags & CHANNEL_NONBLOCKING) {
Tcl_SetErrno(result);
} else {
panic("Blocking channel driver did not block on input");
}
} else {
Tcl_SetErrno(result);
}
return result;
} else {
bufPtr->nextAdded += nread;
if (nread < toRead) {
chanPtr->flags |= CHANNEL_BLOCKED;
}
}
return 0;
}
static int
CopyAndTranslateBuffer(chanPtr, result, space)
Channel *chanPtr;
char *result;
int space;
{
int bytesInBuffer;
int copied;
ChannelBuffer *bufPtr;
char curByte;
int i;
if (chanPtr->inQueueHead == (ChannelBuffer *) NULL) {
return 0;
}
bufPtr = chanPtr->inQueueHead;
bytesInBuffer = bufPtr->nextAdded - bufPtr->nextRemoved;
if (bytesInBuffer < space) {
space = bytesInBuffer;
}
copied = 0;
switch (chanPtr->inputTranslation) {
case TCL_TRANSLATE_LF:
if (space == 0) {
return 0;
}
memcpy((VOID *) result,
(VOID *)(bufPtr->buf + bufPtr->nextRemoved),
(size_t) space);
bufPtr->nextRemoved += space;
copied = space;
break;
case TCL_TRANSLATE_CR:
if (space == 0) {
return 0;
}
memcpy((VOID *) result,
(VOID *)(bufPtr->buf + bufPtr->nextRemoved),
(size_t) space);
bufPtr->nextRemoved += space;
for (copied = 0; copied < space; copied++) {
if (result[copied] == '\r') {
result[copied] = '\n';
}
}
break;
case TCL_TRANSLATE_CRLF:
if (space == 0) {
if ((chanPtr->flags & (INPUT_SAW_CR | CHANNEL_EOF)) ==
(INPUT_SAW_CR | CHANNEL_EOF)) {
result[0] = '\r';
chanPtr->flags &= (~(INPUT_SAW_CR));
return 1;
}
return 0;
}
for (copied = 0;
(copied < space) &&
(bufPtr->nextRemoved < bufPtr->nextAdded);
copied++) {
curByte = bufPtr->buf[bufPtr->nextRemoved];
bufPtr->nextRemoved++;
if (curByte == '\r') {
if (chanPtr->flags & INPUT_SAW_CR) {
result[copied] = '\r';
} else {
chanPtr->flags |= INPUT_SAW_CR;
copied--;
}
} else if (curByte == '\n') {
chanPtr->flags &= (~(INPUT_SAW_CR));
result[copied] = '\n';
} else {
if (chanPtr->flags & INPUT_SAW_CR) {
chanPtr->flags &= (~(INPUT_SAW_CR));
result[copied] = '\r';
bufPtr->nextRemoved--;
} else {
result[copied] = curByte;
}
}
}
break;
case TCL_TRANSLATE_AUTO:
if (space == 0) {
return 0;
}
for (copied = 0;
(copied < space) &&
(bufPtr->nextRemoved < bufPtr->nextAdded); ) {
curByte = bufPtr->buf[bufPtr->nextRemoved];
bufPtr->nextRemoved++;
if (curByte == '\r') {
result[copied] = '\n';
copied++;
if (bufPtr->nextRemoved < bufPtr->nextAdded) {
if (bufPtr->buf[bufPtr->nextRemoved] == '\n') {
bufPtr->nextRemoved++;
}
chanPtr->flags &= (~(INPUT_SAW_CR));
} else {
chanPtr->flags |= INPUT_SAW_CR;
}
} else {
if (curByte == '\n') {
if (!(chanPtr->flags & INPUT_SAW_CR)) {
result[copied] = '\n';
copied++;
}
} else {
result[copied] = curByte;
copied++;
}
chanPtr->flags &= (~(INPUT_SAW_CR));
}
}
break;
default:
panic("unknown eol translation mode");
}
if (chanPtr->inEofChar != 0) {
for (i = 0; i < copied; i++) {
if (result[i] == (char) chanPtr->inEofChar) {
break;
}
}
if (i < copied) {
chanPtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
bufPtr->nextRemoved -= (copied - i);
while ((bufPtr->nextRemoved > 0) &&
(bufPtr->buf[bufPtr->nextRemoved] !=
(char) chanPtr->inEofChar)) {
bufPtr->nextRemoved--;
}
copied = i;
}
}
if (bufPtr->nextRemoved == bufPtr->nextAdded) {
chanPtr->inQueueHead = bufPtr->nextPtr;
if (chanPtr->inQueueHead == (ChannelBuffer *) NULL) {
chanPtr->inQueueTail = (ChannelBuffer *) NULL;
}
RecycleBuffer(chanPtr, bufPtr, 0);
}
return copied;
}
static int
ScanBufferForEOL(chanPtr, bufPtr, translation, eofChar, bytesToEOLPtr,
crSeenPtr)
Channel *chanPtr;
ChannelBuffer *bufPtr;
Tcl_EolTranslation translation;
int eofChar;
int *bytesToEOLPtr;
int *crSeenPtr;
{
char *rPtr;
char *sPtr;
int EOLFound;
int bytesToEOL;
for (EOLFound = 0, rPtr = bufPtr->buf + bufPtr->nextRemoved,
sPtr = bufPtr->buf + bufPtr->nextAdded,
bytesToEOL = *bytesToEOLPtr;
(!EOLFound) && (rPtr < sPtr);
rPtr++) {
switch (translation) {
case TCL_TRANSLATE_AUTO:
if ((*rPtr == (char) eofChar) && (eofChar != 0)) {
chanPtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
EOLFound = 1;
} else if (*rPtr == '\n') {
if (!(*crSeenPtr)) {
bytesToEOL++;
EOLFound = 1;
} else {
bufPtr->nextRemoved++;
*crSeenPtr = 0;
chanPtr->flags &= (~(INPUT_SAW_CR));
}
} else if (*rPtr == '\r') {
bytesToEOL++;
EOLFound = 1;
} else {
*crSeenPtr = 0;
bytesToEOL++;
}
break;
case TCL_TRANSLATE_LF:
if ((*rPtr == (char) eofChar) && (eofChar != 0)) {
chanPtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
EOLFound = 1;
} else {
if (*rPtr == '\n') {
EOLFound = 1;
}
bytesToEOL++;
}
break;
case TCL_TRANSLATE_CR:
if ((*rPtr == (char) eofChar) && (eofChar != 0)) {
chanPtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
EOLFound = 1;
} else {
if (*rPtr == '\r') {
EOLFound = 1;
}
bytesToEOL++;
}
break;
case TCL_TRANSLATE_CRLF:
if ((*rPtr == (char) eofChar) && (eofChar != 0)) {
chanPtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
EOLFound = 1;
} else if (*rPtr == '\n') {
if (*crSeenPtr) {
EOLFound = 1;
} else {
bytesToEOL++;
}
} else {
if (*rPtr == '\r') {
*crSeenPtr = 1;
} else {
*crSeenPtr = 0;
}
bytesToEOL++;
}
break;
default:
panic("unknown eol translation mode");
}
}
*bytesToEOLPtr = bytesToEOL;
return EOLFound;
}
static int
ScanInputForEOL(chanPtr, bytesQueuedPtr)
Channel *chanPtr;
int *bytesQueuedPtr;
{
ChannelBuffer *bufPtr;
int bytesToEOL;
int EOLFound;
int crSeen;
*bytesQueuedPtr = 0;
bytesToEOL = 0;
EOLFound = 0;
for (bufPtr = chanPtr->inQueueHead,
crSeen = (chanPtr->flags & INPUT_SAW_CR) ? 1 : 0;
(!EOLFound) && (bufPtr != (ChannelBuffer *) NULL);
bufPtr = bufPtr->nextPtr) {
EOLFound = ScanBufferForEOL(chanPtr, bufPtr, chanPtr->inputTranslation,
chanPtr->inEofChar, &bytesToEOL, &crSeen);
}
if (EOLFound == 0) {
*bytesQueuedPtr = bytesToEOL;
return -1;
}
return bytesToEOL;
}
static int
GetEOL(chanPtr)
Channel *chanPtr;
{
int bytesToEOL;
int bytesQueued;
if (chanPtr->unreportedError != 0) {
Tcl_SetErrno(chanPtr->unreportedError);
chanPtr->unreportedError = 0;
return -1;
}
if (!(chanPtr->flags & TCL_READABLE)) {
Tcl_SetErrno(EACCES);
return -1;
}
if (chanPtr->csPtr) {
Tcl_SetErrno(EBUSY);
return -1;
}
if (!(chanPtr->flags & CHANNEL_STICKY_EOF)) {
chanPtr->flags &= (~(CHANNEL_EOF));
}
chanPtr->flags &= (~(CHANNEL_BLOCKED | CHANNEL_GETS_BLOCKED));
while (1) {
bytesToEOL = ScanInputForEOL(chanPtr, &bytesQueued);
if (bytesToEOL > 0) {
chanPtr->flags &= (~(CHANNEL_BLOCKED));
return bytesToEOL;
}
if (chanPtr->flags & CHANNEL_EOF) {
return (bytesQueued == 0) ? -1 : bytesQueued ;
}
if (chanPtr->flags & CHANNEL_BLOCKED) {
if (chanPtr->flags & CHANNEL_NONBLOCKING) {
goto blocked;
}
chanPtr->flags &= (~(CHANNEL_BLOCKED));
}
if (GetInput(chanPtr) != 0) {
goto blocked;
}
}
blocked:
chanPtr->flags |= CHANNEL_GETS_BLOCKED;
return -1;
}
int
Tcl_Read(chan, bufPtr, toRead)
Tcl_Channel chan;
char *bufPtr;
int toRead;
{
Channel *chanPtr;
chanPtr = (Channel *) chan;
if (chanPtr->unreportedError != 0) {
Tcl_SetErrno(chanPtr->unreportedError);
chanPtr->unreportedError = 0;
return -1;
}
if (!(chanPtr->flags & TCL_READABLE)) {
Tcl_SetErrno(EACCES);
return -1;
}
if (chanPtr->csPtr) {
Tcl_SetErrno(EBUSY);
return -1;
}
return DoRead(chanPtr, bufPtr, toRead);
}
static int
DoRead(chanPtr, bufPtr, toRead)
Channel *chanPtr;
char *bufPtr;
int toRead;
{
int copied;
int copiedNow;
int result;
if (!(chanPtr->flags & CHANNEL_STICKY_EOF)) {
chanPtr->flags &= (~(CHANNEL_EOF));
}
chanPtr->flags &= (~(CHANNEL_BLOCKED | CHANNEL_GETS_BLOCKED));
for (copied = 0; copied < toRead; copied += copiedNow) {
copiedNow = CopyAndTranslateBuffer(chanPtr, bufPtr + copied,
toRead - copied);
if (copiedNow == 0) {
if (chanPtr->flags & CHANNEL_EOF) {
goto done;
}
if (chanPtr->flags & CHANNEL_BLOCKED) {
if (chanPtr->flags & CHANNEL_NONBLOCKING) {
goto done;
}
chanPtr->flags &= (~(CHANNEL_BLOCKED));
}
result = GetInput(chanPtr);
if (result != 0) {
if (result != EAGAIN) {
copied = -1;
}
goto done;
}
}
}
chanPtr->flags &= (~(CHANNEL_BLOCKED));
done:
UpdateInterest(chanPtr);
return copied;
}
int
Tcl_Gets(chan, lineRead)
Tcl_Channel chan;
Tcl_DString *lineRead;
{
Channel *chanPtr;
char *buf;
int offset;
int copiedTotal;
int copiedNow;
int lineLen;
chanPtr = (Channel *) chan;
lineLen = GetEOL(chanPtr);
if (lineLen < 0) {
copiedTotal = -1;
goto done;
}
offset = Tcl_DStringLength(lineRead);
Tcl_DStringSetLength(lineRead, lineLen + offset);
buf = Tcl_DStringValue(lineRead) + offset;
for (copiedTotal = 0; copiedTotal < lineLen; copiedTotal += copiedNow) {
copiedNow = CopyAndTranslateBuffer(chanPtr, buf + copiedTotal,
lineLen - copiedTotal);
}
if ((copiedTotal > 0) && (buf[copiedTotal - 1] == '\n')) {
copiedTotal--;
}
Tcl_DStringSetLength(lineRead, copiedTotal + offset);
done:
UpdateInterest(chanPtr);
return copiedTotal;
}
int
Tcl_GetsObj(chan, objPtr)
Tcl_Channel chan;
Tcl_Obj *objPtr;
{
Channel *chanPtr;
char *buf;
int offset;
int copiedTotal;
int copiedNow;
int lineLen;
chanPtr = (Channel *) chan;
lineLen = GetEOL(chanPtr);
if (lineLen < 0) {
copiedTotal = -1;
goto done;
}
(void) Tcl_GetStringFromObj(objPtr, &offset);
Tcl_SetObjLength(objPtr, lineLen + offset);
buf = Tcl_GetStringFromObj(objPtr, NULL) + offset;
for (copiedTotal = 0; copiedTotal < lineLen; copiedTotal += copiedNow) {
copiedNow = CopyAndTranslateBuffer(chanPtr, buf + copiedTotal,
lineLen - copiedTotal);
}
if ((copiedTotal > 0) && (buf[copiedTotal - 1] == '\n')) {
copiedTotal--;
}
Tcl_SetObjLength(objPtr, copiedTotal + offset);
done:
UpdateInterest(chanPtr);
return copiedTotal;
}
int
Tcl_Ungets(chan, str, len, atEnd)
Tcl_Channel chan;
char *str;
int len;
int atEnd;
{
Channel *chanPtr;
ChannelBuffer *bufPtr;
int i;
chanPtr = (Channel *) chan;
if (chanPtr->unreportedError != 0) {
Tcl_SetErrno(chanPtr->unreportedError);
chanPtr->unreportedError = 0;
return -1;
}
if (!(chanPtr->flags & TCL_READABLE)) {
Tcl_SetErrno(EACCES);
return -1;
}
if (chanPtr->csPtr) {
Tcl_SetErrno(EBUSY);
return -1;
}
if (chanPtr->flags & CHANNEL_STICKY_EOF) {
return len;
}
chanPtr->flags &= (~(CHANNEL_BLOCKED | CHANNEL_EOF));
bufPtr = (ChannelBuffer *) ckalloc((unsigned)
(CHANNELBUFFER_HEADER_SIZE + len));
for (i = 0; i < len; i++) {
bufPtr->buf[i] = str[i];
}
bufPtr->bufSize = len;
bufPtr->nextAdded = len;
bufPtr->nextRemoved = 0;
if (chanPtr->inQueueHead == (ChannelBuffer *) NULL) {
bufPtr->nextPtr = (ChannelBuffer *) NULL;
chanPtr->inQueueHead = bufPtr;
chanPtr->inQueueTail = bufPtr;
} else if (atEnd) {
bufPtr->nextPtr = (ChannelBuffer *) NULL;
chanPtr->inQueueTail->nextPtr = bufPtr;
chanPtr->inQueueTail = bufPtr;
} else {
bufPtr->nextPtr = chanPtr->inQueueHead;
chanPtr->inQueueHead = bufPtr;
}
UpdateInterest(chanPtr);
return len;
}
int
Tcl_Seek(chan, offset, mode)
Tcl_Channel chan;
int offset;
int mode;
{
Channel *chanPtr;
ChannelBuffer *bufPtr;
int inputBuffered, outputBuffered;
int result;
int curPos;
int wasAsync;
chanPtr = (Channel *) chan;
if (chanPtr->unreportedError != 0) {
Tcl_SetErrno(chanPtr->unreportedError);
chanPtr->unreportedError = 0;
return -1;
}
if (!(chanPtr->flags & (TCL_WRITABLE|TCL_READABLE))) {
Tcl_SetErrno(EACCES);
return -1;
}
if (chanPtr->csPtr) {
Tcl_SetErrno(EBUSY);
return -1;
}
if (CheckForDeadChannel(NULL,chanPtr)) return -1;
if (chanPtr->typePtr->seekProc == (Tcl_DriverSeekProc *) NULL) {
Tcl_SetErrno(EINVAL);
return -1;
}
for (bufPtr = chanPtr->inQueueHead, inputBuffered = 0;
bufPtr != (ChannelBuffer *) NULL;
bufPtr = bufPtr->nextPtr) {
inputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
}
for (bufPtr = chanPtr->outQueueHead, outputBuffered = 0;
bufPtr != (ChannelBuffer *) NULL;
bufPtr = bufPtr->nextPtr) {
outputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
}
if ((chanPtr->curOutPtr != (ChannelBuffer *) NULL) &&
(chanPtr->curOutPtr->nextAdded > chanPtr->curOutPtr->nextRemoved)) {
chanPtr->flags |= BUFFER_READY;
outputBuffered +=
(chanPtr->curOutPtr->nextAdded - chanPtr->curOutPtr->nextRemoved);
}
if ((inputBuffered != 0) && (outputBuffered != 0)) {
Tcl_SetErrno(EFAULT);
return -1;
}
if (mode == SEEK_CUR) {
offset -= inputBuffered;
}
DiscardInputQueued(chanPtr, 0);
chanPtr->flags &=
(~(CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED | INPUT_SAW_CR));
wasAsync = 0;
if (chanPtr->flags & CHANNEL_NONBLOCKING) {
wasAsync = 1;
result = 0;
if (chanPtr->typePtr->blockModeProc != NULL) {
result = (chanPtr->typePtr->blockModeProc) (chanPtr->instanceData,
TCL_MODE_BLOCKING);
}
if (result != 0) {
Tcl_SetErrno(result);
return -1;
}
chanPtr->flags &= (~(CHANNEL_NONBLOCKING));
if (chanPtr->flags & BG_FLUSH_SCHEDULED) {
chanPtr->flags &= (~(BG_FLUSH_SCHEDULED));
}
}
if (FlushChannel(NULL, chanPtr, 0) != 0) {
curPos = -1;
} else {
curPos = (chanPtr->typePtr->seekProc) (chanPtr->instanceData,
(long) offset, mode, &result);
if (curPos == -1) {
Tcl_SetErrno(result);
}
}
if (wasAsync) {
chanPtr->flags |= CHANNEL_NONBLOCKING;
result = 0;
if (chanPtr->typePtr->blockModeProc != NULL) {
result = (chanPtr->typePtr->blockModeProc) (chanPtr->instanceData,
TCL_MODE_NONBLOCKING);
}
if (result != 0) {
Tcl_SetErrno(result);
return -1;
}
}
return curPos;
}
int
Tcl_Tell(chan)
Tcl_Channel chan;
{
Channel *chanPtr;
ChannelBuffer *bufPtr;
int inputBuffered, outputBuffered;
int result;
int curPos;
chanPtr = (Channel *) chan;
if (chanPtr->unreportedError != 0) {
Tcl_SetErrno(chanPtr->unreportedError);
chanPtr->unreportedError = 0;
return -1;
}
if (CheckForDeadChannel(NULL,chanPtr)) return -1;
if (!(chanPtr->flags & (TCL_WRITABLE|TCL_READABLE))) {
Tcl_SetErrno(EACCES);
return -1;
}
if (chanPtr->csPtr) {
Tcl_SetErrno(EBUSY);
return -1;
}
if (chanPtr->typePtr->seekProc == (Tcl_DriverSeekProc *) NULL) {
Tcl_SetErrno(EINVAL);
return -1;
}
for (bufPtr = chanPtr->inQueueHead, inputBuffered = 0;
bufPtr != (ChannelBuffer *) NULL;
bufPtr = bufPtr->nextPtr) {
inputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
}
for (bufPtr = chanPtr->outQueueHead, outputBuffered = 0;
bufPtr != (ChannelBuffer *) NULL;
bufPtr = bufPtr->nextPtr) {
outputBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
}
if ((chanPtr->curOutPtr != (ChannelBuffer *) NULL) &&
(chanPtr->curOutPtr->nextAdded > chanPtr->curOutPtr->nextRemoved)) {
chanPtr->flags |= BUFFER_READY;
outputBuffered +=
(chanPtr->curOutPtr->nextAdded - chanPtr->curOutPtr->nextRemoved);
}
if ((inputBuffered != 0) && (outputBuffered != 0)) {
Tcl_SetErrno(EFAULT);
return -1;
}
curPos = (chanPtr->typePtr->seekProc) (chanPtr->instanceData,
(long) 0, SEEK_CUR, &result);
if (curPos == -1) {
Tcl_SetErrno(result);
return -1;
}
if (inputBuffered != 0) {
return (curPos - inputBuffered);
}
return (curPos + outputBuffered);
}
int
Tcl_Eof(chan)
Tcl_Channel chan;
{
Channel *chanPtr;
chanPtr = (Channel *) chan;
return ((chanPtr->flags & CHANNEL_STICKY_EOF) ||
((chanPtr->flags & CHANNEL_EOF) && (Tcl_InputBuffered(chan) == 0)))
? 1 : 0;
}
int
Tcl_InputBlocked(chan)
Tcl_Channel chan;
{
Channel *chanPtr;
chanPtr = (Channel *) chan;
return (chanPtr->flags & CHANNEL_BLOCKED) ? 1 : 0;
}
int
Tcl_InputBuffered(chan)
Tcl_Channel chan;
{
Channel *chanPtr;
int bytesBuffered;
ChannelBuffer *bufPtr;
chanPtr = (Channel *) chan;
for (bytesBuffered = 0, bufPtr = chanPtr->inQueueHead;
bufPtr != (ChannelBuffer *) NULL;
bufPtr = bufPtr->nextPtr) {
bytesBuffered += (bufPtr->nextAdded - bufPtr->nextRemoved);
}
return bytesBuffered;
}
void
Tcl_SetChannelBufferSize(chan, sz)
Tcl_Channel chan;
int sz;
{
Channel *chanPtr;
if (sz < 10) {
return;
}
if (sz > (1024 * 1024)) {
return;
}
chanPtr = (Channel *) chan;
chanPtr->bufSize = sz;
}
int
Tcl_GetChannelBufferSize(chan)
Tcl_Channel chan;
{
Channel *chanPtr;
chanPtr = (Channel *) chan;
return chanPtr->bufSize;
}
int
Tcl_BadChannelOption(interp, optionName, optionList)
Tcl_Interp *interp;
char *optionName;
char *optionList;
{
if (interp) {
CONST char *genericopt =
"blocking buffering buffersize eofchar translation";
char **argv;
int argc, i;
Tcl_DString ds;
Tcl_DStringInit(&ds);
Tcl_DStringAppend(&ds, (char *) genericopt, -1);
if (optionList && (*optionList)) {
Tcl_DStringAppend(&ds, " ", 1);
Tcl_DStringAppend(&ds, optionList, -1);
}
if (Tcl_SplitList(interp, Tcl_DStringValue(&ds),
&argc, &argv) != TCL_OK) {
panic("malformed option list in channel driver");
}
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, "bad option \"", optionName,
"\": should be one of ", (char *) NULL);
argc--;
for (i = 0; i < argc; i++) {
Tcl_AppendResult(interp, "-", argv[i], ", ", (char *) NULL);
}
Tcl_AppendResult(interp, "or -", argv[i], (char *) NULL);
Tcl_DStringFree(&ds);
ckfree((char *) argv);
}
Tcl_SetErrno(EINVAL);
return TCL_ERROR;
}
int
Tcl_GetChannelOption(interp, chan, optionName, dsPtr)
Tcl_Interp *interp;
Tcl_Channel chan;
char *optionName;
Tcl_DString *dsPtr;
{
size_t len;
char optionVal[128];
Channel *chanPtr = (Channel *) chan;
int flags;
if (chanPtr->csPtr) {
if (chanPtr == chanPtr->csPtr->readPtr) {
flags = chanPtr->csPtr->readFlags;
} else {
flags = chanPtr->csPtr->writeFlags;
}
} else {
flags = chanPtr->flags;
}
if (CheckForDeadChannel(interp,chanPtr)) return TCL_ERROR;
if (optionName == (char *) NULL) {
len = 0;
} else {
len = strlen(optionName);
}
if ((len == 0) || ((len > 2) && (optionName[1] == 'b') &&
(strncmp(optionName, "-blocking", len) == 0))) {
if (len == 0) {
Tcl_DStringAppendElement(dsPtr, "-blocking");
}
Tcl_DStringAppendElement(dsPtr,
(flags & CHANNEL_NONBLOCKING) ? "0" : "1");
if (len > 0) {
return TCL_OK;
}
}
if ((len == 0) || ((len > 7) && (optionName[1] == 'b') &&
(strncmp(optionName, "-buffering", len) == 0))) {
if (len == 0) {
Tcl_DStringAppendElement(dsPtr, "-buffering");
}
if (flags & CHANNEL_LINEBUFFERED) {
Tcl_DStringAppendElement(dsPtr, "line");
} else if (flags & CHANNEL_UNBUFFERED) {
Tcl_DStringAppendElement(dsPtr, "none");
} else {
Tcl_DStringAppendElement(dsPtr, "full");
}
if (len > 0) {
return TCL_OK;
}
}
if ((len == 0) || ((len > 7) && (optionName[1] == 'b') &&
(strncmp(optionName, "-buffersize", len) == 0))) {
if (len == 0) {
Tcl_DStringAppendElement(dsPtr, "-buffersize");
}
TclFormatInt(optionVal, chanPtr->bufSize);
Tcl_DStringAppendElement(dsPtr, optionVal);
if (len > 0) {
return TCL_OK;
}
}
if ((len == 0) ||
((len > 1) && (optionName[1] == 'e') &&
(strncmp(optionName, "-eofchar", len) == 0))) {
if (len == 0) {
Tcl_DStringAppendElement(dsPtr, "-eofchar");
}
if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
(TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
Tcl_DStringStartSublist(dsPtr);
}
if (flags & TCL_READABLE) {
if (chanPtr->inEofChar == 0) {
Tcl_DStringAppendElement(dsPtr, "");
} else {
char buf[4];
sprintf(buf, "%c", chanPtr->inEofChar);
Tcl_DStringAppendElement(dsPtr, buf);
}
}
if (flags & TCL_WRITABLE) {
if (chanPtr->outEofChar == 0) {
Tcl_DStringAppendElement(dsPtr, "");
} else {
char buf[4];
sprintf(buf, "%c", chanPtr->outEofChar);
Tcl_DStringAppendElement(dsPtr, buf);
}
}
if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
(TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
Tcl_DStringEndSublist(dsPtr);
}
if (len > 0) {
return TCL_OK;
}
}
if ((len == 0) ||
((len > 1) && (optionName[1] == 't') &&
(strncmp(optionName, "-translation", len) == 0))) {
if (len == 0) {
Tcl_DStringAppendElement(dsPtr, "-translation");
}
if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
(TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
Tcl_DStringStartSublist(dsPtr);
}
if (flags & TCL_READABLE) {
if (chanPtr->inputTranslation == TCL_TRANSLATE_AUTO) {
Tcl_DStringAppendElement(dsPtr, "auto");
} else if (chanPtr->inputTranslation == TCL_TRANSLATE_CR) {
Tcl_DStringAppendElement(dsPtr, "cr");
} else if (chanPtr->inputTranslation == TCL_TRANSLATE_CRLF) {
Tcl_DStringAppendElement(dsPtr, "crlf");
} else {
Tcl_DStringAppendElement(dsPtr, "lf");
}
}
if (flags & TCL_WRITABLE) {
if (chanPtr->outputTranslation == TCL_TRANSLATE_AUTO) {
Tcl_DStringAppendElement(dsPtr, "auto");
} else if (chanPtr->outputTranslation == TCL_TRANSLATE_CR) {
Tcl_DStringAppendElement(dsPtr, "cr");
} else if (chanPtr->outputTranslation == TCL_TRANSLATE_CRLF) {
Tcl_DStringAppendElement(dsPtr, "crlf");
} else {
Tcl_DStringAppendElement(dsPtr, "lf");
}
}
if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
(TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
Tcl_DStringEndSublist(dsPtr);
}
if (len > 0) {
return TCL_OK;
}
}
if (chanPtr->typePtr->getOptionProc != (Tcl_DriverGetOptionProc *) NULL) {
return (chanPtr->typePtr->getOptionProc) (chanPtr->instanceData,
interp, optionName, dsPtr);
} else {
if (len == 0) {
return TCL_OK;
}
return Tcl_BadChannelOption(interp, optionName, NULL);
}
}
int
Tcl_SetChannelOption(interp, chan, optionName, newValue)
Tcl_Interp *interp;
Tcl_Channel chan;
char *optionName;
char *newValue;
{
int newMode;
Channel *chanPtr;
size_t len;
int argc;
char **argv;
chanPtr = (Channel *) chan;
if (chanPtr->csPtr) {
if (interp) {
Tcl_AppendResult(interp,
"unable to set channel options: background copy in progress",
(char *) NULL);
}
return TCL_ERROR;
}
if (CheckForDeadChannel(NULL,chanPtr)) return TCL_ERROR;
len = strlen(optionName);
if ((len > 2) && (optionName[1] == 'b') &&
(strncmp(optionName, "-blocking", len) == 0)) {
if (Tcl_GetBoolean(interp, newValue, &newMode) == TCL_ERROR) {
return TCL_ERROR;
}
if (newMode) {
newMode = TCL_MODE_BLOCKING;
} else {
newMode = TCL_MODE_NONBLOCKING;
}
return SetBlockMode(interp, chanPtr, newMode);
}
if ((len > 7) && (optionName[1] == 'b') &&
(strncmp(optionName, "-buffering", len) == 0)) {
len = strlen(newValue);
if ((newValue[0] == 'f') && (strncmp(newValue, "full", len) == 0)) {
chanPtr->flags &=
(~(CHANNEL_UNBUFFERED|CHANNEL_LINEBUFFERED));
} else if ((newValue[0] == 'l') &&
(strncmp(newValue, "line", len) == 0)) {
chanPtr->flags &= (~(CHANNEL_UNBUFFERED));
chanPtr->flags |= CHANNEL_LINEBUFFERED;
} else if ((newValue[0] == 'n') &&
(strncmp(newValue, "none", len) == 0)) {
chanPtr->flags &= (~(CHANNEL_LINEBUFFERED));
chanPtr->flags |= CHANNEL_UNBUFFERED;
} else {
if (interp) {
Tcl_AppendResult(interp, "bad value for -buffering: ",
"must be one of full, line, or none",
(char *) NULL);
return TCL_ERROR;
}
}
return TCL_OK;
}
if ((len > 7) && (optionName[1] == 'b') &&
(strncmp(optionName, "-buffersize", len) == 0)) {
chanPtr->bufSize = atoi(newValue);
if ((chanPtr->bufSize < 10) || (chanPtr->bufSize > (1024 * 1024))) {
chanPtr->bufSize = CHANNELBUFFER_DEFAULT_SIZE;
}
return TCL_OK;
}
if ((len > 1) && (optionName[1] == 'e') &&
(strncmp(optionName, "-eofchar", len) == 0)) {
if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
return TCL_ERROR;
}
if (argc == 0) {
chanPtr->inEofChar = 0;
chanPtr->outEofChar = 0;
} else if (argc == 1) {
if (chanPtr->flags & TCL_WRITABLE) {
chanPtr->outEofChar = (int) argv[0][0];
}
if (chanPtr->flags & TCL_READABLE) {
chanPtr->inEofChar = (int) argv[0][0];
}
} else if (argc != 2) {
if (interp) {
Tcl_AppendResult(interp,
"bad value for -eofchar: should be a list of one or",
" two elements", (char *) NULL);
}
ckfree((char *) argv);
return TCL_ERROR;
} else {
if (chanPtr->flags & TCL_READABLE) {
chanPtr->inEofChar = (int) argv[0][0];
}
if (chanPtr->flags & TCL_WRITABLE) {
chanPtr->outEofChar = (int) argv[1][0];
}
}
if (argv != (char **) NULL) {
ckfree((char *) argv);
}
return TCL_OK;
}
if ((len > 1) && (optionName[1] == 't') &&
(strncmp(optionName, "-translation", len) == 0)) {
char *readMode, *writeMode;
if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
return TCL_ERROR;
}
if (argc == 1) {
readMode = (chanPtr->flags & TCL_READABLE) ? argv[0] : NULL;
writeMode = (chanPtr->flags & TCL_WRITABLE) ? argv[0] : NULL;
} else if (argc == 2) {
readMode = (chanPtr->flags & TCL_READABLE) ? argv[0] : NULL;
writeMode = (chanPtr->flags & TCL_WRITABLE) ? argv[1] : NULL;
} else {
if (interp) {
Tcl_AppendResult(interp,
"bad value for -translation: must be a one or two",
" element list", (char *) NULL);
}
ckfree((char *) argv);
return TCL_ERROR;
}
if (readMode) {
if (*readMode == '\0') {
newMode = chanPtr->inputTranslation;
} else if (strcmp(readMode, "auto") == 0) {
newMode = TCL_TRANSLATE_AUTO;
} else if (strcmp(readMode, "binary") == 0) {
chanPtr->inEofChar = 0;
newMode = TCL_TRANSLATE_LF;
} else if (strcmp(readMode, "lf") == 0) {
newMode = TCL_TRANSLATE_LF;
} else if (strcmp(readMode, "cr") == 0) {
newMode = TCL_TRANSLATE_CR;
} else if (strcmp(readMode, "crlf") == 0) {
newMode = TCL_TRANSLATE_CRLF;
} else if (strcmp(readMode, "platform") == 0) {
newMode = TCL_PLATFORM_TRANSLATION;
} else {
if (interp) {
Tcl_AppendResult(interp,
"bad value for -translation: ",
"must be one of auto, binary, cr, lf, crlf,",
" or platform", (char *) NULL);
}
ckfree((char *) argv);
return TCL_ERROR;
}
if (newMode != chanPtr->inputTranslation) {
chanPtr->inputTranslation = (Tcl_EolTranslation) newMode;
chanPtr->flags &= ~(INPUT_SAW_CR);
chanPtr->flags &= ~(CHANNEL_GETS_BLOCKED);
UpdateInterest(chanPtr);
}
}
if (writeMode) {
if (*writeMode == '\0') {
} else if (strcmp(writeMode, "auto") == 0) {
if (strcmp(chanPtr->typePtr->typeName, "tcp") == 0) {
chanPtr->outputTranslation = TCL_TRANSLATE_CRLF;
} else {
chanPtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
}
} else if (strcmp(writeMode, "binary") == 0) {
chanPtr->outEofChar = 0;
chanPtr->outputTranslation = TCL_TRANSLATE_LF;
} else if (strcmp(writeMode, "lf") == 0) {
chanPtr->outputTranslation = TCL_TRANSLATE_LF;
} else if (strcmp(writeMode, "cr") == 0) {
chanPtr->outputTranslation = TCL_TRANSLATE_CR;
} else if (strcmp(writeMode, "crlf") == 0) {
chanPtr->outputTranslation = TCL_TRANSLATE_CRLF;
} else if (strcmp(writeMode, "platform") == 0) {
chanPtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
} else {
if (interp) {
Tcl_AppendResult(interp,
"bad value for -translation: ",
"must be one of auto, binary, cr, lf, crlf,",
" or platform", (char *) NULL);
}
ckfree((char *) argv);
return TCL_ERROR;
}
}
ckfree((char *) argv);
return TCL_OK;
}
if (chanPtr->typePtr->setOptionProc != (Tcl_DriverSetOptionProc *) NULL) {
return (chanPtr->typePtr->setOptionProc) (chanPtr->instanceData,
interp, optionName, newValue);
}
return Tcl_BadChannelOption(interp, optionName, (char *) NULL);
}
static void
CleanupChannelHandlers(interp, chanPtr)
Tcl_Interp *interp;
Channel *chanPtr;
{
EventScriptRecord *sPtr, *prevPtr, *nextPtr;
for (sPtr = chanPtr->scriptRecordPtr,
prevPtr = (EventScriptRecord *) NULL;
sPtr != (EventScriptRecord *) NULL;
sPtr = nextPtr) {
nextPtr = sPtr->nextPtr;
if (sPtr->interp == interp) {
if (prevPtr == (EventScriptRecord *) NULL) {
chanPtr->scriptRecordPtr = nextPtr;
} else {
prevPtr->nextPtr = nextPtr;
}
Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
ChannelEventScriptInvoker, (ClientData) sPtr);
ckfree(sPtr->script);
ckfree((char *) sPtr);
} else {
prevPtr = sPtr;
}
}
}
void
Tcl_NotifyChannel(channel, mask)
Tcl_Channel channel;
int mask;
{
Channel *chanPtr = (Channel *) channel;
ChannelHandler *chPtr;
NextChannelHandler nh;
Tcl_Preserve((ClientData) channel);
if ((chanPtr->flags & BG_FLUSH_SCHEDULED) && (mask & TCL_WRITABLE)) {
FlushChannel(NULL, chanPtr, 1);
mask &= ~TCL_WRITABLE;
}
nh.nextHandlerPtr = (ChannelHandler *) NULL;
nh.nestedHandlerPtr = nestedHandlerPtr;
nestedHandlerPtr = &nh;
for (chPtr = chanPtr->chPtr; chPtr != (ChannelHandler *) NULL; ) {
if ((chPtr->mask & mask) != 0) {
nh.nextHandlerPtr = chPtr->nextPtr;
(*(chPtr->proc))(chPtr->clientData, mask);
chPtr = nh.nextHandlerPtr;
} else {
chPtr = chPtr->nextPtr;
}
}
if (chanPtr->typePtr != NULL) {
UpdateInterest(chanPtr);
}
Tcl_Release((ClientData) channel);
nestedHandlerPtr = nh.nestedHandlerPtr;
}
static void
UpdateInterest(chanPtr)
Channel *chanPtr;
{
int mask = chanPtr->interestMask;
if (chanPtr->flags & BG_FLUSH_SCHEDULED) {
mask |= TCL_WRITABLE;
}
if (mask & TCL_READABLE) {
if (!(chanPtr->flags & CHANNEL_GETS_BLOCKED)
&& (chanPtr->inQueueHead != (ChannelBuffer *) NULL)
&& (chanPtr->inQueueHead->nextRemoved <
chanPtr->inQueueHead->nextAdded)) {
mask &= ~TCL_READABLE;
if (!chanPtr->timer) {
chanPtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,
(ClientData) chanPtr);
}
}
}
(chanPtr->typePtr->watchProc)(chanPtr->instanceData, mask);
}
static void
ChannelTimerProc(clientData)
ClientData clientData;
{
Channel *chanPtr = (Channel *) clientData;
if (!(chanPtr->flags & CHANNEL_GETS_BLOCKED)
&& (chanPtr->interestMask & TCL_READABLE)
&& (chanPtr->inQueueHead != (ChannelBuffer *) NULL)
&& (chanPtr->inQueueHead->nextRemoved <
chanPtr->inQueueHead->nextAdded)) {
chanPtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,
(ClientData) chanPtr);
Tcl_NotifyChannel((Tcl_Channel)chanPtr, TCL_READABLE);
} else {
chanPtr->timer = NULL;
UpdateInterest(chanPtr);
}
}
void
Tcl_CreateChannelHandler(chan, mask, proc, clientData)
Tcl_Channel chan;
int mask;
Tcl_ChannelProc *proc;
ClientData clientData;
{
ChannelHandler *chPtr;
Channel *chanPtr;
chanPtr = (Channel *) chan;
for (chPtr = chanPtr->chPtr;
chPtr != (ChannelHandler *) NULL;
chPtr = chPtr->nextPtr) {
if ((chPtr->chanPtr == chanPtr) && (chPtr->proc == proc) &&
(chPtr->clientData == clientData)) {
break;
}
}
if (chPtr == (ChannelHandler *) NULL) {
chPtr = (ChannelHandler *) ckalloc((unsigned) sizeof(ChannelHandler));
chPtr->mask = 0;
chPtr->proc = proc;
chPtr->clientData = clientData;
chPtr->chanPtr = chanPtr;
chPtr->nextPtr = chanPtr->chPtr;
chanPtr->chPtr = chPtr;
}
chPtr->mask = mask;
chanPtr->interestMask = 0;
for (chPtr = chanPtr->chPtr;
chPtr != (ChannelHandler *) NULL;
chPtr = chPtr->nextPtr) {
chanPtr->interestMask |= chPtr->mask;
}
UpdateInterest(chanPtr);
}
void
Tcl_DeleteChannelHandler(chan, proc, clientData)
Tcl_Channel chan;
Tcl_ChannelProc *proc;
ClientData clientData;
{
ChannelHandler *chPtr, *prevChPtr;
Channel *chanPtr;
NextChannelHandler *nhPtr;
chanPtr = (Channel *) chan;
for (prevChPtr = (ChannelHandler *) NULL, chPtr = chanPtr->chPtr;
chPtr != (ChannelHandler *) NULL;
chPtr = chPtr->nextPtr) {
if ((chPtr->chanPtr == chanPtr) && (chPtr->clientData == clientData)
&& (chPtr->proc == proc)) {
break;
}
prevChPtr = chPtr;
}
if (chPtr == (ChannelHandler *) NULL) {
return;
}
for (nhPtr = nestedHandlerPtr;
nhPtr != (NextChannelHandler *) NULL;
nhPtr = nhPtr->nestedHandlerPtr) {
if (nhPtr->nextHandlerPtr == chPtr) {
nhPtr->nextHandlerPtr = chPtr->nextPtr;
}
}
if (prevChPtr == (ChannelHandler *) NULL) {
chanPtr->chPtr = chPtr->nextPtr;
} else {
prevChPtr->nextPtr = chPtr->nextPtr;
}
ckfree((char *) chPtr);
chanPtr->interestMask = 0;
for (chPtr = chanPtr->chPtr;
chPtr != (ChannelHandler *) NULL;
chPtr = chPtr->nextPtr) {
chanPtr->interestMask |= chPtr->mask;
}
UpdateInterest(chanPtr);
}
static void
DeleteScriptRecord(interp, chanPtr, mask)
Tcl_Interp *interp;
Channel *chanPtr;
int mask;
{
EventScriptRecord *esPtr, *prevEsPtr;
for (esPtr = chanPtr->scriptRecordPtr,
prevEsPtr = (EventScriptRecord *) NULL;
esPtr != (EventScriptRecord *) NULL;
prevEsPtr = esPtr, esPtr = esPtr->nextPtr) {
if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
if (esPtr == chanPtr->scriptRecordPtr) {
chanPtr->scriptRecordPtr = esPtr->nextPtr;
} else {
prevEsPtr->nextPtr = esPtr->nextPtr;
}
Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
ChannelEventScriptInvoker, (ClientData) esPtr);
ckfree(esPtr->script);
ckfree((char *) esPtr);
break;
}
}
}
static void
CreateScriptRecord(interp, chanPtr, mask, script)
Tcl_Interp *interp;
Channel *chanPtr;
int mask;
char *script;
{
EventScriptRecord *esPtr;
for (esPtr = chanPtr->scriptRecordPtr;
esPtr != (EventScriptRecord *) NULL;
esPtr = esPtr->nextPtr) {
if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
ckfree(esPtr->script);
esPtr->script = (char *) NULL;
break;
}
}
if (esPtr == (EventScriptRecord *) NULL) {
esPtr = (EventScriptRecord *) ckalloc((unsigned)
sizeof(EventScriptRecord));
Tcl_CreateChannelHandler((Tcl_Channel) chanPtr, mask,
ChannelEventScriptInvoker, (ClientData) esPtr);
esPtr->nextPtr = chanPtr->scriptRecordPtr;
chanPtr->scriptRecordPtr = esPtr;
}
esPtr->chanPtr = chanPtr;
esPtr->interp = interp;
esPtr->mask = mask;
esPtr->script = ckalloc((unsigned) (strlen(script) + 1));
strcpy(esPtr->script, script);
}
static void
ChannelEventScriptInvoker(clientData, mask)
ClientData clientData;
int mask;
{
Tcl_Interp *interp;
Channel *chanPtr;
char *script;
EventScriptRecord *esPtr;
int result;
esPtr = (EventScriptRecord *) clientData;
chanPtr = esPtr->chanPtr;
mask = esPtr->mask;
interp = esPtr->interp;
script = esPtr->script;
Tcl_Preserve((ClientData) interp);
result = Tcl_GlobalEval(interp, script);
if (result != TCL_OK) {
if (chanPtr->typePtr != NULL) {
DeleteScriptRecord(interp, chanPtr, mask);
}
Tcl_BackgroundError(interp);
}
Tcl_Release((ClientData) interp);
}
int
Tcl_FileEventCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
Channel *chanPtr;
Tcl_Channel chan;
int c;
int mask;
size_t length;
if ((argc != 3) && (argc != 4)) {
Tcl_AppendResult(interp, "wrong # args: must be \"", argv[0],
" channelId event ?script?", (char *) NULL);
return TCL_ERROR;
}
c = argv[2][0];
length = strlen(argv[2]);
if ((c == 'r') && (strncmp(argv[2], "readable", length) == 0)) {
mask = TCL_READABLE;
} else if ((c == 'w') && (strncmp(argv[2], "writable", length) == 0)) {
mask = TCL_WRITABLE;
} else {
Tcl_AppendResult(interp, "bad event name \"", argv[2],
"\": must be readable or writable", (char *) NULL);
return TCL_ERROR;
}
chan = Tcl_GetChannel(interp, argv[1], NULL);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
chanPtr = (Channel *) chan;
if ((chanPtr->flags & mask) == 0) {
Tcl_AppendResult(interp, "channel is not ",
(mask == TCL_READABLE) ? "readable" : "writable",
(char *) NULL);
return TCL_ERROR;
}
if (argc == 3) {
EventScriptRecord *esPtr;
for (esPtr = chanPtr->scriptRecordPtr;
esPtr != (EventScriptRecord *) NULL;
esPtr = esPtr->nextPtr) {
if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
Tcl_SetResult(interp, esPtr->script, TCL_STATIC);
break;
}
}
return TCL_OK;
}
if (argv[3][0] == 0) {
DeleteScriptRecord(interp, chanPtr, mask);
return TCL_OK;
}
CreateScriptRecord(interp, chanPtr, mask, argv[3]);
return TCL_OK;
}
int
TclTestChannelCmd(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp;
int argc;
char **argv;
{
char *cmdName;
Tcl_HashTable *hTblPtr;
Tcl_HashSearch hSearch;
Tcl_HashEntry *hPtr;
Channel *chanPtr;
Tcl_Channel chan;
size_t len;
int IOQueued;
ChannelBuffer *bufPtr;
char buf[128];
if (argc < 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" subcommand ?additional args..?\"", (char *) NULL);
return TCL_ERROR;
}
cmdName = argv[1];
len = strlen(cmdName);
chanPtr = (Channel *) NULL;
if (argc > 2) {
chan = Tcl_GetChannel(interp, argv[2], NULL);
if (chan == (Tcl_Channel) NULL) {
return TCL_ERROR;
}
chanPtr = (Channel *) chan;
}
if ((cmdName[0] == 'i') && (strncmp(cmdName, "info", len) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" info channelName\"", (char *) NULL);
return TCL_ERROR;
}
Tcl_AppendElement(interp, argv[2]);
Tcl_AppendElement(interp, chanPtr->typePtr->typeName);
if (chanPtr->flags & TCL_READABLE) {
Tcl_AppendElement(interp, "read");
} else {
Tcl_AppendElement(interp, "");
}
if (chanPtr->flags & TCL_WRITABLE) {
Tcl_AppendElement(interp, "write");
} else {
Tcl_AppendElement(interp, "");
}
if (chanPtr->flags & CHANNEL_NONBLOCKING) {
Tcl_AppendElement(interp, "nonblocking");
} else {
Tcl_AppendElement(interp, "blocking");
}
if (chanPtr->flags & CHANNEL_LINEBUFFERED) {
Tcl_AppendElement(interp, "line");
} else if (chanPtr->flags & CHANNEL_UNBUFFERED) {
Tcl_AppendElement(interp, "none");
} else {
Tcl_AppendElement(interp, "full");
}
if (chanPtr->flags & BG_FLUSH_SCHEDULED) {
Tcl_AppendElement(interp, "async_flush");
} else {
Tcl_AppendElement(interp, "");
}
if (chanPtr->flags & CHANNEL_EOF) {
Tcl_AppendElement(interp, "eof");
} else {
Tcl_AppendElement(interp, "");
}
if (chanPtr->flags & CHANNEL_BLOCKED) {
Tcl_AppendElement(interp, "blocked");
} else {
Tcl_AppendElement(interp, "unblocked");
}
if (chanPtr->inputTranslation == TCL_TRANSLATE_AUTO) {
Tcl_AppendElement(interp, "auto");
if (chanPtr->flags & INPUT_SAW_CR) {
Tcl_AppendElement(interp, "saw_cr");
} else {
Tcl_AppendElement(interp, "");
}
} else if (chanPtr->inputTranslation == TCL_TRANSLATE_LF) {
Tcl_AppendElement(interp, "lf");
Tcl_AppendElement(interp, "");
} else if (chanPtr->inputTranslation == TCL_TRANSLATE_CR) {
Tcl_AppendElement(interp, "cr");
Tcl_AppendElement(interp, "");
} else if (chanPtr->inputTranslation == TCL_TRANSLATE_CRLF) {
Tcl_AppendElement(interp, "crlf");
if (chanPtr->flags & INPUT_SAW_CR) {
Tcl_AppendElement(interp, "queued_cr");
} else {
Tcl_AppendElement(interp, "");
}
}
if (chanPtr->outputTranslation == TCL_TRANSLATE_AUTO) {
Tcl_AppendElement(interp, "auto");
} else if (chanPtr->outputTranslation == TCL_TRANSLATE_LF) {
Tcl_AppendElement(interp, "lf");
} else if (chanPtr->outputTranslation == TCL_TRANSLATE_CR) {
Tcl_AppendElement(interp, "cr");
} else if (chanPtr->outputTranslation == TCL_TRANSLATE_CRLF) {
Tcl_AppendElement(interp, "crlf");
}
for (IOQueued = 0, bufPtr = chanPtr->inQueueHead;
bufPtr != (ChannelBuffer *) NULL;
bufPtr = bufPtr->nextPtr) {
IOQueued += bufPtr->nextAdded - bufPtr->nextRemoved;
}
TclFormatInt(buf, IOQueued);
Tcl_AppendElement(interp, buf);
IOQueued = 0;
if (chanPtr->curOutPtr != (ChannelBuffer *) NULL) {
IOQueued = chanPtr->curOutPtr->nextAdded -
chanPtr->curOutPtr->nextRemoved;
}
for (bufPtr = chanPtr->outQueueHead;
bufPtr != (ChannelBuffer *) NULL;
bufPtr = bufPtr->nextPtr) {
IOQueued += (bufPtr->nextAdded - bufPtr->nextRemoved);
}
TclFormatInt(buf, IOQueued);
Tcl_AppendElement(interp, buf);
TclFormatInt(buf, Tcl_Tell((Tcl_Channel) chanPtr));
Tcl_AppendElement(interp, buf);
TclFormatInt(buf, chanPtr->refCount);
Tcl_AppendElement(interp, buf);
return TCL_OK;
}
if ((cmdName[0] == 'i') &&
(strncmp(cmdName, "inputbuffered", len) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "channel name required",
(char *) NULL);
return TCL_ERROR;
}
for (IOQueued = 0, bufPtr = chanPtr->inQueueHead;
bufPtr != (ChannelBuffer *) NULL;
bufPtr = bufPtr->nextPtr) {
IOQueued += bufPtr->nextAdded - bufPtr->nextRemoved;
}
sprintf(buf, "%d", IOQueued);
Tcl_AppendResult(interp, buf, (char *) NULL);
return TCL_OK;
}
if ((cmdName[0] == 'm') && (strncmp(cmdName, "mode", len) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "channel name required",
(char *) NULL);
return TCL_ERROR;
}
if (chanPtr->flags & TCL_READABLE) {
Tcl_AppendElement(interp, "read");
} else {
Tcl_AppendElement(interp, "");
}
if (chanPtr->flags & TCL_WRITABLE) {
Tcl_AppendElement(interp, "write");
} else {
Tcl_AppendElement(interp, "");
}
return TCL_OK;
}
if ((cmdName[0] == 'n') && (strncmp(cmdName, "name", len) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "channel name required",
(char *) NULL);
return TCL_ERROR;
}
Tcl_AppendResult(interp, chanPtr->channelName, (char *) NULL);
return TCL_OK;
}
if ((cmdName[0] == 'o') && (strncmp(cmdName, "open", len) == 0)) {
hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
if (hTblPtr == (Tcl_HashTable *) NULL) {
return TCL_OK;
}
for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
hPtr != (Tcl_HashEntry *) NULL;
hPtr = Tcl_NextHashEntry(&hSearch)) {
Tcl_AppendElement(interp, Tcl_GetHashKey(hTblPtr, hPtr));
}
return TCL_OK;
}
if ((cmdName[0] == 'o') &&
(strncmp(cmdName, "outputbuffered", len) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "channel name required",
(char *) NULL);
return TCL_ERROR;
}
IOQueued = 0;
if (chanPtr->curOutPtr != (ChannelBuffer *) NULL) {
IOQueued = chanPtr->curOutPtr->nextAdded -
chanPtr->curOutPtr->nextRemoved;
}
for (bufPtr = chanPtr->outQueueHead;
bufPtr != (ChannelBuffer *) NULL;
bufPtr = bufPtr->nextPtr) {
IOQueued += (bufPtr->nextAdded - bufPtr->nextRemoved);
}
sprintf(buf, "%d", IOQueued);
Tcl_AppendResult(interp, buf, (char *) NULL);
return TCL_OK;
}
if ((cmdName[0] == 'q') &&
(strncmp(cmdName, "queuedcr", len) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "channel name required",
(char *) NULL);
return TCL_ERROR;
}
Tcl_AppendResult(interp,
(chanPtr->flags & INPUT_SAW_CR) ? "1" : "0",
(char *) NULL);
return TCL_OK;
}
if ((cmdName[0] == 'r') && (strncmp(cmdName, "readable", len) == 0)) {
hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
if (hTblPtr == (Tcl_HashTable *) NULL) {
return TCL_OK;
}
for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
hPtr != (Tcl_HashEntry *) NULL;
hPtr = Tcl_NextHashEntry(&hSearch)) {
chanPtr = (Channel *) Tcl_GetHashValue(hPtr);
if (chanPtr->flags & TCL_READABLE) {
Tcl_AppendElement(interp, Tcl_GetHashKey(hTblPtr, hPtr));
}
}
return TCL_OK;
}
if ((cmdName[0] == 'r') && (strncmp(cmdName, "refcount", len) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "channel name required",
(char *) NULL);
return TCL_ERROR;
}
sprintf(buf, "%d", chanPtr->refCount);
Tcl_AppendResult(interp, buf, (char *) NULL);
return TCL_OK;
}
if ((cmdName[0] == 't') && (strncmp(cmdName, "type", len) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "channel name required",
(char *) NULL);
return TCL_ERROR;
}
Tcl_AppendResult(interp, chanPtr->typePtr->typeName, (char *) NULL);
return TCL_OK;
}
if ((cmdName[0] == 'w') && (strncmp(cmdName, "writable", len) == 0)) {
hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
if (hTblPtr == (Tcl_HashTable *) NULL) {
return TCL_OK;
}
for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
hPtr != (Tcl_HashEntry *) NULL;
hPtr = Tcl_NextHashEntry(&hSearch)) {
chanPtr = (Channel *) Tcl_GetHashValue(hPtr);
if (chanPtr->flags & TCL_WRITABLE) {
Tcl_AppendElement(interp, Tcl_GetHashKey(hTblPtr, hPtr));
}
}
return TCL_OK;
}
Tcl_AppendResult(interp, "bad option \"", cmdName, "\": should be ",
"info, open, readable, or writable",
(char *) NULL);
return TCL_ERROR;
}
int
TclTestChannelEventCmd(dummy, interp, argc, argv)
ClientData dummy;
Tcl_Interp *interp;
int argc;
char **argv;
{
Channel *chanPtr;
EventScriptRecord *esPtr, *prevEsPtr, *nextEsPtr;
char *cmd;
int index, i, mask, len;
if ((argc < 3) || (argc > 5)) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" channelName cmd ?arg1? ?arg2?\"", (char *) NULL);
return TCL_ERROR;
}
chanPtr = (Channel *) Tcl_GetChannel(interp, argv[1], NULL);
if (chanPtr == (Channel *) NULL) {
return TCL_ERROR;
}
cmd = argv[2];
len = strlen(cmd);
if ((cmd[0] == 'a') && (strncmp(cmd, "add", (unsigned) len) == 0)) {
if (argc != 5) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" channelName add eventSpec script\"", (char *) NULL);
return TCL_ERROR;
}
if (strcmp(argv[3], "readable") == 0) {
mask = TCL_READABLE;
} else if (strcmp(argv[3], "writable") == 0) {
mask = TCL_WRITABLE;
} else if (strcmp(argv[3], "none") == 0) {
mask = 0;
} else {
Tcl_AppendResult(interp, "bad event name \"", argv[3],
"\": must be readable, writable, or none", (char *) NULL);
return TCL_ERROR;
}
esPtr = (EventScriptRecord *) ckalloc((unsigned)
sizeof(EventScriptRecord));
esPtr->nextPtr = chanPtr->scriptRecordPtr;
chanPtr->scriptRecordPtr = esPtr;
esPtr->chanPtr = chanPtr;
esPtr->interp = interp;
esPtr->mask = mask;
esPtr->script = ckalloc((unsigned) (strlen(argv[4]) + 1));
strcpy(esPtr->script, argv[4]);
Tcl_CreateChannelHandler((Tcl_Channel) chanPtr, mask,
ChannelEventScriptInvoker, (ClientData) esPtr);
return TCL_OK;
}
if ((cmd[0] == 'd') && (strncmp(cmd, "delete", (unsigned) len) == 0)) {
if (argc != 4) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" channelName delete index\"", (char *) NULL);
return TCL_ERROR;
}
if (Tcl_GetInt(interp, argv[3], &index) == TCL_ERROR) {
return TCL_ERROR;
}
if (index < 0) {
Tcl_AppendResult(interp, "bad event index: ", argv[3],
": must be nonnegative", (char *) NULL);
return TCL_ERROR;
}
for (i = 0, esPtr = chanPtr->scriptRecordPtr;
(i < index) && (esPtr != (EventScriptRecord *) NULL);
i++, esPtr = esPtr->nextPtr) {
}
if (esPtr == (EventScriptRecord *) NULL) {
Tcl_AppendResult(interp, "bad event index ", argv[3],
": out of range", (char *) NULL);
return TCL_ERROR;
}
if (esPtr == chanPtr->scriptRecordPtr) {
chanPtr->scriptRecordPtr = esPtr->nextPtr;
} else {
for (prevEsPtr = chanPtr->scriptRecordPtr;
(prevEsPtr != (EventScriptRecord *) NULL) &&
(prevEsPtr->nextPtr != esPtr);
prevEsPtr = prevEsPtr->nextPtr) {
}
if (prevEsPtr == (EventScriptRecord *) NULL) {
panic("TclTestChannelEventCmd: damaged event script list");
}
prevEsPtr->nextPtr = esPtr->nextPtr;
}
Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
ChannelEventScriptInvoker, (ClientData) esPtr);
ckfree(esPtr->script);
ckfree((char *) esPtr);
return TCL_OK;
}
if ((cmd[0] == 'l') && (strncmp(cmd, "list", (unsigned) len) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" channelName list\"", (char *) NULL);
return TCL_ERROR;
}
for (esPtr = chanPtr->scriptRecordPtr;
esPtr != (EventScriptRecord *) NULL;
esPtr = esPtr->nextPtr) {
char *event;
if (esPtr->mask) {
event = ((esPtr->mask == TCL_READABLE)
? "readable" : "writable");
} else {
event = "none";
}
Tcl_AppendElement(interp, event);
Tcl_AppendElement(interp, esPtr->script);
}
return TCL_OK;
}
if ((cmd[0] == 'r') && (strncmp(cmd, "removeall", (unsigned) len) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" channelName removeall\"", (char *) NULL);
return TCL_ERROR;
}
for (esPtr = chanPtr->scriptRecordPtr;
esPtr != (EventScriptRecord *) NULL;
esPtr = nextEsPtr) {
nextEsPtr = esPtr->nextPtr;
Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
ChannelEventScriptInvoker, (ClientData) esPtr);
ckfree(esPtr->script);
ckfree((char *) esPtr);
}
chanPtr->scriptRecordPtr = (EventScriptRecord *) NULL;
return TCL_OK;
}
if ((cmd[0] == 's') && (strncmp(cmd, "set", (unsigned) len) == 0)) {
if (argc != 5) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" channelName delete index event\"", (char *) NULL);
return TCL_ERROR;
}
if (Tcl_GetInt(interp, argv[3], &index) == TCL_ERROR) {
return TCL_ERROR;
}
if (index < 0) {
Tcl_AppendResult(interp, "bad event index: ", argv[3],
": must be nonnegative", (char *) NULL);
return TCL_ERROR;
}
for (i = 0, esPtr = chanPtr->scriptRecordPtr;
(i < index) && (esPtr != (EventScriptRecord *) NULL);
i++, esPtr = esPtr->nextPtr) {
}
if (esPtr == (EventScriptRecord *) NULL) {
Tcl_AppendResult(interp, "bad event index ", argv[3],
": out of range", (char *) NULL);
return TCL_ERROR;
}
if (strcmp(argv[4], "readable") == 0) {
mask = TCL_READABLE;
} else if (strcmp(argv[4], "writable") == 0) {
mask = TCL_WRITABLE;
} else if (strcmp(argv[4], "none") == 0) {
mask = 0;
} else {
Tcl_AppendResult(interp, "bad event name \"", argv[4],
"\": must be readable, writable, or none", (char *) NULL);
return TCL_ERROR;
}
esPtr->mask = mask;
Tcl_CreateChannelHandler((Tcl_Channel) chanPtr, mask,
ChannelEventScriptInvoker, (ClientData) esPtr);
return TCL_OK;
}
Tcl_AppendResult(interp, "bad command ", cmd, ", must be one of ",
"add, delete, list, set, or removeall", (char *) NULL);
return TCL_ERROR;
}
int
TclCopyChannel(interp, inChan, outChan, toRead, cmdPtr)
Tcl_Interp *interp;
Tcl_Channel inChan;
Tcl_Channel outChan;
int toRead;
Tcl_Obj *cmdPtr;
{
Channel *inPtr = (Channel *) inChan;
Channel *outPtr = (Channel *) outChan;
int readFlags, writeFlags;
CopyState *csPtr;
int nonBlocking = (cmdPtr) ? CHANNEL_NONBLOCKING : 0;
if (inPtr->csPtr) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
Tcl_GetChannelName(inChan), "\" is busy", NULL);
return TCL_ERROR;
}
if (outPtr->csPtr) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel \"",
Tcl_GetChannelName(outChan), "\" is busy", NULL);
return TCL_ERROR;
}
readFlags = inPtr->flags;
writeFlags = outPtr->flags;
if (nonBlocking != (readFlags & CHANNEL_NONBLOCKING)) {
if (SetBlockMode(interp, inPtr,
nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING)
!= TCL_OK) {
return TCL_ERROR;
}
}
if (inPtr != outPtr) {
if (nonBlocking != (writeFlags & CHANNEL_NONBLOCKING)) {
if (SetBlockMode(NULL, outPtr,
nonBlocking ? TCL_MODE_BLOCKING : TCL_MODE_NONBLOCKING)
!= TCL_OK) {
if (nonBlocking != (readFlags & CHANNEL_NONBLOCKING)) {
SetBlockMode(NULL, inPtr,
(readFlags & CHANNEL_NONBLOCKING)
? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
return TCL_ERROR;
}
}
}
}
outPtr->flags = (outPtr->flags & ~(CHANNEL_LINEBUFFERED))
| CHANNEL_UNBUFFERED;
csPtr = (CopyState*) ckalloc(sizeof(CopyState) + inPtr->bufSize);
csPtr->bufSize = inPtr->bufSize;
csPtr->readPtr = inPtr;
csPtr->writePtr = outPtr;
csPtr->readFlags = readFlags;
csPtr->writeFlags = writeFlags;
csPtr->toRead = toRead;
csPtr->total = 0;
csPtr->interp = interp;
if (cmdPtr) {
Tcl_IncrRefCount(cmdPtr);
}
csPtr->cmdPtr = cmdPtr;
inPtr->csPtr = csPtr;
outPtr->csPtr = csPtr;
return CopyData(csPtr, 0);
}
static int
CopyData(csPtr, mask)
CopyState *csPtr;
int mask;
{
Tcl_Interp *interp;
Tcl_Obj *cmdPtr, *errObj = NULL;
Tcl_Channel inChan, outChan;
int result = TCL_OK;
int size;
int total;
inChan = (Tcl_Channel)csPtr->readPtr;
outChan = (Tcl_Channel)csPtr->writePtr;
interp = csPtr->interp;
cmdPtr = csPtr->cmdPtr;
while (csPtr->toRead != 0) {
if (csPtr->readPtr->unreportedError != 0) {
Tcl_SetErrno(csPtr->readPtr->unreportedError);
csPtr->readPtr->unreportedError = 0;
goto readError;
}
if (csPtr->writePtr->unreportedError != 0) {
Tcl_SetErrno(csPtr->writePtr->unreportedError);
csPtr->writePtr->unreportedError = 0;
goto writeError;
}
if ((csPtr->toRead == -1)
|| (csPtr->toRead > csPtr->bufSize)) {
size = csPtr->bufSize;
} else {
size = csPtr->toRead;
}
size = DoRead(csPtr->readPtr, csPtr->buffer, size);
if (size < 0) {
readError:
errObj = Tcl_NewObj();
Tcl_AppendStringsToObj(errObj, "error reading \"",
Tcl_GetChannelName(inChan), "\": ",
Tcl_PosixError(interp), (char *) NULL);
break;
} else if (size == 0) {
if (Tcl_Eof(inChan)) {
break;
} else if (!(mask & TCL_READABLE)) {
if (mask & TCL_WRITABLE) {
Tcl_DeleteChannelHandler(outChan, CopyEventProc,
(ClientData) csPtr);
}
Tcl_CreateChannelHandler(inChan, TCL_READABLE,
CopyEventProc, (ClientData) csPtr);
}
return TCL_OK;
}
size = DoWrite(csPtr->writePtr, csPtr->buffer, size);
if (size < 0) {
writeError:
errObj = Tcl_NewObj();
Tcl_AppendStringsToObj(errObj, "error writing \"",
Tcl_GetChannelName(outChan), "\": ",
Tcl_PosixError(interp), (char *) NULL);
break;
}
if (csPtr->writePtr->flags & BG_FLUSH_SCHEDULED) {
if (!(mask & TCL_WRITABLE)) {
if (mask & TCL_READABLE) {
Tcl_DeleteChannelHandler(outChan, CopyEventProc,
(ClientData) csPtr);
}
Tcl_CreateChannelHandler(outChan, TCL_WRITABLE,
CopyEventProc, (ClientData) csPtr);
}
return TCL_OK;
}
if (csPtr->toRead != -1) {
csPtr->toRead -= size;
}
csPtr->total += size;
if (cmdPtr) {
if (mask == 0) {
Tcl_CreateChannelHandler(outChan, TCL_WRITABLE,
CopyEventProc, (ClientData) csPtr);
}
return TCL_OK;
}
}
total = csPtr->total;
if (cmdPtr) {
cmdPtr = Tcl_DuplicateObj(cmdPtr);
Tcl_IncrRefCount(cmdPtr);
StopCopy(csPtr);
Tcl_Preserve((ClientData) interp);
Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewIntObj(total));
if (errObj) {
Tcl_ListObjAppendElement(interp, cmdPtr, errObj);
}
if (Tcl_GlobalEvalObj(interp, cmdPtr) != TCL_OK) {
Tcl_BackgroundError(interp);
result = TCL_ERROR;
}
Tcl_DecrRefCount(cmdPtr);
Tcl_Release((ClientData) interp);
} else {
StopCopy(csPtr);
if (errObj) {
Tcl_SetObjResult(interp, errObj);
result = TCL_ERROR;
} else {
Tcl_ResetResult(interp);
Tcl_SetIntObj(Tcl_GetObjResult(interp), total);
}
}
return result;
}
static void
CopyEventProc(clientData, mask)
ClientData clientData;
int mask;
{
(void) CopyData((CopyState *)clientData, mask);
}
static void
StopCopy(csPtr)
CopyState *csPtr;
{
int nonBlocking;
if (!csPtr) {
return;
}
nonBlocking = (csPtr->readFlags & CHANNEL_NONBLOCKING);
if (nonBlocking != (csPtr->readPtr->flags & CHANNEL_NONBLOCKING)) {
SetBlockMode(NULL, csPtr->readPtr,
nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
}
if (csPtr->writePtr != csPtr->writePtr) {
if (nonBlocking != (csPtr->writePtr->flags & CHANNEL_NONBLOCKING)) {
SetBlockMode(NULL, csPtr->writePtr,
nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
}
}
csPtr->writePtr->flags &= ~(CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED);
csPtr->writePtr->flags |=
csPtr->writeFlags & (CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED);
if (csPtr->cmdPtr) {
Tcl_DeleteChannelHandler((Tcl_Channel)csPtr->readPtr, CopyEventProc,
(ClientData)csPtr);
if (csPtr->readPtr != csPtr->writePtr) {
Tcl_DeleteChannelHandler((Tcl_Channel)csPtr->writePtr,
CopyEventProc, (ClientData)csPtr);
}
Tcl_DecrRefCount(csPtr->cmdPtr);
}
csPtr->readPtr->csPtr = NULL;
csPtr->writePtr->csPtr = NULL;
ckfree((char*) csPtr);
}