diff options
Diffstat (limited to 'generic/tclIO.c')
-rw-r--r-- | generic/tclIO.c | 877 |
1 files changed, 462 insertions, 415 deletions
diff --git a/generic/tclIO.c b/generic/tclIO.c index e74624f..d144cbc 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -116,7 +116,7 @@ typedef struct CopyState { * The structure defined below is used in this file only. */ -typedef struct ThreadSpecificData { +typedef struct { NextChannelHandler *nestedHandlerPtr; /* This variable holds the list of nested * Tcl_NotifyChannel invocations. */ @@ -321,9 +321,9 @@ static int WillRead(Channel *chanPtr); typedef struct ResolvedChanName { ChannelState *statePtr; /* The saved lookup result */ Tcl_Interp *interp; /* The interp in which the lookup was done. */ - int epoch; /* The epoch of the channel when the lookup + size_t epoch; /* The epoch of the channel when the lookup * was done. Use to verify validity. */ - int refCount; /* Share this struct among many Tcl_Obj. */ + size_t refCount; /* Share this struct among many Tcl_Obj. */ } ResolvedChanName; static void DupChannelIntRep(Tcl_Obj *objPtr, Tcl_Obj *copyPtr); @@ -337,6 +337,22 @@ static const Tcl_ObjType chanObjType = { NULL /* setFromAnyProc */ }; +#define ChanSetIntRep(objPtr, resPtr) \ + do { \ + Tcl_ObjIntRep ir; \ + (resPtr)->refCount++; \ + ir.twoPtrValue.ptr1 = (resPtr); \ + ir.twoPtrValue.ptr2 = NULL; \ + Tcl_StoreIntRep((objPtr), &chanObjType, &ir); \ + } while (0) + +#define ChanGetIntRep(objPtr, resPtr) \ + do { \ + const Tcl_ObjIntRep *irPtr; \ + irPtr = Tcl_FetchIntRep((objPtr), &chanObjType); \ + (resPtr) = irPtr ? irPtr->twoPtrValue.ptr1 : NULL; \ + } while (0) + #define BUSY_STATE(st, fl) \ ((((st)->csPtrR) && ((fl) & TCL_READABLE)) || \ (((st)->csPtrW) && ((fl) & TCL_WRITABLE))) @@ -381,20 +397,20 @@ ChanCloseHalf( * * ChanRead -- * - * Read up to dstSize bytes using the inputProc of chanPtr, store - * them at dst, and return the number of bytes stored. + * Read up to dstSize bytes using the inputProc of chanPtr, store them at + * dst, and return the number of bytes stored. * * Results: * The return value of the driver inputProc, * - number of bytes stored at dst, ot - * - -1 on error, with a Posix error code available to the - * caller by calling Tcl_GetErrno(). + * - -1 on error, with a Posix error code available to the caller by + * calling Tcl_GetErrno(). * * Side effects: - * The CHANNEL_BLOCKED and CHANNEL_EOF flags of the channel state are - * set as appropriate. - * On EOF, the inputEncodingFlags are set to perform ending operations - * on decoding. + * The CHANNEL_BLOCKED and CHANNEL_EOF flags of the channel state are set + * as appropriate. On EOF, the inputEncodingFlags are set to perform + * ending operations on decoding. + * * TODO - Is this really the right place for that? * *--------------------------------------------------------------------------- @@ -408,15 +424,17 @@ ChanRead( int bytesRead, result; /* - * If the caller asked for zero bytes, we'd force the inputProc - * to return zero bytes, and then misinterpret that as EOF. + * If the caller asked for zero bytes, we'd force the inputProc to return + * zero bytes, and then misinterpret that as EOF. */ + assert(dstSize > 0); /* * Each read op must set the blocked and eof states anew, not let * the effect of prior reads leak through. */ + if (GotFlag(chanPtr->state, CHANNEL_EOF)) { chanPtr->state->inputEncodingFlags |= TCL_ENCODING_START; } @@ -429,7 +447,10 @@ ChanRead( bytesRead = chanPtr->typePtr->inputProc(chanPtr->instanceData, dst, dstSize, &result); - /* Stop any flag leakage through stacked channel levels */ + /* + * Stop any flag leakage through stacked channel levels. + */ + if (GotFlag(chanPtr->state, CHANNEL_EOF)) { chanPtr->state->inputEncodingFlags |= TCL_ENCODING_START; } @@ -437,10 +458,10 @@ ChanRead( chanPtr->state->inputEncodingFlags &= ~TCL_ENCODING_END; if (bytesRead > 0) { /* - * If we get a short read, signal up that we may be BLOCKED. - * We should avoid calling the driver because on some - * platforms we will block in the low level reading code even - * though the channel is set into nonblocking mode. + * If we get a short read, signal up that we may be BLOCKED. We should + * avoid calling the driver because on some platforms we will block in + * the low level reading code even though the channel is set into + * nonblocking mode. */ if (bytesRead < dstSize) { @@ -477,13 +498,13 @@ ChanSeek( offset, mode, errnoPtr); } - if (offset<Tcl_LongAsWide(LONG_MIN) || offset>Tcl_LongAsWide(LONG_MAX)) { + if (offset<LONG_MIN || offset>LONG_MAX) { *errnoPtr = EOVERFLOW; - return Tcl_LongAsWide(-1); + return -1; } - return Tcl_LongAsWide(chanPtr->typePtr->seekProc(chanPtr->instanceData, - Tcl_WideAsLong(offset), mode, errnoPtr)); + return chanPtr->typePtr->seekProc(chanPtr->instanceData, + offset, mode, errnoPtr); } static inline void @@ -574,7 +595,10 @@ TclFinalizeIOSubsystem(void) int active = 1; /* Flag == 1 while there's still work to do */ int doflushnb; - /* Fetch the pre-TIP#398 compatibility flag */ + /* + * Fetch the pre-TIP#398 compatibility flag. + */ + { const char *s; Tcl_DString ds; @@ -619,18 +643,20 @@ TclFinalizeIOSubsystem(void) */ if (active) { - TclChannelPreserve((Tcl_Channel)chanPtr); + /* - * 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 + * 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". */ + if (doflushnb) { - /* Set the channel back into blocking mode to ensure that we wait - * for all data to flush out. - */ + /* + * 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"); @@ -711,17 +737,18 @@ Tcl_SetStdChannel( { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + int init = channel ? 1 : -1; switch (type) { case TCL_STDIN: - tsdPtr->stdinInitialized = 1; + tsdPtr->stdinInitialized = init; tsdPtr->stdinChannel = channel; break; case TCL_STDOUT: - tsdPtr->stdoutInitialized = 1; + tsdPtr->stdoutInitialized = init; tsdPtr->stdoutChannel = channel; break; case TCL_STDERR: - tsdPtr->stderrInitialized = 1; + tsdPtr->stderrInitialized = init; tsdPtr->stderrChannel = channel; break; } @@ -758,8 +785,8 @@ Tcl_GetStdChannel( switch (type) { case TCL_STDIN: if (!tsdPtr->stdinInitialized) { + tsdPtr->stdinInitialized = -1; tsdPtr->stdinChannel = TclpGetDefaultStdChannel(TCL_STDIN); - tsdPtr->stdinInitialized = 1; /* * Artificially bump the refcount to ensure that the channel is @@ -771,6 +798,7 @@ Tcl_GetStdChannel( */ if (tsdPtr->stdinChannel != NULL) { + tsdPtr->stdinInitialized = 1; Tcl_RegisterChannel(NULL, tsdPtr->stdinChannel); } } @@ -778,9 +806,10 @@ Tcl_GetStdChannel( break; case TCL_STDOUT: if (!tsdPtr->stdoutInitialized) { + tsdPtr->stdoutInitialized = -1; tsdPtr->stdoutChannel = TclpGetDefaultStdChannel(TCL_STDOUT); - tsdPtr->stdoutInitialized = 1; if (tsdPtr->stdoutChannel != NULL) { + tsdPtr->stdoutInitialized = 1; Tcl_RegisterChannel(NULL, tsdPtr->stdoutChannel); } } @@ -788,9 +817,10 @@ Tcl_GetStdChannel( break; case TCL_STDERR: if (!tsdPtr->stderrInitialized) { + tsdPtr->stderrInitialized = -1; tsdPtr->stderrChannel = TclpGetDefaultStdChannel(TCL_STDERR); - tsdPtr->stderrInitialized = 1; if (tsdPtr->stderrChannel != NULL) { + tsdPtr->stderrInitialized = 1; Tcl_RegisterChannel(NULL, tsdPtr->stderrChannel); } } @@ -1058,7 +1088,7 @@ CheckForStdChannelsBeingClosed( ChannelState *statePtr = ((Channel *) chan)->state; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - if (tsdPtr->stdinInitialized + if (tsdPtr->stdinInitialized == 1 && tsdPtr->stdinChannel != NULL && statePtr == ((Channel *)tsdPtr->stdinChannel)->state) { if (statePtr->refCount < 2) { @@ -1066,7 +1096,7 @@ CheckForStdChannelsBeingClosed( tsdPtr->stdinChannel = NULL; return; } - } else if (tsdPtr->stdoutInitialized + } else if (tsdPtr->stdoutInitialized == 1 && tsdPtr->stdoutChannel != NULL && statePtr == ((Channel *)tsdPtr->stdoutChannel)->state) { if (statePtr->refCount < 2) { @@ -1074,7 +1104,7 @@ CheckForStdChannelsBeingClosed( tsdPtr->stdoutChannel = NULL; return; } - } else if (tsdPtr->stderrInitialized + } else if (tsdPtr->stderrInitialized == 1 && tsdPtr->stderrChannel != NULL && statePtr == ((Channel *)tsdPtr->stderrChannel)->state) { if (statePtr->refCount < 2) { @@ -1501,18 +1531,20 @@ TclGetChannelFromObj( return TCL_ERROR; } - if (objPtr->typePtr == &chanObjType) { + ChanGetIntRep(objPtr, resPtr); + if (resPtr) { /* * Confirm validity of saved lookup results. */ - resPtr = (ResolvedChanName *) objPtr->internalRep.twoPtrValue.ptr1; statePtr = resPtr->statePtr; if ((resPtr->interp == interp) /* Same interp context */ /* No epoch change in channel since lookup */ && (resPtr->epoch == statePtr->epoch)) { + /* + * Have a valid saved lookup. Jump to end to return it. + */ - /* Have a valid saved lookup. Jump to end to return it. */ goto valid; } } @@ -1521,22 +1553,21 @@ TclGetChannelFromObj( if (chan == NULL) { if (resPtr) { - FreeChannelIntRep(objPtr); + Tcl_StoreIntRep(objPtr, &chanObjType, NULL); } return TCL_ERROR; } if (resPtr && resPtr->refCount == 1) { - /* Re-use the ResolvedCmdName struct */ - Tcl_Release((ClientData) resPtr->statePtr); + /* + * Re-use the ResolvedCmdName struct. + */ + Tcl_Release((ClientData) resPtr->statePtr); } else { - TclFreeIntRep(objPtr); - resPtr = (ResolvedChanName *) ckalloc(sizeof(ResolvedChanName)); - resPtr->refCount = 1; - objPtr->internalRep.twoPtrValue.ptr1 = (ClientData) resPtr; - objPtr->typePtr = &chanObjType; + resPtr->refCount = 0; + ChanSetIntRep(objPtr, resPtr); /* Overwrites, if needed */ } statePtr = ((Channel *)chan)->state; resPtr->statePtr = statePtr; @@ -1671,7 +1702,7 @@ Tcl_CreateChannel( * Set the channel up initially in AUTO input translation mode to accept * "\n", "\r" and "\r\n". Output translation mode is set to a platform * specific default value. The eofChar is set to 0 for both input and - * output, so that Tcl does not look for an in-file EOF indicator (e.g. + * output, so that Tcl does not look for an in-file EOF indicator (e.g., * ^Z) and does not append an EOF indicator to files. */ @@ -1899,7 +1930,6 @@ Tcl_StackChannel( */ if (((mask & TCL_READABLE) != 0) && (statePtr->inQueueHead != NULL)) { - /* * When statePtr->inQueueHead is not NULL, we know * prevChanPtr->inQueueHead must be NULL. @@ -2031,9 +2061,7 @@ Tcl_UnstackChannel( * of registered channels we wind down the state of the * transformation, and then restore the state of underlying channel * into the old structure. - */ - - /* + * * TODO: Figure out how to handle the situation where the chan * operations called below by this unstacking operation cause * another unstacking recursively. In that case the downChanPtr @@ -2511,6 +2539,7 @@ RecycleBuffer( /* * Do we have to free the buffer to the OS? */ + if (IsShared(bufPtr)) { mustDiscard = 1; } @@ -2521,9 +2550,8 @@ RecycleBuffer( } /* - * Only save buffers which have the requested buffersize for the - * channel. This is to honor dynamic changes of the buffersize - * made by the user. + * Only save buffers which have the requested buffersize for the channel. + * This is to honor dynamic changes of the buffersize made by the user. */ if ((bufPtr->bufLength - BUFFER_PADDING) != statePtr->bufSize) { @@ -2693,14 +2721,18 @@ FlushChannel( /* * Should we shift the current output buffer over to the output queue? * First check that there are bytes in it. If so then... - * If the output queue is empty, then yes, trusting the caller called - * us only when written bytes ought to be flushed. - * If the current output buffer is full, then yes, so we can meet - * the post-condition that on a successful return to caller we've - * left space in the current output buffer for more writing (the flush - * call was to make new room). - * If the channel is blocking, then yes, so we guarantee that - * blocking flushes actually flush all pending data. + * + * If the output queue is empty, then yes, trusting the caller called us + * only when written bytes ought to be flushed. + * + * If the current output buffer is full, then yes, so we can meet the + * post-condition that on a successful return to caller we've left space + * in the current output buffer for more writing (the flush call was to + * make new room). + * + * If the channel is blocking, then yes, so we guarantee that blocking + * flushes actually flush all pending data. + * * Otherwise, no. Keep the current output buffer where it is so more * can be written to it, possibly filling it, to promote more efficient * buffer usage. @@ -2794,8 +2826,8 @@ FlushChannel( /* * TIP #219, Tcl Channel Reflection API. * When defering the error copy a message from the bypass into - * the unreported area. Or discard it if the new error is to be - * ignored in favor of an earlier defered error. + * the unreported area. Or discard it if the new error is to + * be ignored in favor of an earlier defered error. */ Tcl_Obj *msg = statePtr->chanMsg; @@ -2847,8 +2879,11 @@ FlushChannel( ReleaseChannelBuffer(bufPtr); break; } else { - /* TODO: Consider detecting and reacting to short writes - * on blocking channels. Ought not happen. See iocmd-24.2. */ + /* + * TODO: Consider detecting and reacting to short writes on + * blocking channels. Ought not happen. See iocmd-24.2. + */ + wroteSome = 1; } @@ -2882,7 +2917,6 @@ FlushChannel( ResetFlag(statePtr, BG_FLUSH_SCHEDULED); ChanWatch(chanPtr, statePtr->interestMask); } else { - /* * When we are calledFromAsyncFlush, that means a writable * state on the channel triggered the call, so we should be @@ -2927,7 +2961,8 @@ FlushChannel( (statePtr->outQueueHead == NULL) && ((statePtr->curOutPtr == NULL) || IsBufferEmpty(statePtr->curOutPtr))) { - errorCode = CloseChannelPart(interp, chanPtr, errorCode, TCL_CLOSE_WRITE); + errorCode = CloseChannelPart(interp, chanPtr, errorCode, + TCL_CLOSE_WRITE); goto done; } @@ -3398,7 +3433,6 @@ Tcl_Close( if (GotFlag(statePtr, TCL_WRITABLE) && (statePtr->encoding != NULL) && !(statePtr->outputEncodingFlags & TCL_ENCODING_START)) { - int code = CheckChannelErrors(statePtr, TCL_WRITABLE); if (code == 0) { @@ -3488,12 +3522,14 @@ Tcl_Close( } return TCL_ERROR; } + /* * Bug 97069ea11a: set error message if a flush code is set and no error * message set up to now. */ + if (flushcode != 0 && interp != NULL - && 0 == Tcl_GetCharLength(Tcl_GetObjResult(interp)) ) { + && 0 == Tcl_GetCharLength(Tcl_GetObjResult(interp))) { Tcl_SetErrno(flushcode); Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_PosixError(interp), -1)); @@ -3588,8 +3624,8 @@ Tcl_CloseEx( } /* - * A user may try to call half-close from within a channel close - * handler. That won't do. + * A user may try to call half-close from within a channel close handler. + * That won't do. */ if (statePtr->flags & CHANNEL_INCLOSE) { @@ -3660,9 +3696,12 @@ CloseWrite( * 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) */ + /* + * Notes: clear-channel-handlers - write side only ? or keep around, just + * not called. + * + * No close callbacks are run - channel is still open (read side) + */ ChannelState *statePtr = chanPtr->state; /* State of real IO channel. */ @@ -3687,9 +3726,9 @@ CloseWrite( * 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. + * 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)) { @@ -3913,10 +3952,10 @@ Tcl_ClearChannelHandlers( StopCopy(statePtr->csPtrW); /* - * Must set the interest mask now to 0, otherwise infinite loops - * will occur if Tcl_DoOneEvent is called before the channel is - * finally deleted in FlushChannel. This can happen if the channel - * has a background flush active. + * Must set the interest mask now to 0, otherwise infinite loops will + * occur if Tcl_DoOneEvent is called before the channel is finally deleted + * in FlushChannel. This can happen if the channel has a background flush + * active. */ statePtr->interestMask = 0; @@ -4185,22 +4224,24 @@ WillRead( Channel *chanPtr) { if (chanPtr->typePtr == NULL) { - /* Prevent read attempts on a closed channel */ + /* + * Prevent read attempts on a closed channel. + */ + DiscardInputQueued(chanPtr->state, 0); Tcl_SetErrno(EINVAL); return -1; } if ((chanPtr->typePtr->seekProc != NULL) && (Tcl_OutputBuffered((Tcl_Channel) chanPtr) > 0)) { - /* - * CAVEAT - The assumption here is that FlushChannel() will - * push out the bytes of any writes that are in progress. - * Since this is a seekable channel, we assume it is not one - * that can block and force bg flushing. Channels we know that - * can do that -- sockets, pipes -- are not seekable. If the - * assumption is wrong, more drastic measures may be required here - * like temporarily setting the channel into blocking mode. + * CAVEAT - The assumption here is that FlushChannel() will push out + * the bytes of any writes that are in progress. Since this is a + * seekable channel, we assume it is not one that can block and force + * bg flushing. Channels we know that can do that - sockets, pipes - + * are not seekable. If the assumption is wrong, more drastic measures + * may be required here like temporarily setting the channel into + * blocking mode. */ if (FlushChannel(NULL, chanPtr, 0) != 0) { @@ -4292,11 +4333,17 @@ Write( &statePtr->outputEncodingState, dst, dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL); - /* See chan-io-1.[89]. Tcl Bug 506297. */ + /* + * See chan-io-1.[89]. Tcl Bug 506297. + */ + statePtr->outputEncodingFlags &= ~TCL_ENCODING_START; if ((result != TCL_OK) && (srcRead + dstWrote == 0)) { - /* We're reading from invalid/incomplete UTF-8 */ + /* + * We're reading from invalid/incomplete UTF-8. + */ + ReleaseChannelBuffer(bufPtr); if (total == 0) { Tcl_SetErrno(EINVAL); @@ -4336,11 +4383,10 @@ Write( } result |= Tcl_UtfToExternal(NULL, encoding, nl, nlLen, - statePtr->outputEncodingFlags, - &statePtr->outputEncodingState, dst, - dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL); - - assert (srcRead == nlLen); + statePtr->outputEncodingFlags, + &statePtr->outputEncodingState, dst, + dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL); + assert(srcRead == nlLen); bufPtr->nextAdded += dstWrote; src++; @@ -4354,11 +4400,11 @@ Write( if (IsBufferOverflowing(bufPtr)) { /* - * When translating from UTF-8 to external encoding, we - * allowed the translation to produce a character that crossed - * the end of the output buffer, so that we would get a - * completely full buffer before flushing it. The extra bytes - * will be moved to the beginning of the next buffer. + * When translating from UTF-8 to external encoding, we allowed + * the translation to produce a character that crossed the end of + * the output buffer, so that we would get a completely full + * buffer before flushing it. The extra bytes will be moved to the + * beginning of the next buffer. */ saved = -SpaceLeft(bufPtr); @@ -4378,15 +4424,16 @@ Write( flushed += statePtr->bufSize; /* - * We just flushed. So if we have needNlFlush set to record - * that we need to flush because theres a (translated) newline - * in the buffer, that's likely not true any more. But there - * is a tricky exception. If we have saved bytes that did not - * really get flushed and those bytes came from a translation - * of a newline as the last thing taken from the src array, - * then needNlFlush needs to remain set to flag that the - * next buffer still needs a newline flush. + * We just flushed. So if we have needNlFlush set to record that + * we need to flush because theres a (translated) newline in the + * buffer, that's likely not true any more. But there is a tricky + * exception. If we have saved bytes that did not really get + * flushed and those bytes came from a translation of a newline as + * the last thing taken from the src array, then needNlFlush needs + * to remain set to flag that the next buffer still needs a + * newline flush. */ + if (needNlFlush && (saved == 0 || src[-1] != '\n')) { needNlFlush = 0; } @@ -4492,8 +4539,8 @@ Tcl_GetsObj( if (GotFlag(statePtr, CHANNEL_STICKY_EOF)) { SetFlag(statePtr, CHANNEL_EOF); - assert( statePtr->inputEncodingFlags & TCL_ENCODING_END ); - assert( !GotFlag(statePtr, CHANNEL_BLOCKED|INPUT_SAW_CR) ); + assert(statePtr->inputEncodingFlags & TCL_ENCODING_END); + assert(!GotFlag(statePtr, CHANNEL_BLOCKED|INPUT_SAW_CR)); /* TODO: Do we need this? */ UpdateInterest(chanPtr); @@ -4833,17 +4880,17 @@ Tcl_GetsObj( */ done: - assert(!GotFlag(statePtr, CHANNEL_EOF) - || GotFlag(statePtr, CHANNEL_STICKY_EOF) - || Tcl_InputBuffered((Tcl_Channel)chanPtr) == 0); - - assert( !(GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED) - == (CHANNEL_EOF|CHANNEL_BLOCKED)) ); + assert(!GotFlag(statePtr, CHANNEL_EOF) + || GotFlag(statePtr, CHANNEL_STICKY_EOF) + || Tcl_InputBuffered((Tcl_Channel)chanPtr) == 0); + assert(!(GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED) + == (CHANNEL_EOF|CHANNEL_BLOCKED))); /* * Regenerate the top channel, in case it was changed due to * self-modifying reflected transforms. */ + if (chanPtr != statePtr->topChanPtr) { TclChannelRelease((Tcl_Channel)chanPtr); chanPtr = statePtr->topChanPtr; @@ -4863,10 +4910,9 @@ Tcl_GetsObj( * end-of-line or end-of-file has been seen. Bytes read from the input * channel return as a ByteArray obj. * - * WARNING! The notion of "binary" used here is different from - * notions of "binary" used in other places. In particular, this - * "binary" routine may be called when an -eofchar is set on the - * channel. + * WARNING! The notion of "binary" used here is different from notions + * of "binary" used in other places. In particular, this "binary" routine + * may be called when an -eofchar is set on the channel. * * Results: * Number of characters accumulated in the object or -1 if error, @@ -4932,8 +4978,8 @@ TclGetsObjBinary( ResetFlag(statePtr, CHANNEL_BLOCKED); while (1) { /* - * Subtract the number of bytes that were removed from channel - * buffer during last call. + * Subtract the number of bytes that were removed from channel buffer + * during last call. */ if (bufPtr != NULL) { @@ -4945,10 +4991,11 @@ TclGetsObjBinary( if ((bufPtr == NULL) || (bufPtr->nextAdded == BUFFER_PADDING)) { /* - * All channel buffers were exhausted and the caller still - * hasn't seen EOL. Need to read more bytes from the channel - * device. Side effect is to allocate another channel buffer. + * All channel buffers were exhausted and the caller still hasn't + * seen EOL. Need to read more bytes from the channel device. Side + * effect is to allocate another channel buffer. */ + if (GetInput(chanPtr) != 0) { goto restore; } @@ -4958,15 +5005,15 @@ TclGetsObjBinary( } } else { /* - * Incoming CHANNEL_STICKY_EOF is filtered out on entry. - * A new CHANNEL_STICKY_EOF set in this routine leads to - * return before coming back here. When we are not dealing - * with CHANNEL_STICKY_EOF, a CHANNEL_EOF implies an - * empty buffer. Here the buffer is non-empty so we know - * we're a non-EOF */ + * Incoming CHANNEL_STICKY_EOF is filtered out on entry. A new + * CHANNEL_STICKY_EOF set in this routine leads to return before + * coming back here. When we are not dealing with + * CHANNEL_STICKY_EOF, a CHANNEL_EOF implies an empty buffer. + * Here the buffer is non-empty so we know we're a non-EOF. + */ - assert ( !GotFlag(statePtr, CHANNEL_STICKY_EOF) ); - assert ( !GotFlag(statePtr, CHANNEL_EOF) ); + assert(!GotFlag(statePtr, CHANNEL_STICKY_EOF)); + assert(!GotFlag(statePtr, CHANNEL_EOF)); } dst = (unsigned char *) RemovePoint(bufPtr); @@ -5033,8 +5080,8 @@ TclGetsObjBinary( } /* - * Copy bytes from the channel buffer to the ByteArray. - * This may realloc space, so keep track of result. + * Copy bytes from the channel buffer to the ByteArray. This may + * realloc space, so keep track of result. */ rawLen = dstEnd - dst; @@ -5118,11 +5165,11 @@ TclGetsObjBinary( */ done: - assert(!GotFlag(statePtr, CHANNEL_EOF) - || GotFlag(statePtr, CHANNEL_STICKY_EOF) - || Tcl_InputBuffered((Tcl_Channel)chanPtr) == 0); - assert( !(GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED) - == (CHANNEL_EOF|CHANNEL_BLOCKED)) ); + assert(!GotFlag(statePtr, CHANNEL_EOF) + || GotFlag(statePtr, CHANNEL_STICKY_EOF) + || Tcl_InputBuffered((Tcl_Channel)chanPtr) == 0); + assert(!(GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED) + == (CHANNEL_EOF|CHANNEL_BLOCKED))); UpdateInterest(chanPtr); TclChannelRelease((Tcl_Channel)chanPtr); return copiedTotal; @@ -5255,15 +5302,15 @@ FilterInputBytes( } } else { /* - * Incoming CHANNEL_STICKY_EOF is filtered out on entry. - * A new CHANNEL_STICKY_EOF set in this routine leads to - * return before coming back here. When we are not dealing - * with CHANNEL_STICKY_EOF, a CHANNEL_EOF implies an - * empty buffer. Here the buffer is non-empty so we know - * we're a non-EOF */ + * Incoming CHANNEL_STICKY_EOF is filtered out on entry. A new + * CHANNEL_STICKY_EOF set in this routine leads to return before + * coming back here. When we are not dealing with CHANNEL_STICKY_EOF, + * a CHANNEL_EOF implies an empty buffer. Here the buffer is + * non-empty so we know we're a non-EOF. + */ - assert ( !GotFlag(statePtr, CHANNEL_STICKY_EOF) ); - assert ( !GotFlag(statePtr, CHANNEL_EOF) ); + assert(!GotFlag(statePtr, CHANNEL_STICKY_EOF)); + assert(!GotFlag(statePtr, CHANNEL_EOF)); } /* @@ -5593,7 +5640,9 @@ Tcl_ReadRaw( return -1; } - /* First read bytes from the push-back buffers. */ + /* + * First read bytes from the push-back buffers. + */ while (chanPtr->inQueueHead && bytesToRead > 0) { ChannelBuffer *bufPtr = chanPtr->inQueueHead; @@ -5601,7 +5650,9 @@ Tcl_ReadRaw( int toCopy = (bytesInBuffer < bytesToRead) ? bytesInBuffer : bytesToRead; - /* Copy the current chunk into the read buffer. */ + /* + * Copy the current chunk into the read buffer. + */ memcpy(readBuf, RemovePoint(bufPtr), (size_t) toCopy); bufPtr->nextRemoved += toCopy; @@ -5609,7 +5660,9 @@ Tcl_ReadRaw( readBuf += toCopy; bytesToRead -= toCopy; - /* If the current buffer is empty recycle it. */ + /* + * If the current buffer is empty recycle it. + */ if (IsBufferEmpty(bufPtr)) { chanPtr->inQueueHead = bufPtr->nextPtr; @@ -5621,37 +5674,40 @@ Tcl_ReadRaw( } /* - * Go to the driver only if we got nothing from pushback. - * Have to do it this way to avoid EOF mis-timings when we - * consider the ability that EOF may not be a permanent - * condition in the driver, and in that case we have to - * synchronize. + * Go to the driver only if we got nothing from pushback. Have to do it + * this way to avoid EOF mis-timings when we consider the ability that EOF + * may not be a permanent condition in the driver, and in that case we + * have to synchronize. */ if (copied) { return copied; } - /* This test not needed. */ - if (bytesToRead > 0) { + /* + * This test not needed. + */ + if (bytesToRead > 0) { int nread = ChanRead(chanPtr, readBuf, bytesToRead); if (nread > 0) { - /* Successful read (short is OK) - add to bytes copied */ + /* + * Successful read (short is OK) - add to bytes copied. + */ + copied += nread; } else if (nread < 0) { /* - * An error signaled. If CHANNEL_BLOCKED, then the error - * is not real, but an indication of blocked state. In - * that case, retain the flag and let caller receive the - * short read of copied bytes from the pushback. - * HOWEVER, if copied==0 bytes from pushback then repeat - * signalling the blocked state as an error to caller so - * there is no false report of an EOF. - * When !CHANNEL_BLOCKED, the error is real and passes on - * to caller. + * An error signaled. If CHANNEL_BLOCKED, then the error is not + * real, but an indication of blocked state. In that case, retain + * the flag and let caller receive the short read of copied bytes + * from the pushback. HOWEVER, if copied==0 bytes from pushback + * then repeat signalling the blocked state as an error to caller + * so there is no false report of an EOF. When !CHANNEL_BLOCKED, + * the error is real and passes on to caller. */ + if (!GotFlag(statePtr, CHANNEL_BLOCKED) || copied == 0) { copied = -1; } @@ -5788,21 +5844,23 @@ DoReadChars( /* * Early out when next read will see eofchar. * - * NOTE: See DoRead for argument that it's a bug (one we're keeping) - * to have this escape before the one for zero-char read request. + * NOTE: See DoRead for argument that it's a bug (one we're keeping) to + * have this escape before the one for zero-char read request. */ if (GotFlag(statePtr, CHANNEL_STICKY_EOF)) { SetFlag(statePtr, CHANNEL_EOF); - assert( statePtr->inputEncodingFlags & TCL_ENCODING_END ); - assert( !GotFlag(statePtr, CHANNEL_BLOCKED|INPUT_SAW_CR) ); + assert(statePtr->inputEncodingFlags & TCL_ENCODING_END); + assert(!GotFlag(statePtr, CHANNEL_BLOCKED|INPUT_SAW_CR)); /* TODO: We don't need this call? */ UpdateInterest(chanPtr); return 0; } - /* Special handling for zero-char read request. */ + /* + * Special handling for zero-char read request. + */ if (toRead == 0) { if (GotFlag(statePtr, CHANNEL_EOF)) { statePtr->inputEncodingFlags |= TCL_ENCODING_START; @@ -5821,7 +5879,10 @@ DoReadChars( chanPtr = statePtr->topChanPtr; TclChannelPreserve((Tcl_Channel)chanPtr); - /* Must clear the BLOCKED|EOF flags here since we check before reading */ + /* + * Must clear the BLOCKED|EOF flags here since we check before reading. + */ + if (GotFlag(statePtr, CHANNEL_EOF)) { statePtr->inputEncodingFlags |= TCL_ENCODING_START; } @@ -5879,10 +5940,11 @@ DoReadChars( } /* - * Failure to fill a channel buffer may have left channel reporting - * a "blocked" state, but so long as we fulfilled the request here, - * the caller does not consider us blocked. + * Failure to fill a channel buffer may have left channel reporting a + * "blocked" state, but so long as we fulfilled the request here, the + * caller does not consider us blocked. */ + if (toRead == 0) { ResetFlag(statePtr, CHANNEL_BLOCKED); } @@ -5891,6 +5953,7 @@ DoReadChars( * Regenerate the top channel, in case it was changed due to * self-modifying reflected transforms. */ + if (chanPtr != statePtr->topChanPtr) { TclChannelRelease((Tcl_Channel)chanPtr); chanPtr = statePtr->topChanPtr; @@ -5901,11 +5964,12 @@ DoReadChars( * Update the notifier state so we don't block while there is still data * in the buffers. */ - assert(!GotFlag(statePtr, CHANNEL_EOF) - || GotFlag(statePtr, CHANNEL_STICKY_EOF) - || Tcl_InputBuffered((Tcl_Channel)chanPtr) == 0); - assert( !(GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED) - == (CHANNEL_EOF|CHANNEL_BLOCKED)) ); + + assert(!GotFlag(statePtr, CHANNEL_EOF) + || GotFlag(statePtr, CHANNEL_STICKY_EOF) + || Tcl_InputBuffered((Tcl_Channel)chanPtr) == 0); + assert(!(GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED) + == (CHANNEL_EOF|CHANNEL_BLOCKED))); UpdateInterest(chanPtr); TclChannelRelease((Tcl_Channel)chanPtr); return copied; @@ -6022,11 +6086,10 @@ ReadChars( int numBytes, srcLen = BytesLeft(bufPtr); /* - * One src byte can yield at most one character. So when the - * number of src bytes we plan to read is less than the limit on - * character count to be read, clearly we will remain within that - * limit, and we can use the value of "srcLen" as a tighter limit - * for sizing receiving buffers. + * One src byte can yield at most one character. So when the number of + * src bytes we plan to read is less than the limit on character count to + * be read, clearly we will remain within that limit, and we can use the + * value of "srcLen" as a tighter limit for sizing receiving buffers. */ int toRead = ((charsToRead<0)||(charsToRead > srcLen)) ? srcLen : charsToRead; @@ -6044,6 +6107,7 @@ ReadChars( Tcl_AppendToObj(objPtr, NULL, dstLimit); if (toRead == srcLen) { unsigned int size; + dst = TclGetStringStorage(objPtr, &size) + numBytes; dstLimit = size - numBytes; } else { @@ -6051,19 +6115,18 @@ ReadChars( } /* - * This routine is burdened with satisfying several constraints. - * It cannot append more than 'charsToRead` chars onto objPtr. - * This is measured after encoding and translation transformations - * are completed. There is no precise number of src bytes that can - * be associated with the limit. Yet, when we are done, we must know - * precisely the number of src bytes that were consumed to produce - * the appended chars, so that all subsequent bytes are left in - * the buffers for future read operations. + * This routine is burdened with satisfying several constraints. It cannot + * append more than 'charsToRead` chars onto objPtr. This is measured + * after encoding and translation transformations are completed. There is + * no precise number of src bytes that can be associated with the limit. + * Yet, when we are done, we must know precisely the number of src bytes + * that were consumed to produce the appended chars, so that all + * subsequent bytes are left in the buffers for future read operations. * - * The consequence is that we have no choice but to implement a - * "trial and error" approach, where in general we may need to - * perform transformations and copies multiple times to achieve - * a consistent set of results. This takes the shape of a loop. + * The consequence is that we have no choice but to implement a "trial and + * error" approach, where in general we may need to perform + * transformations and copies multiple times to achieve a consistent set + * of results. This takes the shape of a loop. */ while (1) { @@ -6076,18 +6139,17 @@ ReadChars( } /* - * Perform the encoding transformation. Read no more than - * srcLen bytes, write no more than dstLimit bytes. + * Perform the encoding transformation. Read no more than srcLen + * bytes, write no more than dstLimit bytes. * - * Some trickiness with encoding flags here. We do not want - * the end of a buffer to be treated as the end of all input - * when the presence of bytes in a next buffer are already - * known to exist. This is checked with an assert() because - * so far no test case causing the assertion to be false has - * been created. The normal operations of channel reading - * appear to cause EOF and TCL_ENCODING_END setting to appear - * only in situations where there are no further bytes in - * any buffers. + * Some trickiness with encoding flags here. We do not want the end + * of a buffer to be treated as the end of all input when the presence + * of bytes in a next buffer are already known to exist. This is + * checked with an assert() because so far no test case causing the + * assertion to be false has been created. The normal operations of + * channel reading appear to cause EOF and TCL_ENCODING_END setting to + * appear only in situations where there are no further bytes in any + * buffers. */ assert(bufPtr->nextPtr == NULL || BytesLeft(bufPtr->nextPtr) == 0 @@ -6098,10 +6160,10 @@ ReadChars( dst, dstLimit, &srcRead, &dstDecoded, &numChars); /* - * Perform the translation transformation in place. Read no more - * than the dstDecoded bytes the encoding transformation actually - * produced. Capture the number of bytes written in dstWrote. - * Capture the number of bytes actually consumed in dstRead. + * Perform the translation transformation in place. Read no more than + * the dstDecoded bytes the encoding transformation actually produced. + * Capture the number of bytes written in dstWrote. Capture the number + * of bytes actually consumed in dstRead. */ dstWrote = dstLimit; @@ -6109,11 +6171,9 @@ ReadChars( TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead); if (dstRead < dstDecoded) { - /* - * The encoding transformation produced bytes that the - * translation transformation did not consume. Why did - * this happen? + * The encoding transformation produced bytes that the translation + * transformation did not consume. Why did this happen? */ if (statePtr->inEofChar && dst[dstRead] == statePtr->inEofChar) { @@ -6122,40 +6182,38 @@ ReadChars( * we saw it and stopped translating at that point. * * NOTE the bizarre spec of TranslateInputEOL in this case. - * Clearly the eof char had to be read in order to account - * for the stopping, but the value of dstRead does not - * include it. + * Clearly the eof char had to be read in order to account for + * the stopping, but the value of dstRead does not include it. * - * Also rather bizarre, our caller can only notice an - * EOF condition if we return the value -1 as the number - * of chars read. This forces us to perform a 2-call - * dance where the first call can read all the chars - * up to the eof char, and the second call is solely - * for consuming the encoded eof char then pointed at - * by src so that we can return that magic -1 value. - * This seems really wasteful, especially since - * the first decoding pass of each call is likely to - * decode many bytes beyond that eof char that's all we - * care about. + * Also rather bizarre, our caller can only notice an EOF + * condition if we return the value -1 as the number of chars + * read. This forces us to perform a 2-call dance where the + * first call can read all the chars up to the eof char, and + * the second call is solely for consuming the encoded eof + * char then pointed at by src so that we can return that + * magic -1 value. This seems really wasteful, especially + * since the first decoding pass of each call is likely to + * decode many bytes beyond that eof char that's all we care + * about. */ if (dstRead == 0) { /* - * Curious choice in the eof char handling. We leave - * the eof char in the buffer. So, no need to compute - * a proper srcRead value. At this point, there - * are no chars before the eof char in the buffer. + * Curious choice in the eof char handling. We leave the + * eof char in the buffer. So, no need to compute a proper + * srcRead value. At this point, there are no chars before + * the eof char in the buffer. */ + Tcl_SetObjLength(objPtr, numBytes); return -1; } { /* - * There are chars leading the buffer before the eof - * char. Adjust the dstLimit so we go back and read - * only those and do not encounter the eof char this - * time. + * There are chars leading the buffer before the eof char. + * Adjust the dstLimit so we go back and read only those + * and do not encounter the eof char this time. */ dstLimit = dstRead - 1 + TCL_UTF_MAX; @@ -6167,10 +6225,9 @@ ReadChars( } /* - * 2) The other way to read fewer bytes than are decoded - * is when the final byte is \r and we're in a CRLF - * translation mode so we cannot decide whether to - * record \r or \n yet. + * 2) The other way to read fewer bytes than are decoded is when + * the final byte is \r and we're in a CRLF translation mode so + * we cannot decide whether to record \r or \n yet. */ assert(dst[dstRead] == '\r'); @@ -6178,10 +6235,10 @@ ReadChars( if (dstWrote > 0) { /* - * There are chars we can read before we hit the bare cr. - * Go back with a smaller dstLimit so we get them in the - * next pass, compute a matching srcRead, and don't end - * up back here in this call. + * There are chars we can read before we hit the bare CR. Go + * back with a smaller dstLimit so we get them in the next + * pass, compute a matching srcRead, and don't end up back + * here in this call. */ dstLimit = dstRead - 1 + TCL_UTF_MAX; @@ -6195,9 +6252,9 @@ ReadChars( assert(dstRead == 0); /* - * We decoded only the bare cr, and we cannot read a - * translated char from that alone. We have to know what's - * next. So why do we only have the one decoded char? + * We decoded only the bare CR, and we cannot read a translated + * char from that alone. We have to know what's next. So why do + * we only have the one decoded char? */ if (code != TCL_OK) { @@ -6238,10 +6295,9 @@ ReadChars( } } else if (statePtr->flags & CHANNEL_EOF) { - /* - * The bare \r is the only char and we will never read - * a subsequent char to make the determination. + * The bare \r is the only char and we will never read a + * subsequent char to make the determination. */ dst[0] = '\r'; @@ -6251,8 +6307,8 @@ ReadChars( } /* - * Revise the dstRead value so that the numChars calc - * below correctly computes zero characters read. + * Revise the dstRead value so that the numChars calc below + * correctly computes zero characters read. */ dstRead = numChars; @@ -6261,9 +6317,9 @@ ReadChars( } /* - * The translation transformation can only reduce the number - * of chars when it converts \r\n into \n. The reduction in - * the number of chars is the difference in bytes read and written. + * The translation transformation can only reduce the number of chars + * when it converts \r\n into \n. The reduction in the number of chars + * is the difference in bytes read and written. */ numChars -= (dstRead - dstWrote); @@ -6273,10 +6329,9 @@ ReadChars( /* * TODO: This cannot happen anymore. * - * We read more chars than allowed. Reset limits to - * prevent that and try again. Don't forget the extra - * padding of TCL_UTF_MAX bytes demanded by the - * Tcl_ExternalToUtf() call! + * We read more chars than allowed. Reset limits to prevent that + * and try again. Don't forget the extra padding of TCL_UTF_MAX + * bytes demanded by the Tcl_ExternalToUtf() call! */ dstLimit = Tcl_UtfAtIndex(dst, charsToRead) - 1 + TCL_UTF_MAX - dst; @@ -6289,18 +6344,19 @@ ReadChars( if (dstWrote == 0) { ChannelBuffer *nextPtr; - /* We were not able to read any chars. */ + /* + * We were not able to read any chars. + */ - assert (numChars == 0); + assert(numChars == 0); /* - * There is one situation where this is the correct final - * result. If the src buffer contains only a single \n - * byte, and we are in TCL_TRANSLATE_AUTO mode, and - * when the translation pass was made the INPUT_SAW_CR - * flag was set on the channel. In that case, the - * correct behavior is to consume that \n and produce the - * empty string. + * There is one situation where this is the correct final result. + * If the src buffer contains only a single \n byte, and we are in + * TCL_TRANSLATE_AUTO mode, and when the translation pass was made + * the INPUT_SAW_CR flag was set on the channel. In that case, the + * correct behavior is to consume that \n and produce the empty + * string. */ if (dstRead == 1 && dst[0] == '\n') { @@ -6309,12 +6365,13 @@ ReadChars( goto consume; } - /* Otherwise, reading zero characters indicates there's - * something incomplete at the end of the src buffer. - * Maybe there were not enough src bytes to decode into - * a char. Maybe a lone \r could not be translated (crlf - * mode). Need to combine any unused src bytes we have - * in the first buffer with subsequent bytes to try again. + /* + * Otherwise, reading zero characters indicates there's something + * incomplete at the end of the src buffer. Maybe there were not + * enough src bytes to decode into a char. Maybe a lone \r could + * not be translated (crlf mode). Need to combine any unused src + * bytes we have in the first buffer with subsequent bytes to try + * again. */ nextPtr = bufPtr->nextPtr; @@ -6329,15 +6386,15 @@ ReadChars( /* * Space is made at the beginning of the buffer to copy the - * previous unused bytes there. Check first if the buffer we - * are using actually has enough space at its beginning for - * the data we are copying. Because if not we will write over - * the buffer management information, especially the 'nextPtr'. + * previous unused bytes there. Check first if the buffer we are + * using actually has enough space at its beginning for the data + * we are copying. Because if not we will write over the buffer + * management information, especially the 'nextPtr'. * - * Note that the BUFFER_PADDING (See AllocChannelBuffer) is - * used to prevent exactly this situation. I.e. it should never - * happen. Therefore it is ok to panic should it happen despite - * the precautions. + * Note that the BUFFER_PADDING (See AllocChannelBuffer) is used + * to prevent exactly this situation. I.e. it should never happen. + * Therefore it is ok to panic should it happen despite the + * precautions. */ if (nextPtr->nextRemoved - srcLen < 0) { @@ -6356,10 +6413,12 @@ ReadChars( consume: bufPtr->nextRemoved += srcRead; + /* - * If this read contained multibyte characters, revise factorPtr - * so the next read will allocate bigger buffers. + * If this read contained multibyte characters, revise factorPtr so + * the next read will allocate bigger buffers. */ + if (numChars && numChars < srcRead) { *factorPtr = srcRead * UTF_EXPANSION_FACTOR / numChars; } @@ -6407,22 +6466,27 @@ TranslateInputEOL( int inEofChar = statePtr->inEofChar; /* - * Depending on the translation mode in use, there's no need - * to scan more srcLen bytes at srcStart than can possibly transform - * to dstLen bytes. This keeps the scan for eof char below from - * being pointlessly long. + * Depending on the translation mode in use, there's no need to scan more + * srcLen bytes at srcStart than can possibly transform to dstLen bytes. + * This keeps the scan for eof char below from being pointlessly long. */ switch (statePtr->inputTranslation) { case TCL_TRANSLATE_LF: case TCL_TRANSLATE_CR: if (srcLen > dstLen) { - /* In these modes, each src byte become a dst byte. */ + /* + * In these modes, each src byte become a dst byte. + */ + srcLen = dstLen; } break; default: - /* In other modes, at most 2 src bytes become a dst byte. */ + /* + * In other modes, at most 2 src bytes become a dst byte. + */ + if (srcLen/2 > dstLen) { srcLen = 2 * dstLen; } @@ -6755,7 +6819,7 @@ GetInput( * eofchar. */ - assert( !GotFlag(statePtr, CHANNEL_STICKY_EOF) ); + assert(!GotFlag(statePtr, CHANNEL_STICKY_EOF)); /* * Prevent reading from a dead channel -- a channel that has been closed @@ -6769,24 +6833,21 @@ GetInput( } /* - * WARNING: There was once a comment here claiming that it was - * a bad idea to make another call to the inputproc of a channel - * driver when EOF has already been detected on the channel. Through - * much of Tcl's history, this warning was then completely negated - * by having all (most?) read paths clear the EOF setting before - * reaching here. So we had a guard that was never triggered. + * WARNING: There was once a comment here claiming that it was a bad idea + * to make another call to the inputproc of a channel driver when EOF has + * already been detected on the channel. Through much of Tcl's history, + * this warning was then completely negated by having all (most?) read + * paths clear the EOF setting before reaching here. So we had a guard + * that was never triggered. + * + * Don't be tempted to restore the guard. Even if EOF is set on the + * channel, continue through and call the inputproc again. This is the + * way to enable the ability to [read] again beyond the EOF, which seems a + * strange thing to do, but for which use cases exist [Tcl Bug 5adc350683] + * and which may even be essential for channels representing things like + * ttys or other devices where the stream might take the logical form of a + * series of 'files' separated by an EOF condition. * - * Don't be tempted to restore the guard. Even if EOF is set on - * the channel, continue through and call the inputproc again. This - * is the way to enable the ability to [read] again beyond the EOF, - * which seems a strange thing to do, but for which use cases exist - * [Tcl Bug 5adc350683] and which may even be essential for channels - * representing things like ttys or other devices where the stream - * might take the logical form of a series of 'files' separated by - * an EOF condition. - */ - - /* * First check for more buffers in the pushback area of the topmost * channel in the stack and use them. They can be the result of a * transformation which went away without reading all the information @@ -6794,7 +6855,6 @@ GetInput( */ if (chanPtr->inQueueHead != NULL) { - /* TODO: Tests to cover this. */ assert(statePtr->inQueueHead == NULL); @@ -6824,8 +6884,9 @@ GetInput( /* * Check the actual buffersize against the requested buffersize. - * Saved buffers of the wrong size are squashed. This is done - * to honor dynamic changes of the buffersize made by the user. + * Saved buffers of the wrong size are squashed. This is done to honor + * dynamic changes of the buffersize made by the user. + * * TODO: Tests to cover this. */ @@ -6904,7 +6965,7 @@ Tcl_Seek( * non-blocking mode after the seek. */ if (CheckChannelErrors(statePtr, TCL_WRITABLE | TCL_READABLE) != 0) { - return Tcl_LongAsWide(-1); + return -1; } /* @@ -6915,7 +6976,7 @@ Tcl_Seek( */ if (CheckForDeadChannel(NULL, statePtr)) { - return Tcl_LongAsWide(-1); + return -1; } /* @@ -6931,7 +6992,7 @@ Tcl_Seek( if (chanPtr->typePtr->seekProc == NULL) { Tcl_SetErrno(EINVAL); - return Tcl_LongAsWide(-1); + return -1; } /* @@ -6944,7 +7005,7 @@ Tcl_Seek( if ((inputBuffered != 0) && (outputBuffered != 0)) { Tcl_SetErrno(EFAULT); - return Tcl_LongAsWide(-1); + return -1; } /* @@ -6987,7 +7048,7 @@ Tcl_Seek( wasAsync = 1; result = StackSetBlockMode(chanPtr, TCL_MODE_BLOCKING); if (result != 0) { - return Tcl_LongAsWide(-1); + return -1; } ResetFlag(statePtr, CHANNEL_NONBLOCKING); if (GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { @@ -7012,7 +7073,7 @@ Tcl_Seek( */ curPos = ChanSeek(chanPtr, offset, mode, &result); - if (curPos == Tcl_LongAsWide(-1)) { + if (curPos == -1) { Tcl_SetErrno(result); } } @@ -7028,7 +7089,7 @@ Tcl_Seek( SetFlag(statePtr, CHANNEL_NONBLOCKING); result = StackSetBlockMode(chanPtr, TCL_MODE_NONBLOCKING); if (result != 0) { - return Tcl_LongAsWide(-1); + return -1; } } @@ -7068,7 +7129,7 @@ Tcl_Tell( Tcl_WideInt curPos; /* Position on device. */ if (CheckChannelErrors(statePtr, TCL_WRITABLE | TCL_READABLE) != 0) { - return Tcl_LongAsWide(-1); + return -1; } /* @@ -7079,7 +7140,7 @@ Tcl_Tell( */ if (CheckForDeadChannel(NULL, statePtr)) { - return Tcl_LongAsWide(-1); + return -1; } /* @@ -7095,7 +7156,7 @@ Tcl_Tell( if (chanPtr->typePtr->seekProc == NULL) { Tcl_SetErrno(EINVAL); - return Tcl_LongAsWide(-1); + return -1; } /* @@ -7112,10 +7173,10 @@ Tcl_Tell( * wideSeekProc if that is available and non-NULL... */ - curPos = ChanSeek(chanPtr, Tcl_LongAsWide(0), SEEK_CUR, &result); - if (curPos == Tcl_LongAsWide(-1)) { + curPos = ChanSeek(chanPtr, 0, SEEK_CUR, &result); + if (curPos == -1) { Tcl_SetErrno(result); - return Tcl_LongAsWide(-1); + return -1; } if (inputBuffered != 0) { @@ -7127,53 +7188,12 @@ Tcl_Tell( /* *--------------------------------------------------------------------------- * - * Tcl_SeekOld, Tcl_TellOld -- - * - * Backward-compatibility versions of the seek/tell interface that do not - * support 64-bit offsets. This interface is not documented or expected - * to be supported indefinitely. - * - * Results: - * As for Tcl_Seek and Tcl_Tell respectively, except truncated to - * whatever value will fit in an 'int'. - * - * Side effects: - * As for Tcl_Seek and Tcl_Tell respectively. - * - *--------------------------------------------------------------------------- - */ - -int -Tcl_SeekOld( - Tcl_Channel chan, /* The channel on which to seek. */ - int offset, /* Offset to seek to. */ - int mode) /* Relative to which location to seek? */ -{ - Tcl_WideInt wOffset, wResult; - - wOffset = Tcl_LongAsWide((long) offset); - wResult = Tcl_Seek(chan, wOffset, mode); - return (int) Tcl_WideAsLong(wResult); -} - -int -Tcl_TellOld( - Tcl_Channel chan) /* The channel to return pos for. */ -{ - Tcl_WideInt wResult = Tcl_Tell(chan); - - return (int) Tcl_WideAsLong(wResult); -} - -/* - *--------------------------------------------------------------------------- - * * Tcl_TruncateChannel -- * * Truncate a channel to the given length. * * Results: - * TCL_OK on success, TCL_ERROR if the operation failed (e.g. is not + * TCL_OK on success, TCL_ERROR if the operation failed (e.g., is not * supported by the type of channel, or the underlying OS operation * failed in some way). * @@ -9028,6 +9048,7 @@ ZeroTransferTimerProc( *---------------------------------------------------------------------- */ +#if !defined(TCL_NO_DEPRECATED) int TclCopyChannelOld( Tcl_Interp *interp, /* Current interpreter. */ @@ -9039,6 +9060,7 @@ TclCopyChannelOld( return TclCopyChannel(interp, inChan, outChan, (Tcl_WideInt) toRead, cmdPtr); } +#endif int TclCopyChannel( @@ -9533,7 +9555,10 @@ CopyData( } if (size == 0) { if (!GotFlag(inStatePtr, CHANNEL_NONBLOCKING)) { - /* We allowed a short read. Keep trying. */ + /* + * We allowed a short read. Keep trying. + */ + continue; } if (bufObj != NULL) { @@ -9747,7 +9772,7 @@ DoRead( ChannelState *statePtr = chanPtr->state; char *p = dst; - assert (bytesToRead >= 0); + assert(bytesToRead >= 0); /* * Early out when we know a read will get the eofchar. @@ -9763,15 +9788,18 @@ DoRead( if (GotFlag(statePtr, CHANNEL_STICKY_EOF)) { SetFlag(statePtr, CHANNEL_EOF); - assert( statePtr->inputEncodingFlags & TCL_ENCODING_END ); - assert( !GotFlag(statePtr, CHANNEL_BLOCKED|INPUT_SAW_CR) ); + assert(statePtr->inputEncodingFlags & TCL_ENCODING_END); + assert(!GotFlag(statePtr, CHANNEL_BLOCKED|INPUT_SAW_CR)); /* TODO: Don't need this call */ UpdateInterest(chanPtr); return 0; } - /* Special handling for zero-char read request. */ + /* + * Special handling for zero-char read request. + */ + if (bytesToRead == 0) { if (GotFlag(statePtr, CHANNEL_EOF)) { statePtr->inputEncodingFlags |= TCL_ENCODING_START; @@ -9786,8 +9814,8 @@ DoRead( TclChannelPreserve((Tcl_Channel)chanPtr); while (bytesToRead) { /* - * Each pass through the loop is intended to process up to - * one channel buffer. + * Each pass through the loop is intended to process up to one channel + * buffer. */ int bytesRead, bytesWritten; @@ -9799,33 +9827,39 @@ DoRead( while (!bufPtr || /* We got no buffer! OR */ (!IsBufferFull(bufPtr) && /* Our buffer has room AND */ - (BytesLeft(bufPtr) < bytesToRead) ) ) { - /* Not enough bytes in it - * yet to fill the dst */ + (BytesLeft(bufPtr) < bytesToRead))) { + /* Not enough bytes in it yet + * to fill the dst */ int code; moreData: code = GetInput(chanPtr); bufPtr = statePtr->inQueueHead; - assert (bufPtr != NULL); + assert(bufPtr != NULL); if (GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED)) { - /* Further reads cannot do any more */ + /* + * Further reads cannot do any more. + */ + break; } if (code) { - /* Read error */ + /* + * Read error + */ + UpdateInterest(chanPtr); TclChannelRelease((Tcl_Channel)chanPtr); return -1; } - assert (IsBufferFull(bufPtr)); + assert(IsBufferFull(bufPtr)); } - assert (bufPtr != NULL); + assert(bufPtr != NULL); bytesRead = BytesLeft(bufPtr); bytesWritten = bytesToRead; @@ -9840,8 +9874,8 @@ DoRead( /* * Buffer is not empty. How can that be? * - * 0) We stopped early because we got all the bytes - * we were seeking. That's fine. + * 0) We stopped early because we got all the bytes we were + * seeking. That's fine. */ if (bytesToRead == 0) { @@ -9857,8 +9891,8 @@ DoRead( } /* - * 2) The buffer holds a \r while in CRLF translation, - * followed by the end of the buffer. + * 2) The buffer holds a \r while in CRLF translation, followed by + * the end of the buffer. */ assert(statePtr->inputTranslation == TCL_TRANSLATE_CRLF); @@ -9866,26 +9900,38 @@ DoRead( assert(BytesLeft(bufPtr) == 1); if (bufPtr->nextPtr == NULL) { - /* There's no more buffered data.... */ + /* + * There's no more buffered data... + */ if (statePtr->flags & CHANNEL_EOF) { - /* ...and there never will be. */ + /* + * ...and there never will be. + */ *p++ = '\r'; bytesToRead--; bufPtr->nextRemoved++; } else if (statePtr->flags & CHANNEL_BLOCKED) { - /* ...and we cannot get more now. */ + /* + * ...and we cannot get more now. + */ + SetFlag(statePtr, CHANNEL_NEED_MORE_DATA); break; } else { - /* ... so we need to get some. */ + /* + * ...so we need to get some. + */ + goto moreData; } } if (bufPtr->nextPtr) { - /* There's a next buffer. Shift orphan \r to it. */ + /* + * There's a next buffer. Shift orphan \r to it. + */ ChannelBuffer *nextPtr = bufPtr->nextPtr; @@ -9910,8 +9956,8 @@ DoRead( } /* - * When there's no buffered data to read, and we're at EOF, - * escape to the caller. + * When there's no buffered data to read, and we're at EOF, escape to + * the caller. */ if (GotFlag(statePtr, CHANNEL_EOF) @@ -9923,11 +9969,11 @@ DoRead( ResetFlag(statePtr, CHANNEL_BLOCKED); } - assert(!GotFlag(statePtr, CHANNEL_EOF) - || GotFlag(statePtr, CHANNEL_STICKY_EOF) - || Tcl_InputBuffered((Tcl_Channel)chanPtr) == 0); - assert( !(GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED) - == (CHANNEL_EOF|CHANNEL_BLOCKED)) ); + assert(!GotFlag(statePtr, CHANNEL_EOF) + || GotFlag(statePtr, CHANNEL_STICKY_EOF) + || Tcl_InputBuffered((Tcl_Channel)chanPtr) == 0); + assert(!(GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED) + == (CHANNEL_EOF|CHANNEL_BLOCKED))); UpdateInterest(chanPtr); TclChannelRelease((Tcl_Channel)chanPtr); return (int)(p - dst); @@ -11164,11 +11210,11 @@ DupChannelIntRep( register Tcl_Obj *copyPtr) /* Object with internal rep to set. Must not * currently have an internal rep.*/ { - ResolvedChanName *resPtr = srcPtr->internalRep.twoPtrValue.ptr1; + ResolvedChanName *resPtr; - resPtr->refCount++; - copyPtr->internalRep.twoPtrValue.ptr1 = resPtr; - copyPtr->typePtr = srcPtr->typePtr; + ChanGetIntRep(srcPtr, resPtr); + assert(resPtr); + ChanSetIntRep(copyPtr, resPtr); } /* @@ -11191,10 +11237,11 @@ static void FreeChannelIntRep( Tcl_Obj *objPtr) /* Object with internal rep to free. */ { - ResolvedChanName *resPtr = objPtr->internalRep.twoPtrValue.ptr1; + ResolvedChanName *resPtr; - objPtr->typePtr = NULL; - if (--resPtr->refCount) { + ChanGetIntRep(objPtr, resPtr); + assert(resPtr); + if (resPtr->refCount-- > 1) { return; } Tcl_Release(resPtr->statePtr); |