diff options
Diffstat (limited to 'generic/tclIO.c')
-rw-r--r-- | generic/tclIO.c | 1439 |
1 files changed, 968 insertions, 471 deletions
diff --git a/generic/tclIO.c b/generic/tclIO.c index eeca41b..2de8b53 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -60,6 +60,9 @@ static void CleanupChannelHandlers(Tcl_Interp *interp, Channel *chanPtr); static int CloseChannel(Tcl_Interp *interp, Channel *chanPtr, int errorCode); +static int CloseChannelPart(Tcl_Interp *interp, Channel *chanPtr, + int errorCode, int flags); +static int CloseWrite(Tcl_Interp *interp, Channel *chanPtr); static void CommonGetsCleanup(Channel *chanPtr); static int CopyAndTranslateBuffer(ChannelState *statePtr, char *result, int space); @@ -76,7 +79,7 @@ static int DetachChannel(Tcl_Interp *interp, Tcl_Channel chan); static void DiscardInputQueued(ChannelState *statePtr, int discardSavedBuffers); static void DiscardOutputQueued(ChannelState *chanPtr); -static int DoRead(Channel *chanPtr, char *srcPtr, int slen); +static int DoRead(Channel *chanPtr, char *srcPtr, int slen, int allowShortReads); static int DoWrite(Channel *chanPtr, const char *src, int srcLen); static int DoReadChars(Channel *chan, Tcl_Obj *objPtr, int toRead, int appendFlag); @@ -115,6 +118,7 @@ static int WriteChars(Channel *chanPtr, const char *src, static Tcl_Obj * FixLevelCode(Tcl_Obj *msg); static void SpliceChannel(Tcl_Channel chan); static void CutChannel(Tcl_Channel chan); +static int WillRead(Channel *chanPtr); /* * Simplifying helper macros. All may use their argument(s) multiple times. @@ -160,21 +164,21 @@ static void CutChannel(Tcl_Channel chan); * -------------------------------------------------------------------------- */ -#define BytesLeft(bufPtr) ((bufPtr)->nextAdded - (bufPtr)->nextRemoved) +#define BytesLeft(bufPtr) ((bufPtr)->nextAdded - (bufPtr)->nextRemoved) -#define SpaceLeft(bufPtr) ((bufPtr)->bufLength - (bufPtr)->nextAdded) +#define SpaceLeft(bufPtr) ((bufPtr)->bufLength - (bufPtr)->nextAdded) -#define IsBufferReady(bufPtr) ((bufPtr)->nextAdded > (bufPtr)->nextRemoved) +#define IsBufferReady(bufPtr) ((bufPtr)->nextAdded > (bufPtr)->nextRemoved) -#define IsBufferEmpty(bufPtr) ((bufPtr)->nextAdded == (bufPtr)->nextRemoved) +#define IsBufferEmpty(bufPtr) ((bufPtr)->nextAdded == (bufPtr)->nextRemoved) -#define IsBufferFull(bufPtr) ((bufPtr)->nextAdded >= (bufPtr)->bufLength) +#define IsBufferFull(bufPtr) ((bufPtr)->nextAdded >= (bufPtr)->bufLength) -#define IsBufferOverflowing(bufPtr) ((bufPtr)->nextAdded > (bufPtr)->bufLength) +#define IsBufferOverflowing(bufPtr) ((bufPtr)->nextAdded>(bufPtr)->bufLength) -#define InsertPoint(bufPtr) ((bufPtr)->buf + (bufPtr)->nextAdded) +#define InsertPoint(bufPtr) ((bufPtr)->buf + (bufPtr)->nextAdded) -#define RemovePoint(bufPtr) ((bufPtr)->buf + (bufPtr)->nextRemoved) +#define RemovePoint(bufPtr) ((bufPtr)->buf + (bufPtr)->nextRemoved) /* * For working with channel state flag bits. @@ -182,6 +186,7 @@ static void CutChannel(Tcl_Channel chan); #define SetFlag(statePtr, flag) ((statePtr)->flags |= (flag)) #define ResetFlag(statePtr, flag) ((statePtr)->flags &= ~(flag)) +#define GotFlag(statePtr, flag) ((statePtr)->flags & (flag)) /* * Macro for testing whether a string (in optionName, length len) matches a @@ -202,11 +207,12 @@ static void CutChannel(Tcl_Channel chan); */ static void DupChannelIntRep(Tcl_Obj *objPtr, Tcl_Obj *copyPtr); -static int SetChannelFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); +static int SetChannelFromAny(Tcl_Interp *interp, + Tcl_Obj *objPtr); static void UpdateStringOfChannel(Tcl_Obj *objPtr); static void FreeChannelIntRep(Tcl_Obj *objPtr); -static Tcl_ObjType tclChannelType = { +static const Tcl_ObjType tclChannelType = { "channel", /* name for this type */ FreeChannelIntRep, /* freeIntRepProc */ DupChannelIntRep, /* dupIntRepProc */ @@ -223,7 +229,7 @@ static Tcl_ObjType tclChannelType = { #define SET_CHANNELINTERP(objPtr, storePtr) \ ((objPtr)->internalRep.twoPtrValue.ptr2 = (void *) (storePtr)) -#define BUSY_STATE(st,fl) \ +#define BUSY_STATE(st, fl) \ ((((st)->csPtrR) && ((fl) & TCL_READABLE)) || \ (((st)->csPtrW) && ((fl) & TCL_WRITABLE))) @@ -232,6 +238,113 @@ static Tcl_ObjType tclChannelType = { /* *--------------------------------------------------------------------------- * + * ChanClose, ChanRead, ChanSeek, ChanThreadAction, ChanWatch, ChanWrite -- + * + * Simplify the access to selected channel driver "methods" that are used + * in multiple places in a stereotypical fashion. These are just thin + * wrappers around the driver functions. + * + *--------------------------------------------------------------------------- + */ + +static inline int +ChanClose( + Channel *chanPtr, + Tcl_Interp *interp) +{ + if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) { + return chanPtr->typePtr->closeProc(chanPtr->instanceData, interp); + } else { + return chanPtr->typePtr->close2Proc(chanPtr->instanceData, interp, 0); + } +} + +static inline int +ChanCloseHalf( + Channel *chanPtr, + Tcl_Interp *interp, + int flags) +{ + return chanPtr->typePtr->close2Proc(chanPtr->instanceData, interp, flags); +} + +static inline int +ChanRead( + Channel *chanPtr, + char *dst, + int dstSize, + int *errnoPtr) +{ + if (WillRead(chanPtr) < 0) { + return -1; + } + + return chanPtr->typePtr->inputProc(chanPtr->instanceData, dst, dstSize, + errnoPtr); +} + +static inline Tcl_WideInt +ChanSeek( + Channel *chanPtr, + Tcl_WideInt offset, + int mode, + int *errnoPtr) +{ + /* + * Note that we prefer the wideSeekProc if that field is available in the + * type and non-NULL. + */ + + if (HaveVersion(chanPtr->typePtr, TCL_CHANNEL_VERSION_3) && + chanPtr->typePtr->wideSeekProc != NULL) { + return chanPtr->typePtr->wideSeekProc(chanPtr->instanceData, + offset, mode, errnoPtr); + } + + if (offset<Tcl_LongAsWide(LONG_MIN) || offset>Tcl_LongAsWide(LONG_MAX)) { + *errnoPtr = EOVERFLOW; + return Tcl_LongAsWide(-1); + } + + return Tcl_LongAsWide(chanPtr->typePtr->seekProc(chanPtr->instanceData, + Tcl_WideAsLong(offset), mode, errnoPtr)); +} + +static inline void +ChanThreadAction( + Channel *chanPtr, + int action) +{ + Tcl_DriverThreadActionProc *threadActionProc = + Tcl_ChannelThreadActionProc(chanPtr->typePtr); + + if (threadActionProc != NULL) { + threadActionProc(chanPtr->instanceData, action); + } +} + +static inline void +ChanWatch( + Channel *chanPtr, + int mask) +{ + chanPtr->typePtr->watchProc(chanPtr->instanceData, mask); +} + +static inline int +ChanWrite( + Channel *chanPtr, + const char *src, + int srcLen, + int *errnoPtr) +{ + return chanPtr->typePtr->outputProc(chanPtr->instanceData, src, srcLen, + errnoPtr); +} + +/* + *--------------------------------------------------------------------------- + * * TclInitIOSubsystem -- * * Initialize all resources used by this subsystem on a per-process @@ -283,6 +396,19 @@ TclFinalizeIOSubsystem(void) Channel *chanPtr = NULL; /* Iterates over open channels. */ ChannelState *statePtr; /* State of channel stack */ int active = 1; /* Flag == 1 while there's still work to do */ + int doflushnb; + + /* Fetch the pre-TIP#398 compatibility flag */ + { + const char *s; + Tcl_DString ds; + + s = TclGetEnv("TCL_FLUSH_NONBLOCKING_ON_EXIT", &ds); + doflushnb = ((s != NULL) && strcmp(s, "0")); + if (s != NULL) { + Tcl_DStringFree(&ds); + } + } /* * Walk all channel state structures known to this thread and close @@ -301,24 +427,37 @@ TclFinalizeIOSubsystem(void) statePtr != NULL; statePtr = statePtr->nextCSPtr) { chanPtr = statePtr->topChanPtr; - if (!(statePtr->flags & (CHANNEL_INCLOSE|CHANNEL_CLOSED|CHANNEL_DEAD))) { + if (GotFlag(statePtr, CHANNEL_DEAD)) { + continue; + } + if (!GotFlag(statePtr, CHANNEL_INCLOSE | CHANNEL_CLOSED ) + || GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { + ResetFlag(statePtr, BG_FLUSH_SCHEDULED); active = 1; break; } } /* - * We've found a live channel. Close it. + * We've found a live (or bg-closing) channel. Close it. */ if (active) { + /* - * Set the channel back into blocking mode to ensure that we wait - * for all data to flush out. + * TIP #398: by default, we no longer set the channel back into + * blocking mode. To restore the old blocking behavior, the + * environment variable TCL_FLUSH_NONBLOCKING_ON_EXIT must be set + * and not be "0". */ - - (void) Tcl_SetChannelOption(NULL, (Tcl_Channel) chanPtr, - "-blocking", "on"); + if (doflushnb) { + /* Set the channel back into blocking mode to ensure that we wait + * for all data to flush out. + */ + + (void) Tcl_SetChannelOption(NULL, (Tcl_Channel) chanPtr, + "-blocking", "on"); + } if ((chanPtr == (Channel *) tsdPtr->stdinChannel) || (chanPtr == (Channel *) tsdPtr->stdoutChannel) || @@ -351,12 +490,7 @@ TclFinalizeIOSubsystem(void) * device for this channel. */ - if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) { - (chanPtr->typePtr->closeProc)(chanPtr->instanceData, NULL); - } else { - (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, - NULL, 0); - } + (void) ChanClose(chanPtr, NULL); /* * Finally, we clean up the fields in the channel data @@ -398,6 +532,7 @@ Tcl_SetStdChannel( int type) /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */ { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + switch (type) { case TCL_STDIN: tsdPtr->stdinInitialized = 1; @@ -514,12 +649,10 @@ Tcl_CreateCloseHandler( ClientData clientData) /* Arbitrary data to pass to the close * callback. */ { - ChannelState *statePtr; + ChannelState *statePtr = ((Channel *) chan)->state; CloseCallback *cbPtr; - statePtr = ((Channel *) chan)->state; - - cbPtr = (CloseCallback *) ckalloc(sizeof(CloseCallback)); + cbPtr = ckalloc(sizeof(CloseCallback)); cbPtr->proc = proc; cbPtr->clientData = clientData; @@ -554,21 +687,19 @@ Tcl_DeleteCloseHandler( ClientData clientData) /* The callback data for the callback to * remove. */ { - ChannelState *statePtr; + ChannelState *statePtr = ((Channel *) chan)->state; CloseCallback *cbPtr, *cbPrevPtr; - statePtr = ((Channel *) chan)->state; for (cbPtr = statePtr->closeCbPtr, cbPrevPtr = NULL; cbPtr != NULL; cbPtr = cbPtr->nextPtr) { if ((cbPtr->proc == proc) && (cbPtr->clientData == clientData)) { if (cbPrevPtr == NULL) { statePtr->closeCbPtr = cbPtr->nextPtr; } - ckfree((char *) cbPtr); + ckfree(cbPtr); break; - } else { - cbPrevPtr = cbPtr; } + cbPrevPtr = cbPtr; } } @@ -600,7 +731,7 @@ GetChannelTable( hTblPtr = Tcl_GetAssocData(interp, "tclIO", NULL); if (hTblPtr == NULL) { - hTblPtr = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); + hTblPtr = ckalloc(sizeof(Tcl_HashTable)); Tcl_InitHashTable(hTblPtr, TCL_STRING_KEYS); Tcl_SetAssocData(interp, "tclIO", (Tcl_InterpDeleteProc *) DeleteChannelTable, hTblPtr); @@ -689,10 +820,10 @@ DeleteChannelTable( } Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr, - TclChannelEventScriptInvoker, (ClientData) sPtr); + TclChannelEventScriptInvoker, sPtr); TclDecrRefCount(sPtr->scriptPtr); - ckfree((char *) sPtr); + ckfree(sPtr); } else { prevPtr = sPtr; } @@ -709,14 +840,14 @@ DeleteChannelTable( SetFlag(statePtr, CHANNEL_TAINTED); statePtr->refCount--; if (statePtr->refCount <= 0) { - if (!(statePtr->flags & BG_FLUSH_SCHEDULED)) { + if (!GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { (void) Tcl_Close(interp, (Tcl_Channel) chanPtr); } } } Tcl_DeleteHashTable(hTblPtr); - ckfree((char *) hTblPtr); + ckfree(hTblPtr); } /* @@ -748,21 +879,19 @@ CheckForStdChannelsBeingClosed( ChannelState *statePtr = ((Channel *) chan)->state; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - if ((chan == tsdPtr->stdinChannel) && (tsdPtr->stdinInitialized)) { + if ((chan == tsdPtr->stdinChannel) && tsdPtr->stdinInitialized) { if (statePtr->refCount < 2) { statePtr->refCount = 0; tsdPtr->stdinChannel = NULL; return; } - } else if ((chan == tsdPtr->stdoutChannel) - && (tsdPtr->stdoutInitialized)) { + } else if ((chan == tsdPtr->stdoutChannel) && tsdPtr->stdoutInitialized) { if (statePtr->refCount < 2) { statePtr->refCount = 0; tsdPtr->stdoutChannel = NULL; return; } - } else if ((chan == tsdPtr->stderrChannel) - && (tsdPtr->stderrInitialized)) { + } else if ((chan == tsdPtr->stderrChannel) && tsdPtr->stderrInitialized) { if (statePtr->refCount < 2) { statePtr->refCount = 0; tsdPtr->stderrChannel = NULL; @@ -896,7 +1025,7 @@ Tcl_UnregisterChannel( statePtr = ((Channel *) chan)->state->bottomChanPtr->state; - if (statePtr->flags & CHANNEL_INCLOSE) { + if (GotFlag(statePtr, CHANNEL_INCLOSE)) { if (interp != NULL) { Tcl_AppendResult(interp, "Illegal recursive call to close " "through close-handler of channel", NULL); @@ -934,22 +1063,22 @@ Tcl_UnregisterChannel( IsBufferReady(statePtr->curOutPtr)) { SetFlag(statePtr, BUFFER_READY); } - Tcl_Preserve((ClientData)statePtr); - if (!(statePtr->flags & BG_FLUSH_SCHEDULED)) { + Tcl_Preserve(statePtr); + if (!GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { /* * We don't want to re-enter Tcl_Close(). */ - if (!(statePtr->flags & CHANNEL_CLOSED)) { + if (!GotFlag(statePtr, CHANNEL_CLOSED)) { if (Tcl_Close(interp, chan) != TCL_OK) { SetFlag(statePtr, CHANNEL_CLOSED); - Tcl_Release((ClientData)statePtr); + Tcl_Release(statePtr); return TCL_ERROR; } } } SetFlag(statePtr, CHANNEL_CLOSED); - Tcl_Release((ClientData)statePtr); + Tcl_Release(statePtr); } return TCL_OK; } @@ -1149,7 +1278,7 @@ Tcl_GetChannel( chanPtr = Tcl_GetHashValue(hPtr); chanPtr = chanPtr->state->bottomChanPtr; if (modePtr != NULL) { - *modePtr = (chanPtr->state->flags & (TCL_READABLE|TCL_WRITABLE)); + *modePtr = chanPtr->state->flags & (TCL_READABLE|TCL_WRITABLE); } return (Tcl_Channel) chanPtr; @@ -1194,10 +1323,10 @@ TclGetChannelFromObj( } statePtr = GET_CHANNELSTATE(objPtr); - *channelPtr = (Tcl_Channel) (statePtr->bottomChanPtr); + *channelPtr = (Tcl_Channel) statePtr->bottomChanPtr; if (modePtr != NULL) { - *modePtr = (statePtr->flags & (TCL_READABLE|TCL_WRITABLE)); + *modePtr = statePtr->flags & (TCL_READABLE|TCL_WRITABLE); } return TCL_OK; @@ -1221,7 +1350,7 @@ TclGetChannelFromObj( Tcl_Channel Tcl_CreateChannel( - Tcl_ChannelType *typePtr, /* The channel type record. */ + const Tcl_ChannelType *typePtr, /* The channel type record. */ const char *chanName, /* Name of channel to record. */ ClientData instanceData, /* Instance specific data. */ int mask) /* TCL_READABLE & TCL_WRITABLE to indicate if @@ -1231,6 +1360,7 @@ Tcl_CreateChannel( ChannelState *statePtr; /* The stack-level independent state info for * the channel. */ const char *name; + char *tmp; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); /* @@ -1243,15 +1373,15 @@ Tcl_CreateChannel( * as well. */ - assert(sizeof(Tcl_ChannelTypeVersion) == sizeof(Tcl_DriverBlockModeProc*)); + assert(sizeof(Tcl_ChannelTypeVersion) == sizeof(Tcl_DriverBlockModeProc *)); /* * JH: We could subsequently memset these to 0 to avoid the numerous * assignments to 0/NULL below. */ - chanPtr = (Channel *) ckalloc(sizeof(Channel)); - statePtr = (ChannelState *) ckalloc(sizeof(ChannelState)); + chanPtr = ckalloc(sizeof(Channel)); + statePtr = ckalloc(sizeof(ChannelState)); chanPtr->state = statePtr; chanPtr->instanceData = instanceData; @@ -1263,14 +1393,20 @@ Tcl_CreateChannel( */ if (chanName != NULL) { - char *tmp = ckalloc((unsigned) (strlen(chanName) + 1)); + unsigned len = strlen(chanName) + 1; - statePtr->channelName = tmp; + /* + * Make sure we allocate at least 7 bytes, so it fits for "stdout" + * later. + */ + + tmp = ckalloc((len < 7) ? 7 : len); strcpy(tmp, chanName); } else { - Tcl_Panic("Tcl_CreateChannel: NULL channel name"); + tmp = ckalloc(7); + tmp[0] = '\0'; } - + statePtr->channelName = tmp; statePtr->flags = mask; /* @@ -1324,9 +1460,8 @@ Tcl_CreateChannel( statePtr->csPtrW = NULL; statePtr->outputStage = NULL; - if ((statePtr->encoding != NULL) && (statePtr->flags & TCL_WRITABLE)) { - statePtr->outputStage = (char *) - ckalloc((unsigned) (statePtr->bufSize + 2)); + if ((statePtr->encoding != NULL) && GotFlag(statePtr, TCL_WRITABLE)) { + statePtr->outputStage = ckalloc(statePtr->bufSize + 2); } /* @@ -1373,14 +1508,17 @@ Tcl_CreateChannel( */ if ((tsdPtr->stdinChannel == NULL) && (tsdPtr->stdinInitialized == 1)) { + strcpy(tmp, "stdin"); Tcl_SetStdChannel((Tcl_Channel) chanPtr, TCL_STDIN); Tcl_RegisterChannel(NULL, (Tcl_Channel) chanPtr); } else if ((tsdPtr->stdoutChannel == NULL) && (tsdPtr->stdoutInitialized == 1)) { + strcpy(tmp, "stdout"); Tcl_SetStdChannel((Tcl_Channel) chanPtr, TCL_STDOUT); Tcl_RegisterChannel(NULL, (Tcl_Channel) chanPtr); } else if ((tsdPtr->stderrChannel == NULL) && (tsdPtr->stderrInitialized == 1)) { + strcpy(tmp, "stderr"); Tcl_SetStdChannel((Tcl_Channel) chanPtr, TCL_STDERR); Tcl_RegisterChannel(NULL, (Tcl_Channel) chanPtr); } @@ -1417,7 +1555,8 @@ Tcl_CreateChannel( Tcl_Channel Tcl_StackChannel( Tcl_Interp *interp, /* The interpreter we are working in */ - Tcl_ChannelType *typePtr, /* The channel type record for the new + const Tcl_ChannelType *typePtr, + /* The channel type record for the new * channel. */ ClientData instanceData, /* Instance specific data for the new * channel. */ @@ -1428,7 +1567,6 @@ Tcl_StackChannel( ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); Channel *chanPtr, *prevChanPtr; ChannelState *statePtr; - Tcl_DriverThreadActionProc *threadActionProc; /* * Find the given channel (prevChan) in the list of all channels. If we do @@ -1482,13 +1620,10 @@ Tcl_StackChannel( */ if ((mask & TCL_WRITABLE) != 0) { - CopyState *csPtrR; - CopyState *csPtrW; + CopyState *csPtrR = statePtr->csPtrR; + CopyState *csPtrW = statePtr->csPtrW; - csPtrR = statePtr->csPtrR; statePtr->csPtrR = NULL; - - csPtrW = statePtr->csPtrW; statePtr->csPtrW = NULL; if (Tcl_Flush((Tcl_Channel) prevChanPtr) != TCL_OK) { @@ -1537,7 +1672,7 @@ Tcl_StackChannel( statePtr->inQueueTail = NULL; } - chanPtr = (Channel *) ckalloc(sizeof(Channel)); + chanPtr = ckalloc(sizeof(Channel)); /* * Save some of the current state into the new structure, reinitialize the @@ -1573,10 +1708,7 @@ Tcl_StackChannel( * time, mangling it. */ - threadActionProc = Tcl_ChannelThreadActionProc(chanPtr->typePtr); - if (threadActionProc != NULL) { - (*threadActionProc)(chanPtr->instanceData, TCL_CHANNEL_THREAD_INSERT); - } + ChanThreadAction(chanPtr, TCL_CHANNEL_THREAD_INSERT); return (Tcl_Channel) chanPtr; } @@ -1607,7 +1739,6 @@ Tcl_UnstackChannel( Channel *chanPtr = (Channel *) chan; ChannelState *statePtr = chanPtr->state; int result = 0; - Tcl_DriverThreadActionProc *threadActionProc; /* * This operation should occur at the top of a channel stack. @@ -1618,9 +1749,9 @@ Tcl_UnstackChannel( if (chanPtr->downChanPtr != NULL) { /* * Instead of manipulating the per-thread / per-interp list/hashtable - * of registered channels we wind down the state of the transformation, - * and then restore the state of underlying channel into the old - * structure. + * of registered channels we wind down the state of the + * transformation, and then restore the state of underlying channel + * into the old structure. */ Channel *downChanPtr = chanPtr->downChanPtr; @@ -1633,14 +1764,11 @@ Tcl_UnstackChannel( * CheckForChannelErrors inside. */ - if (statePtr->flags & TCL_WRITABLE) { - CopyState *csPtrR; - CopyState *csPtrW; + if (GotFlag(statePtr, TCL_WRITABLE)) { + CopyState *csPtrR = statePtr->csPtrR; + CopyState *csPtrW = statePtr->csPtrW; - csPtrR = statePtr->csPtrR; statePtr->csPtrR = NULL; - - csPtrW = statePtr->csPtrW; statePtr->csPtrW = NULL; if (Tcl_Flush((Tcl_Channel) chanPtr) != TCL_OK) { @@ -1677,16 +1805,14 @@ Tcl_UnstackChannel( * 'DiscardInputQueued' on that. */ - if ((((statePtr->flags & TCL_READABLE) != 0)) && + if (GotFlag(statePtr, TCL_READABLE) && ((statePtr->inQueueHead != NULL) || (chanPtr->inQueueHead != NULL))) { - if ((statePtr->inQueueHead != NULL) && (chanPtr->inQueueHead != NULL)) { statePtr->inQueueTail->nextPtr = chanPtr->inQueueHead; statePtr->inQueueTail = chanPtr->inQueueTail; statePtr->inQueueHead = statePtr->inQueueTail; - } else if (chanPtr->inQueueHead != NULL) { statePtr->inQueueHead = chanPtr->inQueueHead; statePtr->inQueueTail = chanPtr->inQueueTail; @@ -1710,11 +1836,7 @@ Tcl_UnstackChannel( * the state which are still active. */ - threadActionProc = Tcl_ChannelThreadActionProc(chanPtr->typePtr); - if (threadActionProc != NULL) { - (*threadActionProc)(chanPtr->instanceData, - TCL_CHANNEL_THREAD_REMOVE); - } + ChanThreadAction(chanPtr, TCL_CHANNEL_THREAD_REMOVE); statePtr->topChanPtr = downChanPtr; downChanPtr->upChanPtr = NULL; @@ -1728,14 +1850,7 @@ Tcl_UnstackChannel( * Close and free the channel driver state. */ - if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) { - result = (chanPtr->typePtr->closeProc)(chanPtr->instanceData, - interp); - } else { - result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, - interp, 0); - } - + result = ChanClose(chanPtr, interp); chanPtr->typePtr = NULL; /* @@ -1911,7 +2026,7 @@ Tcl_GetChannelThread( *---------------------------------------------------------------------- */ -Tcl_ChannelType * +const Tcl_ChannelType * Tcl_GetChannelType( Tcl_Channel chan) /* The channel to return type for. */ { @@ -1970,9 +2085,9 @@ const char * Tcl_GetChannelName( Tcl_Channel chan) /* The channel for which to return the name. */ { - ChannelState *statePtr; /* State of actual channel. */ + ChannelState *statePtr = ((Channel *) chan)->state; + /* State of actual channel. */ - statePtr = ((Channel *) chan)->state; return statePtr->channelName; } @@ -2005,15 +2120,13 @@ Tcl_GetChannelHandle( chanPtr = ((Channel *) chan)->state->bottomChanPtr; if (!chanPtr->typePtr->getHandleProc) { - Tcl_Obj* err; - TclNewLiteralStringObj(err, "channel \""); - Tcl_AppendToObj(err, Tcl_GetChannelName(chan), -1); - Tcl_AppendToObj(err, "\" does not support OS handles", -1); - Tcl_SetChannelError (chan,err); + Tcl_SetChannelError(chan, Tcl_ObjPrintf( + "channel \"%s\" does not support OS handles", + Tcl_GetChannelName(chan))); return TCL_ERROR; } - result = (chanPtr->typePtr->getHandleProc)(chanPtr->instanceData, - direction, &handle); + result = chanPtr->typePtr->getHandleProc(chanPtr->instanceData, direction, + &handle); if (handlePtr) { *handlePtr = handle; } @@ -2052,7 +2165,7 @@ AllocChannelBuffer( int n; n = length + CHANNELBUFFER_HEADER_SIZE + BUFFER_PADDING + BUFFER_PADDING; - bufPtr = (ChannelBuffer *) ckalloc((unsigned) n); + bufPtr = ckalloc(n); bufPtr->nextAdded = BUFFER_PADDING; bufPtr->nextRemoved = BUFFER_PADDING; bufPtr->bufLength = length + BUFFER_PADDING; @@ -2091,7 +2204,7 @@ RecycleBuffer( */ if (mustDiscard) { - ckfree((char *) bufPtr); + ckfree(bufPtr); return; } @@ -2102,7 +2215,7 @@ RecycleBuffer( */ if ((bufPtr->bufLength - BUFFER_PADDING) < statePtr->bufSize) { - ckfree((char *) bufPtr); + ckfree(bufPtr); return; } @@ -2110,7 +2223,7 @@ RecycleBuffer( * Only save buffers for the input queue if the channel is readable. */ - if (statePtr->flags & TCL_READABLE) { + if (GotFlag(statePtr, TCL_READABLE)) { if (statePtr->inQueueHead == NULL) { statePtr->inQueueHead = bufPtr; statePtr->inQueueTail = bufPtr; @@ -2126,7 +2239,7 @@ RecycleBuffer( * Only save buffers for the output queue if the channel is writable. */ - if (statePtr->flags & TCL_WRITABLE) { + if (GotFlag(statePtr, TCL_WRITABLE)) { if (statePtr->curOutPtr == NULL) { statePtr->curOutPtr = bufPtr; goto keepBuffer; @@ -2137,7 +2250,7 @@ RecycleBuffer( * If we reached this code we return the buffer to the OS. */ - ckfree((char *) bufPtr); + ckfree(bufPtr); return; keepBuffer: @@ -2199,15 +2312,16 @@ CheckForDeadChannel( Tcl_Interp *interp, /* For error reporting (can be NULL) */ ChannelState *statePtr) /* The channel state to check. */ { - if (statePtr->flags & CHANNEL_DEAD) { - Tcl_SetErrno(EINVAL); - if (interp) { - Tcl_AppendResult(interp, - "unable to access channel: invalid channel", NULL); - } - return 1; + if (!GotFlag(statePtr, CHANNEL_DEAD)) { + return 0; } - return 0; + + Tcl_SetErrno(EINVAL); + if (interp) { + Tcl_AppendResult(interp, "unable to access channel: invalid channel", + NULL); + } + return 1; } /* @@ -2215,9 +2329,9 @@ CheckForDeadChannel( * * FlushChannel -- * - * This function flushes as much of the queued output as is possible - * now. If calledFromAsyncFlush is nonzero, it is being called in an - * event handler to flush channel output asynchronously. + * This function flushes as much of the queued output as is possible now. + * If calledFromAsyncFlush is nonzero, it is being called in an event + * handler to flush channel output asynchronously. * * Results: * 0 if successful, else the error code that was returned by the channel @@ -2266,6 +2380,7 @@ FlushChannel( * of the queued output to the channel. */ + Tcl_Preserve(chanPtr); while (1) { /* * If the queue is empty and there is a ready current buffer, OR if @@ -2275,7 +2390,7 @@ FlushChannel( if (((statePtr->curOutPtr != NULL) && IsBufferFull(statePtr->curOutPtr)) - || ((statePtr->flags & BUFFER_READY) && + || (GotFlag(statePtr, BUFFER_READY) && (statePtr->outQueueHead == NULL))) { ResetFlag(statePtr, BUFFER_READY); statePtr->curOutPtr->nextPtr = NULL; @@ -2294,9 +2409,9 @@ FlushChannel( * is active, we just return without producing any output. */ - if ((!calledFromAsyncFlush) && - (statePtr->flags & BG_FLUSH_SCHEDULED)) { - return 0; + if (!calledFromAsyncFlush && GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { + errorCode = 0; + goto done; } /* @@ -2304,7 +2419,7 @@ FlushChannel( */ if (bufPtr == NULL) { - break; /* Out of the "while (1)". */ + break; /* Out of the "while (1)". */ } /* @@ -2313,10 +2428,10 @@ FlushChannel( toWrite = BytesLeft(bufPtr); if (toWrite == 0) { - written = 0; + written = 0; } else { - written = (chanPtr->typePtr->outputProc)(chanPtr->instanceData, - RemovePoint(bufPtr), toWrite, &errorCode); + written = ChanWrite(chanPtr, RemovePoint(bufPtr), toWrite, + &errorCode); } /* @@ -2348,7 +2463,7 @@ FlushChannel( * it's a tty channel (dup'ed underneath) */ - if (!(statePtr->flags & BG_FLUSH_SCHEDULED)) { + if (!GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { SetFlag(statePtr, BG_FLUSH_SCHEDULED); UpdateInterest(chanPtr); } @@ -2398,14 +2513,8 @@ FlushChannel( Tcl_SetErrno(errorCode); if (interp != NULL && !TclChanCaughtErrorBypass(interp, (Tcl_Channel) chanPtr)) { - /* - * Casting away const here is safe because the - * TCL_VOLATILE flag guarantees const treatment of the - * Posix error string. - */ - - Tcl_SetResult(interp, (char *) Tcl_PosixError(interp), - TCL_VOLATILE); + Tcl_SetObjResult(interp, + Tcl_NewStringObj(Tcl_PosixError(interp), -1)); } /* @@ -2425,7 +2534,9 @@ FlushChannel( wroteSome = 1; } - bufPtr->nextRemoved += written; + if (!IsBufferEmpty(bufPtr)) { + bufPtr->nextRemoved += written; + } /* * If this buffer is now empty, recycle it. @@ -2447,13 +2558,12 @@ FlushChannel( * data has been flushed at the system level. */ - if (statePtr->flags & BG_FLUSH_SCHEDULED) { + if (GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { if (wroteSome) { - return errorCode; + goto done; } else if (statePtr->outQueueHead == NULL) { ResetFlag(statePtr, BG_FLUSH_SCHEDULED); - (chanPtr->typePtr->watchProc)(chanPtr->instanceData, - statePtr->interestMask); + ChanWatch(chanPtr, statePtr->interestMask); } } @@ -2463,12 +2573,30 @@ FlushChannel( * current output buffer. */ - if ((statePtr->flags & CHANNEL_CLOSED) && (statePtr->refCount <= 0) && + if (GotFlag(statePtr, CHANNEL_CLOSED) && (statePtr->refCount <= 0) && + (statePtr->outQueueHead == NULL) && + ((statePtr->curOutPtr == NULL) || + IsBufferEmpty(statePtr->curOutPtr))) { + errorCode = CloseChannel(interp, chanPtr, errorCode); + goto done; + } + + /* + * If the write-side of the channel is flagged as closed, delete it when + * the output queue is empty and there is no output in the current output + * buffer. + */ + + if (GotFlag(statePtr, CHANNEL_CLOSEDWRITE) && (statePtr->outQueueHead == NULL) && ((statePtr->curOutPtr == NULL) || IsBufferEmpty(statePtr->curOutPtr))) { - return CloseChannel(interp, chanPtr, errorCode); + errorCode = CloseChannelPart(interp, chanPtr, errorCode, TCL_CLOSE_WRITE); + goto done; } + + done: + Tcl_Release(chanPtr); return errorCode; } @@ -2522,7 +2650,7 @@ CloseChannel( */ if (statePtr->curOutPtr != NULL) { - ckfree((char *) statePtr->curOutPtr); + ckfree(statePtr->curOutPtr); statePtr->curOutPtr = NULL; } @@ -2539,11 +2667,11 @@ CloseChannel( * device. */ - if ((statePtr->outEofChar != 0) && (statePtr->flags & TCL_WRITABLE)) { + if ((statePtr->outEofChar != 0) && GotFlag(statePtr, TCL_WRITABLE)) { int dummy; char c = (char) statePtr->outEofChar; - (chanPtr->typePtr->outputProc)(chanPtr->instanceData, &c, 1, &dummy); + (void) ChanWrite(chanPtr, &c, 1, &dummy); } /* @@ -2554,7 +2682,7 @@ CloseChannel( if (statePtr->chanMsg != NULL) { if (interp != NULL) { - Tcl_SetChannelErrorInterp(interp,statePtr->chanMsg); + Tcl_SetChannelErrorInterp(interp, statePtr->chanMsg); } TclDecrRefCount(statePtr->chanMsg); statePtr->chanMsg = NULL; @@ -2571,12 +2699,7 @@ CloseChannel( * This may leave a TIP #219 error message in the interp. */ - if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) { - result = (chanPtr->typePtr->closeProc)(chanPtr->instanceData, interp); - } else { - result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, - interp, 0); - } + result = ChanClose(chanPtr, interp); /* * Some resources can be cleared only if the bottom channel in a stack is @@ -2585,13 +2708,13 @@ CloseChannel( if (chanPtr == statePtr->bottomChanPtr) { if (statePtr->channelName != NULL) { - ckfree((char *) statePtr->channelName); + ckfree(statePtr->channelName); statePtr->channelName = NULL; } Tcl_FreeEncoding(statePtr->encoding); if (statePtr->outputStage != NULL) { - ckfree((char *) statePtr->outputStage); + ckfree(statePtr->outputStage); statePtr->outputStage = NULL; } } @@ -2615,7 +2738,7 @@ CloseChannel( statePtr->chanMsg = NULL; } if (interp) { - Tcl_SetChannelErrorInterp(interp,statePtr->unreportedMsg); + Tcl_SetChannelErrorInterp(interp, statePtr->unreportedMsg); } } if (errorCode == 0) { @@ -2700,7 +2823,6 @@ CutChannel( * the list on close. */ ChannelState *statePtr = ((Channel *) chan)->state; /* State of the channel stack. */ - Tcl_DriverThreadActionProc *threadActionProc; /* * Remove this channel from of the list of all channels (in the current @@ -2727,11 +2849,7 @@ CutChannel( * TIP #218, Channel Thread Actions */ - threadActionProc = Tcl_ChannelThreadActionProc(Tcl_GetChannelType(chan)); - if (threadActionProc != NULL) { - (*threadActionProc)(Tcl_GetChannelInstanceData(chan), - TCL_CHANNEL_THREAD_REMOVE); - } + ChanThreadAction((Channel *) chan, TCL_CHANNEL_THREAD_REMOVE); } void @@ -2746,7 +2864,6 @@ Tcl_CutChannel( * the list on close. */ ChannelState *statePtr = chanPtr->state; /* State of the channel stack. */ - Tcl_DriverThreadActionProc *threadActionProc; /* * Remove this channel from of the list of all channels (in the current @@ -2774,13 +2891,8 @@ Tcl_CutChannel( * For all transformations and the base channel. */ - while (chanPtr) { - threadActionProc = Tcl_ChannelThreadActionProc(chanPtr->typePtr); - if (threadActionProc != NULL) { - (*threadActionProc)(chanPtr->instanceData, - TCL_CHANNEL_THREAD_REMOVE); - } - chanPtr= chanPtr->upChanPtr; + for (; chanPtr != NULL ; chanPtr = chanPtr->upChanPtr) { + ChanThreadAction(chanPtr, TCL_CHANNEL_THREAD_REMOVE); } } @@ -2817,7 +2929,6 @@ SpliceChannel( { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); ChannelState *statePtr = ((Channel *) chan)->state; - Tcl_DriverThreadActionProc *threadActionProc; if (statePtr->nextCSPtr != NULL) { Tcl_Panic("SpliceChannel: trying to add channel used in different list"); @@ -2838,11 +2949,7 @@ SpliceChannel( * TIP #218, Channel Thread Actions */ - threadActionProc = Tcl_ChannelThreadActionProc(Tcl_GetChannelType(chan)); - if (threadActionProc != NULL) { - (*threadActionProc) (Tcl_GetChannelInstanceData(chan), - TCL_CHANNEL_THREAD_INSERT); - } + ChanThreadAction((Channel *) chan, TCL_CHANNEL_THREAD_INSERT); } void @@ -2853,7 +2960,6 @@ Tcl_SpliceChannel( Channel *chanPtr = ((Channel *) chan)->state->bottomChanPtr; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); ChannelState *statePtr = chanPtr->state; - Tcl_DriverThreadActionProc *threadActionProc; if (statePtr->nextCSPtr != NULL) { Tcl_Panic("SpliceChannel: trying to add channel used in different list"); @@ -2875,13 +2981,8 @@ Tcl_SpliceChannel( * For all transformations and the base channel. */ - while (chanPtr) { - threadActionProc = Tcl_ChannelThreadActionProc(chanPtr->typePtr); - if (threadActionProc != NULL) { - (*threadActionProc)(chanPtr->instanceData, - TCL_CHANNEL_THREAD_INSERT); - } - chanPtr= chanPtr->upChanPtr; + for (; chanPtr != NULL ; chanPtr = chanPtr->upChanPtr) { + ChanThreadAction(chanPtr, TCL_CHANNEL_THREAD_INSERT); } } @@ -2948,7 +3049,7 @@ Tcl_Close( Tcl_Panic("called Tcl_Close on channel with refCount > 0"); } - if (statePtr->flags & CHANNEL_INCLOSE) { + if (GotFlag(statePtr, CHANNEL_INCLOSE)) { if (interp) { Tcl_AppendResult(interp, "Illegal recursive call to close " "through close-handler of channel", NULL); @@ -2979,7 +3080,7 @@ Tcl_Close( if (statePtr->chanMsg != NULL) { if (interp != NULL) { - Tcl_SetChannelErrorInterp(interp,statePtr->chanMsg); + Tcl_SetChannelErrorInterp(interp, statePtr->chanMsg); } TclDecrRefCount(statePtr->chanMsg); statePtr->chanMsg = NULL; @@ -2995,8 +3096,8 @@ Tcl_Close( while (statePtr->closeCbPtr != NULL) { cbPtr = statePtr->closeCbPtr; statePtr->closeCbPtr = cbPtr->nextPtr; - (cbPtr->proc)(cbPtr->clientData); - ckfree((char *) cbPtr); + cbPtr->proc(cbPtr->clientData); + ckfree(cbPtr); } ResetFlag(statePtr, CHANNEL_INCLOSE); @@ -3015,7 +3116,7 @@ Tcl_Close( */ if (chanPtr->typePtr->closeProc == TCL_CLOSE2PROC) { - result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, interp, + result = chanPtr->typePtr->close2Proc(chanPtr->instanceData, interp, TCL_CLOSE_READ); } else { result = 0; @@ -3065,6 +3166,352 @@ Tcl_Close( /* *---------------------------------------------------------------------- * + * Tcl_CloseEx -- + * + * Closes one side of a channel, read or write. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Closes one direction of the channel. + * + * NOTE: + * Tcl_CloseEx closes the specified direction of the channel as far as + * the user is concerned. The channel keeps existing however. You cannot + * calls this function to close the last possible direction of the + * channel. Use Tcl_Close for that. + * + *---------------------------------------------------------------------- + */ + + /* ARGSUSED */ +int +Tcl_CloseEx( + Tcl_Interp *interp, /* Interpreter for errors. */ + Tcl_Channel chan, /* The channel being closed. May still be used + * by some interpreter. */ + int flags) /* Flags telling us which side to close. */ +{ + Channel *chanPtr; /* The real IO channel. */ + ChannelState *statePtr; /* State of real IO channel. */ + + if (chan == NULL) { + return TCL_OK; + } + + /* TODO: assert flags validity ? */ + + chanPtr = (Channel *) chan; + statePtr = chanPtr->state; + + /* + * Does the channel support half-close anyway? Error if not. + */ + + if (!chanPtr->typePtr->close2Proc) { + Tcl_AppendResult(interp, "Half-close of channels not supported by ", + chanPtr->typePtr->typeName, "s", NULL); + return TCL_ERROR; + } + + /* + * Is the channel unstacked ? If not we fail. + */ + + if (chanPtr != statePtr->topChanPtr) { + Tcl_AppendResult(interp, + "Half-close not applicable to stack of transformations", + NULL); + return TCL_ERROR; + } + + /* + * Check direction against channel mode. It is an error if we try to close + * a direction not supported by the channel (already closed, or never + * opened for that direction). + */ + + if (!(statePtr->flags & (TCL_READABLE | TCL_WRITABLE) & flags)) { + const char *msg; + + if (flags & TCL_CLOSE_READ) { + msg = "read"; + } else { + msg = "write"; + } + Tcl_AppendResult(interp, "Half-close of ", msg, + "-side not possible, side not opened or already closed", + NULL); + return TCL_ERROR; + } + + /* + * A user may try to call half-close from within a channel close + * handler. That won't do. + */ + + if (statePtr->flags & CHANNEL_INCLOSE) { + if (interp) { + Tcl_AppendResult(interp, "Illegal recursive call to close " + "through close-handler of channel", NULL); + } + return TCL_ERROR; + } + + if (flags & TCL_CLOSE_READ) { + /* + * Call the finalization code directly. There are no events to handle, + * there cannot be for the read-side. + */ + + return CloseChannelPart(interp, chanPtr, 0, flags); + } else if (flags & TCL_CLOSE_WRITE) { + if ((statePtr->curOutPtr != NULL) && + IsBufferReady(statePtr->curOutPtr)) { + SetFlag(statePtr, BUFFER_READY); + } + Tcl_Preserve(statePtr); + if (!GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { + /* + * We don't want to re-enter CloseWrite(). + */ + + if (!GotFlag(statePtr, CHANNEL_CLOSEDWRITE)) { + if (CloseWrite(interp, chanPtr) != TCL_OK) { + SetFlag(statePtr, CHANNEL_CLOSEDWRITE); + Tcl_Release(statePtr); + return TCL_ERROR; + } + } + } + SetFlag(statePtr, CHANNEL_CLOSEDWRITE); + Tcl_Release(statePtr); + } + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * CloseWrite -- + * + * Closes the write side a channel. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Closes the write side of the channel. + * + * NOTE: + * CloseWrite removes the channel as far as the user is concerned. + * However, the ooutput data structures may continue to exist for a while + * longer if it has a background flush scheduled. The device itself is + * eventually closed and the channel structures modified, in + * CloseChannelPart, below. + * + *---------------------------------------------------------------------- + */ + +static int +CloseWrite( + Tcl_Interp *interp, /* Interpreter for errors. */ + Channel *chanPtr) /* The channel whose write side is being + * closed. May still be used by some + * interpreter */ +{ + /* Notes: clear-channel-handlers - write side only ? or keep around, just + * not called. */ + /* No close cllbacks are run - channel is still open (read side) */ + + ChannelState *statePtr = chanPtr->state; + /* State of real IO channel. */ + int flushcode; + int result = 0; + + /* + * Ensure that the last output buffer will be flushed. + */ + + if ((statePtr->curOutPtr != NULL) && IsBufferReady(statePtr->curOutPtr)) { + SetFlag(statePtr, BUFFER_READY); + } + + /* + * The call to FlushChannel will flush any queued output and invoke the + * close function of the channel driver, or it will set up the channel to + * be flushed and closed asynchronously. + */ + + SetFlag(statePtr, CHANNEL_CLOSEDWRITE); + + flushcode = FlushChannel(interp, chanPtr, 0); + + /* + * TIP #219. + * Capture error messages put by the driver into the bypass area and put + * them into the regular interpreter result. + * + * Notes: Due to the assertion of CHANNEL_CLOSEDWRITE in the flags + * FlushChannel() has called CloseChannelPart(). While we can still access + * "chan" (no structures were freed), the only place which may still + * contain a message is the interpreter itself, and "CloseChannelPart" made + * sure to lift any channel message it generated into it. Hence the NULL + * argument in the call below. + */ + + if (TclChanCaughtErrorBypass(interp, NULL)) { + result = EINVAL; + } + + if ((flushcode != 0) || (result != 0)) { + return TCL_ERROR; + } + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * CloseChannelPart -- + * + * Utility procedure to close a channel partially and free associated + * resources. If the channel was stacked it will never be run (The higher + * level forbid this). If the channel was not stacked, then we will free + * all the bits of the chosen side (read, or write) for the TOP channel. + * + * Results: + * Error code from an unreported error or the driver close2 operation. + * + * Side effects: + * May free memory, may change the value of errno. + * + *---------------------------------------------------------------------- + */ + +static int +CloseChannelPart( + Tcl_Interp *interp, /* Interpreter for errors. */ + Channel *chanPtr, /* The channel being closed. May still be used + * by some interpreter. */ + int errorCode, /* Status of operation so far. */ + int flags) /* Flags telling us which side to close. */ +{ + ChannelState *statePtr; /* State of real IO channel. */ + int result; /* Of calling the close2proc. */ + + statePtr = chanPtr->state; + + if (flags & TCL_CLOSE_READ) { + /* + * No more input can be consumed so discard any leftover input. + */ + + DiscardInputQueued(statePtr, 1); + } else if (flags & TCL_CLOSE_WRITE) { + /* + * The caller guarantees that there are no more buffers queued for + * output. + */ + + if (statePtr->outQueueHead != NULL) { + Tcl_Panic("ClosechanHalf, closed write-side of channel: " + "queued output left"); + } + + /* + * If the EOF character is set in the channel, append that to the + * output device. + */ + + if ((statePtr->outEofChar != 0) && GotFlag(statePtr, TCL_WRITABLE)) { + int dummy; + char c = (char) statePtr->outEofChar; + + (void) ChanWrite(chanPtr, &c, 1, &dummy); + } + + /* + * TIP #219, Tcl Channel Reflection API. + * Move a leftover error message in the channel bypass into the + * interpreter bypass. Just clear it if there is no interpreter. + */ + + if (statePtr->chanMsg != NULL) { + if (interp != NULL) { + Tcl_SetChannelErrorInterp(interp, statePtr->chanMsg); + } + TclDecrRefCount(statePtr->chanMsg); + statePtr->chanMsg = NULL; + } + } + + /* + * Finally do what is asked of us. Close and free the channel driver state + * for the chosen side of the channel. This may leave a TIP #219 error + * message in the interp. + */ + + result = ChanCloseHalf(chanPtr, interp, flags); + + /* + * If we are being called synchronously, report either any latent error on + * the channel or the current error. + */ + + if (statePtr->unreportedError != 0) { + errorCode = statePtr->unreportedError; + + /* + * TIP #219, Tcl Channel Reflection API. + * Move an error message found in the unreported area into the regular + * bypass (interp). This kills any message in the channel bypass area. + */ + + if (statePtr->chanMsg != NULL) { + TclDecrRefCount(statePtr->chanMsg); + statePtr->chanMsg = NULL; + } + if (interp) { + Tcl_SetChannelErrorInterp(interp, statePtr->unreportedMsg); + } + } + if (errorCode == 0) { + errorCode = result; + if (errorCode != 0) { + Tcl_SetErrno(errorCode); + } + } + + /* + * TIP #219. + * Capture error messages put by the driver into the bypass area and put + * them into the regular interpreter result. See also the bottom of + * CloseWrite(). + */ + + if (TclChanCaughtErrorBypass(interp, (Tcl_Channel) chanPtr)) { + result = EINVAL; + } + + if (result != 0) { + return TCL_ERROR; + } + + /* + * Remove the closed side from the channel mode/flags. + */ + + ResetFlag(statePtr, flags & (TCL_READABLE | TCL_WRITABLE)); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * * Tcl_ClearChannelHandlers -- * * Removes all channel handlers and event scripts from the channel, @@ -3124,7 +3571,7 @@ Tcl_ClearChannelHandlers( for (chPtr = statePtr->chPtr; chPtr != NULL; chPtr = chNext) { chNext = chPtr->nextPtr; - ckfree((char *) chPtr); + ckfree(chPtr); } statePtr->chPtr = NULL; @@ -3151,7 +3598,7 @@ Tcl_ClearChannelHandlers( for (ePtr = statePtr->scriptRecordPtr; ePtr != NULL; ePtr = eNextPtr) { eNextPtr = ePtr->nextPtr; TclDecrRefCount(ePtr->scriptPtr); - ckfree((char *) ePtr); + ckfree(ePtr); } statePtr->scriptRecordPtr = NULL; } @@ -3256,9 +3703,7 @@ Tcl_WriteRaw( * The code was stolen from 'FlushChannel'. */ - written = (chanPtr->typePtr->outputProc) (chanPtr->instanceData, - src, srcLen, &errorCode); - + written = ChanWrite(chanPtr, src, srcLen, &errorCode); if (written < 0) { Tcl_SetErrno(errorCode); } @@ -3359,12 +3804,13 @@ DoWriteChars( * be extended to more efficient translation of the src string. */ - int result; + int result; if ((len == 1) && (UCHAR(*src) < 0xC0)) { result = WriteBytes(chanPtr, src, len); } else { Tcl_Obj *objPtr = Tcl_NewStringObj(src, len); + src = (char *) Tcl_GetByteArrayFromObj(objPtr, &len); result = WriteBytes(chanPtr, src, len); TclDecrRefCount(objPtr); @@ -3410,7 +3856,7 @@ Tcl_WriteObj( Channel *chanPtr; ChannelState *statePtr; /* State info for channel */ - char *src; + const char *src; int srcLen; statePtr = ((Channel *) chan)->state; @@ -3428,6 +3874,38 @@ Tcl_WriteObj( } } +static void +WillWrite( + Channel *chanPtr) +{ + int inputBuffered; + + if ((chanPtr->typePtr->seekProc != NULL) && + ((inputBuffered = Tcl_InputBuffered((Tcl_Channel) chanPtr)) > 0)){ + int ignore; + + DiscardInputQueued(chanPtr->state, 0); + ChanSeek(chanPtr, -inputBuffered, SEEK_CUR, &ignore); + } +} + +static int +WillRead( + Channel *chanPtr) +{ + if ((chanPtr->typePtr->seekProc != NULL) + && (Tcl_OutputBuffered((Tcl_Channel) chanPtr) > 0)) { + if ((chanPtr->state->curOutPtr != NULL) + && IsBufferReady(chanPtr->state->curOutPtr)) { + SetFlag(chanPtr->state, BUFFER_READY); + } + if (FlushChannel(NULL, chanPtr, 0) != 0) { + return -1; + } + } + return 0; +} + /* *---------------------------------------------------------------------- * @@ -3461,11 +3939,15 @@ WriteBytes( char *dst; int dstMax, sawLF, savedLF, total, dstLen, toWrite, translate; + if (srcLen) { + WillWrite(chanPtr); + } + total = 0; sawLF = 0; savedLF = 0; - translate = (statePtr->flags & CHANNEL_LINEBUFFERED) - || (statePtr->outputTranslation != TCL_TRANSLATE_LF); + translate = GotFlag(statePtr, CHANNEL_LINEBUFFERED) + || (statePtr->outputTranslation != TCL_TRANSLATE_LF); /* * Loop over all bytes in src, storing them in output buffer with proper @@ -3562,6 +4044,10 @@ WriteChars( Tcl_Encoding encoding; char safe[BUFFER_PADDING]; + if (srcLen) { + WillWrite(chanPtr); + } + total = 0; sawLF = 0; savedLF = 0; @@ -3574,8 +4060,8 @@ WriteChars( endEncoding = ((statePtr->outputEncodingFlags & TCL_ENCODING_END) != 0); - translate = (statePtr->flags & CHANNEL_LINEBUFFERED) - || (statePtr->outputTranslation != TCL_TRANSLATE_LF); + translate = GotFlag(statePtr, CHANNEL_LINEBUFFERED) + || (statePtr->outputTranslation != TCL_TRANSLATE_LF); /* * Loop over all UTF-8 characters in src, storing them in staging buffer @@ -3598,16 +4084,17 @@ WriteChars( if (savedLF) { /* * A '\n' was left over from last call to TranslateOutputEOL() - * and we need to store it in the staging buffer. If the channel - * is line-based, we will need to flush the output buffer (after - * translating the staging buffer). + * and we need to store it in the staging buffer. If the + * channel is line-based, we will need to flush the output + * buffer (after translating the staging buffer). */ *stage++ = '\n'; stageLen--; sawLF++; } - if (TranslateOutputEOL(statePtr, stage, src, &stageLen, &toWrite)) { + if (TranslateOutputEOL(statePtr, stage, src, &stageLen, + &toWrite)) { sawLF++; } @@ -3900,18 +4387,18 @@ CheckFlush( * 3. if it contains any output and this channel is unbuffered. */ - if ((statePtr->flags & BUFFER_READY) == 0) { + if (!GotFlag(statePtr, BUFFER_READY)) { if (IsBufferFull(bufPtr)) { SetFlag(statePtr, BUFFER_READY); - } else if (statePtr->flags & CHANNEL_LINEBUFFERED) { + } else if (GotFlag(statePtr, CHANNEL_LINEBUFFERED)) { if (newlineFlag != 0) { SetFlag(statePtr, BUFFER_READY); } - } else if (statePtr->flags & CHANNEL_UNBUFFERED) { + } else if (GotFlag(statePtr, CHANNEL_UNBUFFERED)) { SetFlag(statePtr, BUFFER_READY); } } - if (statePtr->flags & BUFFER_READY) { + if (GotFlag(statePtr, BUFFER_READY)) { if (FlushChannel(NULL, chanPtr, 0) != 0) { return -1; } @@ -3947,14 +4434,12 @@ Tcl_Gets( * for managing the storage. */ { Tcl_Obj *objPtr; - int charsStored, length; - char *string; + int charsStored; TclNewObj(objPtr); charsStored = Tcl_GetsObj(chan, objPtr); if (charsStored > 0) { - string = TclGetStringFromObj(objPtr, &length); - Tcl_DStringAppend(lineRead, string, length); + TclDStringAppendObj(lineRead, objPtr); } TclDecrRefCount(objPtr); return charsStored; @@ -4162,7 +4647,7 @@ Tcl_GetsObj( case TCL_TRANSLATE_AUTO: eol = dst; skip = 1; - if (statePtr->flags & INPUT_SAW_CR) { + if (GotFlag(statePtr, INPUT_SAW_CR)) { ResetFlag(statePtr, INPUT_SAW_CR); if ((eol < dstEnd) && (*eol == '\n')) { /* @@ -4230,7 +4715,7 @@ Tcl_GetsObj( SetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF); statePtr->inputEncodingFlags |= TCL_ENCODING_END; } - if (statePtr->flags & CHANNEL_EOF) { + if (GotFlag(statePtr, CHANNEL_EOF)) { skip = 0; eol = dstEnd; if (eol == objPtr->bytes + oldLength) { @@ -4258,6 +4743,13 @@ Tcl_GetsObj( */ gotEOL: + /* + * Regenerate the top channel, in case it was changed due to + * self-modifying reflected transforms. + */ + + chanPtr = statePtr->topChanPtr; + bufPtr = gs.bufPtr; if (bufPtr == NULL) { Tcl_Panic("Tcl_GetsObj: gotEOL reached with bufPtr==NULL"); @@ -4286,6 +4778,13 @@ Tcl_GetsObj( */ restore: + /* + * Regenerate the top channel, in case it was changed due to + * self-modifying reflected transforms. + */ + + chanPtr = statePtr->topChanPtr; + bufPtr = statePtr->inQueueHead; if (bufPtr == NULL) { Tcl_Panic("Tcl_GetsObj: restore reached with bufPtr==NULL"); @@ -4321,6 +4820,13 @@ Tcl_GetsObj( */ done: + /* + * Regenerate the top channel, in case it was changed due to + * self-modifying reflected transforms. + */ + + chanPtr = statePtr->topChanPtr; + UpdateInterest(chanPtr); return copiedTotal; } @@ -4387,7 +4893,11 @@ TclGetsObjBinary( skip = 0; eof = NULL; inEofChar = statePtr->inEofChar; - /* Only handle TCL_TRANSLATE_LF and TCL_TRANSLATE_CR */ + + /* + * Only handle TCL_TRANSLATE_LF and TCL_TRANSLATE_CR. + */ + eolChar = (statePtr->inputTranslation == TCL_TRANSLATE_LF) ? '\n' : '\r'; while (1) { @@ -4410,8 +4920,8 @@ TclGetsObjBinary( * device. Side effect is to allocate another channel buffer. */ - if (statePtr->flags & CHANNEL_BLOCKED) { - if (statePtr->flags & CHANNEL_NONBLOCKING) { + if (GotFlag(statePtr, CHANNEL_BLOCKED)) { + if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) { goto restore; } ResetFlag(statePtr, CHANNEL_BLOCKED); @@ -4462,7 +4972,7 @@ TclGetsObjBinary( SetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF); statePtr->inputEncodingFlags |= TCL_ENCODING_END; } - if (statePtr->flags & CHANNEL_EOF) { + if (GotFlag(statePtr, CHANNEL_EOF)) { skip = 0; eol = dstEnd; if ((dst == dstEnd) && (byteLen == oldLength)) { @@ -4661,8 +5171,8 @@ FilterInputBytes( */ read: - if (statePtr->flags & CHANNEL_BLOCKED) { - if (statePtr->flags & CHANNEL_NONBLOCKING) { + if (GotFlag(statePtr, CHANNEL_BLOCKED)) { + if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) { gsPtr->charsWrote = 0; gsPtr->rawRead = 0; return -1; @@ -4742,7 +5252,7 @@ FilterInputBytes( * returning those UTF-8 characters because a EOL might be * present in them. */ - } else if (statePtr->flags & CHANNEL_EOF) { + } else if (GotFlag(statePtr, CHANNEL_EOF)) { /* * There was a partial character followed by EOF on the * device. Fall through, returning that nothing was found. @@ -4764,7 +5274,7 @@ FilterInputBytes( statePtr->inQueueTail = nextPtr; } extra = rawLen - gsPtr->rawRead; - memcpy(nextPtr->buf + BUFFER_PADDING - extra, + memcpy(nextPtr->buf + (BUFFER_PADDING - extra), raw + gsPtr->rawRead, (size_t) extra); nextPtr->nextRemoved -= extra; bufPtr->nextAdded -= extra; @@ -4831,7 +5341,7 @@ PeekAhead( goto cleanup; } - if ((statePtr->flags & CHANNEL_NONBLOCKING) == 0) { + if (!GotFlag(statePtr, CHANNEL_NONBLOCKING)) { blockModeProc = Tcl_ChannelBlockModeProc(chanPtr->typePtr); if (blockModeProc == NULL) { /* @@ -4913,7 +5423,7 @@ CommonGetsCleanup( extra = SpaceLeft(bufPtr); if (extra > 0) { memcpy(InsertPoint(bufPtr), - nextPtr->buf + BUFFER_PADDING - extra, + nextPtr->buf + (BUFFER_PADDING - extra), (size_t) extra); bufPtr->nextAdded += extra; nextPtr->nextRemoved = BUFFER_PADDING; @@ -4965,7 +5475,7 @@ Tcl_Read( return -1; } - return DoRead(chanPtr, dst, bytesToRead); + return DoRead(chanPtr, dst, bytesToRead, 0); } /* @@ -5027,11 +5537,11 @@ Tcl_ReadRaw( copiedNow = CopyBuffer(chanPtr, bufPtr + copied, bytesToRead - copied); if (copiedNow == 0) { - if (statePtr->flags & CHANNEL_EOF) { + if (GotFlag(statePtr, CHANNEL_EOF)) { goto done; } - if (statePtr->flags & CHANNEL_BLOCKED) { - if (statePtr->flags & CHANNEL_NONBLOCKING) { + if (GotFlag(statePtr, CHANNEL_BLOCKED)) { + if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) { goto done; } ResetFlag(statePtr, CHANNEL_BLOCKED); @@ -5045,9 +5555,9 @@ Tcl_ReadRaw( * and only if we are sure to have data. */ - if ((statePtr->flags & CHANNEL_NONBLOCKING) && + if (GotFlag(statePtr, CHANNEL_NONBLOCKING) && (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) && - !(statePtr->flags & CHANNEL_HAS_MORE_DATA)) { + !GotFlag(statePtr, CHANNEL_HAS_MORE_DATA)) { /* * We bypass the driver; it would block as no data is * available. @@ -5055,9 +5565,9 @@ Tcl_ReadRaw( nread = -1; result = EWOULDBLOCK; - } else { + } else #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */ - + { /* * Now go to the driver to get as much as is possible to fill * the remaining request. Do all the error handling by @@ -5067,12 +5577,9 @@ Tcl_ReadRaw( * The case of 'bytesToRead == 0' at this point cannot happen. */ - nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData, - bufPtr + copied, bytesToRead - copied, &result); - -#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING + nread = ChanRead(chanPtr, bufPtr + copied, + bytesToRead - copied, &result); } -#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */ if (nread > 0) { /* @@ -5096,7 +5603,6 @@ Tcl_ReadRaw( ResetFlag(statePtr, CHANNEL_HAS_MORE_DATA); } #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */ - } else if (nread == 0) { SetFlag(statePtr, CHANNEL_EOF); statePtr->inputEncodingFlags |= TCL_ENCODING_END; @@ -5272,9 +5778,8 @@ DoReadChars( bufPtr = statePtr->inQueueHead; if (IsBufferEmpty(bufPtr)) { - ChannelBuffer *nextPtr; + ChannelBuffer *nextPtr = bufPtr->nextPtr; - nextPtr = bufPtr->nextPtr; RecycleBuffer(statePtr, bufPtr, 0); statePtr->inQueueHead = nextPtr; if (nextPtr == NULL) { @@ -5284,11 +5789,11 @@ DoReadChars( } if (copiedNow < 0) { - if (statePtr->flags & CHANNEL_EOF) { + if (GotFlag(statePtr, CHANNEL_EOF)) { break; } - if (statePtr->flags & CHANNEL_BLOCKED) { - if (statePtr->flags & CHANNEL_NONBLOCKING) { + if (GotFlag(statePtr, CHANNEL_BLOCKED)) { + if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) { break; } ResetFlag(statePtr, CHANNEL_BLOCKED); @@ -5320,6 +5825,13 @@ DoReadChars( */ done: + /* + * Regenerate the top channel, in case it was changed due to + * self-modifying reflected transforms. + */ + + chanPtr = statePtr->topChanPtr; + UpdateInterest(chanPtr); return copied; } @@ -5400,7 +5912,7 @@ ReadBytes( } dst += offset; - if (statePtr->flags & INPUT_NEED_NL) { + if (GotFlag(statePtr, INPUT_NEED_NL)) { ResetFlag(statePtr, INPUT_NEED_NL); if ((srcLen == 0) || (*src != '\n')) { *dst = '\r'; @@ -5492,7 +6004,7 @@ ReadChars( srcLen = BytesLeft(bufPtr); toRead = charsToRead; - if ((unsigned)toRead > (unsigned)srcLen) { + if ((unsigned) toRead > (unsigned) srcLen) { toRead = srcLen; } @@ -5581,7 +6093,7 @@ ReadChars( } oldState = statePtr->inputEncodingState; - if (statePtr->flags & INPUT_NEED_NL) { + if (GotFlag(statePtr, INPUT_NEED_NL)) { /* * We want a '\n' because the last character we saw was '\r'. */ @@ -5697,16 +6209,15 @@ ReadChars( * '\n' in dst. */ - numChars -= (dstRead - dstWrote); + numChars -= dstRead - dstWrote; if ((unsigned) numChars > (unsigned) toRead) { /* * Got too many chars. */ - const char *eof; + const char *eof = Tcl_UtfAtIndex(dst, toRead); - eof = Tcl_UtfAtIndex(dst, toRead); statePtr->inputEncodingState = oldState; Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen, statePtr->inputEncodingFlags, &statePtr->inputEncodingState, @@ -5774,9 +6285,8 @@ TranslateInputEOL( * buffer. */ - const char *src, *srcMax; + const char *src, *srcMax = srcStart + *srcLenPtr; - srcMax = srcStart + *srcLenPtr; for (src = srcStart; src < srcMax; src++) { if (*src == inEofChar) { eof = src; @@ -5847,7 +6357,7 @@ TranslateInputEOL( srcEnd = srcStart + dstLen; srcMax = srcStart + *srcLenPtr; - if ((statePtr->flags & INPUT_SAW_CR) && (src < srcMax)) { + if (GotFlag(statePtr, INPUT_SAW_CR) && (src < srcMax)) { if (*src == '\n') { src++; } @@ -5952,7 +6462,7 @@ Tcl_Ungets( * bit. We want to discover these conditions anew in each operation. */ - if (statePtr->flags & CHANNEL_STICKY_EOF) { + if (GotFlag(statePtr, CHANNEL_STICKY_EOF)) { goto done; } ResetFlag(statePtr, CHANNEL_BLOCKED | CHANNEL_EOF); @@ -6078,7 +6588,7 @@ DiscardInputQueued( */ if (discardSavedBuffers && statePtr->saveInBufPtr != NULL) { - ckfree((char *) statePtr->saveInBufPtr); + ckfree(statePtr->saveInBufPtr); statePtr->saveInBufPtr = NULL; } } @@ -6171,7 +6681,7 @@ GetInput( if ((bufPtr != NULL) && (bufPtr->bufLength - BUFFER_PADDING < statePtr->bufSize)) { - ckfree((char *) bufPtr); + ckfree(bufPtr); bufPtr = NULL; } @@ -6209,36 +6719,32 @@ GetInput( * platforms it is impossible to read from a device after EOF. */ - if (statePtr->flags & CHANNEL_EOF) { + if (GotFlag(statePtr, CHANNEL_EOF)) { return 0; } #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING /* - * [SF Tcl Bug 943274]. Better emulation of non-blocking channels for - * channels without BlockModeProc, by keeping track of true fileevents - * generated by the OS == Data waiting and reading if and only if we are - * sure to have data. + * [Bug 943274]: Better emulation of non-blocking channels for channels + * without BlockModeProc, by keeping track of true fileevents generated by + * the OS == Data waiting and reading if and only if we are sure to have + * data. */ - if ((statePtr->flags & CHANNEL_NONBLOCKING) && + if (GotFlag(statePtr, CHANNEL_NONBLOCKING) && (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) && - !(statePtr->flags & CHANNEL_HAS_MORE_DATA)) { + !GotFlag(statePtr, CHANNEL_HAS_MORE_DATA)) { /* * Bypass the driver, it would block, as no data is available */ nread = -1; result = EWOULDBLOCK; - } else { + } else #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */ - - nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData, - InsertPoint(bufPtr), toRead, &result); - -#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING + { + nread = ChanRead(chanPtr, InsertPoint(bufPtr), toRead, &result); } -#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */ if (nread > 0) { bufPtr->nextAdded += nread; @@ -6257,14 +6763,12 @@ GetInput( #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING if (nread <= toRead) { /* - * [SF Tcl Bug 943274] We have read the available data, clear - * flag. + * [Bug 943274]: We have read the available data, clear flag. */ ResetFlag(statePtr, CHANNEL_HAS_MORE_DATA); } #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */ - } else if (nread == 0) { SetFlag(statePtr, CHANNEL_EOF); statePtr->inputEncodingFlags |= TCL_ENCODING_END; @@ -6380,8 +6884,8 @@ Tcl_Seek( * point. Also clear CR related flags. */ - statePtr->flags &= - ~(CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED | INPUT_SAW_CR); + ResetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED | + INPUT_SAW_CR); /* * If the channel is in asynchronous output mode, switch it back to @@ -6391,14 +6895,14 @@ Tcl_Seek( */ wasAsync = 0; - if (statePtr->flags & CHANNEL_NONBLOCKING) { + if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) { wasAsync = 1; result = StackSetBlockMode(chanPtr, TCL_MODE_BLOCKING); if (result != 0) { return Tcl_LongAsWide(-1); } ResetFlag(statePtr, CHANNEL_NONBLOCKING); - if (statePtr->flags & BG_FLUSH_SCHEDULED) { + if (GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { ResetFlag(statePtr, BG_FLUSH_SCHEDULED); } } @@ -6425,23 +6929,10 @@ Tcl_Seek( } else { /* * Now seek to the new position in the channel as requested by the - * caller. Note that we prefer the wideSeekProc if that is available - * and non-NULL... + * caller. */ - if (HaveVersion(chanPtr->typePtr, TCL_CHANNEL_VERSION_3) && - chanPtr->typePtr->wideSeekProc != NULL) { - curPos = (chanPtr->typePtr->wideSeekProc) (chanPtr->instanceData, - offset, mode, &result); - } else if (offset < Tcl_LongAsWide(LONG_MIN) || - offset > Tcl_LongAsWide(LONG_MAX)) { - result = EOVERFLOW; - curPos = Tcl_LongAsWide(-1); - } else { - curPos = Tcl_LongAsWide((chanPtr->typePtr->seekProc) ( - chanPtr->instanceData, Tcl_WideAsLong(offset), mode, - &result)); - } + curPos = ChanSeek(chanPtr, offset, mode, &result); if (curPos == Tcl_LongAsWide(-1)) { Tcl_SetErrno(result); } @@ -6536,29 +7027,18 @@ Tcl_Tell( inputBuffered = Tcl_InputBuffered(chan); outputBuffered = Tcl_OutputBuffered(chan); - if ((inputBuffered != 0) && (outputBuffered != 0)) { - Tcl_SetErrno(EFAULT); - return Tcl_LongAsWide(-1); - } - /* * Get the current position in the device and compute the position where * the next character will be read or written. Note that we prefer the * wideSeekProc if that is available and non-NULL... */ - if (HaveVersion(chanPtr->typePtr, TCL_CHANNEL_VERSION_3) && - chanPtr->typePtr->wideSeekProc != NULL) { - curPos = (chanPtr->typePtr->wideSeekProc) (chanPtr->instanceData, - Tcl_LongAsWide(0), SEEK_CUR, &result); - } else { - curPos = Tcl_LongAsWide((chanPtr->typePtr->seekProc) ( - chanPtr->instanceData, 0, SEEK_CUR, &result)); - } + curPos = ChanSeek(chanPtr, Tcl_LongAsWide(0), SEEK_CUR, &result); if (curPos == Tcl_LongAsWide(-1)) { Tcl_SetErrno(result); return Tcl_LongAsWide(-1); } + if (inputBuffered != 0) { return curPos - inputBuffered; } @@ -6592,19 +7072,18 @@ Tcl_SeekOld( { Tcl_WideInt wOffset, wResult; - wOffset = Tcl_LongAsWide((long)offset); + wOffset = Tcl_LongAsWide((long) offset); wResult = Tcl_Seek(chan, wOffset, mode); - return (int)Tcl_WideAsLong(wResult); + return (int) Tcl_WideAsLong(wResult); } int Tcl_TellOld( Tcl_Channel chan) /* The channel to return pos for. */ { - Tcl_WideInt wResult; + Tcl_WideInt wResult = Tcl_Tell(chan); - wResult = Tcl_Tell(chan); - return (int)Tcl_WideAsLong(wResult); + return (int) Tcl_WideAsLong(wResult); } /* @@ -6645,7 +7124,7 @@ Tcl_TruncateChannel( return TCL_ERROR; } - if (!(chanPtr->state->flags & TCL_WRITABLE)) { + if (!GotFlag(chanPtr->state, TCL_WRITABLE)) { /* * We require that the file was opened of writing. Do that check now * so that we only flush if we think we're going to succeed. @@ -6660,8 +7139,10 @@ Tcl_TruncateChannel( * pre-read input data. */ - if (Tcl_Seek(chan, (Tcl_WideInt)0, SEEK_CUR) == Tcl_LongAsWide(-1)) { - return TCL_ERROR; + WillWrite(chanPtr); + + if (WillRead(chanPtr) < 0) { + return TCL_ERROR; } /* @@ -6731,8 +7212,7 @@ CheckChannelErrors( * order to drain data from stacked channels. */ - if ((statePtr->flags & CHANNEL_CLOSED) && - ((flags & CHANNEL_RAW_MODE) == 0)) { + if (GotFlag(statePtr, CHANNEL_CLOSED) && !(flags & CHANNEL_RAW_MODE)) { Tcl_SetErrno(EACCES); return -1; } @@ -6754,7 +7234,7 @@ CheckChannelErrors( * retrieving and transforming the data to copy. */ - if (BUSY_STATE(statePtr,flags) && ((flags & CHANNEL_RAW_MODE) == 0)) { + if (BUSY_STATE(statePtr, flags) && ((flags & CHANNEL_RAW_MODE) == 0)) { Tcl_SetErrno(EBUSY); return -1; } @@ -6767,7 +7247,7 @@ CheckChannelErrors( * discover these conditions anew in each operation. */ - if ((statePtr->flags & CHANNEL_STICKY_EOF) == 0) { + if (!GotFlag(statePtr, CHANNEL_STICKY_EOF)) { ResetFlag(statePtr, CHANNEL_EOF); } ResetFlag(statePtr, CHANNEL_BLOCKED | CHANNEL_NEED_MORE_DATA); @@ -6799,8 +7279,8 @@ Tcl_Eof( ChannelState *statePtr = ((Channel *) chan)->state; /* State of real channel structure. */ - return ((statePtr->flags & CHANNEL_STICKY_EOF) || - ((statePtr->flags & CHANNEL_EOF) && + return (GotFlag(statePtr, CHANNEL_STICKY_EOF) || + (GotFlag(statePtr, CHANNEL_EOF) && (Tcl_InputBuffered(chan) == 0))) ? 1 : 0; } @@ -6827,7 +7307,7 @@ Tcl_InputBlocked( ChannelState *statePtr = ((Channel *) chan)->state; /* State of real channel structure. */ - return (statePtr->flags & CHANNEL_BLOCKED) ? 1 : 0; + return GotFlag(statePtr, CHANNEL_BLOCKED) ? 1 : 0; } /* @@ -6980,21 +7460,20 @@ Tcl_SetChannelBufferSize( */ if (sz < 1) { - sz = 1; + sz = 1; } else if (sz > MAX_CHANNEL_BUFFER_SIZE) { - sz = MAX_CHANNEL_BUFFER_SIZE; + sz = MAX_CHANNEL_BUFFER_SIZE; } statePtr = ((Channel *) chan)->state; statePtr->bufSize = sz; if (statePtr->outputStage != NULL) { - ckfree((char *) statePtr->outputStage); + ckfree(statePtr->outputStage); statePtr->outputStage = NULL; } - if ((statePtr->encoding != NULL) && (statePtr->flags & TCL_WRITABLE)) { - statePtr->outputStage = (char *) - ckalloc((unsigned) (statePtr->bufSize + 2)); + if ((statePtr->encoding != NULL) && GotFlag(statePtr, TCL_WRITABLE)) { + statePtr->outputStage = ckalloc(statePtr->bufSize + 2); } } @@ -7072,7 +7551,7 @@ Tcl_BadChannelOption( Tcl_DStringInit(&ds); Tcl_DStringAppend(&ds, genericopt, -1); if (optionList && (*optionList)) { - Tcl_DStringAppend(&ds, " ", 1); + TclDStringAppendLiteral(&ds, " "); Tcl_DStringAppend(&ds, optionList, -1); } if (Tcl_SplitList(interp, Tcl_DStringValue(&ds), @@ -7088,7 +7567,7 @@ Tcl_BadChannelOption( } Tcl_AppendResult(interp, "or -", argv[i], NULL); Tcl_DStringFree(&ds); - ckfree((char *) argv); + ckfree(argv); } Tcl_SetErrno(EINVAL); return TCL_ERROR; @@ -7150,9 +7629,9 @@ Tcl_GetChannelOption( */ if (statePtr->csPtrR) { - flags = statePtr->csPtrR->readFlags; + flags = statePtr->csPtrR->readFlags; } else if (statePtr->csPtrW) { - flags = statePtr->csPtrW->writeFlags; + flags = statePtr->csPtrW->writeFlags; } else { flags = statePtr->flags; } @@ -7312,8 +7791,8 @@ Tcl_GetChannelOption( * and message. */ - return (chanPtr->typePtr->getOptionProc) (chanPtr->instanceData, - interp, optionName, dsPtr); + return chanPtr->typePtr->getOptionProc(chanPtr->instanceData, interp, + optionName, dsPtr); } else { /* * No driver specific options case. @@ -7404,8 +7883,7 @@ Tcl_SetChannelOption( } else if (HaveOpt(7, "-buffering")) { len = strlen(newValue); if ((newValue[0] == 'f') && (strncmp(newValue, "full", len) == 0)) { - statePtr->flags &= - ~(CHANNEL_UNBUFFERED|CHANNEL_LINEBUFFERED); + ResetFlag(statePtr, CHANNEL_UNBUFFERED | CHANNEL_LINEBUFFERED); } else if ((newValue[0] == 'l') && (strncmp(newValue, "line", len) == 0)) { ResetFlag(statePtr, CHANNEL_UNBUFFERED); @@ -7414,12 +7892,10 @@ Tcl_SetChannelOption( (strncmp(newValue, "none", len) == 0)) { ResetFlag(statePtr, CHANNEL_LINEBUFFERED); SetFlag(statePtr, CHANNEL_UNBUFFERED); - } else { - if (interp) { - Tcl_AppendResult(interp, "bad value for -buffering: " - "must be one of full, line, or none", NULL); - return TCL_ERROR; - } + } else if (interp) { + Tcl_AppendResult(interp, "bad value for -buffering: " + "must be one of full, line, or none", NULL); + return TCL_ERROR; } return TCL_OK; } else if (HaveOpt(7, "-buffersize")) { @@ -7470,18 +7946,19 @@ Tcl_SetChannelOption( int outIndex = (argc - 1); int inValue = (int) argv[0][0]; int outValue = (int) argv[outIndex][0]; + if (inValue & 0x80 || outValue & 0x80) { if (interp) { Tcl_AppendResult(interp, "bad value for -eofchar: ", "must be non-NUL ASCII character", NULL); } - ckfree((char *) argv); + ckfree(argv); return TCL_ERROR; } - if (statePtr->flags & TCL_READABLE) { + if (GotFlag(statePtr, TCL_READABLE)) { statePtr->inEofChar = inValue; } - if (statePtr->flags & TCL_WRITABLE) { + if (GotFlag(statePtr, TCL_WRITABLE)) { statePtr->outEofChar = outValue; } } else { @@ -7490,11 +7967,11 @@ Tcl_SetChannelOption( "bad value for -eofchar: should be a list of zero," " one, or two elements", NULL); } - ckfree((char *) argv); + ckfree(argv); return TCL_ERROR; } if (argv != NULL) { - ckfree((char *) argv); + ckfree(argv); } /* @@ -7503,9 +7980,7 @@ Tcl_SetChannelOption( * ahead'. Ditto for blocked. */ - statePtr->flags &= - ~(CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED); - + ResetFlag(statePtr, CHANNEL_EOF|CHANNEL_STICKY_EOF|CHANNEL_BLOCKED); return TCL_OK; } else if (HaveOpt(1, "-translation")) { const char *readMode, *writeMode; @@ -7515,23 +7990,24 @@ Tcl_SetChannelOption( } if (argc == 1) { - readMode = (statePtr->flags & TCL_READABLE) ? argv[0] : NULL; - writeMode = (statePtr->flags & TCL_WRITABLE) ? argv[0] : NULL; + readMode = GotFlag(statePtr, TCL_READABLE) ? argv[0] : NULL; + writeMode = GotFlag(statePtr, TCL_WRITABLE) ? argv[0] : NULL; } else if (argc == 2) { - readMode = (statePtr->flags & TCL_READABLE) ? argv[0] : NULL; - writeMode = (statePtr->flags & TCL_WRITABLE) ? argv[1] : NULL; + readMode = GotFlag(statePtr, TCL_READABLE) ? argv[0] : NULL; + writeMode = GotFlag(statePtr, TCL_WRITABLE) ? argv[1] : NULL; } else { if (interp) { Tcl_AppendResult(interp, "bad value for -translation: must be a one or two" " element list", NULL); } - ckfree((char *) argv); + ckfree(argv); return TCL_ERROR; } if (readMode) { TclEolTranslation translation; + if (*readMode == '\0') { translation = statePtr->inputTranslation; } else if (strcmp(readMode, "auto") == 0) { @@ -7556,7 +8032,7 @@ Tcl_SetChannelOption( "must be one of auto, binary, cr, lf, crlf," " or platform", NULL); } - ckfree((char *) argv); + ckfree(argv); return TCL_ERROR; } @@ -7607,15 +8083,15 @@ Tcl_SetChannelOption( "must be one of auto, binary, cr, lf, crlf," " or platform", NULL); } - ckfree((char *) argv); + ckfree(argv); return TCL_ERROR; } } - ckfree((char *) argv); + ckfree(argv); return TCL_OK; } else if (chanPtr->typePtr->setOptionProc != NULL) { - return (*chanPtr->typePtr->setOptionProc)(chanPtr->instanceData, - interp, optionName, newValue); + return chanPtr->typePtr->setOptionProc(chanPtr->instanceData, interp, + optionName, newValue); } else { return Tcl_BadChannelOption(interp, optionName, NULL); } @@ -7644,8 +8120,8 @@ Tcl_SetChannelOption( ckfree(statePtr->outputStage); statePtr->outputStage = NULL; } - if ((statePtr->encoding != NULL) && (statePtr->flags & TCL_WRITABLE)) { - statePtr->outputStage = ckalloc((unsigned) (statePtr->bufSize + 2)); + if ((statePtr->encoding != NULL) && GotFlag(statePtr, TCL_WRITABLE)) { + statePtr->outputStage = ckalloc(statePtr->bufSize + 2); } return TCL_OK; } @@ -7697,7 +8173,7 @@ CleanupChannelHandlers( TclChannelEventScriptInvoker, sPtr); TclDecrRefCount(sPtr->scriptPtr); - ckfree((char *) sPtr); + ckfree(sPtr); } else { prevPtr = sPtr; } @@ -7746,9 +8222,9 @@ Tcl_NotifyChannel( */ if ((mask & TCL_READABLE) && - (statePtr->flags & CHANNEL_NONBLOCKING) && + GotFlag(statePtr, CHANNEL_NONBLOCKING) && (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) && - !(statePtr->flags & CHANNEL_TIMER_FEV)) { + !GotFlag(statePtr, CHANNEL_TIMER_FEV)) { SetFlag(statePtr, CHANNEL_HAS_MORE_DATA); } #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */ @@ -7765,14 +8241,14 @@ Tcl_NotifyChannel( * their own events and pass them upward. */ - while (mask && (chanPtr->upChanPtr != (NULL))) { + while (mask && (chanPtr->upChanPtr != NULL)) { Tcl_DriverHandlerProc *upHandlerProc; upChanPtr = chanPtr->upChanPtr; upTypePtr = upChanPtr->typePtr; upHandlerProc = Tcl_ChannelHandlerProc(upTypePtr); if (upHandlerProc != NULL) { - mask = (*upHandlerProc) (upChanPtr->instanceData, mask); + mask = upHandlerProc(upChanPtr->instanceData, mask); } /* @@ -7811,7 +8287,7 @@ Tcl_NotifyChannel( * don't call any write handlers before the flush is complete. */ - if ((statePtr->flags & BG_FLUSH_SCHEDULED) && (mask & TCL_WRITABLE)) { + if (GotFlag(statePtr, BG_FLUSH_SCHEDULED) && (mask & TCL_WRITABLE)) { FlushChannel(NULL, chanPtr, 1); mask &= ~TCL_WRITABLE; } @@ -7833,7 +8309,7 @@ Tcl_NotifyChannel( if ((chPtr->mask & mask) != 0) { nh.nextHandlerPtr = chPtr->nextPtr; - (*(chPtr->proc))(chPtr->clientData, mask); + chPtr->proc(chPtr->clientData, mask); chPtr = nh.nextHandlerPtr; } else { chPtr = chPtr->nextPtr; @@ -7886,7 +8362,7 @@ UpdateInterest( * watch for the channel to become writable. */ - if (statePtr->flags & BG_FLUSH_SCHEDULED) { + if (GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { mask |= TCL_WRITABLE; } @@ -7898,7 +8374,7 @@ UpdateInterest( */ if (mask & TCL_READABLE) { - if (!(statePtr->flags & CHANNEL_NEED_MORE_DATA) + if (!GotFlag(statePtr, CHANNEL_NEED_MORE_DATA) && (statePtr->inQueueHead != NULL) && IsBufferReady(statePtr->inQueueHead)) { mask &= ~TCL_READABLE; @@ -7944,12 +8420,12 @@ UpdateInterest( mask &= ~TCL_EXCEPTION; if (!statePtr->timer) { - statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc, - chanPtr); + statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, + ChannelTimerProc, chanPtr); } } } - (chanPtr->typePtr->watchProc)(chanPtr->instanceData, mask); + ChanWatch(chanPtr, mask); } /* @@ -7977,7 +8453,7 @@ ChannelTimerProc( ChannelState *statePtr = chanPtr->state; /* State info for channel */ - if (!(statePtr->flags & CHANNEL_NEED_MORE_DATA) + if (!GotFlag(statePtr, CHANNEL_NEED_MORE_DATA) && (statePtr->interestMask & TCL_READABLE) && (statePtr->inQueueHead != NULL) && IsBufferReady(statePtr->inQueueHead)) { @@ -7986,7 +8462,8 @@ ChannelTimerProc( * before UpdateInterest gets called by Tcl_NotifyChannel. */ - statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,chanPtr); + statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, + ChannelTimerProc,chanPtr); #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING /* @@ -7997,14 +8474,14 @@ ChannelTimerProc( * similar test is done in "PeekAhead". */ - if ((statePtr->flags & CHANNEL_NONBLOCKING) && - (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL)) { + if (GotFlag(statePtr, CHANNEL_NONBLOCKING) && + (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL)) { SetFlag(statePtr, CHANNEL_TIMER_FEV); } #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */ Tcl_Preserve(statePtr); - Tcl_NotifyChannel((Tcl_Channel)chanPtr, TCL_READABLE); + Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_READABLE); #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING ResetFlag(statePtr, CHANNEL_TIMER_FEV); @@ -8068,7 +8545,7 @@ Tcl_CreateChannelHandler( } } if (chPtr == NULL) { - chPtr = (ChannelHandler *) ckalloc(sizeof(ChannelHandler)); + chPtr = ckalloc(sizeof(ChannelHandler)); chPtr->mask = 0; chPtr->proc = proc; chPtr->clientData = clientData; @@ -8172,7 +8649,7 @@ Tcl_DeleteChannelHandler( } else { prevChPtr->nextPtr = chPtr->nextPtr; } - ckfree((char *) chPtr); + ckfree(chPtr); /* * Recompute the interest list for the channel, so that infinite loops @@ -8223,6 +8700,7 @@ DeleteScriptRecord( if (esPtr == statePtr->scriptRecordPtr) { statePtr->scriptRecordPtr = esPtr->nextPtr; } else { + CLANG_ASSERT(prevEsPtr); prevEsPtr->nextPtr = esPtr->nextPtr; } @@ -8230,7 +8708,7 @@ DeleteScriptRecord( TclChannelEventScriptInvoker, esPtr); TclDecrRefCount(esPtr->scriptPtr); - ckfree((char *) esPtr); + ckfree(esPtr); break; } @@ -8279,12 +8757,12 @@ CreateScriptRecord( makeCH = (esPtr == NULL); if (makeCH) { - esPtr = (EventScriptRecord *) ckalloc(sizeof(EventScriptRecord)); + esPtr = ckalloc(sizeof(EventScriptRecord)); } /* * Initialize the structure before calling Tcl_CreateChannelHandler, - * because a reflected channel caling 'chan postevent' aka + * because a reflected channel calling 'chan postevent' aka * 'Tcl_NotifyChannel' in its 'watch'Proc will invoke * 'TclChannelEventScriptInvoker' immediately, and we do not wish it to * see uninitialized memory and crash. See [Bug 2918110]. @@ -8362,7 +8840,7 @@ TclChannelEventScriptInvoker( if (chanPtr->typePtr != NULL) { DeleteScriptRecord(interp, chanPtr, mask); } - TclBackgroundException(interp, result); + Tcl_BackgroundException(interp, result); } Tcl_Release(chanPtr); Tcl_Release(interp); @@ -8399,11 +8877,11 @@ Tcl_FileEventObjCmd( Channel *chanPtr; /* The channel to create the handler for. */ ChannelState *statePtr; /* State info for channel */ Tcl_Channel chan; /* The opaque type for the channel. */ - char *chanName; + const char *chanName; int modeIndex; /* Index of mode argument. */ int mask; - static const char *modeOptions[] = {"readable", "writable", NULL}; - static CONST int maskArray[] = {TCL_READABLE, TCL_WRITABLE}; + static const char *const modeOptions[] = {"readable", "writable", NULL}; + static const int maskArray[] = {TCL_READABLE, TCL_WRITABLE}; if ((objc != 3) && (objc != 4)) { Tcl_WrongNumArgs(interp, 1, objv, "channelId event ?script?"); @@ -8434,6 +8912,7 @@ Tcl_FileEventObjCmd( if (objc == 3) { EventScriptRecord *esPtr; + for (esPtr = statePtr->scriptRecordPtr; esPtr != NULL; esPtr = esPtr->nextPtr) { if ((esPtr->interp == interp) && (esPtr->mask == mask)) { @@ -8513,13 +8992,25 @@ ZeroTransferTimerProc( */ int -TclCopyChannel( +TclCopyChannelOld( Tcl_Interp *interp, /* Current interpreter. */ Tcl_Channel inChan, /* Channel to read from. */ Tcl_Channel outChan, /* Channel to write to. */ int toRead, /* Amount of data to copy, or -1 for all. */ Tcl_Obj *cmdPtr) /* Pointer to script to execute or NULL. */ { + return TclCopyChannel(interp, inChan, outChan, (Tcl_WideInt) toRead, + cmdPtr); +} + +int +TclCopyChannel( + Tcl_Interp *interp, /* Current interpreter. */ + Tcl_Channel inChan, /* Channel to read from. */ + Tcl_Channel outChan, /* Channel to write to. */ + Tcl_WideInt toRead, /* Amount of data to copy, or -1 for all. */ + Tcl_Obj *cmdPtr) /* Pointer to script to execute or NULL. */ +{ Channel *inPtr = (Channel *) inChan; Channel *outPtr = (Channel *) outChan; ChannelState *inStatePtr, *outStatePtr; @@ -8530,14 +9021,14 @@ TclCopyChannel( inStatePtr = inPtr->state; outStatePtr = outPtr->state; - if (BUSY_STATE(inStatePtr,TCL_READABLE)) { + if (BUSY_STATE(inStatePtr, TCL_READABLE)) { if (interp) { Tcl_AppendResult(interp, "channel \"", Tcl_GetChannelName(inChan), "\" is busy", NULL); } return TCL_ERROR; } - if (BUSY_STATE(outStatePtr,TCL_WRITABLE)) { + if (BUSY_STATE(outStatePtr, TCL_WRITABLE)) { if (interp) { Tcl_AppendResult(interp, "channel \"", Tcl_GetChannelName(outChan), "\" is busy", NULL); @@ -8573,8 +9064,8 @@ TclCopyChannel( * Make sure the output side is unbuffered. */ - outStatePtr->flags = (outStatePtr->flags & ~(CHANNEL_LINEBUFFERED)) - | CHANNEL_UNBUFFERED; + outStatePtr->flags = (outStatePtr->flags & ~CHANNEL_LINEBUFFERED) + | CHANNEL_UNBUFFERED; /* * Allocate a new CopyState to maintain info about the current copy in @@ -8582,14 +9073,14 @@ TclCopyChannel( * completed. */ - csPtr = (CopyState *) ckalloc(sizeof(CopyState) + inStatePtr->bufSize); + csPtr = ckalloc(sizeof(CopyState) + inStatePtr->bufSize); csPtr->bufSize = inStatePtr->bufSize; csPtr->readPtr = inPtr; csPtr->writePtr = outPtr; csPtr->readFlags = readFlags; csPtr->writeFlags = writeFlags; csPtr->toRead = toRead; - csPtr->total = 0; + csPtr->total = (Tcl_WideInt) 0; csPtr->interp = interp; if (cmdPtr) { Tcl_IncrRefCount(cmdPtr); @@ -8644,7 +9135,7 @@ CopyData( ChannelState *inStatePtr, *outStatePtr; int result = TCL_OK, size, sizeb; Tcl_WideInt total; - char *buffer; + const char *buffer; int inBinary, outBinary, sameEncoding; /* Encoding control */ int underflow; /* Input underflow */ @@ -8673,7 +9164,7 @@ CopyData( Tcl_IncrRefCount(bufObj); } - while (csPtr->toRead != 0) { + while (csPtr->toRead != (Tcl_WideInt) 0) { /* * Check for unreported background errors. */ @@ -8697,24 +9188,26 @@ CopyData( * underflow instead to prime the readable fileevent. */ - size = 0; + size = 0; underflow = 1; } else { /* * Read up to bufSize bytes. */ - if ((csPtr->toRead == -1) || (csPtr->toRead > csPtr->bufSize)) { + if ((csPtr->toRead == (Tcl_WideInt) -1) + || (csPtr->toRead > (Tcl_WideInt) csPtr->bufSize)) { sizeb = csPtr->bufSize; } else { - sizeb = csPtr->toRead; + sizeb = (int) csPtr->toRead; } if (inBinary || sameEncoding) { - size = DoRead(inStatePtr->topChanPtr, csPtr->buffer, sizeb); + size = DoRead(inStatePtr->topChanPtr, csPtr->buffer, sizeb, + !GotFlag(inStatePtr, CHANNEL_NONBLOCKING)); } else { size = DoReadChars(inStatePtr->topChanPtr, bufObj, sizeb, - 0 /* No append */); + 0 /* No append */); } underflow = (size >= 0) && (size < sizeb); /* Input underflow */ } @@ -8747,8 +9240,8 @@ CopyData( if ((size == 0) && Tcl_Eof(inChan) && !(cmdPtr && (mask == 0))) { break; } - if (((!Tcl_Eof(inChan)) || (cmdPtr && (mask == 0))) && - !(mask & TCL_READABLE)) { + if (cmdPtr && (!Tcl_Eof(inChan) || (mask == 0)) && + !(mask & TCL_READABLE)) { if (mask & TCL_WRITABLE) { Tcl_DeleteChannelHandler(outChan, CopyEventProc, csPtr); } @@ -8813,7 +9306,7 @@ CopyData( } /* - * (UP) Update the current byte count. Do it now so the count is valid + * Update the current byte count. Do it now so the count is valid * before a return or break takes us out of the loop. The invariant at * the top of the loop should be that csPtr->toRead holds the number * of bytes left to copy. @@ -8839,7 +9332,7 @@ CopyData( * therefore we don't need a writable handler. */ - if (!underflow && (outStatePtr->flags & BG_FLUSH_SCHEDULED)) { + if (!underflow && GotFlag(outStatePtr, BG_FLUSH_SCHEDULED)) { if (!(mask & TCL_WRITABLE)) { if (mask & TCL_READABLE) { Tcl_DeleteChannelHandler(inChan, CopyEventProc, csPtr); @@ -8890,6 +9383,7 @@ CopyData( total = csPtr->total; if (cmdPtr && interp) { int code; + /* * Get a private copy of the command so we can mutate it by adding * arguments. Note that StopCopy frees our saved reference to the @@ -8907,7 +9401,7 @@ CopyData( } code = Tcl_EvalObjEx(interp, cmdPtr, TCL_EVAL_GLOBAL); if (code != TCL_OK) { - TclBackgroundException(interp, code); + Tcl_BackgroundException(interp, code); result = TCL_ERROR; } TclDecrRefCount(cmdPtr); @@ -8932,9 +9426,8 @@ CopyData( * * DoRead -- * - * Reads a given number of bytes from a channel. - * - * No encoding conversions are applied to the bytes being read. + * Reads a given number of bytes from a channel. No encoding conversions + * are applied to the bytes being read. * * Results: * The number of characters read, or -1 on error. Use Tcl_GetErrno() to @@ -8950,7 +9443,8 @@ static int DoRead( Channel *chanPtr, /* The channel from which to read. */ char *bufPtr, /* Where to store input read. */ - int toRead) /* Maximum number of bytes to read. */ + int toRead, /* Maximum number of bytes to read. */ + int allowShortReads) /* Allow half-blocking (pipes,sockets) */ { ChannelState *statePtr = chanPtr->state; /* State info for channel */ @@ -8966,7 +9460,7 @@ DoRead( * operation. */ - if (!(statePtr->flags & CHANNEL_STICKY_EOF)) { + if (!GotFlag(statePtr, CHANNEL_STICKY_EOF)) { ResetFlag(statePtr, CHANNEL_EOF); } ResetFlag(statePtr, CHANNEL_BLOCKED | CHANNEL_NEED_MORE_DATA); @@ -8975,11 +9469,11 @@ DoRead( copiedNow = CopyAndTranslateBuffer(statePtr, bufPtr + copied, toRead - copied); if (copiedNow == 0) { - if (statePtr->flags & CHANNEL_EOF) { + if (GotFlag(statePtr, CHANNEL_EOF)) { goto done; } - if (statePtr->flags & CHANNEL_BLOCKED) { - if (statePtr->flags & CHANNEL_NONBLOCKING) { + if (GotFlag(statePtr, CHANNEL_BLOCKED)) { + if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) { goto done; } ResetFlag(statePtr, CHANNEL_BLOCKED); @@ -8991,7 +9485,10 @@ DoRead( } goto done; } - } + } else if (allowShortReads) { + copied += copiedNow; + break; + } } ResetFlag(statePtr, CHANNEL_BLOCKED); @@ -9133,7 +9630,7 @@ CopyAndTranslateBuffer( curByte = *src; if (curByte == '\n') { ResetFlag(statePtr, INPUT_SAW_CR); - } else if (statePtr->flags & INPUT_SAW_CR) { + } else if (GotFlag(statePtr, INPUT_SAW_CR)) { ResetFlag(statePtr, INPUT_SAW_CR); *dst = '\r'; dst++; @@ -9176,7 +9673,7 @@ CopyAndTranslateBuffer( *dst = '\n'; dst++; } else { - if ((curByte != '\n') || !(statePtr->flags & INPUT_SAW_CR)) { + if ((curByte != '\n') || !GotFlag(statePtr, INPUT_SAW_CR)) { *dst = (char) curByte; dst++; } @@ -9445,10 +9942,10 @@ DoWrite( */ outBufPtr->nextAdded += destCopied; - if (!(statePtr->flags & BUFFER_READY)) { + if (!GotFlag(statePtr, BUFFER_READY)) { if (IsBufferFull(outBufPtr)) { SetFlag(statePtr, BUFFER_READY); - } else if (statePtr->flags & CHANNEL_LINEBUFFERED) { + } else if (GotFlag(statePtr, CHANNEL_LINEBUFFERED)) { for (sPtr = src, i = 0, foundNewline = 0; (i < srcCopied) && (!foundNewline); i++, sPtr++) { @@ -9460,7 +9957,7 @@ DoWrite( if (foundNewline) { SetFlag(statePtr, BUFFER_READY); } - } else if (statePtr->flags & CHANNEL_UNBUFFERED) { + } else if (GotFlag(statePtr, CHANNEL_UNBUFFERED)) { SetFlag(statePtr, BUFFER_READY); } } @@ -9469,7 +9966,7 @@ DoWrite( src += srcCopied; srcLen -= srcCopied; - if (statePtr->flags & BUFFER_READY) { + if (GotFlag(statePtr, BUFFER_READY)) { if (FlushChannel(NULL, chanPtr, 0) != 0) { return -1; } @@ -9502,7 +9999,7 @@ CopyEventProc( ClientData clientData, int mask) { - (void) CopyData((CopyState *) clientData, mask); + (void) CopyData(clientData, mask); } /* @@ -9540,19 +10037,19 @@ StopCopy( * Restore the old blocking mode and output buffering mode. */ - nonBlocking = (csPtr->readFlags & CHANNEL_NONBLOCKING); + nonBlocking = csPtr->readFlags & CHANNEL_NONBLOCKING; if (nonBlocking != (inStatePtr->flags & CHANNEL_NONBLOCKING)) { SetBlockMode(NULL, csPtr->readPtr, nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING); } if (csPtr->readPtr != csPtr->writePtr) { - nonBlocking = (csPtr->writeFlags & CHANNEL_NONBLOCKING); + nonBlocking = csPtr->writeFlags & CHANNEL_NONBLOCKING; if (nonBlocking != (outStatePtr->flags & CHANNEL_NONBLOCKING)) { SetBlockMode(NULL, csPtr->writePtr, nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING); } } - outStatePtr->flags &= ~(CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED); + ResetFlag(outStatePtr, CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED); outStatePtr->flags |= csPtr->writeFlags & (CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED); @@ -9567,7 +10064,7 @@ StopCopy( } inStatePtr->csPtrR = NULL; outStatePtr->csPtrW = NULL; - ckfree((char *) csPtr); + ckfree(csPtr); } /* @@ -9605,7 +10102,7 @@ StackSetBlockMode( while (chanPtr != NULL) { blockModeProc = Tcl_ChannelBlockModeProc(chanPtr->typePtr); if (blockModeProc != NULL) { - result = (*blockModeProc) (chanPtr->instanceData, mode); + result = blockModeProc(chanPtr->instanceData, mode); if (result != 0) { Tcl_SetErrno(result); return result; @@ -9758,10 +10255,11 @@ Tcl_GetChannelNamesEx( } goto done; } + for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch); hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) { - statePtr = ((Channel *) Tcl_GetHashValue(hPtr))->state; + if (statePtr->topChanPtr == (Channel *) tsdPtr->stdinChannel) { name = "stdin"; } else if (statePtr->topChanPtr == (Channel *) tsdPtr->stdoutChannel) { @@ -9907,11 +10405,8 @@ Tcl_IsChannelExisting( name = statePtr->channelName; } - /* Bug 2333466. Include \0 in the compare to prevent partial matching - * on prefixes. - */ if ((*chanName == *name) && - (memcmp(name, chanName, (size_t) chanNameLen+1) == 0)) { + (memcmp(name, chanName, (size_t) chanNameLen + 1) == 0)) { return 1; } } @@ -10030,13 +10525,13 @@ Tcl_ChannelBlockModeProc( { if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) { return chanTypePtr->blockModeProc; - } else { - /* - * The v1 structure had the blockModeProc in a different place. - */ - - return (Tcl_DriverBlockModeProc *) (chanTypePtr->version); } + + /* + * The v1 structure had the blockModeProc in a different place. + */ + + return (Tcl_DriverBlockModeProc *) chanTypePtr->version; } /* @@ -10278,9 +10773,8 @@ Tcl_ChannelFlushProc( { if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) { return chanTypePtr->flushProc; - } else { - return NULL; } + return NULL; } /* @@ -10306,9 +10800,8 @@ Tcl_ChannelHandlerProc( { if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) { return chanTypePtr->handlerProc; - } else { - return NULL; } + return NULL; } /* @@ -10334,9 +10827,8 @@ Tcl_ChannelWideSeekProc( { if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_3)) { return chanTypePtr->wideSeekProc; - } else { - return NULL; } + return NULL; } /* @@ -10363,9 +10855,8 @@ Tcl_ChannelThreadActionProc( { if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_4)) { return chanTypePtr->threadActionProc; - } else { - return NULL; } + return NULL; } /* @@ -10482,7 +10973,7 @@ FixLevelCode( res = Tcl_ListObjGetElements(NULL, msg, &lc, &lv); if (res != TCL_OK) { - Tcl_Panic("Tcl_SetChannelError(Interp): Bad syntax of message"); + Tcl_Panic("Tcl_SetChannelError: bad syntax of message"); } explicitResult = (1 == (lc % 2)); @@ -10542,7 +11033,7 @@ FixLevelCode( lcn += 2; } - lvn = (Tcl_Obj **) ckalloc(lcn * sizeof(Tcl_Obj *)); + lvn = ckalloc(lcn * sizeof(Tcl_Obj *)); /* * New level/code information is spliced into the first occurence of @@ -10595,7 +11086,7 @@ FixLevelCode( msg = Tcl_NewListObj(j, lvn); - ckfree((char *) lvn); + ckfree(lvn); return msg; } @@ -10679,9 +11170,8 @@ Tcl_ChannelTruncateProc( { if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_5)) { return chanTypePtr->truncateProc; - } else { - return NULL; } + return NULL; } /* @@ -10710,11 +11200,11 @@ DupChannelIntRep( * currently have an internal rep.*/ { ChannelState *statePtr = GET_CHANNELSTATE(srcPtr); - Interp *interpPtr = GET_CHANNELINTERP(srcPtr); + Interp *interpPtr = GET_CHANNELINTERP(srcPtr); SET_CHANNELSTATE(copyPtr, statePtr); SET_CHANNELINTERP(copyPtr, interpPtr); - Tcl_Preserve((ClientData) statePtr); + Tcl_Preserve(statePtr); copyPtr->typePtr = &tclChannelType; } @@ -10741,7 +11231,7 @@ SetChannelFromAny( register Tcl_Obj *objPtr) /* The object to convert. */ { ChannelState *statePtr; - Interp *interpPtr; + Interp *interpPtr; if (interp == NULL) { return TCL_ERROR; @@ -10751,15 +11241,16 @@ SetChannelFromAny( * The channel is valid until any call to DetachChannel occurs. * Ensure consistency checks are done. */ - statePtr = GET_CHANNELSTATE(objPtr); + + statePtr = GET_CHANNELSTATE(objPtr); interpPtr = GET_CHANNELINTERP(objPtr); - if (statePtr->flags & (CHANNEL_TAINTED|CHANNEL_CLOSED)) { + if (GotFlag(statePtr, CHANNEL_TAINTED|CHANNEL_CLOSED)) { ResetFlag(statePtr, CHANNEL_TAINTED); - Tcl_Release((ClientData) statePtr); + Tcl_Release(statePtr); UpdateStringOfChannel(objPtr); objPtr->typePtr = NULL; } else if (interpPtr != (Interp*) interp) { - Tcl_Release((ClientData) statePtr); + Tcl_Release(statePtr); UpdateStringOfChannel(objPtr); objPtr->typePtr = NULL; } @@ -10771,6 +11262,7 @@ SetChannelFromAny( * We need a valid string with which to check for a valid channel, but * make sure not to free internal rep until validated. [Bug 1847044] */ + if ((objPtr->typePtr != NULL) && (objPtr->bytes == NULL)) { objPtr->typePtr->updateStringProc(objPtr); } @@ -10781,8 +11273,8 @@ SetChannelFromAny( } TclFreeIntRep(objPtr); - statePtr = ((Channel *)chan)->state; - Tcl_Preserve((ClientData) statePtr); + statePtr = ((Channel *) chan)->state; + Tcl_Preserve(statePtr); SET_CHANNELSTATE(objPtr, statePtr); SET_CHANNELINTERP(objPtr, interp); objPtr->typePtr = &tclChannelType; @@ -10815,9 +11307,11 @@ UpdateStringOfChannel( if (objPtr->bytes == NULL) { ChannelState *statePtr = GET_CHANNELSTATE(objPtr); const char *name = statePtr->channelName; + if (name) { size_t len = strlen(name); - objPtr->bytes = (char *) ckalloc(len + 1); + + objPtr->bytes = ckalloc(len + 1); objPtr->length = len; memcpy(objPtr->bytes, name, len); } else { @@ -10847,7 +11341,8 @@ static void FreeChannelIntRep( Tcl_Obj *objPtr) /* Object with internal rep to free. */ { - Tcl_Release((ClientData) GET_CHANNELSTATE(objPtr)); + Tcl_Release(GET_CHANNELSTATE(objPtr)); + objPtr->typePtr = NULL; } #if 0 @@ -10864,7 +11359,7 @@ DumpFlags( char buf[20]; int i = 0; -#define ChanFlag(chr,bit) (buf[i++] = ((flags & (bit)) ? (chr) : '_')) +#define ChanFlag(chr, bit) (buf[i++] = ((flags & (bit)) ? (chr) : '_')) ChanFlag('r', TCL_READABLE); ChanFlag('w', TCL_WRITABLE); @@ -10899,5 +11394,7 @@ DumpFlags( * mode: c * c-basic-offset: 4 * fill-column: 78 + * tab-width: 8 + * indent-tabs-mode: nil * End: */ |