diff options
Diffstat (limited to 'generic/tclIO.c')
| -rw-r--r-- | generic/tclIO.c | 3676 |
1 files changed, 1273 insertions, 2403 deletions
diff --git a/generic/tclIO.c b/generic/tclIO.c index 15e0785..7bc849e 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -4,8 +4,8 @@ * This file provides the generic portions (those that are the same on * all platforms and for all channel types) of Tcl's IO facilities. * - * Copyright © 1998-2000 Ajuba Solutions - * Copyright © 1995-1997 Sun Microsystems, Inc. + * Copyright (c) 1998-2000 Ajuba Solutions + * Copyright (c) 1995-1997 Sun Microsystems, Inc. * Contributions from Don Porter, NIST, 2014. (not subject to US copyright) * * See the file "license.terms" for information on usage and redistribution of @@ -28,22 +28,22 @@ typedef struct ChannelHandler { int mask; /* Mask of desired events. */ Tcl_ChannelProc *proc; /* Procedure to call in the type of * Tcl_CreateChannelHandler. */ - void *clientData; /* Argument to pass to procedure. */ + ClientData clientData; /* Argument to pass to procedure. */ struct ChannelHandler *nextPtr; /* Next one in list of registered handlers. */ } ChannelHandler; /* * This structure keeps track of the current ChannelHandler being invoked in - * the current invocation of Tcl_NotifyChannel. There is a potential + * the current invocation of ChannelHandlerEventProc. There is a potential * problem if a ChannelHandler is deleted while it is the current one, since - * Tcl_NotifyChannel needs to look at the nextPtr field. To handle this + * ChannelHandlerEventProc needs to look at the nextPtr field. To handle this * problem, structures of the type below indicate the next handler to be * processed for any (recursively nested) dispatches in progress. The * nextHandlerPtr field is updated if the handler being pointed to is deleted. - * The nestedHandlerPtr field is used to chain together all recursive - * invocations, so that Tcl_DeleteChannelHandler can find all the recursively - * nested invocations of Tcl_NotifyChannel and compare the handler being + * The nextPtr field is used to chain together all recursive invocations, so + * that Tcl_DeleteChannelHandler can find all the recursively nested + * invocations of ChannelHandlerEventProc and compare the handler being * deleted against the NEXT handler to be invoked in that invocation; when it * finds such a situation, Tcl_DeleteChannelHandler updates the nextHandlerPtr * field of the structure to the next handler. @@ -54,10 +54,21 @@ typedef struct NextChannelHandler { * this invocation. */ struct NextChannelHandler *nestedHandlerPtr; /* Next nested invocation of - * Tcl_NotifyChannel. */ + * ChannelHandlerEventProc. */ } NextChannelHandler; /* + * The following structure describes the event that is added to the Tcl + * event queue by the channel handler check procedure. + */ + +typedef struct ChannelHandlerEvent { + Tcl_Event header; /* Standard header for all events. */ + Channel *chanPtr; /* The channel that is ready. */ + int readyMask; /* Events that have occurred. */ +} ChannelHandlerEvent; + +/* * The following structure is used by Tcl_GetsObj() to encapsulates the * state for a "gets" operation. */ @@ -98,12 +109,12 @@ typedef struct CopyState { struct Channel *writePtr; /* Pointer to output channel. */ int readFlags; /* Original read channel flags. */ int writeFlags; /* Original write channel flags. */ - Tcl_WideInt toRead; /* Number of bytes to copy, or -1. */ + int toRead; /* Number of bytes to copy, or -1. */ Tcl_WideInt total; /* Total bytes transferred (written). */ Tcl_Interp *interp; /* Interp that started the copy. */ Tcl_Obj *cmdPtr; /* Command to be invoked at completion. */ - Tcl_Size bufSize; /* Size of appended buffer. */ - char buffer[TCLFLEXARRAY]; /* Copy buffer, this must be the last + int bufSize; /* Size of appended buffer. */ + char buffer[1]; /* Copy buffer, this must be the last * field. */ } CopyState; @@ -116,21 +127,21 @@ typedef struct CopyState { * The structure defined below is used in this file only. */ -typedef struct { +typedef struct ThreadSpecificData { NextChannelHandler *nestedHandlerPtr; /* This variable holds the list of nested - * Tcl_NotifyChannel invocations. */ + * ChannelHandlerEventProc invocations. */ ChannelState *firstCSPtr; /* List of all channels currently open, * indexed by ChannelState, as only one * ChannelState exists per set of stacked * channels. */ Tcl_Channel stdinChannel; /* Static variable for the stdin channel. */ - Tcl_Channel stdoutChannel; /* Static variable for the stdout channel. */ - Tcl_Channel stderrChannel; /* Static variable for the stderr channel. */ - Tcl_Encoding binaryEncoding; int stdinInitialized; + Tcl_Channel stdoutChannel; /* Static variable for the stdout channel. */ int stdoutInitialized; + Tcl_Channel stderrChannel; /* Static variable for the stderr channel. */ int stderrInitialized; + Tcl_Encoding binaryEncoding; } ThreadSpecificData; static Tcl_ThreadDataKey dataKey; @@ -142,7 +153,7 @@ static Tcl_ThreadDataKey dataKey; typedef struct CloseCallback { Tcl_CloseProc *proc; /* The procedure to call. */ - void *clientData; /* Arbitrary one-word data to pass + ClientData clientData; /* Arbitrary one-word data to pass * to the callback. */ struct CloseCallback *nextPtr; /* For chaining close callbacks. */ } CloseCallback; @@ -151,12 +162,12 @@ typedef struct CloseCallback { * Static functions in this file: */ -static ChannelBuffer * AllocChannelBuffer(Tcl_Size length); +static ChannelBuffer * AllocChannelBuffer(int length); static void PreserveChannelBuffer(ChannelBuffer *bufPtr); static void ReleaseChannelBuffer(ChannelBuffer *bufPtr); static int IsShared(ChannelBuffer *bufPtr); static void ChannelFree(Channel *chanPtr); -static void ChannelTimerProc(void *clientData); +static void ChannelTimerProc(ClientData clientData); static int ChanRead(Channel *chanPtr, char *dst, int dstSize); static int CheckChannelErrors(ChannelState *statePtr, int direction); @@ -165,27 +176,14 @@ static int CheckForDeadChannel(Tcl_Interp *interp, static void CheckForStdChannelsBeingClosed(Tcl_Channel chan); static void CleanupChannelHandlers(Tcl_Interp *interp, Channel *chanPtr); -static void CleanupTimerHandler(ChannelState *statePtr); 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 CopyData(CopyState *csPtr, int mask); -static void DeleteTimerHandler(ChannelState *statePtr); -static int MoveBytes(CopyState *csPtr); - -static void MBCallback(CopyState *csPtr, Tcl_Obj *errObj); -static void MBError(CopyState *csPtr, int mask, int errorCode); -static int MBRead(CopyState *csPtr); -static int MBWrite(CopyState *csPtr); -static void MBEvent(void *clientData, int mask); - -static void CopyEventProc(void *clientData, int mask); +static void CopyEventProc(ClientData clientData, int mask); static void CreateScriptRecord(Tcl_Interp *interp, Channel *chanPtr, int mask, Tcl_Obj *scriptPtr); -static void DeleteChannelTable(void *clientData, +static void DeleteChannelTable(ClientData clientData, Tcl_Interp *interp); static void DeleteScriptRecord(Tcl_Interp *interp, Channel *chanPtr, int mask); @@ -193,19 +191,20 @@ static int DetachChannel(Tcl_Interp *interp, Tcl_Channel chan); static void DiscardInputQueued(ChannelState *statePtr, int discardSavedBuffers); static void DiscardOutputQueued(ChannelState *chanPtr); -static Tcl_Size DoRead(Channel *chanPtr, char *dst, Tcl_Size bytesToRead, - int allowShortReads); -static Tcl_Size DoReadChars(Channel *chan, Tcl_Obj *objPtr, Tcl_Size toRead, - int allowShortReads, int appendFlag); +static int DoRead(Channel *chanPtr, char *dst, int bytesToRead); +static int DoReadChars(Channel *chan, Tcl_Obj *objPtr, int toRead, + int appendFlag); static int FilterInputBytes(Channel *chanPtr, GetsState *statePtr); static int FlushChannel(Tcl_Interp *interp, Channel *chanPtr, int calledFromAsyncFlush); static int TclGetsObjBinary(Tcl_Channel chan, Tcl_Obj *objPtr); -static Tcl_Encoding GetBinaryEncoding(void); -static Tcl_ExitProc FreeBinaryEncoding; +static Tcl_Encoding GetBinaryEncoding(); +static void FreeBinaryEncoding(ClientData clientData); static Tcl_HashTable * GetChannelTable(Tcl_Interp *interp); static int GetInput(Channel *chanPtr); +static int HaveVersion(const Tcl_ChannelType *typePtr, + Tcl_ChannelTypeVersion minimumVersion); static void PeekAhead(Channel *chanPtr, char **dstEndPtr, GetsState *gsPtr); static int ReadBytes(ChannelState *statePtr, Tcl_Obj *objPtr, @@ -221,12 +220,12 @@ static void StopCopy(CopyState *csPtr); static void TranslateInputEOL(ChannelState *statePtr, char *dst, const char *src, int *dstLenPtr, int *srcLenPtr); static void UpdateInterest(Channel *chanPtr); -static Tcl_Size Write(Channel *chanPtr, const char *src, - Tcl_Size srcLen, Tcl_Encoding encoding); +static int Write(Channel *chanPtr, const char *src, + int srcLen, Tcl_Encoding encoding); static Tcl_Obj * FixLevelCode(Tcl_Obj *msg); static void SpliceChannel(Tcl_Channel chan); static void CutChannel(Tcl_Channel chan); -static int WillRead(Channel *chanPtr); +static int WillRead(Channel *chanPtr); #define WriteChars(chanPtr, src, srcLen) \ Write(chanPtr, src, srcLen, chanPtr->state->encoding) @@ -239,7 +238,7 @@ static int WillRead(Channel *chanPtr); * short description of what the macro does. * * -------------------------------------------------------------------------- - * Tcl_Size BytesLeft(ChannelBuffer *bufPtr) + * int BytesLeft(ChannelBuffer *bufPtr) * * Returns the number of bytes of data remaining in the buffer. * @@ -277,21 +276,21 @@ static int WillRead(Channel *chanPtr); * -------------------------------------------------------------------------- */ -#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) && (bufPtr)->nextAdded >= (bufPtr)->bufLength) +#define IsBufferFull(bufPtr) ((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. @@ -313,47 +312,34 @@ static int WillRead(Channel *chanPtr); && (strncmp(optionName, (nameString), len) == 0)) /* - * The ChannelObjType type. Used to store the result of looking up - * a channel name in the context of an interp. Saves the lookup - * result and values needed to check its continued validity. + * The ChannelObjType type. We actually store the ChannelState structure + * as that lives longest and we want to return the bottomChanPtr when + * requested (consistent with Tcl_GetChannel). The setFromAny and + * updateString can be NULL as they should not be called. */ -typedef struct ResolvedChanName { - ChannelState *statePtr; /* The saved lookup result */ - Tcl_Interp *interp; /* The interp in which the lookup was done. */ - size_t epoch; /* The epoch of the channel when the lookup - * was done. Use to verify validity. */ - size_t refCount; /* Share this struct among many Tcl_Obj. */ -} ResolvedChanName; - -static void DupChannelInternalRep(Tcl_Obj *objPtr, Tcl_Obj *copyPtr); -static void FreeChannelInternalRep(Tcl_Obj *objPtr); +static void DupChannelIntRep(Tcl_Obj *objPtr, Tcl_Obj *copyPtr); +static int SetChannelFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); +static void FreeChannelIntRep(Tcl_Obj *objPtr); -static const Tcl_ObjType chanObjType = { +static Tcl_ObjType chanObjType = { "channel", /* name for this type */ - FreeChannelInternalRep, /* freeIntRepProc */ - DupChannelInternalRep, /* dupIntRepProc */ + FreeChannelIntRep, /* freeIntRepProc */ + DupChannelIntRep, /* dupIntRepProc */ NULL, /* updateStringProc */ - NULL /* setFromAnyProc */ + NULL /* setFromAnyProc SetChannelFromAny */ }; -#define ChanSetInternalRep(objPtr, resPtr) \ - do { \ - Tcl_ObjInternalRep ir; \ - (resPtr)->refCount++; \ - ir.twoPtrValue.ptr1 = (resPtr); \ - ir.twoPtrValue.ptr2 = NULL; \ - Tcl_StoreInternalRep((objPtr), &chanObjType, &ir); \ - } while (0) - -#define ChanGetInternalRep(objPtr, resPtr) \ - do { \ - const Tcl_ObjInternalRep *irPtr; \ - irPtr = TclFetchInternalRep((objPtr), &chanObjType); \ - (resPtr) = irPtr ? (ResolvedChanName *)irPtr->twoPtrValue.ptr1 : NULL; \ - } while (0) - -#define BUSY_STATE(st, fl) \ +#define GET_CHANNELSTATE(objPtr) \ + ((ChannelState *) (objPtr)->internalRep.twoPtrValue.ptr1) +#define SET_CHANNELSTATE(objPtr, storePtr) \ + ((objPtr)->internalRep.twoPtrValue.ptr1 = (void *) (storePtr)) +#define GET_CHANNELINTERP(objPtr) \ + ((Tcl_Interp *) (objPtr)->internalRep.twoPtrValue.ptr2) +#define SET_CHANNELINTERP(objPtr, storePtr) \ + ((objPtr)->internalRep.twoPtrValue.ptr2 = (void *) (storePtr)) + +#define BUSY_STATE(st,fl) \ ((((st)->csPtrR) && ((fl) & TCL_READABLE)) || \ (((st)->csPtrW) && ((fl) & TCL_WRITABLE))) @@ -362,47 +348,22 @@ static const Tcl_ObjType chanObjType = { /* *--------------------------------------------------------------------------- * - * 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) -{ -#ifndef TCL_NO_DEPRECATED - if ((chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) && (chanPtr->typePtr->closeProc != NULL)) { - return chanPtr->typePtr->closeProc(chanPtr->instanceData, interp); - } -#endif - return chanPtr->typePtr->close2Proc(chanPtr->instanceData, interp, 0); -} - -/* - *--------------------------------------------------------------------------- - * * 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_ENCODING_ERROR, 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? * *--------------------------------------------------------------------------- @@ -416,17 +377,15 @@ 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; } @@ -439,35 +398,32 @@ 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; } ResetFlag(chanPtr->state, CHANNEL_BLOCKED | CHANNEL_EOF); chanPtr->state->inputEncodingFlags &= ~TCL_ENCODING_END; - if (bytesRead < 0) { - if ((result == EWOULDBLOCK) || (result == EAGAIN)) { + 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 (bytesRead < dstSize) { SetFlag(chanPtr->state, CHANNEL_BLOCKED); - result = EAGAIN; } - Tcl_SetErrno(result); } else if (bytesRead == 0) { SetFlag(chanPtr->state, CHANNEL_EOF); chanPtr->state->inputEncodingFlags |= TCL_ENCODING_END; - } else { - /* - * 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) { + } else if (bytesRead < 0) { + if ((result == EWOULDBLOCK) || (result == EAGAIN)) { SetFlag(chanPtr->state, CHANNEL_BLOCKED); + result = EAGAIN; } + Tcl_SetErrno(result); } return bytesRead; } @@ -484,56 +440,21 @@ ChanSeek( * type and non-NULL. */ - if (Tcl_ChannelWideSeekProc(chanPtr->typePtr) == NULL) { -#ifndef TCL_NO_DEPRECATED - if (offset<LONG_MIN || offset>LONG_MAX) { - *errnoPtr = EOVERFLOW; - return TCL_INDEX_NONE; - } - - return Tcl_ChannelSeekProc(chanPtr->typePtr)(chanPtr->instanceData, + if (HaveVersion(chanPtr->typePtr, TCL_CHANNEL_VERSION_3) && + chanPtr->typePtr->wideSeekProc != NULL) { + return chanPtr->typePtr->wideSeekProc(chanPtr->instanceData, offset, mode, errnoPtr); -#else - *errnoPtr = EINVAL; - return TCL_INDEX_NONE; -#endif } - return Tcl_ChannelWideSeekProc(chanPtr->typePtr)(chanPtr->instanceData, - 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); + if (offset<Tcl_LongAsWide(LONG_MIN) || offset>Tcl_LongAsWide(LONG_MAX)) { + *errnoPtr = EOVERFLOW; + return Tcl_LongAsWide(-1); } -} -static inline void -ChanWatch( - Channel *chanPtr, - int mask) -{ - chanPtr->typePtr->watchProc(chanPtr->instanceData, mask); + return Tcl_LongAsWide(chanPtr->typePtr->seekProc(chanPtr->instanceData, + Tcl_WideAsLong(offset), mode, errnoPtr)); } -static inline int -ChanWrite( - Channel *chanPtr, - const char *src, - int srcLen, - int *errnoPtr) -{ - return chanPtr->typePtr->outputProc(chanPtr->instanceData, src, srcLen, - errnoPtr); -} /* *--------------------------------------------------------------------------- @@ -581,6 +502,7 @@ TclInitIOSubsystem(void) *------------------------------------------------------------------------- */ + /* ARGSUSED */ void TclFinalizeIOSubsystem(void) { @@ -588,22 +510,6 @@ 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 @@ -622,40 +528,26 @@ TclFinalizeIOSubsystem(void) statePtr != NULL; statePtr = statePtr->nextCSPtr) { chanPtr = statePtr->topChanPtr; - if (GotFlag(statePtr, CHANNEL_DEAD)) { - continue; - } - if (!GotFlag(statePtr, CHANNEL_INCLOSE | CHANNEL_CLOSED ) - || GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { - ResetFlag(statePtr, BG_FLUSH_SCHEDULED); + if (!GotFlag(statePtr, CHANNEL_INCLOSE|CHANNEL_CLOSED|CHANNEL_DEAD)) { active = 1; break; } } /* - * We've found a live (or bg-closing) channel. Close it. + * We've found a live channel. Close it. */ 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 - * and not be "0". + * Set the channel back into blocking mode to ensure that we wait + * for all data to flush out. */ - if (doflushnb) { - /* - * Set the channel back into blocking mode to ensure that we - * wait for all data to flush out. - */ + TclChannelPreserve((Tcl_Channel)chanPtr); - (void) Tcl_SetChannelOption(NULL, (Tcl_Channel) chanPtr, - "-blocking", "on"); - } + (void) Tcl_SetChannelOption(NULL, (Tcl_Channel) chanPtr, + "-blocking", "on"); if ((chanPtr == (Channel *) tsdPtr->stdinChannel) || (chanPtr == (Channel *) tsdPtr->stdoutChannel) || @@ -688,7 +580,12 @@ TclFinalizeIOSubsystem(void) * device for this channel. */ - (void) ChanClose(chanPtr, NULL); + if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) { + (chanPtr->typePtr->closeProc)(chanPtr->instanceData, NULL); + } else { + (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, + NULL, 0); + } /* * Finally, we clean up the fields in the channel data @@ -732,18 +629,17 @@ Tcl_SetStdChannel( { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - int init = channel ? 1 : -1; switch (type) { case TCL_STDIN: - tsdPtr->stdinInitialized = init; + tsdPtr->stdinInitialized = 1; tsdPtr->stdinChannel = channel; break; case TCL_STDOUT: - tsdPtr->stdoutInitialized = init; + tsdPtr->stdoutInitialized = 1; tsdPtr->stdoutChannel = channel; break; case TCL_STDERR: - tsdPtr->stderrInitialized = init; + tsdPtr->stderrInitialized = 1; tsdPtr->stderrChannel = channel; break; } @@ -780,8 +676,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 @@ -793,7 +689,6 @@ Tcl_GetStdChannel( */ if (tsdPtr->stdinChannel != NULL) { - tsdPtr->stdinInitialized = 1; Tcl_RegisterChannel(NULL, tsdPtr->stdinChannel); } } @@ -801,10 +696,9 @@ 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); } } @@ -812,10 +706,9 @@ 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); } } @@ -849,13 +742,13 @@ Tcl_CreateCloseHandler( * callback. */ Tcl_CloseProc *proc, /* The callback routine to call when the * channel will be closed. */ - void *clientData) /* Arbitrary data to pass to the close + ClientData clientData) /* Arbitrary data to pass to the close * callback. */ { ChannelState *statePtr = ((Channel *) chan)->state; CloseCallback *cbPtr; - cbPtr = (CloseCallback *)ckalloc(sizeof(CloseCallback)); + cbPtr = (CloseCallback *) ckalloc(sizeof(CloseCallback)); cbPtr->proc = proc; cbPtr->clientData = clientData; @@ -887,7 +780,7 @@ Tcl_DeleteCloseHandler( * callback. */ Tcl_CloseProc *proc, /* The procedure for the callback to * remove. */ - void *clientData) /* The callback data for the callback to + ClientData clientData) /* The callback data for the callback to * remove. */ { ChannelState *statePtr = ((Channel *) chan)->state; @@ -901,10 +794,11 @@ Tcl_DeleteCloseHandler( } else { cbPrevPtr->nextPtr = cbPtr->nextPtr; } - ckfree(cbPtr); + ckfree((char *) cbPtr); break; + } else { + cbPrevPtr = cbPtr; } - cbPrevPtr = cbPtr; } } @@ -934,9 +828,9 @@ GetChannelTable( Tcl_HashTable *hTblPtr; /* Hash table of channels. */ Tcl_Channel stdinChan, stdoutChan, stderrChan; - hTblPtr = (Tcl_HashTable *)Tcl_GetAssocData(interp, "tclIO", NULL); + hTblPtr = Tcl_GetAssocData(interp, "tclIO", NULL); if (hTblPtr == NULL) { - hTblPtr = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable)); + hTblPtr = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); Tcl_InitHashTable(hTblPtr, TCL_STRING_KEYS); Tcl_SetAssocData(interp, "tclIO", (Tcl_InterpDeleteProc *) DeleteChannelTable, hTblPtr); @@ -978,7 +872,7 @@ GetChannelTable( * * Side effects: * Deletes the hash table of channels. May close channels. May flush - * output on closed channels. Removes any channelEvent handlers that were + * output on closed channels. Removes any channeEvent handlers that were * registered in this interpreter. * *---------------------------------------------------------------------- @@ -986,7 +880,7 @@ GetChannelTable( static void DeleteChannelTable( - void *clientData, /* The per-interpreter data structure. */ + ClientData clientData, /* The per-interpreter data structure. */ Tcl_Interp *interp) /* The interpreter being deleted. */ { Tcl_HashTable *hTblPtr; /* The hash table. */ @@ -1004,14 +898,14 @@ DeleteChannelTable( * refcount reaches zero. */ - hTblPtr = (Tcl_HashTable *)clientData; + hTblPtr = clientData; for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch); hPtr != NULL; hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch)) { - chanPtr = (Channel *)Tcl_GetHashValue(hPtr); + chanPtr = Tcl_GetHashValue(hPtr); statePtr = chanPtr->state; /* - * Remove any file events registered in this interpreter. + * Remove any fileevents registered in this interpreter. */ for (sPtr = statePtr->scriptRecordPtr, prevPtr = NULL; @@ -1025,10 +919,10 @@ DeleteChannelTable( } Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr, - TclChannelEventScriptInvoker, sPtr); + TclChannelEventScriptInvoker, (ClientData) sPtr); TclDecrRefCount(sPtr->scriptPtr); - ckfree(sPtr); + ckfree((char *) sPtr); } else { prevPtr = sPtr; } @@ -1042,8 +936,9 @@ DeleteChannelTable( */ Tcl_DeleteHashEntry(hPtr); - statePtr->epoch++; - if (statePtr->refCount-- <= 1) { + SetFlag(statePtr, CHANNEL_TAINTED); + statePtr->refCount--; + if (statePtr->refCount <= 0) { if (!GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { (void) Tcl_Close(interp, (Tcl_Channel) chanPtr); } @@ -1051,7 +946,7 @@ DeleteChannelTable( } Tcl_DeleteHashTable(hTblPtr); - ckfree(hTblPtr); + ckfree((char *) hTblPtr); } /* @@ -1083,7 +978,7 @@ CheckForStdChannelsBeingClosed( ChannelState *statePtr = ((Channel *) chan)->state; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - if (tsdPtr->stdinInitialized == 1 + if (tsdPtr->stdinInitialized && tsdPtr->stdinChannel != NULL && statePtr == ((Channel *)tsdPtr->stdinChannel)->state) { if (statePtr->refCount < 2) { @@ -1091,7 +986,7 @@ CheckForStdChannelsBeingClosed( tsdPtr->stdinChannel = NULL; return; } - } else if (tsdPtr->stdoutInitialized == 1 + } else if (tsdPtr->stdoutInitialized && tsdPtr->stdoutChannel != NULL && statePtr == ((Channel *)tsdPtr->stdoutChannel)->state) { if (statePtr->refCount < 2) { @@ -1099,7 +994,7 @@ CheckForStdChannelsBeingClosed( tsdPtr->stdoutChannel = NULL; return; } - } else if (tsdPtr->stderrInitialized == 1 + } else if (tsdPtr->stderrInitialized && tsdPtr->stderrChannel != NULL && statePtr == ((Channel *)tsdPtr->stderrChannel)->state) { if (statePtr->refCount < 2) { @@ -1237,9 +1132,8 @@ Tcl_UnregisterChannel( if (GotFlag(statePtr, CHANNEL_INCLOSE)) { if (interp != NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "illegal recursive call to close through close-handler" - " of channel", -1)); + Tcl_AppendResult(interp, "Illegal recursive call to close " + "through close-handler of channel", NULL); } return TCL_ERROR; } @@ -1265,7 +1159,7 @@ Tcl_UnregisterChannel( */ if (statePtr->refCount <= 0) { - Tcl_Preserve(statePtr); + Tcl_Preserve((ClientData)statePtr); if (!GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { /* * We don't want to re-enter Tcl_Close(). @@ -1274,13 +1168,13 @@ Tcl_UnregisterChannel( if (!GotFlag(statePtr, CHANNEL_CLOSED)) { if (Tcl_Close(interp, chan) != TCL_OK) { SetFlag(statePtr, CHANNEL_CLOSED); - Tcl_Release(statePtr); + Tcl_Release((ClientData)statePtr); return TCL_ERROR; } } } SetFlag(statePtr, CHANNEL_CLOSED); - Tcl_Release(statePtr); + Tcl_Release((ClientData)statePtr); } return TCL_OK; } @@ -1374,7 +1268,7 @@ DetachChannel( statePtr = chanPtr->state; if (interp != NULL) { - hTblPtr = (Tcl_HashTable *)Tcl_GetAssocData(interp, "tclIO", NULL); + hTblPtr = Tcl_GetAssocData(interp, "tclIO", NULL); if (hTblPtr == NULL) { return TCL_ERROR; } @@ -1386,7 +1280,7 @@ DetachChannel( return TCL_ERROR; } Tcl_DeleteHashEntry(hPtr); - statePtr->epoch++; + SetFlag(statePtr, CHANNEL_TAINTED); /* * Remove channel handlers that refer to this interpreter, so that @@ -1430,7 +1324,7 @@ Tcl_GetChannel( * channel. */ const char *chanName, /* The name of the channel. */ int *modePtr) /* Where to store the mode in which the - * channel was opened? Will contain an OR'ed + * channel was opened? Will contain an ORed * combination of TCL_READABLE and * TCL_WRITABLE, if non-NULL. */ { @@ -1465,9 +1359,9 @@ Tcl_GetChannel( hTblPtr = GetChannelTable(interp); hPtr = Tcl_FindHashEntry(hTblPtr, name); if (hPtr == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "can not find channel named \"%s\"", chanName)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "CHANNEL", chanName, (void *)NULL); + Tcl_AppendResult(interp, "can not find channel named \"", chanName, + "\"", NULL); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "CHANNEL", chanName, NULL); return NULL; } @@ -1477,7 +1371,7 @@ Tcl_GetChannel( * compensate where necessary to retrieve the topmost channel again. */ - chanPtr = (Channel *)Tcl_GetHashValue(hPtr); + chanPtr = Tcl_GetHashValue(hPtr); chanPtr = chanPtr->state->bottomChanPtr; if (modePtr != NULL) { *modePtr = GotFlag(chanPtr->state, TCL_READABLE|TCL_WRITABLE); @@ -1513,65 +1407,19 @@ TclGetChannelFromObj( Tcl_Obj *objPtr, Tcl_Channel *channelPtr, int *modePtr, /* Where to store the mode in which the - * channel was opened? Will contain an OR'ed + * channel was opened? Will contain an ORed * combination of TCL_READABLE and * TCL_WRITABLE, if non-NULL. */ - TCL_UNUSED(int) /*flags*/) + int flags) { ChannelState *statePtr; - ResolvedChanName *resPtr = NULL; - Tcl_Channel chan; - if (interp == NULL) { + if (SetChannelFromAny(interp, objPtr) != TCL_OK) { return TCL_ERROR; } - ChanGetInternalRep(objPtr, resPtr); - if (resPtr) { - /* - * Confirm validity of saved lookup results. - */ - - 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. - */ - - goto valid; - } - } - - chan = Tcl_GetChannel(interp, TclGetString(objPtr), NULL); - - if (chan == NULL) { - if (resPtr) { - Tcl_StoreInternalRep(objPtr, &chanObjType, NULL); - } - return TCL_ERROR; - } - - if (resPtr && resPtr->refCount == 1) { - /* - * Re-use the ResolvedCmdName struct. - */ - - Tcl_Release((void *) resPtr->statePtr); - } else { - resPtr = (ResolvedChanName *) ckalloc(sizeof(ResolvedChanName)); - resPtr->refCount = 0; - ChanSetInternalRep(objPtr, resPtr); /* Overwrites, if needed */ - } - statePtr = ((Channel *)chan)->state; - resPtr->statePtr = statePtr; - Tcl_Preserve((void *) statePtr); - resPtr->interp = interp; - resPtr->epoch = statePtr->epoch; - - valid: - *channelPtr = (Tcl_Channel) statePtr->bottomChanPtr; + statePtr = GET_CHANNELSTATE(objPtr); + *channelPtr = (Tcl_Channel) (statePtr->bottomChanPtr); if (modePtr != NULL) { *modePtr = GotFlag(statePtr, TCL_READABLE|TCL_WRITABLE); @@ -1598,9 +1446,9 @@ TclGetChannelFromObj( Tcl_Channel Tcl_CreateChannel( - const Tcl_ChannelType *typePtr, /* The channel type record. */ + Tcl_ChannelType *typePtr, /* The channel type record. */ const char *chanName, /* Name of channel to record. */ - void *instanceData, /* Instance specific data. */ + ClientData instanceData, /* Instance specific data. */ int mask) /* TCL_READABLE & TCL_WRITABLE to indicate if * the channel is readable, writable. */ { @@ -1608,7 +1456,6 @@ Tcl_CreateChannel( ChannelState *statePtr; /* The stack-level independent state info for * the channel. */ const char *name; - char *tmp; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); /* @@ -1621,42 +1468,15 @@ Tcl_CreateChannel( * as well. */ - assert(sizeof(Tcl_ChannelTypeVersion) == sizeof(Tcl_DriverBlockModeProc *)); - assert(typePtr->typeName != NULL); -#ifndef TCL_NO_DEPRECATED - if (((NULL == typePtr->closeProc) || (TCL_CLOSE2PROC == typePtr->closeProc)) && (typePtr->close2Proc == NULL)) { - Tcl_Panic("channel type %s must define closeProc or close2Proc", typePtr->typeName); - } -#else - if (Tcl_ChannelVersion(typePtr) < TCL_CHANNEL_VERSION_5) { - Tcl_Panic("channel type %s must be version TCL_CHANNEL_VERSION_5", typePtr->typeName); - } - if (typePtr->close2Proc == NULL) { - Tcl_Panic("channel type %s must define close2Proc", typePtr->typeName); - } -#endif - if ((TCL_READABLE & mask) && (NULL == typePtr->inputProc)) { - Tcl_Panic("channel type %s must define inputProc when used for reader channel", typePtr->typeName); - } - if ((TCL_WRITABLE & mask) && (NULL == typePtr->outputProc)) { - Tcl_Panic("channel type %s must define outputProc when used for writer channel", typePtr->typeName); - } - if (NULL == typePtr->watchProc) { - Tcl_Panic("channel type %s must define watchProc", typePtr->typeName); - } -#ifndef TCL_NO_DEPRECATED - if ((NULL != typePtr->wideSeekProc) && (NULL == typePtr->seekProc)) { - Tcl_Panic("channel type %s must define seekProc if defining wideSeekProc", typePtr->typeName); - } -#endif + 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 = (Channel *) ckalloc(sizeof(Channel)); + statePtr = (ChannelState *) ckalloc(sizeof(ChannelState)); chanPtr->state = statePtr; chanPtr->instanceData = instanceData; @@ -1668,22 +1488,15 @@ Tcl_CreateChannel( */ if (chanName != NULL) { - unsigned len = strlen(chanName) + 1; - - /* - * Make sure we allocate at least 7 bytes, so it fits for "stdout" - * later. - */ + char *tmp = ckalloc((unsigned) (strlen(chanName) + 1)); - tmp = (char *)ckalloc((len < 7) ? 7 : len); + statePtr->channelName = tmp; strcpy(tmp, chanName); } else { - tmp = (char *)ckalloc(7); - tmp[0] = '\0'; + Tcl_Panic("Tcl_CreateChannel: NULL channel name"); } - statePtr->channelName = tmp; + statePtr->flags = mask; - statePtr->maxPerms = mask; /* Save max privileges for close callback */ /* * Set the channel to system default encoding. @@ -1702,18 +1515,14 @@ Tcl_CreateChannel( } statePtr->inputEncodingState = NULL; statePtr->inputEncodingFlags = TCL_ENCODING_START; - ENCODING_PROFILE_SET(statePtr->inputEncodingFlags, - TCL_ENCODING_PROFILE_TCL8); statePtr->outputEncodingState = NULL; statePtr->outputEncodingFlags = TCL_ENCODING_START; - ENCODING_PROFILE_SET(statePtr->outputEncodingFlags, - TCL_ENCODING_PROFILE_TCL8); /* * 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. */ @@ -1736,7 +1545,6 @@ Tcl_CreateChannel( statePtr->scriptRecordPtr = NULL; statePtr->bufSize = CHANNELBUFFER_DEFAULT_SIZE; statePtr->timer = NULL; - statePtr->timerChanPtr = NULL; statePtr->csPtrR = NULL; statePtr->csPtrW = NULL; statePtr->outputStage = NULL; @@ -1760,8 +1568,6 @@ Tcl_CreateChannel( statePtr->chanMsg = NULL; statePtr->unreportedMsg = NULL; - statePtr->epoch = 0; - /* * Link the channel into the list of all channels; create an on-exit * handler if there is not one already, to close off all the channels in @@ -1788,17 +1594,14 @@ 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); } @@ -1835,10 +1638,9 @@ Tcl_CreateChannel( Tcl_Channel Tcl_StackChannel( Tcl_Interp *interp, /* The interpreter we are working in */ - const Tcl_ChannelType *typePtr, - /* The channel type record for the new + Tcl_ChannelType *typePtr, /* The channel type record for the new * channel. */ - void *instanceData, /* Instance specific data for the new + ClientData instanceData, /* Instance specific data for the new * channel. */ int mask, /* TCL_READABLE & TCL_WRITABLE to indicate if * the channel is readable, writable. */ @@ -1847,6 +1649,7 @@ 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 @@ -1864,9 +1667,8 @@ Tcl_StackChannel( if (statePtr == NULL) { if (interp) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "couldn't find state for channel \"%s\"", - Tcl_GetChannelName(prevChan))); + Tcl_AppendResult(interp, "couldn't find state for channel \"", + Tcl_GetChannelName(prevChan), "\"", NULL); } return NULL; } @@ -1884,11 +1686,11 @@ Tcl_StackChannel( * --+---+---+---+----+ */ - if ((mask & GotFlag(statePtr, TCL_READABLE|TCL_WRITABLE)) == 0) { + if ((mask & (GotFlag(statePtr, TCL_READABLE | TCL_WRITABLE))) == 0) { if (interp) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "reading and writing both disallowed for channel \"%s\"", - Tcl_GetChannelName(prevChan))); + Tcl_AppendResult(interp, + "reading and writing both disallowed for channel \"", + Tcl_GetChannelName(prevChan), "\"", NULL); } return NULL; } @@ -1915,9 +1717,8 @@ Tcl_StackChannel( statePtr->csPtrR = csPtrR; statePtr->csPtrW = csPtrW; if (interp) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "could not flush channel \"%s\"", - Tcl_GetChannelName(prevChan))); + Tcl_AppendResult(interp, "could not flush channel \"", + Tcl_GetChannelName(prevChan), "\"", NULL); } return NULL; } @@ -1937,11 +1738,12 @@ Tcl_StackChannel( * impossible) we move the buffers from the common state structure into * the channel itself. We use the buffers in the channel below the new * transformation to hold the data. In the future this allows us to write - * transformations which preread data and push the unused part back when + * transformations which pre-read data and push the unused part back when * they are going away. */ if (((mask & TCL_READABLE) != 0) && (statePtr->inQueueHead != NULL)) { + /* * When statePtr->inQueueHead is not NULL, we know * prevChanPtr->inQueueHead must be NULL. @@ -1957,7 +1759,7 @@ Tcl_StackChannel( statePtr->inQueueTail = NULL; } - chanPtr = (Channel *)ckalloc(sizeof(Channel)); + chanPtr = (Channel *) ckalloc(sizeof(Channel)); /* * Save some of the current state into the new structure, reinitialize the @@ -1994,7 +1796,10 @@ Tcl_StackChannel( * time, mangling it. */ - ChanThreadAction(chanPtr, TCL_CHANNEL_THREAD_INSERT); + threadActionProc = Tcl_ChannelThreadActionProc(chanPtr->typePtr); + if (threadActionProc != NULL) { + (*threadActionProc)(chanPtr->instanceData, TCL_CHANNEL_THREAD_INSERT); + } return (Tcl_Channel) chanPtr; } @@ -2019,7 +1824,7 @@ TclChannelRelease( return; } if (chanPtr->typePtr == NULL) { - ckfree(chanPtr); + ckfree((char *)chanPtr); } } @@ -2028,7 +1833,7 @@ ChannelFree( Channel *chanPtr) { if (chanPtr->refCount == 0) { - ckfree(chanPtr); + ckfree((char *)chanPtr); return; } chanPtr->typePtr = NULL; @@ -2046,7 +1851,7 @@ ChannelFree( * A standard Tcl result. * * Side effects: - * If TCL_ERROR is returned, the Posix error code will be set with + * If TCL_ERROR is returned, the posix error code will be set with * Tcl_SetErrno. May leave a message in interp result as well. * *---------------------------------------------------------------------- @@ -2060,6 +1865,7 @@ 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. @@ -2069,11 +1875,13 @@ Tcl_UnstackChannel( if (chanPtr->downChanPtr != NULL) { /* - * Instead of manipulating the per-thread / per-interp list/hash table - * of registered channels we wind down the state of the - * transformation, and then restore the state of underlying channel - * into the old structure. - * + * 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. + */ + + /* * 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 @@ -2110,9 +1918,9 @@ Tcl_UnstackChannel( */ if (!TclChanCaughtErrorBypass(interp, chan) && interp) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "could not flush channel \"%s\"", - Tcl_GetChannelName((Tcl_Channel) chanPtr))); + Tcl_AppendResult(interp, "could not flush channel \"", + Tcl_GetChannelName((Tcl_Channel) chanPtr), "\"", + NULL); } return TCL_ERROR; } @@ -2162,7 +1970,11 @@ Tcl_UnstackChannel( * the state which are still active. */ - ChanThreadAction(chanPtr, TCL_CHANNEL_THREAD_REMOVE); + threadActionProc = Tcl_ChannelThreadActionProc(chanPtr->typePtr); + if (threadActionProc != NULL) { + (*threadActionProc)(chanPtr->instanceData, + TCL_CHANNEL_THREAD_REMOVE); + } statePtr->topChanPtr = downChanPtr; downChanPtr->upChanPtr = NULL; @@ -2174,12 +1986,16 @@ Tcl_UnstackChannel( /* * Close and free the channel driver state. - * TIP #220: This is done with maximum privileges (as created). */ - ResetFlag(statePtr, TCL_READABLE|TCL_WRITABLE); - SetFlag(statePtr, statePtr->maxPerms); - result = ChanClose(chanPtr, interp); + if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) { + result = (chanPtr->typePtr->closeProc)(chanPtr->instanceData, + interp); + } else { + result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, + interp, 0); + } + ChannelFree(chanPtr); UpdateInterest(statePtr->topChanPtr); @@ -2297,7 +2113,7 @@ Tcl_GetTopChannel( *---------------------------------------------------------------------- */ -void * +ClientData Tcl_GetChannelInstanceData( Tcl_Channel chan) /* Channel for which to return client data. */ { @@ -2350,7 +2166,7 @@ Tcl_GetChannelThread( *---------------------------------------------------------------------- */ -const Tcl_ChannelType * +Tcl_ChannelType * Tcl_GetChannelType( Tcl_Channel chan) /* The channel to return type for. */ { @@ -2385,7 +2201,7 @@ Tcl_GetChannelMode( ChannelState *statePtr = ((Channel *) chan)->state; /* State of actual channel. */ - return GotFlag(statePtr, TCL_READABLE|TCL_WRITABLE); + return GotFlag(statePtr, TCL_READABLE | TCL_WRITABLE); } /* @@ -2409,9 +2225,9 @@ const char * Tcl_GetChannelName( Tcl_Channel chan) /* The channel for which to return the name. */ { - ChannelState *statePtr = ((Channel *) chan)->state; - /* State of actual channel. */ + ChannelState *statePtr; /* State of actual channel. */ + statePtr = ((Channel *) chan)->state; return statePtr->channelName; } @@ -2436,21 +2252,23 @@ int Tcl_GetChannelHandle( Tcl_Channel chan, /* The channel to get file from. */ int direction, /* TCL_WRITABLE or TCL_READABLE. */ - void **handlePtr) /* Where to store handle */ + ClientData *handlePtr) /* Where to store handle */ { Channel *chanPtr; /* The actual channel. */ - void *handle; + ClientData handle; int result; chanPtr = ((Channel *) chan)->state->bottomChanPtr; if (!chanPtr->typePtr->getHandleProc) { - Tcl_SetChannelError(chan, Tcl_ObjPrintf( - "channel \"%s\" does not support OS handles", - Tcl_GetChannelName(chan))); + 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); return TCL_ERROR; } - result = chanPtr->typePtr->getHandleProc(chanPtr->instanceData, direction, - &handle); + result = (chanPtr->typePtr->getHandleProc)(chanPtr->instanceData, + direction, &handle); if (handlePtr) { *handlePtr = handle; } @@ -2458,54 +2276,6 @@ Tcl_GetChannelHandle( } /* - *---------------------------------------------------------------------- - * - * Tcl_RemoveChannelMode -- - * - * Remove either read or write privileges from the channel. - * - * Results: - * A standard Tcl result code. - * - * Side effects: - * May change the access mode of the channel. - * May leave an error message in the interp. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_RemoveChannelMode( - Tcl_Interp* interp, /* The interp for an error message. Allowed to be NULL. */ - Tcl_Channel chan, /* The channel which is modified. */ - int mode) /* The access mode to drop from the channel */ -{ - const char* emsg; - ChannelState *statePtr = ((Channel *) chan)->state; - /* State of actual channel. */ - - if ((mode != TCL_READABLE) && (mode != TCL_WRITABLE)) { - emsg = "Illegal mode value."; - goto error; - } - if (0 == (GotFlag(statePtr, TCL_READABLE|TCL_WRITABLE) & ~mode)) { - emsg = "Bad mode, would make channel inacessible"; - goto error; - } - - ResetFlag(statePtr, mode); - return TCL_OK; - - error: - if (interp != NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "Tcl_RemoveChannelMode error: %s. Channel: \"%s\"", - emsg, Tcl_GetChannelName((Tcl_Channel) chan))); - } - return TCL_ERROR; -} - -/* *--------------------------------------------------------------------------- * * AllocChannelBuffer -- @@ -2531,13 +2301,13 @@ Tcl_RemoveChannelMode( static ChannelBuffer * AllocChannelBuffer( - Tcl_Size length) /* Desired length of channel buffer. */ + int length) /* Desired length of channel buffer. */ { ChannelBuffer *bufPtr; - Tcl_Size n; + int n; n = length + CHANNELBUFFER_HEADER_SIZE + BUFFER_PADDING + BUFFER_PADDING; - bufPtr = (ChannelBuffer *)ckalloc(n); + bufPtr = (ChannelBuffer *) ckalloc((unsigned) n); bufPtr->nextAdded = BUFFER_PADDING; bufPtr->nextRemoved = BUFFER_PADDING; bufPtr->bufLength = length + BUFFER_PADDING; @@ -2550,9 +2320,6 @@ static void PreserveChannelBuffer( ChannelBuffer *bufPtr) { - if (!bufPtr->refCount) { - Tcl_Panic("Reuse of ChannelBuffer! %p", bufPtr); - } bufPtr->refCount++; } @@ -2563,7 +2330,7 @@ ReleaseChannelBuffer( if (--bufPtr->refCount) { return; } - ckfree(bufPtr); + ckfree((char *) bufPtr); } static int @@ -2602,7 +2369,6 @@ RecycleBuffer( /* * Do we have to free the buffer to the OS? */ - if (IsShared(bufPtr)) { mustDiscard = 1; } @@ -2613,11 +2379,12 @@ RecycleBuffer( } /* - * Only save buffers which have the requested buffer size for the channel. - * This is to honor dynamic changes of the buffe rsize 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) != statePtr->bufSize + BUFFER_PADDING) { + if ((bufPtr->bufLength - BUFFER_PADDING) != statePtr->bufSize) { ReleaseChannelBuffer(bufPtr); return; } @@ -2720,16 +2487,15 @@ CheckForDeadChannel( Tcl_Interp *interp, /* For error reporting (can be NULL) */ ChannelState *statePtr) /* The channel state to check. */ { - if (!GotFlag(statePtr, CHANNEL_DEAD)) { - return 0; - } - - Tcl_SetErrno(EINVAL); - if (interp) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "unable to access channel: invalid channel", -1)); + if (GotFlag(statePtr, CHANNEL_DEAD)) { + Tcl_SetErrno(EINVAL); + if (interp) { + Tcl_AppendResult(interp, + "unable to access channel: invalid channel", NULL); + } + return 1; } - return 1; + return 0; } /* @@ -2737,9 +2503,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 @@ -2770,11 +2536,10 @@ FlushChannel( int wroteSome = 0; /* Set to one if any data was written to the * driver. */ - int bufExists; /* * Prevent writing on a dead channel -- a channel that has been closed but * not yet deallocated. This can occur if the exit handler for the channel - * deallocation runs before all channels are unregistered in all + * deallocation runs before all channels are deregistered in all * interpreters. */ @@ -2785,18 +2550,14 @@ 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. @@ -2840,8 +2601,8 @@ FlushChannel( */ PreserveChannelBuffer(bufPtr); - written = ChanWrite(chanPtr, RemovePoint(bufPtr), BytesLeft(bufPtr), - &errorCode); + written = (chanPtr->typePtr->outputProc)(chanPtr->instanceData, + RemovePoint(bufPtr), BytesLeft(bufPtr), &errorCode); /* * If the write failed completely attempt to start the asynchronous @@ -2873,7 +2634,7 @@ FlushChannel( * it's a tty channel (dup'ed underneath) */ - if (!GotFlag(statePtr, BG_FLUSH_SCHEDULED) && !TclInExit()) { + if (!GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { SetFlag(statePtr, BG_FLUSH_SCHEDULED); UpdateInterest(chanPtr); } @@ -2889,9 +2650,9 @@ FlushChannel( if (calledFromAsyncFlush) { /* * TIP #219, Tcl Channel Reflection API. - * When deferring 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 deferred error. + * 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. */ Tcl_Obj *msg = statePtr->chanMsg; @@ -2924,8 +2685,14 @@ FlushChannel( Tcl_SetErrno(errorCode); if (interp != NULL && !TclChanCaughtErrorBypass(interp, (Tcl_Channel) chanPtr)) { - Tcl_SetObjResult(interp, - Tcl_NewStringObj(Tcl_PosixError(interp), -1)); + /* + * 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); } /* @@ -2939,44 +2706,29 @@ FlushChannel( * queued. */ - ReleaseChannelBuffer(bufPtr); DiscardOutputQueued(statePtr); + 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; } - bufExists = bufPtr->refCount > 1; - ReleaseChannelBuffer(bufPtr); - if (bufExists) { - /* There is still a reference to this buffer other than the one - * this routine just released, meaning that final cleanup of the - * buffer hasn't been ordered by, e.g. by a reflected channel - * closing the channel from within one of its handler scripts (not - * something one would expecte, but it must be considered). Normal - * operations on the buffer can proceed. - */ - - bufPtr->nextRemoved += written; + bufPtr->nextRemoved += written; - /* - * If this buffer is now empty, recycle it. - */ + /* + * If this buffer is now empty, recycle it. + */ - if (IsBufferEmpty(bufPtr)) { - statePtr->outQueueHead = bufPtr->nextPtr; - if (statePtr->outQueueHead == NULL) { - statePtr->outQueueTail = NULL; - } - RecycleBuffer(statePtr, bufPtr, 0); + if (IsBufferEmpty(bufPtr)) { + statePtr->outQueueHead = bufPtr->nextPtr; + if (statePtr->outQueueHead == NULL) { + statePtr->outQueueTail = NULL; } + RecycleBuffer(statePtr, bufPtr, 0); } - + ReleaseChannelBuffer(bufPtr); } /* Closes "while". */ /* @@ -2991,25 +2743,14 @@ FlushChannel( goto done; } else if (statePtr->outQueueHead == NULL) { ResetFlag(statePtr, BG_FLUSH_SCHEDULED); - ChanWatch(chanPtr, statePtr->interestMask); + (chanPtr->typePtr->watchProc)(chanPtr->instanceData, + statePtr->interestMask); } else { - /* - * When we are calledFromAsyncFlush, that means a writable - * state on the channel triggered the call, so we should be - * able to write something. Either we did write something - * and wroteSome should be set, or there was nothing left to - * write in this call, and we've completed the BG flush. - * These are the two cases above. If we get here, that means - * there is some kind failure in the writable event machinery. - * - * The tls extension indeed suffers from flaws in its channel - * event mgmt. See https://core.tcl-lang.org/tcl/info/c31ca233ca. - * Until that patch is broadly distributed, disable the - * assertion checking here, so that programs using Tcl and - * tls can be debugged. - - assert(!calledFromAsyncFlush); - */ + /* TODO: If code reaches this point, it means a writable + * event is being handled on the channel, but the channel + * could not in fact be written to. This ought not happen, + * but Unix pipes appear to act this way (see io-53.4). + * Also can imagine broken reflected channels. */ } } @@ -3024,24 +2765,7 @@ FlushChannel( ((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))) { - errorCode = CloseChannelPart(interp, chanPtr, errorCode, - TCL_CLOSE_WRITE); - goto done; - } - done: TclChannelRelease((Tcl_Channel)chanPtr); return errorCode; @@ -3118,7 +2842,7 @@ CloseChannel( int dummy; char c = (char) statePtr->outEofChar; - (void) ChanWrite(chanPtr, &c, 1, &dummy); + (chanPtr->typePtr->outputProc)(chanPtr->instanceData, &c, 1, &dummy); } /* @@ -3129,7 +2853,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; @@ -3146,7 +2870,12 @@ CloseChannel( * This may leave a TIP #219 error message in the interp. */ - result = ChanClose(chanPtr, interp); + if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) { + result = (chanPtr->typePtr->closeProc)(chanPtr->instanceData, interp); + } else { + result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, + interp, 0); + } /* * Some resources can be cleared only if the bottom channel in a stack is @@ -3155,7 +2884,7 @@ CloseChannel( if (chanPtr == statePtr->bottomChanPtr) { if (statePtr->channelName != NULL) { - ckfree(statePtr->channelName); + ckfree((char *) statePtr->channelName); statePtr->channelName = NULL; } @@ -3181,7 +2910,7 @@ CloseChannel( statePtr->chanMsg = NULL; } if (interp) { - Tcl_SetChannelErrorInterp(interp, statePtr->unreportedMsg); + Tcl_SetChannelErrorInterp(interp,statePtr->unreportedMsg); } } if (errorCode == 0) { @@ -3194,8 +2923,8 @@ CloseChannel( /* * Cancel any outstanding timer. */ - DeleteTimerHandler(statePtr); + Tcl_DeleteTimerHandler(statePtr->timer); /* * Mark the channel as deleted by clearing the type structure. @@ -3247,8 +2976,8 @@ CloseChannel( * The channel to cut out of the list must not be referenced in any * interpreter. This is something this procedure cannot check (despite * the refcount) because the caller usually wants fiddle with the channel - * (like transferring it to a different thread) and thus keeps the - * refcount artificially high to prevent its destruction. + * (like transfering it to a different thread) and thus keeps the + * refcount artifically high to prevent its destruction. * *---------------------------------------------------------------------- */ @@ -3264,6 +2993,7 @@ 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 @@ -3290,10 +3020,11 @@ CutChannel( * TIP #218, Channel Thread Actions */ - ChanThreadAction((Channel *) chan, TCL_CHANNEL_THREAD_REMOVE); - - /* Channel is not managed by any thread */ - statePtr->managingThread = NULL; + threadActionProc = Tcl_ChannelThreadActionProc(Tcl_GetChannelType(chan)); + if (threadActionProc != NULL) { + (*threadActionProc)(Tcl_GetChannelInstanceData(chan), + TCL_CHANNEL_THREAD_REMOVE); + } } void @@ -3308,6 +3039,7 @@ 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 @@ -3335,12 +3067,14 @@ Tcl_CutChannel( * For all transformations and the base channel. */ - for (; chanPtr != NULL ; chanPtr = chanPtr->upChanPtr) { - ChanThreadAction(chanPtr, TCL_CHANNEL_THREAD_REMOVE); + while (chanPtr) { + threadActionProc = Tcl_ChannelThreadActionProc(chanPtr->typePtr); + if (threadActionProc != NULL) { + (*threadActionProc)(chanPtr->instanceData, + TCL_CHANNEL_THREAD_REMOVE); + } + chanPtr= chanPtr->upChanPtr; } - - /* Channel is not managed by any thread */ - statePtr->managingThread = NULL; } /* @@ -3362,9 +3096,9 @@ Tcl_CutChannel( * NOTE: * The channel to splice into the list must not be referenced in any * interpreter. This is something this procedure cannot check (despite - * the refcount) because the caller usually wants fiddle with the channel - * (like transferring it to a different thread) and thus keeps the - * refcount artificially high to prevent its destruction. + * the refcount) because the caller usually wants figgle with the channel + * (like transfering it to a different thread) and thus keeps the + * refcount artifically high to prevent its destruction. * *---------------------------------------------------------------------- */ @@ -3376,6 +3110,7 @@ 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"); @@ -3396,7 +3131,11 @@ SpliceChannel( * TIP #218, Channel Thread Actions */ - ChanThreadAction((Channel *) chan, TCL_CHANNEL_THREAD_INSERT); + threadActionProc = Tcl_ChannelThreadActionProc(Tcl_GetChannelType(chan)); + if (threadActionProc != NULL) { + (*threadActionProc) (Tcl_GetChannelInstanceData(chan), + TCL_CHANNEL_THREAD_INSERT); + } } void @@ -3407,6 +3146,7 @@ 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"); @@ -3428,8 +3168,13 @@ Tcl_SpliceChannel( * For all transformations and the base channel. */ - for (; chanPtr != NULL ; chanPtr = chanPtr->upChanPtr) { - ChanThreadAction(chanPtr, TCL_CHANNEL_THREAD_INSERT); + while (chanPtr) { + threadActionProc = Tcl_ChannelThreadActionProc(chanPtr->typePtr); + if (threadActionProc != NULL) { + (*threadActionProc)(chanPtr->instanceData, + TCL_CHANNEL_THREAD_INSERT); + } + chanPtr= chanPtr->upChanPtr; } } @@ -3455,18 +3200,18 @@ Tcl_SpliceChannel( *---------------------------------------------------------------------- */ + /* ARGSUSED */ int Tcl_Close( Tcl_Interp *interp, /* Interpreter for errors. */ Tcl_Channel chan) /* The channel being closed. Must not be - * referenced in any interpreter. May be NULL, - * in which case this is a no-op. */ + * referenced in any interpreter. */ { CloseCallback *cbPtr; /* Iterate over close callbacks for this * channel. */ Channel *chanPtr; /* The real IO channel. */ ChannelState *statePtr; /* State of real IO channel. */ - int result = 0; /* Of calling FlushChannel. */ + int result; /* Of calling FlushChannel. */ int flushcode; int stickyError; @@ -3498,9 +3243,8 @@ Tcl_Close( if (GotFlag(statePtr, CHANNEL_INCLOSE)) { if (interp) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "illegal recursive call to close through close-handler" - " of channel", -1)); + Tcl_AppendResult(interp, "Illegal recursive call to close " + "through close-handler of channel", NULL); } return TCL_ERROR; } @@ -3515,6 +3259,7 @@ Tcl_Close( if (GotFlag(statePtr, TCL_WRITABLE) && (statePtr->encoding != NULL) && !(statePtr->outputEncodingFlags & TCL_ENCODING_START)) { + int code = CheckChannelErrors(statePtr, TCL_WRITABLE); if (code == 0) { @@ -3535,7 +3280,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; @@ -3545,19 +3290,14 @@ Tcl_Close( Tcl_ClearChannelHandlers(chan); /* - * Cancel any outstanding timer. - */ - DeleteTimerHandler(statePtr); - - /* * Invoke the registered close callbacks and delete their records. */ while (statePtr->closeCbPtr != NULL) { cbPtr = statePtr->closeCbPtr; statePtr->closeCbPtr = cbPtr->nextPtr; - cbPtr->proc(cbPtr->clientData); - ckfree(cbPtr); + (cbPtr->proc)(cbPtr->clientData); + ckfree((char *) cbPtr); } ResetFlag(statePtr, CHANNEL_INCLOSE); @@ -3567,20 +3307,12 @@ Tcl_Close( * it anymore and this will help avoid deadlocks on some channel types. */ -#ifndef TCL_NO_DEPRECATED - if ((chanPtr->typePtr->closeProc == TCL_CLOSE2PROC) || (chanPtr->typePtr->closeProc == NULL)) { - /* If this half-close gives a EINVAL or ENOTCONN, just continue the full close */ - result = chanPtr->typePtr->close2Proc(chanPtr->instanceData, interp, TCL_CLOSE_READ); - if ((result == EINVAL) || result == ENOTCONN) { - result = 0; - } - } -#else - result = chanPtr->typePtr->close2Proc(chanPtr->instanceData, interp, TCL_CLOSE_READ); - if ((result == EINVAL) || result == ENOTCONN) { + if (chanPtr->typePtr->closeProc == TCL_CLOSE2PROC) { + result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, interp, + TCL_CLOSE_READ); + } else { result = 0; } -#endif /* * The call to FlushChannel will flush any queued output and invoke the @@ -3617,368 +3349,19 @@ 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) { - /* flushcode has precedence, if available */ - result = flushcode; - } - if ((result != 0) && (result != TCL_ERROR) && (interp != NULL) - && 0 == TclGetCharLength(Tcl_GetObjResult(interp))) { - Tcl_SetErrno(result); + if (flushcode != 0 && interp != NULL + && 0 == Tcl_GetCharLength(Tcl_GetObjResult(interp)) ) { + Tcl_SetErrno(flushcode); Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_PosixError(interp), -1)); } - if (result != 0) { - return TCL_ERROR; - } - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_CloseEx -- - * - * Closes one side of a channel, read or write, close all. - * - * Results: - * A standard Tcl result. - * - * Side effects: - * Closes one direction of the channel, or do a full close. - * - * NOTE: - * Tcl_CloseEx closes the specified direction of the channel as far as - * the user is concerned. If flags = 0, this is equivalent to Tcl_Close. - * - *---------------------------------------------------------------------- - */ - -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; - } - - chanPtr = (Channel *) chan; - statePtr = chanPtr->state; - - if ((flags & (TCL_READABLE | TCL_WRITABLE)) == 0) { - return Tcl_Close(interp, chan); - } - if ((flags & (TCL_READABLE | TCL_WRITABLE)) == (TCL_READABLE | TCL_WRITABLE)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "double-close of channels not supported by %ss", - chanPtr->typePtr->typeName)); - return TCL_ERROR; - } - - /* - * Does the channel support half-close anyway? Error if not. - */ - - if (!chanPtr->typePtr->close2Proc) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "half-close of channels not supported by %ss", - chanPtr->typePtr->typeName)); - return TCL_ERROR; - } - - /* - * Is the channel unstacked ? If not we fail. - */ - - if (chanPtr != statePtr->topChanPtr) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "half-close not applicable to stack of transformations", -1)); - 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 (!(GotFlag(statePtr, TCL_READABLE|TCL_WRITABLE) & flags)) { - const char *msg; - - if (flags & TCL_CLOSE_READ) { - msg = "read"; - } else { - msg = "write"; - } - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "Half-close of %s-side not possible, side not opened or" - " already closed", msg)); - return TCL_ERROR; - } - - /* - * A user may try to call half-close from within a channel close handler. - * That won't do. - */ - - if (GotFlag(statePtr, CHANNEL_INCLOSE)) { - if (interp) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "illegal recursive call to close through close-handler" - " of channel", -1)); - } - 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) { - 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 output 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 callbacks are run - channel is still open (read side) - */ - - ChannelState *statePtr = chanPtr->state; - /* State of real IO channel. */ - int flushcode; - int result = 0; - - /* - * 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 = chanPtr->typePtr->close2Proc(chanPtr->instanceData, NULL, 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; } @@ -4022,7 +3405,8 @@ Tcl_ClearChannelHandlers( /* * Cancel any outstanding timer. */ - DeleteTimerHandler(statePtr); + + Tcl_DeleteTimerHandler(statePtr->timer); /* * Remove any references to channel handlers for this channel that may be @@ -4043,7 +3427,7 @@ Tcl_ClearChannelHandlers( for (chPtr = statePtr->chPtr; chPtr != NULL; chPtr = chNext) { chNext = chPtr->nextPtr; - ckfree(chPtr); + ckfree((char *) chPtr); } statePtr->chPtr = NULL; @@ -4055,10 +3439,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; @@ -4070,7 +3454,7 @@ Tcl_ClearChannelHandlers( for (ePtr = statePtr->scriptRecordPtr; ePtr != NULL; ePtr = eNextPtr) { eNextPtr = ePtr->nextPtr; TclDecrRefCount(ePtr->scriptPtr); - ckfree(ePtr); + ckfree((char *) ePtr); } statePtr->scriptRecordPtr = NULL; } @@ -4089,8 +3473,8 @@ Tcl_ClearChannelHandlers( * No encoding conversions are applied to the bytes being read. * * Results: - * The number of bytes written or TCL_INDEX_NONE in case of error. If - * TCL_INDEX_NONE, Tcl_GetErrno will return the error code. + * The number of bytes written or -1 in case of error. If -1, + * Tcl_GetErrno will return the error code. * * Side effects: * May buffer up output and may cause output to be produced on the @@ -4099,11 +3483,11 @@ Tcl_ClearChannelHandlers( *---------------------------------------------------------------------- */ -Tcl_Size +int Tcl_Write( Tcl_Channel chan, /* The channel to buffer output for. */ const char *src, /* Data to queue in output buffer. */ - Tcl_Size srcLen) /* Length of data in bytes, or < 0 for + int srcLen) /* Length of data in bytes, or < 0 for * strlen(). */ { /* @@ -4117,14 +3501,14 @@ Tcl_Write( chanPtr = statePtr->topChanPtr; if (CheckChannelErrors(statePtr, TCL_WRITABLE) != 0) { - return TCL_INDEX_NONE; + return -1; } if (srcLen < 0) { srcLen = strlen(src); } if (WriteBytes(chanPtr, src, srcLen) < 0) { - return TCL_INDEX_NONE; + return -1; } return srcLen; } @@ -4143,8 +3527,8 @@ Tcl_Write( * No encoding conversions are applied to the bytes being read. * * Results: - * The number of bytes written or TCL_INDEX_NONE in case of error. If - * TCL_INDEX_NONE, Tcl_GetErrno will return the error code. + * The number of bytes written or -1 in case of error. If -1, + * Tcl_GetErrno will return the error code. * * Side effects: * May buffer up output and may cause output to be produced on the @@ -4153,21 +3537,20 @@ Tcl_Write( *---------------------------------------------------------------------- */ -Tcl_Size +int Tcl_WriteRaw( Tcl_Channel chan, /* The channel to buffer output for. */ const char *src, /* Data to queue in output buffer. */ - Tcl_Size srcLen) /* Length of data in bytes, or TCL_INDEX_NONE for + int srcLen) /* Length of data in bytes, or < 0 for * strlen(). */ { Channel *chanPtr = ((Channel *) chan); ChannelState *statePtr = chanPtr->state; /* State info for channel */ - int errorCode; - Tcl_Size written; + int errorCode, written; if (CheckChannelErrors(statePtr, TCL_WRITABLE | CHANNEL_RAW_MODE) != 0) { - return TCL_INDEX_NONE; + return -1; } if (srcLen < 0) { @@ -4179,7 +3562,9 @@ Tcl_WriteRaw( * The code was stolen from 'FlushChannel'. */ - written = ChanWrite(chanPtr, src, srcLen, &errorCode); + written = (chanPtr->typePtr->outputProc) (chanPtr->instanceData, + src, srcLen, &errorCode); + if (written < 0) { Tcl_SetErrno(errorCode); } @@ -4200,8 +3585,8 @@ Tcl_WriteRaw( * specified channel to the topmost channel in a stack. * * Results: - * The number of bytes written or TCL_INDEX_NONE in case of error. If - * TCL_INDEX_NONE, Tcl_GetErrno will return the error code. + * The number of bytes written or -1 in case of error. If -1, + * Tcl_GetErrno will return the error code. * * Side effects: * May buffer up output and may cause output to be produced on the @@ -4210,21 +3595,21 @@ Tcl_WriteRaw( *---------------------------------------------------------------------- */ -Tcl_Size +int Tcl_WriteChars( Tcl_Channel chan, /* The channel to buffer output for. */ const char *src, /* UTF-8 characters to queue in output * buffer. */ - Tcl_Size len) /* Length of string in bytes, or TCL_INDEX_NONE for + int len) /* Length of string in bytes, or < 0 for * strlen(). */ { Channel *chanPtr = (Channel *) chan; ChannelState *statePtr = chanPtr->state; /* State info for channel */ - Tcl_Size result; + int result; Tcl_Obj *objPtr; if (CheckChannelErrors(statePtr, TCL_WRITABLE) != 0) { - return TCL_INDEX_NONE; + return -1; } chanPtr = statePtr->topChanPtr; @@ -4239,7 +3624,7 @@ Tcl_WriteChars( /* * Inefficient way to convert UTF-8 to byte-array, but the code * parallels the way it is done for objects. Special case for 1-byte - * (used by e.g. [puts] for the \n) could be extended to more efficient + * (used by eg [puts] for the \n) could be extended to more efficient * translation of the src string. */ @@ -4269,8 +3654,8 @@ Tcl_WriteChars( * line buffering mode. * * Results: - * The number of bytes written or TCL_INDEX_NONE in case of error. If - * TCL_INDEX_NONE, Tcl_GetErrno() will return the error code. + * The number of bytes written or -1 in case of error. If -1, + * Tcl_GetErrno() will return the error code. * * Side effects: * May buffer up output and may cause output to be produced on the @@ -4279,7 +3664,7 @@ Tcl_WriteChars( *---------------------------------------------------------------------- */ -Tcl_Size +int Tcl_WriteObj( Tcl_Channel chan, /* The channel to buffer output for. */ Tcl_Obj *objPtr) /* The object to write. */ @@ -4290,14 +3675,14 @@ Tcl_WriteObj( Channel *chanPtr; ChannelState *statePtr; /* State info for channel */ - const char *src; - Tcl_Size srcLen; + char *src; + int srcLen; statePtr = ((Channel *) chan)->state; chanPtr = statePtr->topChanPtr; if (CheckChannelErrors(statePtr, TCL_WRITABLE) != 0) { - return TCL_INDEX_NONE; + return -1; } if (statePtr->encoding == NULL) { src = (char *) Tcl_GetByteArrayFromObj(objPtr, &srcLen); @@ -4308,55 +3693,42 @@ Tcl_WriteObj( } } -static void -WillWrite( - Channel *chanPtr) +static void WillWrite(Channel *chanPtr) { int inputBuffered; - if (((Tcl_ChannelWideSeekProc(chanPtr->typePtr) != NULL) -#ifndef TCL_NO_DEPRECATED - || (Tcl_ChannelSeekProc(chanPtr->typePtr) != NULL) -#endif - ) && ((inputBuffered = Tcl_InputBuffered((Tcl_Channel) chanPtr)) > 0)){ - int ignore; - - DiscardInputQueued(chanPtr->state, 0); - ChanSeek(chanPtr, -inputBuffered, SEEK_CUR, &ignore); + 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) +static int WillRead(Channel *chanPtr) { if (chanPtr->typePtr == NULL) { - /* - * Prevent read attempts on a closed channel. - */ - - DiscardInputQueued(chanPtr->state, 0); + /* Prevent read attempts on a closed channel */ + DiscardInputQueued(chanPtr->state, 0); Tcl_SetErrno(EINVAL); return -1; } - if (((Tcl_ChannelWideSeekProc(chanPtr->typePtr) != NULL) -#ifndef TCL_NO_DEPRECATED - || (Tcl_ChannelSeekProc(chanPtr->typePtr) != NULL) -#endif - ) && (Tcl_OutputBuffered((Tcl_Channel) chanPtr) > 0)) { + 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) { - return -1; - } + if (FlushChannel(NULL, chanPtr, 0) != 0) { + return -1; + } } return 0; } @@ -4372,7 +3744,7 @@ WillRead( * ready e.g. if it contains a newline and we are in line buffering mode. * * Results: - * The number of bytes written or TCL_INDEX_NONE in case of error. If TCL_INDEX_NONE, + * The number of bytes written or -1 in case of error. If -1, * Tcl_GetErrno will return the error code. * * Side effects: @@ -4382,20 +3754,17 @@ WillRead( *---------------------------------------------------------------------- */ -static Tcl_Size +static int Write( Channel *chanPtr, /* The channel to buffer output for. */ const char *src, /* UTF-8 string to write. */ - Tcl_Size srcLen, /* Length of UTF-8 string in bytes. */ + int srcLen, /* Length of UTF-8 string in bytes. */ Tcl_Encoding encoding) { ChannelState *statePtr = chanPtr->state; /* State info for channel */ char *nextNewLine = NULL; - int endEncoding, needNlFlush = 0; - int saved = 0, total = 0, flushed = 0; - char safe[BUFFER_PADDING]; - int encodingError = 0; + int endEncoding, saved = 0, total = 0, flushed = 0, needNlFlush = 0; if (srcLen) { WillWrite(chanPtr); @@ -4406,21 +3775,21 @@ Write( */ endEncoding = ((statePtr->outputEncodingFlags & TCL_ENCODING_END) != 0); + if (GotFlag(statePtr, CHANNEL_LINEBUFFERED) || (statePtr->outputTranslation != TCL_TRANSLATE_LF)) { - nextNewLine = (char *)memchr(src, '\n', srcLen); + nextNewLine = memchr(src, '\n', srcLen); } - while (srcLen + saved + endEncoding > 0 && !encodingError) { + while (srcLen + saved + endEncoding > 0) { ChannelBuffer *bufPtr; - char *dst; - int result, srcRead, dstLen, dstWrote; - Tcl_Size srcLimit = srcLen; + char *dst, safe[BUFFER_PADDING]; + int result, srcRead, dstLen, dstWrote, srcLimit = srcLen; if (nextNewLine) { srcLimit = nextNewLine - src; } - + /* Get space to write into */ bufPtr = statePtr->curOutPtr; if (bufPtr == NULL) { @@ -4433,10 +3802,11 @@ Write( * that we need to stick at the beginning of this buffer. */ - memcpy(InsertPoint(bufPtr), safe, saved); + memcpy(InsertPoint(bufPtr), safe, (size_t) saved); bufPtr->nextAdded += saved; saved = 0; } + PreserveChannelBuffer(bufPtr); dst = InsertPoint(bufPtr); dstLen = SpaceLeft(bufPtr); @@ -4445,32 +3815,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; - - /* - * See io-75.2, TCL bug 6978c01b65. - * Check, if an encoding error occured and should be reported to the - * script level. - * This happens, if a written character may not be represented by the - * current output encoding and strict encoding is active. - */ - - if (result == TCL_CONVERT_UNKNOWN || result == TCL_CONVERT_SYNTAX) { - encodingError = 1; - result = TCL_OK; - } - + if ((result != TCL_OK) && (srcRead + dstWrote == 0)) { - /* - * We're reading from invalid/incomplete UTF-8. - */ - - encodingError = 1; - result = TCL_OK; + /* We're reading from invalid/incomplete UTF-8 */ + ReleaseChannelBuffer(bufPtr); + if (total == 0) { + Tcl_SetErrno(EINVAL); + return -1; + } + break; } bufPtr->nextAdded += dstWrote; @@ -4502,12 +3857,13 @@ Write( Tcl_Panic("unknown output translation requested"); break; } - + 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++; @@ -4515,21 +3871,21 @@ Write( total += dstWrote; dst += dstWrote; dstLen -= dstWrote; - nextNewLine = (char *)memchr(src, '\n', srcLen); + nextNewLine = memchr(src, '\n', srcLen); needNlFlush = 1; } 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); - memcpy(safe, dst + dstLen, saved); + memcpy(safe, dst + dstLen, (size_t) saved); bufPtr->nextAdded = bufPtr->bufLength; } @@ -4539,39 +3895,34 @@ Write( if (IsBufferFull(bufPtr)) { if (FlushChannel(NULL, chanPtr, 0) != 0) { + ReleaseChannelBuffer(bufPtr); return -1; } flushed += statePtr->bufSize; /* - * We just flushed. So if we have needNlFlush set to record that - * we need to flush because there is 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; } } + ReleaseChannelBuffer(bufPtr); } - if (((flushed < total) && GotFlag(statePtr, CHANNEL_UNBUFFERED)) || - (needNlFlush && GotFlag(statePtr, CHANNEL_LINEBUFFERED))) { + if ((flushed < total) && (GotFlag(statePtr, CHANNEL_UNBUFFERED) || + (needNlFlush && GotFlag(statePtr, CHANNEL_LINEBUFFERED)))) { if (FlushChannel(NULL, chanPtr, 0) != 0) { return -1; } } - UpdateInterest(chanPtr); - - if (encodingError) { - Tcl_SetErrno(EILSEQ); - return -1; - } return total; } @@ -4583,8 +3934,8 @@ Write( * Reads a complete line of input from the channel into a Tcl_DString. * * Results: - * Length of line read (in characters) or TCL_INDEX_NONE if error, EOF, or blocked. - * If TCL_INDEX_NONE, use Tcl_GetErrno() to retrieve the POSIX error code for the + * Length of line read (in characters) or -1 if error, EOF, or blocked. + * If -1, use Tcl_GetErrno() to retrieve the POSIX error code for the * error or condition that occurred. * * Side effects: @@ -4594,7 +3945,7 @@ Write( *--------------------------------------------------------------------------- */ -Tcl_Size +int Tcl_Gets( Tcl_Channel chan, /* Channel from which to read. */ Tcl_DString *lineRead) /* The line read will be appended to this @@ -4603,12 +3954,14 @@ Tcl_Gets( * for managing the storage. */ { Tcl_Obj *objPtr; - Tcl_Size charsStored; + int charsStored, length; + char *string; TclNewObj(objPtr); charsStored = Tcl_GetsObj(chan, objPtr); if (charsStored > 0) { - TclDStringAppendObj(lineRead, objPtr); + string = TclGetStringFromObj(objPtr, &length); + Tcl_DStringAppend(lineRead, string, length); } TclDecrRefCount(objPtr); return charsStored; @@ -4624,8 +3977,8 @@ Tcl_Gets( * converted to UTF-8 using the encoding specified by the channel. * * Results: - * Number of characters accumulated in the object or TCL_INDEX_NONE if error, - * blocked, or EOF. If TCL_INDEX_NONE, use Tcl_GetErrno() to retrieve the POSIX error + * Number of characters accumulated in the object or -1 if error, + * blocked, or EOF. If -1, use Tcl_GetErrno() to retrieve the POSIX error * code for the error or condition that occurred. * * Side effects: @@ -4637,7 +3990,7 @@ Tcl_Gets( *--------------------------------------------------------------------------- */ -Tcl_Size +int Tcl_GetsObj( Tcl_Channel chan, /* Channel from which to read. */ Tcl_Obj *objPtr) /* The line read will be appended to this @@ -4648,21 +4001,13 @@ Tcl_GetsObj( ChannelState *statePtr = chanPtr->state; /* State info for channel */ ChannelBuffer *bufPtr; - int inEofChar, skip, copiedTotal, oldFlags, oldRemoved; - Tcl_Size oldLength; + int inEofChar, skip, copiedTotal, oldLength, oldFlags, oldRemoved; Tcl_Encoding encoding; char *dst, *dstEnd, *eol, *eof; Tcl_EncodingState oldState; - if (GotFlag(statePtr, CHANNEL_ENCODING_ERROR)) { - UpdateInterest(chanPtr); - ResetFlag(statePtr, CHANNEL_EOF|CHANNEL_ENCODING_ERROR); - Tcl_SetErrno(EILSEQ); - return TCL_INDEX_NONE; - } - if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) { - return TCL_INDEX_NONE; + return -1; } /* @@ -4672,12 +4017,12 @@ 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); - return TCL_INDEX_NONE; + return -1; } /* @@ -4749,12 +4094,6 @@ Tcl_GetsObj( ResetFlag(statePtr, CHANNEL_BLOCKED); while (1) { if (dst >= dstEnd) { - /* - * In case of encoding errors, state gets flag - * CHANNEL_ENCODING_ERROR set in the call below. First, the - * EOF/EOL condition is checked, as we may have valid data with - * EOF/EOL before the encoding error. - */ if (FilterInputBytes(chanPtr, &gs) != 0) { goto restore; } @@ -4805,12 +4144,12 @@ Tcl_GetsObj( /* * If a CR is at the end of the buffer, then check for a - * LF at the beginning of the next buffer, unless EOF char + * LF at the begining of the next buffer, unless EOF char * was found already. */ if (eol >= dstEnd) { - Tcl_Size offset; + int offset; if (eol != eof) { offset = eol - objPtr->bytes; @@ -4838,24 +4177,25 @@ Tcl_GetsObj( eol = dst; skip = 1; if (GotFlag(statePtr, INPUT_SAW_CR)) { + ResetFlag(statePtr, INPUT_SAW_CR); if ((eol < dstEnd) && (*eol == '\n')) { /* * Skip the raw bytes that make up the '\n'. */ + char tmp[1 + TCL_UTF_MAX]; int rawRead; - char tmp[TCL_UTF_MAX]; bufPtr = gs.bufPtr; Tcl_ExternalToUtf(NULL, gs.encoding, RemovePoint(bufPtr), - gs.rawRead, statePtr->inputEncodingFlags - | TCL_ENCODING_NO_TERMINATE, &gs.state, tmp, - sizeof(tmp), &rawRead, NULL, NULL); + gs.rawRead, statePtr->inputEncodingFlags, + &gs.state, tmp, 1 + TCL_UTF_MAX, &rawRead, NULL, + NULL); bufPtr->nextRemoved += rawRead; gs.rawRead -= rawRead; gs.bytesWrote--; gs.charsWrote--; - memmove(dst, dst + 1, dstEnd - dst); + memmove(dst, dst + 1, (size_t) (dstEnd - dst)); dstEnd--; } } @@ -4887,10 +4227,8 @@ Tcl_GetsObj( skip++; } eol--; - ResetFlag(statePtr, INPUT_SAW_CR); goto gotEOL; } else if (*eol == '\n') { - ResetFlag(statePtr, INPUT_SAW_CR); goto gotEOL; } } @@ -4919,32 +4257,10 @@ Tcl_GetsObj( Tcl_SetObjLength(objPtr, oldLength); CommonGetsCleanup(chanPtr); copiedTotal = -1; - ResetFlag(statePtr, CHANNEL_BLOCKED|INPUT_SAW_CR); + ResetFlag(statePtr, CHANNEL_BLOCKED); goto done; } goto gotEOL; - } else if (gs.bytesWrote == 0 - && GotFlag(statePtr, CHANNEL_ENCODING_ERROR)) { - /* Ticket c4eb46a1 Harald Oehlmann 2023-11-12 debugging session. - * In non blocking mode we loop indifenitly on a decoding error in - * this while-loop. - * Removed the following from the upper condition: - * "&& !GotFlag(statePtr, CHANNEL_NONBLOCKING)" - * In case of an encoding error with leading correct bytes, we pass here - * two times, as gs.bytesWrote is not 0 on the first pass. This feels - * once to much, as the data is anyway not used. - */ - - /* Set eol to the position that caused the encoding error, and then - * continue to gotEOL, which stores the data that was decoded - * without error to objPtr. This allows the caller to do something - * useful with the data decoded so far, and also results in the - * position of the file being the first byte that was not - * successfully decoded, allowing further processing at exactly that - * point, if desired. - */ - eol = dstEnd; - goto gotEOL; } dst = dstEnd; } @@ -4975,9 +4291,8 @@ Tcl_GetsObj( } statePtr->inputEncodingState = gs.state; Tcl_ExternalToUtf(NULL, gs.encoding, RemovePoint(bufPtr), gs.rawRead, - statePtr->inputEncodingFlags | TCL_ENCODING_NO_TERMINATE, - &statePtr->inputEncodingState, dst, - eol - dst + skip + TCL_UTF_MAX - 1, &gs.rawRead, NULL, + statePtr->inputEncodingFlags, &statePtr->inputEncodingState, dst, + eol - dst + skip + TCL_UTF_MAX, &gs.rawRead, NULL, &gs.charsWrote); bufPtr->nextRemoved += gs.rawRead; @@ -5042,18 +4357,17 @@ Tcl_GetsObj( */ done: - assert(!GotFlag(statePtr, CHANNEL_EOF) - || GotFlag(statePtr, CHANNEL_STICKY_EOF) - || GotFlag(statePtr, CHANNEL_ENCODING_ERROR) - || 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; @@ -5061,12 +4375,6 @@ Tcl_GetsObj( } UpdateInterest(chanPtr); TclChannelRelease((Tcl_Channel)chanPtr); - if (GotFlag(statePtr, CHANNEL_ENCODING_ERROR) && gs.bytesWrote == 0) { - bufPtr->nextRemoved = oldRemoved; - Tcl_SetErrno(EILSEQ); - copiedTotal = -1; - } - ResetFlag(statePtr, CHANNEL_ENCODING_ERROR); return copiedTotal; } @@ -5079,13 +4387,14 @@ 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 TCL_INDEX_NONE if error, - * blocked, or EOF. If TCL_INDEX_NONE, use Tcl_GetErrno() to retrieve the POSIX error + * Number of characters accumulated in the object or -1 if error, + * blocked, or EOF. If -1, use Tcl_GetErrno() to retrieve the POSIX error * code for the error or condition that occurred. * * Side effects: @@ -5107,9 +4416,8 @@ TclGetsObjBinary( ChannelState *statePtr = chanPtr->state; /* State info for channel */ ChannelBuffer *bufPtr; - int inEofChar, skip, copiedTotal, oldFlags, oldRemoved; - Tcl_Size rawLen, byteLen, oldLength; - int eolChar; + int inEofChar, skip, copiedTotal, oldLength, oldFlags, oldRemoved; + int rawLen, byteLen, eolChar; unsigned char *dst, *dstEnd, *eol, *eof, *byteArray; /* @@ -5138,18 +4446,14 @@ 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'; 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) { @@ -5161,11 +4465,10 @@ 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; } @@ -5175,15 +4478,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); @@ -5233,7 +4536,7 @@ TclGetsObjBinary( if ((dst == dstEnd) && (byteLen == oldLength)) { /* * If we didn't append any bytes before encountering EOF, - * caller needs to see TCL_INDEX_NONE. + * caller needs to see -1. */ byteArray = Tcl_SetByteArrayLength(objPtr, oldLength); @@ -5250,13 +4553,13 @@ 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; byteArray = Tcl_SetByteArrayLength(objPtr, byteLen + rawLen); - memcpy(byteArray + byteLen, dst, rawLen); + memcpy(byteArray + byteLen, dst, (size_t) rawLen); byteLen += rawLen; } @@ -5273,7 +4576,7 @@ TclGetsObjBinary( rawLen = eol - dst; byteArray = Tcl_SetByteArrayLength(objPtr, byteLen + rawLen); - memcpy(byteArray + byteLen, dst, rawLen); + memcpy(byteArray + byteLen, dst, (size_t) rawLen); byteLen += rawLen; bufPtr->nextRemoved += rawLen + skip; @@ -5335,11 +4638,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; @@ -5361,7 +4664,7 @@ TclGetsObjBinary( static void FreeBinaryEncoding( - TCL_UNUSED(void *)) + ClientData dummy) /* Not used */ { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); @@ -5372,7 +4675,7 @@ FreeBinaryEncoding( } static Tcl_Encoding -GetBinaryEncoding(void) +GetBinaryEncoding() { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); @@ -5472,15 +4775,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) ); } /* @@ -5516,18 +4819,10 @@ FilterInputBytes( *gsPtr->dstPtr = dst; } gsPtr->state = statePtr->inputEncodingState; - result = Tcl_ExternalToUtf(NULL, gsPtr->encoding, raw, rawLen, - statePtr->inputEncodingFlags | TCL_ENCODING_NO_TERMINATE, - &statePtr->inputEncodingState, dst, spaceLeft, &gsPtr->rawRead, - &gsPtr->bytesWrote, &gsPtr->charsWrote); - - if (result == TCL_CONVERT_UNKNOWN || result == TCL_CONVERT_SYNTAX) { - SetFlag(statePtr, CHANNEL_ENCODING_ERROR); - ResetFlag(statePtr, CHANNEL_STICKY_EOF); - ResetFlag(statePtr, CHANNEL_EOF); - result = TCL_OK; - } + statePtr->inputEncodingFlags, &statePtr->inputEncodingState, + dst, spaceLeft+1, &gsPtr->rawRead, &gsPtr->bytesWrote, + &gsPtr->charsWrote); /* * Make sure that if we go through 'gets', that we reset the @@ -5725,7 +5020,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; @@ -5748,7 +5043,7 @@ CommonGetsCleanup( * No encoding conversions are applied to the bytes being read. * * Results: - * The number of bytes read, or TCL_INDEX_NONE on error. Use Tcl_GetErrno() to + * The number of bytes read, or -1 on error. Use Tcl_GetErrno() to * retrieve the error code for the error that occurred. * * Side effects: @@ -5757,11 +5052,11 @@ CommonGetsCleanup( *---------------------------------------------------------------------- */ -Tcl_Size +int Tcl_Read( Tcl_Channel chan, /* The channel from which to read. */ char *dst, /* Where to store input read. */ - Tcl_Size bytesToRead) /* Maximum number of bytes to read. */ + int bytesToRead) /* Maximum number of bytes to read. */ { Channel *chanPtr = (Channel *) chan; ChannelState *statePtr = chanPtr->state; @@ -5774,10 +5069,10 @@ Tcl_Read( chanPtr = statePtr->topChanPtr; if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) { - return TCL_INDEX_NONE; + return -1; } - return DoRead(chanPtr, dst, bytesToRead, 0); + return DoRead(chanPtr, dst, bytesToRead); } /* @@ -5793,7 +5088,7 @@ Tcl_Read( * No encoding conversions are applied to the bytes being read. * * Results: - * The number of bytes read, or TCL_INDEX_NONE on error. Use Tcl_GetErrno() to + * The number of bytes read, or -1 on error. Use Tcl_GetErrno() to * retrieve the error code for the error that occurred. * * Side effects: @@ -5802,11 +5097,11 @@ Tcl_Read( *---------------------------------------------------------------------- */ -Tcl_Size +int Tcl_ReadRaw( Tcl_Channel chan, /* The channel from which to read. */ char *readBuf, /* Where to store input read. */ - Tcl_Size bytesToRead) /* Maximum number of bytes to read. */ + int bytesToRead) /* Maximum number of bytes to read. */ { Channel *chanPtr = (Channel *) chan; ChannelState *statePtr = chanPtr->state; @@ -5815,12 +5110,10 @@ Tcl_ReadRaw( assert(bytesToRead > 0); if (CheckChannelErrors(statePtr, TCL_READABLE | CHANNEL_RAW_MODE) != 0) { - return TCL_INDEX_NONE; + 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; @@ -5828,19 +5121,15 @@ 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), toCopy); + memcpy(readBuf, RemovePoint(bufPtr), (size_t) toCopy); bufPtr->nextRemoved += toCopy; copied += toCopy; 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; @@ -5852,43 +5141,40 @@ Tcl_ReadRaw( } /* - * Go to the driver only if we got nothing from pushback. Have to do it - * this way to avoid EOF mistimings 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. - */ - + /* This test not needed. */ if (bytesToRead > 0) { + int nread = ChanRead(chanPtr, readBuf, bytesToRead); - if (nread < 0) { + if (nread > 0) { + /* 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; } - } else if (nread > 0) { - /* - * Successful read (short is OK) - add to bytes copied. - */ - - copied += nread; } else { /* * nread == 0. Driver is at EOF. Let that state filter up. @@ -5911,7 +5197,7 @@ Tcl_ReadRaw( * object. * * Results: - * The number of characters read, or TCL_INDEX_NONE on error. Use Tcl_GetErrno() to + * The number of characters read, or -1 on error. Use Tcl_GetErrno() to * retrieve the error code for the error that occurred. * * Side effects: @@ -5920,12 +5206,12 @@ Tcl_ReadRaw( *--------------------------------------------------------------------------- */ -Tcl_Size +int Tcl_ReadChars( Tcl_Channel chan, /* The channel to read. */ Tcl_Obj *objPtr, /* Input data is stored in this object. */ - Tcl_Size toRead, /* Maximum number of characters to store, or - * TCL_INDEX_NONE to read all available data (up to EOF or + int toRead, /* Maximum number of characters to store, or + * -1 to read all available data (up to EOF or * when channel blocks). */ int appendFlag) /* If non-zero, data read from the channel * will be appended to the object. Otherwise, @@ -5949,10 +5235,10 @@ Tcl_ReadChars( */ UpdateInterest(chanPtr); - return TCL_INDEX_NONE; + return -1; } - return DoReadChars(chanPtr, objPtr, toRead, 0, appendFlag); + return DoReadChars(chanPtr, objPtr, toRead, appendFlag); } /* *--------------------------------------------------------------------------- @@ -5967,7 +5253,7 @@ Tcl_ReadChars( * object. * * Results: - * The number of characters read, or TCL_INDEX_NONE on error. Use Tcl_GetErrno() to + * The number of characters read, or -1 on error. Use Tcl_GetErrno() to * retrieve the error code for the error that occurred. * * Side effects: @@ -5976,14 +5262,13 @@ Tcl_ReadChars( *--------------------------------------------------------------------------- */ -static Tcl_Size +static int DoReadChars( Channel *chanPtr, /* The channel to read. */ Tcl_Obj *objPtr, /* Input data is stored in this object. */ - Tcl_Size toRead, /* Maximum number of characters to store, or - * TCL_INDEX_NONE to read all available data (up to EOF or + int toRead, /* Maximum number of characters to store, or + * -1 to read all available data (up to EOF or * when channel blocks). */ - int allowShortReads, /* Allow half-blocking (pipes,sockets) */ int appendFlag) /* If non-zero, data read from the channel * will be appended to the object. Otherwise, * the data will replace the existing contents @@ -5992,47 +5277,59 @@ DoReadChars( ChannelState *statePtr = chanPtr->state; /* State info for channel */ ChannelBuffer *bufPtr; - Tcl_Size copied; - int result; + int copied, copiedNow, result; Tcl_Encoding encoding = statePtr->encoding; int binaryMode; #define UTF_EXPANSION_FACTOR 1024 int factor = UTF_EXPANSION_FACTOR; - if (GotFlag(statePtr, CHANNEL_ENCODING_ERROR)) { - ResetFlag(statePtr, CHANNEL_EOF|CHANNEL_ENCODING_ERROR); - /* TODO: UpdateInterest not needed here? */ - UpdateInterest(chanPtr); - Tcl_SetErrno(EILSEQ); - return -1; + binaryMode = (encoding == NULL) + && (statePtr->inputTranslation == TCL_TRANSLATE_LF) + && (statePtr->inEofChar == '\0'); + + if (appendFlag == 0) { + if (binaryMode) { + Tcl_SetByteArrayLength(objPtr, 0); + } else { + Tcl_SetObjLength(objPtr, 0); + + /* + * We're going to access objPtr->bytes directly, so we must ensure + * that this is actually a string object (otherwise it might have + * been pure Unicode). + * + * Probably not needed anymore. + */ + + TclGetString(objPtr); + } } + /* * 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: UpdateInterest not needed here? */ + /* 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; } ResetFlag(statePtr, CHANNEL_BLOCKED|CHANNEL_EOF); statePtr->inputEncodingFlags &= ~TCL_ENCODING_END; - /* TODO: UpdateInterest not needed here? */ + /* TODO: We don't need this call? */ UpdateInterest(chanPtr); return 0; } @@ -6044,43 +5341,14 @@ DoReadChars( chanPtr = statePtr->topChanPtr; TclChannelPreserve((Tcl_Channel)chanPtr); - binaryMode = (encoding == NULL) - && (statePtr->inputTranslation == TCL_TRANSLATE_LF) - && (statePtr->inEofChar == '\0'); - - if (appendFlag) { - if (binaryMode && (NULL == Tcl_GetBytesFromObj(NULL, objPtr, NULL))) { - binaryMode = 0; - } - } else { - if (binaryMode) { - Tcl_SetByteArrayLength(objPtr, 0); - } else { - Tcl_SetObjLength(objPtr, 0); - - /* - * We're going to access objPtr->bytes directly, so we must ensure - * that this is actually a string object (otherwise it might have - * been pure Unicode). - * - * Probably not needed anymore. - */ - - TclGetString(objPtr); - } - } - - /* - * Must clear the BLOCKED|EOF flags here since we check before reading. - */ - + /* Must clear the BLOCKED flag here since we check before reading */ if (GotFlag(statePtr, CHANNEL_EOF)) { statePtr->inputEncodingFlags |= TCL_ENCODING_START; } ResetFlag(statePtr, CHANNEL_BLOCKED|CHANNEL_EOF); statePtr->inputEncodingFlags &= ~TCL_ENCODING_END; - for (copied = 0; toRead != 0 ; ) { - int copiedNow = -1; + for (copied = 0; (unsigned) toRead > 0; ) { + copiedNow = -1; if (statePtr->inQueueHead != NULL) { if (binaryMode) { copiedNow = ReadBytes(statePtr, objPtr, toRead); @@ -6089,40 +5357,28 @@ DoReadChars( } /* - * Recycle current buffer if empty. + * If the current buffer is empty recycle it. */ bufPtr = statePtr->inQueueHead; if (IsBufferEmpty(bufPtr)) { - ChannelBuffer *nextPtr = bufPtr->nextPtr; + ChannelBuffer *nextPtr; + nextPtr = bufPtr->nextPtr; RecycleBuffer(statePtr, bufPtr, 0); statePtr->inQueueHead = nextPtr; if (nextPtr == NULL) { statePtr->inQueueTail = NULL; } } - - /* - * If CHANNEL_ENCODING_ERROR and CHANNEL_STICKY_EOF are both set, - * then CHANNEL_ENCODING_ERROR was caused by data that occurred - * after the EOF character was encountered, so it doesn't count as - * a real error. - */ - - if (GotFlag(statePtr, CHANNEL_ENCODING_ERROR) - && !GotFlag(statePtr, CHANNEL_STICKY_EOF) - && (!GotFlag(statePtr, CHANNEL_NONBLOCKING))) { - goto finish; - } } if (copiedNow < 0) { if (GotFlag(statePtr, CHANNEL_EOF)) { break; } - if ((GotFlag(statePtr, CHANNEL_NONBLOCKING) || allowShortReads) - && GotFlag(statePtr, CHANNEL_BLOCKED)) { + if (GotFlag(statePtr, CHANNEL_NONBLOCKING|CHANNEL_BLOCKED) + == (CHANNEL_NONBLOCKING|CHANNEL_BLOCKED)) { break; } result = GetInput(chanPtr); @@ -6143,22 +5399,19 @@ DoReadChars( } } -finish: /* - * 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); } /* - * Regenerate chanPtr in case it was changed due to + * 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; @@ -6169,26 +5422,12 @@ finish: * 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) - || GotFlag(statePtr, CHANNEL_ENCODING_ERROR) - || 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); - - /* This must comes after UpdateInterest(), which may set errno */ - if (GotFlag(statePtr, CHANNEL_ENCODING_ERROR) - && (!copied || !GotFlag(statePtr, CHANNEL_NONBLOCKING))) { - /* Channel either is blocking or is nonblocking with no data - * succesfully red before the error. Return an error so that callers - * like [read] can also return an error. - */ - ResetFlag(statePtr, CHANNEL_EOF|CHANNEL_ENCODING_ERROR); - Tcl_SetErrno(EILSEQ); - copied = -1; - } TclChannelRelease((Tcl_Channel)chanPtr); return copied; } @@ -6208,7 +5447,7 @@ finish: * * Results: * The return value is the number of bytes appended to the object, or - * TCL_INDEX_NONE to indicate that zero bytes were read due to an EOF. + * -1 to indicate that zero bytes were read due to an EOF. * * Side effects: * The storage of bytes in objPtr can cause (re-)allocation of memory. @@ -6224,7 +5463,7 @@ ReadBytes( * been allocated to hold data, not how many * bytes of data have been stored in the * object. */ - int bytesToRead) /* Maximum number of bytes to store, or -1 to + int bytesToRead) /* Maximum number of bytes to store, or < 0 to * get all available bytes. Bytes are obtained * from the first buffer in the queue - even * if this number is larger than the number of @@ -6277,13 +5516,13 @@ ReadChars( * allocated to hold data, not how many bytes * of data have been stored in the object. */ int charsToRead, /* Maximum number of characters to store, or - * TCL_INDEX_NONE to get all available characters. + * -1 to get all available characters. * Characters are obtained from the first * buffer in the queue -- even if this number * is larger than the number of characters * available in the first buffer, only the * characters from the first buffer are - * returned. The exception is when there is + * returned. The execption is when there is * not any complete character in the first * buffer. In that case, a recursive call * effectively obtains chars from the @@ -6301,99 +5540,84 @@ ReadChars( int savedIEFlags = statePtr->inputEncodingFlags; int savedFlags = statePtr->flags; char *dst, *src = RemovePoint(bufPtr); - Tcl_Size numBytes; - int srcLen = BytesLeft(bufPtr); + int dstLimit, 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; + int toRead = ((unsigned) charsToRead > (unsigned) srcLen) ? srcLen : charsToRead; /* * 'factor' is how much we guess that the bytes in the source buffer will * expand when converted to UTF-8 chars. This guess comes from analyzing * how many characters were produced by the previous pass. */ - + int factor = *factorPtr; - int dstLimit = TCL_UTF_MAX - 1 + toRead * factor / UTF_EXPANSION_FACTOR; + int dstNeeded = TCL_UTF_MAX - 1 + toRead * factor / UTF_EXPANSION_FACTOR; - if (dstLimit <= 0) dstLimit = INT_MAX; /* avoid overflow */ (void) TclGetStringFromObj(objPtr, &numBytes); - TclAppendUtfToUtf(objPtr, NULL, dstLimit); + Tcl_AppendToObj(objPtr, NULL, dstNeeded); if (toRead == srcLen) { unsigned int size; - dst = TclGetStringStorage(objPtr, &size) + numBytes; - dstLimit = size - numBytes; + dstNeeded = size - numBytes; } else { dst = TclGetString(objPtr) + numBytes; } /* - * 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. */ + dstLimit = dstNeeded + 1; while (1) { int dstDecoded, dstRead, dstWrote, srcRead, numChars, code; - int flags = statePtr->inputEncodingFlags | TCL_ENCODING_NO_TERMINATE; - - if (charsToRead > 0) { - flags |= TCL_ENCODING_CHAR_LIMIT; - numChars = charsToRead; - } /* - * 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 || (statePtr->inputEncodingFlags & TCL_ENCODING_END) == 0); code = Tcl_ExternalToUtf(NULL, encoding, src, srcLen, - flags, &statePtr->inputEncodingState, + statePtr->inputEncodingFlags, &statePtr->inputEncodingState, dst, dstLimit, &srcRead, &dstDecoded, &numChars); - if (code == TCL_CONVERT_UNKNOWN || code == TCL_CONVERT_SYNTAX - || ( - code == TCL_CONVERT_MULTIBYTE - && GotFlag(statePtr, CHANNEL_EOF - )) - ) { - SetFlag(statePtr, CHANNEL_ENCODING_ERROR); - code = TCL_OK; - } - /* - * 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; @@ -6401,9 +5625,11 @@ 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) { @@ -6412,41 +5638,43 @@ 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 TCL_INDEX_NONE 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 TCL_INDEX_NONE 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 + (TCL_UTF_MAX - 1); + dstLimit = dstRead + TCL_UTF_MAX; statePtr->flags = savedFlags; statePtr->inputEncodingFlags = savedIEFlags; statePtr->inputEncodingState = savedState; @@ -6455,9 +5683,10 @@ 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'); @@ -6465,13 +5694,13 @@ 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 + (TCL_UTF_MAX - 1); + dstLimit = dstRead + TCL_UTF_MAX; statePtr->flags = savedFlags; statePtr->inputEncodingFlags = savedIEFlags; statePtr->inputEncodingState = savedState; @@ -6482,16 +5711,16 @@ 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) { + char buffer[TCL_UTF_MAX + 2]; int read, decoded, count; - char buffer[TCL_UTF_MAX + 1]; - /* + /* * Didn't get everything the buffer could offer */ @@ -6504,9 +5733,8 @@ ReadChars( (statePtr->inputEncodingFlags & TCL_ENCODING_END)); Tcl_ExternalToUtf(NULL, encoding, src, srcLen, - (statePtr->inputEncodingFlags | TCL_ENCODING_NO_TERMINATE), - &statePtr->inputEncodingState, buffer, sizeof(buffer), - &read, &decoded, &count); + statePtr->inputEncodingFlags, &statePtr->inputEncodingState, + buffer, TCL_UTF_MAX + 2, &read, &decoded, &count); if (count == 2) { if (buffer[1] == '\n') { @@ -6518,6 +5746,7 @@ ReadChars( bufPtr->nextRemoved += srcRead; } + dst[1] = '\0'; statePtr->inputEncodingFlags &= ~TCL_ENCODING_START; Tcl_SetObjLength(objPtr, numBytes + 1); @@ -6525,9 +5754,10 @@ ReadChars( } } else if (GotFlag(statePtr, 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'; @@ -6537,8 +5767,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; @@ -6546,25 +5776,24 @@ ReadChars( /* FALL THROUGH - get more data (dstWrote == 0) */ } - /* - * 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); if (charsToRead > 0 && numChars > charsToRead) { - /* - * 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 = TclUtfAtIndex(dst, charsToRead) - dst + (TCL_UTF_MAX - 1); + dstLimit = Tcl_UtfAtIndex(dst, charsToRead) + TCL_UTF_MAX - dst; statePtr->flags = savedFlags; statePtr->inputEncodingFlags = savedIEFlags; statePtr->inputEncodingState = savedState; @@ -6574,34 +5803,33 @@ 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') { + if (dst[0] == '\n') { assert(statePtr->inputTranslation == TCL_TRANSLATE_AUTO); + assert(dstRead == 1); 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; @@ -6616,23 +5844,23 @@ 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) { + if (nextPtr->nextRemoved - srcLen < 0) { Tcl_Panic("Buffer Underflow, BUFFER_PADDING not enough"); } nextPtr->nextRemoved -= srcLen; - memcpy(RemovePoint(nextPtr), src, srcLen); + memcpy(RemovePoint(nextPtr), src, (size_t) srcLen); RecycleBuffer(statePtr, bufPtr, 0); statePtr->inQueueHead = nextPtr; Tcl_SetObjLength(objPtr, numBytes); @@ -6643,12 +5871,10 @@ 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; } @@ -6675,7 +5901,7 @@ ReadChars( *--------------------------------------------------------------------------- */ -static void +static void TranslateInputEOL( ChannelState *statePtr, /* Channel being read, for EOL translation and * EOF character. */ @@ -6696,27 +5922,22 @@ 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; } @@ -6729,7 +5950,7 @@ TranslateInputEOL( * created by the presence of the input eof char. */ - if ((eof = (const char *)memchr(srcStart, inEofChar, srcLen))) { + if ((eof = memchr(srcStart, inEofChar, srcLen))) { srcLen = eof - srcStart; } } @@ -6738,13 +5959,13 @@ TranslateInputEOL( case TCL_TRANSLATE_LF: case TCL_TRANSLATE_CR: if (dstStart != srcStart) { - memcpy(dstStart, srcStart, srcLen); + memcpy(dstStart, srcStart, (size_t) srcLen); } if (statePtr->inputTranslation == TCL_TRANSLATE_CR) { char *dst = dstStart; char *dstEnd = dstStart + srcLen; - while ((dst = (char *)memchr(dst, '\r', dstEnd - dst))) { + while ((dst = memchr(dst, '\r', dstEnd - dst))) { *dst++ = '\n'; } } @@ -6755,7 +5976,7 @@ TranslateInputEOL( char *dst = dstStart; int lesser = (dstLen < srcLen) ? dstLen : srcLen; - while ((crFound = (const char *)memchr(src, '\r', lesser))) { + while ((crFound = memchr(src, '\r', lesser))) { int numBytes = crFound - src; memmove(dst, src, numBytes); @@ -6795,7 +6016,7 @@ TranslateInputEOL( ResetFlag(statePtr, INPUT_SAW_CR); } lesser = (dstLen < srcLen) ? dstLen : srcLen; - while ((crFound = (const char *)memchr(src, '\r', lesser))) { + while ((crFound = memchr(src, '\r', lesser))) { int numBytes = crFound - src; memmove(dst, src, numBytes); @@ -6825,14 +6046,11 @@ TranslateInputEOL( * EOF character was seen in EOL translated range. Leave current file * position pointing at the EOF character, but don't store the EOF * character in the output string. - * - * If CHANNEL_ENCODING_ERROR is set, it can only be because of data - * encountered after the EOF character, so it is nonsense. Unset it. */ SetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF); statePtr->inputEncodingFlags |= TCL_ENCODING_END; - ResetFlag(statePtr, CHANNEL_BLOCKED|INPUT_SAW_CR|CHANNEL_ENCODING_ERROR); + ResetFlag(statePtr, CHANNEL_BLOCKED|INPUT_SAW_CR); } } @@ -6845,7 +6063,7 @@ TranslateInputEOL( * channel, at either the head or tail of the queue. * * Results: - * The number of bytes stored in the channel, or TCL_INDEX_NONE on error. + * The number of bytes stored in the channel, or -1 on error. * * Side effects: * Adds input to the input queue of a channel. @@ -6853,11 +6071,11 @@ TranslateInputEOL( *---------------------------------------------------------------------- */ -Tcl_Size +int Tcl_Ungets( Tcl_Channel chan, /* The channel for which to add the input. */ const char *str, /* The input itself. */ - Tcl_Size len, /* The length of the input. */ + int len, /* The length of the input. */ int atEnd) /* If non-zero, add at end of queue; otherwise * add at head of queue. */ { @@ -6881,7 +6099,7 @@ Tcl_Ungets( flags = statePtr->flags; if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) { - len = TCL_INDEX_NONE; + len = -1; goto done; } statePtr->flags = flags; @@ -6889,7 +6107,7 @@ Tcl_Ungets( /* * Clear the EOF flags, and clear the BLOCKED bit. */ - + if (GotFlag(statePtr, CHANNEL_EOF)) { statePtr->inputEncodingFlags |= TCL_ENCODING_START; } @@ -6898,7 +6116,7 @@ Tcl_Ungets( statePtr->inputEncodingFlags &= ~TCL_ENCODING_END; bufPtr = AllocChannelBuffer(len); - memcpy(InsertPoint(bufPtr), str, len); + memcpy(InsertPoint(bufPtr), str, (size_t) len); bufPtr->nextAdded += len; if (statePtr->inQueueHead == NULL) { @@ -6957,7 +6175,7 @@ Tcl_Flush( chanPtr = statePtr->topChanPtr; if (CheckChannelErrors(statePtr, TCL_WRITABLE) != 0) { - return TCL_ERROR; + return -1; } result = FlushChannel(NULL, chanPtr, 0); @@ -7046,13 +6264,13 @@ GetInput( ChannelState *statePtr = chanPtr->state; /* State info for channel */ - /* + /* * Verify that all callers know better than to call us when * it's recorded that the next char waiting to be read is the * 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 @@ -7066,21 +6284,24 @@ 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. - * - * 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. + * 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. + */ + + /* * 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 @@ -7088,6 +6309,7 @@ GetInput( */ if (chanPtr->inQueueHead != NULL) { + /* TODO: Tests to cover this. */ assert(statePtr->inQueueHead == NULL); @@ -7117,14 +6339,13 @@ 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. */ if ((bufPtr != NULL) - && (bufPtr->bufLength != statePtr->bufSize + BUFFER_PADDING)) { + && (bufPtr->bufLength - BUFFER_PADDING != statePtr->bufSize)) { ReleaseChannelBuffer(bufPtr); bufPtr = NULL; } @@ -7149,17 +6370,15 @@ GetInput( PreserveChannelBuffer(bufPtr); nread = ChanRead(chanPtr, InsertPoint(bufPtr), toRead); - ReleaseChannelBuffer(bufPtr); if (nread < 0) { result = Tcl_GetErrno(); } else { result = 0; - if (statePtr->inQueueTail != NULL) { - statePtr->inQueueTail->nextAdded += nread; - } + bufPtr->nextAdded += nread; } + ReleaseChannelBuffer(bufPtr); return result; } @@ -7181,10 +6400,10 @@ GetInput( *---------------------------------------------------------------------- */ -long long +Tcl_WideInt Tcl_Seek( Tcl_Channel chan, /* The channel on which to seek. */ - long long offset, /* Offset to seek to. */ + Tcl_WideInt offset, /* Offset to seek to. */ int mode) /* Relative to which location to seek? */ { Channel *chanPtr = (Channel *) chan; @@ -7194,13 +6413,13 @@ Tcl_Seek( int inputBuffered, outputBuffered; /* # bytes held in buffers. */ int result; /* Of device driver operations. */ - long long curPos; /* Position on the device. */ + Tcl_WideInt curPos; /* Position on the device. */ int wasAsync; /* Was the channel nonblocking before the seek * operation? If so, must restore to * non-blocking mode after the seek. */ if (CheckChannelErrors(statePtr, TCL_WRITABLE | TCL_READABLE) != 0) { - return -1; + return Tcl_LongAsWide(-1); } /* @@ -7211,7 +6430,7 @@ Tcl_Seek( */ if (CheckForDeadChannel(NULL, statePtr)) { - return -1; + return Tcl_LongAsWide(-1); } /* @@ -7225,13 +6444,9 @@ Tcl_Seek( * defined. This means that the channel does not support seeking. */ - if ((Tcl_ChannelWideSeekProc(chanPtr->typePtr) == NULL) -#ifndef TCL_NO_DEPRECATED - && (Tcl_ChannelSeekProc(chanPtr->typePtr) == NULL) -#endif - ) { + if (chanPtr->typePtr->seekProc == NULL) { Tcl_SetErrno(EINVAL); - return -1; + return Tcl_LongAsWide(-1); } /* @@ -7244,7 +6459,7 @@ Tcl_Seek( if ((inputBuffered != 0) && (outputBuffered != 0)) { Tcl_SetErrno(EFAULT); - return -1; + return Tcl_LongAsWide(-1); } /* @@ -7271,8 +6486,8 @@ Tcl_Seek( if (GotFlag(statePtr, CHANNEL_EOF)) { statePtr->inputEncodingFlags |= TCL_ENCODING_START; } - ResetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED | - INPUT_SAW_CR); + statePtr->flags &= + ~(CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED | INPUT_SAW_CR); statePtr->inputEncodingFlags &= ~TCL_ENCODING_END; /* @@ -7287,7 +6502,7 @@ Tcl_Seek( wasAsync = 1; result = StackSetBlockMode(chanPtr, TCL_MODE_BLOCKING); if (result != 0) { - return -1; + return Tcl_LongAsWide(-1); } ResetFlag(statePtr, CHANNEL_NONBLOCKING); if (GotFlag(statePtr, BG_FLUSH_SCHEDULED)) { @@ -7308,11 +6523,24 @@ Tcl_Seek( } else { /* * Now seek to the new position in the channel as requested by the - * caller. + * caller. Note that we prefer the wideSeekProc if that is available + * and non-NULL... */ - curPos = ChanSeek(chanPtr, offset, mode, &result); - if (curPos == -1) { + 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)); + } + if (curPos == Tcl_LongAsWide(-1)) { Tcl_SetErrno(result); } } @@ -7328,7 +6556,7 @@ Tcl_Seek( SetFlag(statePtr, CHANNEL_NONBLOCKING); result = StackSetBlockMode(chanPtr, TCL_MODE_NONBLOCKING); if (result != 0) { - return -1; + return Tcl_LongAsWide(-1); } } @@ -7354,7 +6582,7 @@ Tcl_Seek( *---------------------------------------------------------------------- */ -long long +Tcl_WideInt Tcl_Tell( Tcl_Channel chan) /* The channel to return pos for. */ { @@ -7365,10 +6593,10 @@ Tcl_Tell( int inputBuffered, outputBuffered; /* # bytes held in buffers. */ int result; /* Of calling device driver. */ - long long curPos; /* Position on device. */ + Tcl_WideInt curPos; /* Position on device. */ if (CheckChannelErrors(statePtr, TCL_WRITABLE | TCL_READABLE) != 0) { - return -1; + return Tcl_LongAsWide(-1); } /* @@ -7379,7 +6607,7 @@ Tcl_Tell( */ if (CheckForDeadChannel(NULL, statePtr)) { - return -1; + return Tcl_LongAsWide(-1); } /* @@ -7393,13 +6621,9 @@ Tcl_Tell( * defined. This means that the channel does not support seeking. */ - if ((Tcl_ChannelWideSeekProc(chanPtr->typePtr) == NULL) -#ifndef TCL_NO_DEPRECATED - && (Tcl_ChannelSeekProc(chanPtr->typePtr) == NULL) -#endif - ) { + if (chanPtr->typePtr->seekProc == NULL) { Tcl_SetErrno(EINVAL); - return -1; + return Tcl_LongAsWide(-1); } /* @@ -7410,16 +6634,28 @@ 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... */ - curPos = ChanSeek(chanPtr, 0, SEEK_CUR, &result); - if (curPos == -1) { + 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)); + } + if (curPos == Tcl_LongAsWide(-1)) { Tcl_SetErrno(result); - return -1; + return Tcl_LongAsWide(-1); } if (inputBuffered != 0) { @@ -7431,12 +6667,54 @@ Tcl_Tell( /* *--------------------------------------------------------------------------- * + * Tcl_SeekOld, Tcl_TellOld -- + * + * Backward-compatability 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; + + 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). * @@ -7449,7 +6727,7 @@ Tcl_Tell( int Tcl_TruncateChannel( Tcl_Channel chan, /* Channel to truncate. */ - long long length) /* Length to truncate it to. */ + Tcl_WideInt length) /* Length to truncate it to. */ { Channel *chanPtr = (Channel *) chan; Tcl_DriverTruncateProc *truncateProc = @@ -7466,7 +6744,7 @@ Tcl_TruncateChannel( return TCL_ERROR; } - if (!GotFlag(chanPtr->state, TCL_WRITABLE)) { + if (!(chanPtr->state->flags & 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. @@ -7478,7 +6756,7 @@ Tcl_TruncateChannel( /* * Seek first to force a total flush of all pending buffers and ditch any - * preread input data. + * pre-read input data. */ WillWrite(chanPtr); @@ -7538,7 +6816,7 @@ CheckChannelErrors( /* * TIP #219, Tcl Channel Reflection API. - * Move a deferred error message back into the channel bypass. + * Move a defered error message back into the channel bypass. */ if (statePtr->chanMsg != NULL) { @@ -7611,39 +6889,9 @@ Tcl_Eof( ChannelState *statePtr = ((Channel *) chan)->state; /* State of real channel structure. */ - if (GotFlag(statePtr, CHANNEL_ENCODING_ERROR)) { - return 0; - } return GotFlag(statePtr, CHANNEL_EOF) ? 1 : 0; } - -/* - *---------------------------------------------------------------------- - * - * TclChannelGetBlockingMode -- - * - * Returns 1 if the channel is in blocking mode (default), 0 otherwise. - * - * Results: - * 1 or 0, always. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -int -TclChannelGetBlockingMode( - Tcl_Channel chan) -{ - ChannelState *statePtr = ((Channel *) chan)->state; - /* State of real channel structure. */ - - return GotFlag(statePtr, CHANNEL_NONBLOCKING) ? 0 : 1; -} - /* *---------------------------------------------------------------------- * @@ -7703,7 +6951,7 @@ Tcl_InputBuffered( } /* - * Remember the bytes in the topmost pushback area. + * Don't forget the bytes in the topmost pushback area. */ for (bufPtr = statePtr->topChanPtr->inQueueHead; bufPtr != NULL; @@ -7746,7 +6994,7 @@ Tcl_OutputBuffered( bytesBuffered += BytesLeft(bufPtr); } if (statePtr->curOutPtr != NULL) { - ChannelBuffer *curOutPtr = statePtr->curOutPtr; + register ChannelBuffer *curOutPtr = statePtr->curOutPtr; if (IsBufferReady(curOutPtr)) { bytesBuffered += BytesLeft(curOutPtr); @@ -7811,7 +7059,7 @@ Tcl_ChannelBuffered( void Tcl_SetChannelBufferSize( Tcl_Channel chan, /* The channel whose buffer size to set. */ - Tcl_Size sz) /* The size to set. */ + int sz) /* The size to set. */ { ChannelState *statePtr; /* State of real channel structure. */ @@ -7820,9 +7068,9 @@ 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; @@ -7865,7 +7113,7 @@ Tcl_SetChannelBufferSize( *---------------------------------------------------------------------- */ -Tcl_Size +int Tcl_GetChannelBufferSize( Tcl_Channel chan) /* The channel for which to find the buffer * size. */ @@ -7893,7 +7141,7 @@ Tcl_GetChannelBufferSize( * Side effects: * An error message is generated in interp's result object to indicate - * that a command was invoked with a bad option. The message has the + * that a command was invoked with the a bad option. The message has the * form: * bad option "blah": should be one of * <...generic options...>+<...specific options...> @@ -7915,16 +7163,15 @@ Tcl_BadChannelOption( { if (interp != NULL) { const char *genericopt = - "blocking buffering buffersize encoding eofchar profile translation"; + "blocking buffering buffersize encoding eofchar translation"; const char **argv; - Tcl_Size argc, i; + int argc, i; Tcl_DString ds; - Tcl_Obj *errObj; Tcl_DStringInit(&ds); Tcl_DStringAppend(&ds, genericopt, -1); if (optionList && (*optionList)) { - TclDStringAppendLiteral(&ds, " "); + Tcl_DStringAppend(&ds, " ", 1); Tcl_DStringAppend(&ds, optionList, -1); } if (Tcl_SplitList(interp, Tcl_DStringValue(&ds), @@ -7932,16 +7179,15 @@ Tcl_BadChannelOption( Tcl_Panic("malformed option list in channel driver"); } Tcl_ResetResult(interp); - errObj = Tcl_ObjPrintf("bad option \"%s\": should be one of ", - optionName ? optionName : ""); + Tcl_AppendResult(interp, "bad option \"", optionName, + "\": should be one of ", NULL); argc--; for (i = 0; i < argc; i++) { - Tcl_AppendPrintfToObj(errObj, "-%s, ", argv[i]); + Tcl_AppendResult(interp, "-", argv[i], ", ", NULL); } - Tcl_AppendPrintfToObj(errObj, "or -%s", argv[i]); - Tcl_SetObjResult(interp, errObj); + Tcl_AppendResult(interp, "or -", argv[i], NULL); Tcl_DStringFree(&ds); - ckfree(argv); + ckfree((char *) argv); } Tcl_SetErrno(EINVAL); return TCL_ERROR; @@ -7975,7 +7221,7 @@ Tcl_GetChannelOption( Tcl_DString *dsPtr) /* Where to store value(s). */ { size_t len; /* Length of optionName string. */ - char optionVal[128]; /* Buffer for snprintf. */ + char optionVal[128]; /* Buffer for sprintf. */ Channel *chanPtr = (Channel *) chan; ChannelState *statePtr = chanPtr->state; /* State info for channel */ @@ -8003,9 +7249,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; } @@ -8082,10 +7328,9 @@ Tcl_GetChannelOption( if (statePtr->inEofChar == 0) { Tcl_DStringAppendElement(dsPtr, ""); } else { - char buf[2]; + char buf[4]; - buf[1] = '\0'; - buf[0] = statePtr->inEofChar; + sprintf(buf, "%c", statePtr->inEofChar); Tcl_DStringAppendElement(dsPtr, buf); } } @@ -8093,10 +7338,9 @@ Tcl_GetChannelOption( if (statePtr->outEofChar == 0) { Tcl_DStringAppendElement(dsPtr, ""); } else { - char buf[2]; + char buf[4]; - buf[1] = '\0'; - buf[0] = statePtr->outEofChar; + sprintf(buf, "%c", statePtr->outEofChar); Tcl_DStringAppendElement(dsPtr, buf); } } @@ -8115,23 +7359,6 @@ Tcl_GetChannelOption( return TCL_OK; } } - if (len == 0 || HaveOpt(1, "-profile")) { - int profile; - const char *profileName; - if (len == 0) { - Tcl_DStringAppendElement(dsPtr, "-profile"); - } - /* Note currently input and output profiles are same */ - profile = ENCODING_PROFILE_GET(statePtr->inputEncodingFlags); - profileName = TclEncodingProfileIdToName(interp, profile); - if (profileName == NULL) { - return TCL_ERROR; - } - Tcl_DStringAppendElement(dsPtr, profileName); - if (len > 0) { - return TCL_OK; - } - } if (len == 0 || HaveOpt(1, "-translation")) { if (len == 0) { Tcl_DStringAppendElement(dsPtr, "-translation"); @@ -8184,8 +7411,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. @@ -8227,8 +7454,8 @@ Tcl_SetChannelOption( ChannelState *statePtr = chanPtr->state; /* State info for channel */ size_t len; /* Length of optionName string. */ - Tcl_Size argc; - const char **argv = NULL; + int argc; + const char **argv; /* * If the channel is in the middle of a background copy, fail. @@ -8236,9 +7463,8 @@ Tcl_SetChannelOption( if (statePtr->csPtrR || statePtr->csPtrW) { if (interp) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "unable to set channel options: background copy in" - " progress", -1)); + Tcl_AppendResult(interp, "unable to set channel options: " + "background copy in progress", NULL); } return TCL_ERROR; } @@ -8277,7 +7503,8 @@ Tcl_SetChannelOption( } else if (HaveOpt(7, "-buffering")) { len = strlen(newValue); if ((newValue[0] == 'f') && (strncmp(newValue, "full", len) == 0)) { - ResetFlag(statePtr, CHANNEL_UNBUFFERED | CHANNEL_LINEBUFFERED); + statePtr->flags &= + ~(CHANNEL_UNBUFFERED|CHANNEL_LINEBUFFERED); } else if ((newValue[0] == 'l') && (strncmp(newValue, "line", len) == 0)) { ResetFlag(statePtr, CHANNEL_UNBUFFERED); @@ -8286,11 +7513,12 @@ Tcl_SetChannelOption( (strncmp(newValue, "none", len) == 0)) { ResetFlag(statePtr, CHANNEL_LINEBUFFERED); SetFlag(statePtr, CHANNEL_UNBUFFERED); - } else if (interp) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "bad value for -buffering: must be one of" - " full, line, or none", -1)); - 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")) { @@ -8303,7 +7531,6 @@ Tcl_SetChannelOption( return TCL_OK; } else if (HaveOpt(2, "-encoding")) { Tcl_Encoding encoding; - int profile; if ((newValue[0] == '\0') || (strcmp(newValue, "binary") == 0)) { encoding = NULL; @@ -8328,37 +7555,29 @@ Tcl_SetChannelOption( Tcl_FreeEncoding(statePtr->encoding); statePtr->encoding = encoding; statePtr->inputEncodingState = NULL; - profile = ENCODING_PROFILE_GET(statePtr->inputEncodingFlags); statePtr->inputEncodingFlags = TCL_ENCODING_START; - ENCODING_PROFILE_SET(statePtr->inputEncodingFlags, profile); statePtr->outputEncodingState = NULL; statePtr->outputEncodingFlags = TCL_ENCODING_START; - ENCODING_PROFILE_SET(statePtr->outputEncodingFlags, profile); /* Same as input */ - ResetFlag(statePtr, CHANNEL_NEED_MORE_DATA|CHANNEL_ENCODING_ERROR); + ResetFlag(statePtr, CHANNEL_NEED_MORE_DATA); UpdateInterest(chanPtr); return TCL_OK; } else if (HaveOpt(2, "-eofchar")) { - if (!newValue[0] || (!(newValue[0] & 0x80) && !newValue[1])) { - if (GotFlag(statePtr, TCL_READABLE)) { - statePtr->inEofChar = newValue[0]; - } - statePtr->outEofChar = 0; - } else if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) { + if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) { return TCL_ERROR; - } else if (argc == 0) { + } + if (argc == 0) { statePtr->inEofChar = 0; statePtr->outEofChar = 0; } else if (argc == 1 || argc == 2) { + int outIndex = (argc - 1); int inValue = (int) argv[0][0]; - int outValue = (argc == 2) ? (int) argv[1][0] : 0; - + int outValue = (int) argv[outIndex][0]; if (inValue & 0x80 || outValue & 0x80) { if (interp) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "bad value for -eofchar: must be non-NUL ASCII" - " character", TCL_INDEX_NONE)); + Tcl_AppendResult(interp, "bad value for -eofchar: ", + "must be non-NUL ASCII character", NULL); } - ckfree(argv); + ckfree((char *) argv); return TCL_ERROR; } if (GotFlag(statePtr, TCL_READABLE)) { @@ -8369,15 +7588,15 @@ Tcl_SetChannelOption( } } else { if (interp) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( + Tcl_AppendResult(interp, "bad value for -eofchar: should be a list of zero," - " one, or two elements", TCL_INDEX_NONE)); + " one, or two elements", NULL); } - ckfree(argv); + ckfree((char *) argv); return TCL_ERROR; } if (argv != NULL) { - ckfree(argv); + ckfree((char *) argv); } /* @@ -8389,17 +7608,10 @@ Tcl_SetChannelOption( if (GotFlag(statePtr, CHANNEL_EOF)) { statePtr->inputEncodingFlags |= TCL_ENCODING_START; } - ResetFlag(statePtr, CHANNEL_EOF|CHANNEL_STICKY_EOF|CHANNEL_BLOCKED); + statePtr->flags &= + ~(CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED); statePtr->inputEncodingFlags &= ~TCL_ENCODING_END; - return TCL_OK; - } else if (HaveOpt(1, "-profile")) { - int profile; - if (TclEncodingProfileNameToId(interp, newValue, &profile) != TCL_OK) { - return TCL_ERROR; - } - ENCODING_PROFILE_SET(statePtr->inputEncodingFlags, profile); - ENCODING_PROFILE_SET(statePtr->outputEncodingFlags, profile); - ResetFlag(statePtr, CHANNEL_NEED_MORE_DATA|CHANNEL_ENCODING_ERROR); + return TCL_OK; } else if (HaveOpt(1, "-translation")) { const char *readMode, *writeMode; @@ -8416,17 +7628,16 @@ Tcl_SetChannelOption( writeMode = GotFlag(statePtr, TCL_WRITABLE) ? argv[1] : NULL; } else { if (interp) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( + Tcl_AppendResult(interp, "bad value for -translation: must be a one or two" - " element list", -1)); + " element list", NULL); } - ckfree(argv); + ckfree((char *) argv); return TCL_ERROR; } if (readMode) { TclEolTranslation translation; - if (*readMode == '\0') { translation = statePtr->inputTranslation; } else if (strcmp(readMode, "auto") == 0) { @@ -8446,11 +7657,12 @@ Tcl_SetChannelOption( translation = TCL_PLATFORM_TRANSLATION; } else { if (interp) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "bad value for -translation: must be one of " - "auto, binary, cr, lf, crlf, or platform", -1)); + Tcl_AppendResult(interp, + "bad value for -translation: " + "must be one of auto, binary, cr, lf, crlf," + " or platform", NULL); } - ckfree(argv); + ckfree((char *) argv); return TCL_ERROR; } @@ -8496,19 +7708,20 @@ Tcl_SetChannelOption( statePtr->outputTranslation = TCL_PLATFORM_TRANSLATION; } else { if (interp) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "bad value for -translation: must be one of " - "auto, binary, cr, lf, crlf, or platform", -1)); + Tcl_AppendResult(interp, + "bad value for -translation: " + "must be one of auto, binary, cr, lf, crlf," + " or platform", NULL); } - ckfree(argv); + ckfree((char *) argv); return TCL_ERROR; } } - ckfree(argv); + ckfree((char *) 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); } @@ -8563,7 +7776,7 @@ CleanupChannelHandlers( TclChannelEventScriptInvoker, sPtr); TclDecrRefCount(sPtr->scriptPtr); - ckfree(sPtr); + ckfree((char *) sPtr); } else { prevPtr = sPtr; } @@ -8616,14 +7829,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); } /* @@ -8657,13 +7870,6 @@ Tcl_NotifyChannel( Tcl_Preserve(statePtr); /* - * Avoid processing if the channel owner has been changed. - */ - if (statePtr->managingThread != Tcl_GetCurrentThread()) { - goto done; - } - - /* * If we are flushing in the background, be sure to call FlushChannel for * writable events. Note that we have to discard the writable event so we * don't call any write handlers before the flush is complete. @@ -8677,7 +7883,7 @@ Tcl_NotifyChannel( /* * Add this invocation to the list of recursive invocations of - * Tcl_NotifyChannel. + * ChannelHandlerEventProc. */ nh.nextHandlerPtr = NULL; @@ -8692,18 +7898,11 @@ Tcl_NotifyChannel( if ((chPtr->mask & mask) != 0) { nh.nextHandlerPtr = chPtr->nextPtr; - chPtr->proc(chPtr->clientData, chPtr->mask & mask); + (*(chPtr->proc))(chPtr->clientData, chPtr->mask & mask); chPtr = nh.nextHandlerPtr; } else { chPtr = chPtr->nextPtr; } - - /* - * Stop if the channel owner has been changed in-between. - */ - if (chanPtr->state->managingThread != Tcl_GetCurrentThread()) { - goto done; - } } /* @@ -8721,7 +7920,6 @@ Tcl_NotifyChannel( UpdateInterest(chanPtr); } -done: Tcl_Release(statePtr); TclChannelRelease(channel); @@ -8798,9 +7996,9 @@ UpdateInterest( * * - Tcl drops READABLE here, because it has data in its own * buffers waiting to be read by the extension. - * - A READABLE event is synthesized via timer. + * - A READABLE event is syntesized via timer. * - The OS still reports the EXCEPTION condition on the file. - * - And the extension gets the EXCEPTION event first, and handles + * - And the extension gets the EXCPTION event first, and handles * this as EOF. * * End result ==> Premature end of reading from a file. @@ -8821,26 +8019,12 @@ UpdateInterest( mask &= ~TCL_EXCEPTION; if (!statePtr->timer) { - TclChannelPreserve((Tcl_Channel)chanPtr); - statePtr->timerChanPtr = chanPtr; - statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, - ChannelTimerProc, chanPtr); + statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc, + chanPtr); } } } - - if (!statePtr->timer - && mask & TCL_WRITABLE - && GotFlag(statePtr, CHANNEL_NONBLOCKING)) { - - TclChannelPreserve((Tcl_Channel)chanPtr); - statePtr->timerChanPtr = chanPtr; - statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, - ChannelTimerProc,chanPtr); - } - - - ChanWatch(chanPtr, mask); + (chanPtr->typePtr->watchProc)(chanPtr->instanceData, mask); } /* @@ -8862,80 +8046,31 @@ UpdateInterest( static void ChannelTimerProc( - void *clientData) + ClientData clientData) { - Channel *chanPtr = (Channel *)clientData; - /* State info for channel */ + Channel *chanPtr = clientData; ChannelState *statePtr = chanPtr->state; + /* State info for channel */ - /* TclChannelPreserve() must be called before the current function was - * scheduled, is already in effect. In this function it guards against - * deallocation in Tcl_NotifyChannel and also keps the channel preserved - * until ChannelTimerProc is later called again. - */ + if (!GotFlag(statePtr, CHANNEL_NEED_MORE_DATA) + && (statePtr->interestMask & TCL_READABLE) + && (statePtr->inQueueHead != NULL) + && IsBufferReady(statePtr->inQueueHead)) { + /* + * Restart the timer in case a channel handler reenters the event loop + * before UpdateInterest gets called by Tcl_NotifyChannel. + */ - if (chanPtr->typePtr == NULL) { - CleanupTimerHandler(statePtr); - } else { + statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,chanPtr); Tcl_Preserve(statePtr); - statePtr->timer = NULL; - if (statePtr->interestMask & TCL_WRITABLE - && GotFlag(statePtr, CHANNEL_NONBLOCKING) - && !GotFlag(statePtr, BG_FLUSH_SCHEDULED) - ) { - /* - * Restart the timer in case a channel handler reenters the event loop - * before UpdateInterest gets called by Tcl_NotifyChannel. - */ - statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, - ChannelTimerProc,chanPtr); - Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_WRITABLE); - } else { - /* The channel may have just been closed from within Tcl_NotifyChannel */ - if (!GotFlag(statePtr, CHANNEL_INCLOSE)) { - if (!GotFlag(statePtr, CHANNEL_NEED_MORE_DATA) - && (statePtr->interestMask & TCL_READABLE) - && (statePtr->inQueueHead != NULL) - && IsBufferReady(statePtr->inQueueHead)) { - /* - * Restart the timer in case a channel handler reenters the event loop - * before UpdateInterest gets called by Tcl_NotifyChannel. - */ - - statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME, - ChannelTimerProc,chanPtr); - Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_READABLE); - } else { - CleanupTimerHandler(statePtr); - UpdateInterest(chanPtr); - } - } else { - CleanupTimerHandler(statePtr); - } - } + Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_READABLE); Tcl_Release(statePtr); + } else { + statePtr->timer = NULL; + UpdateInterest(chanPtr); } } -static void -DeleteTimerHandler( - ChannelState *statePtr -) -{ - if (statePtr->timer != NULL) { - Tcl_DeleteTimerHandler(statePtr->timer); - CleanupTimerHandler(statePtr); - } -} -static void -CleanupTimerHandler( - ChannelState *statePtr -){ - TclChannelRelease((Tcl_Channel)statePtr->timerChanPtr); - statePtr->timer = NULL; - statePtr->timerChanPtr = NULL; -} - /* *---------------------------------------------------------------------- * @@ -8967,7 +8102,7 @@ Tcl_CreateChannelHandler( * handler. */ Tcl_ChannelProc *proc, /* Procedure to call for each selected * event. */ - void *clientData) /* Arbitrary data to pass to proc. */ + ClientData clientData) /* Arbitrary data to pass to proc. */ { ChannelHandler *chPtr; Channel *chanPtr = (Channel *) chan; @@ -8987,7 +8122,7 @@ Tcl_CreateChannelHandler( } } if (chPtr == NULL) { - chPtr = (ChannelHandler *)ckalloc(sizeof(ChannelHandler)); + chPtr = (ChannelHandler *) ckalloc(sizeof(ChannelHandler)); chPtr->mask = 0; chPtr->proc = proc; chPtr->clientData = clientData; @@ -8998,7 +8133,7 @@ Tcl_CreateChannelHandler( /* * The remainder of the initialization below is done regardless of whether - * this is a new record or a modification of an old one. + * or not this is a new record or a modification of an old one. */ chPtr->mask = mask; @@ -9039,7 +8174,7 @@ Tcl_DeleteChannelHandler( Tcl_Channel chan, /* The channel for which to remove the * callback. */ Tcl_ChannelProc *proc, /* The procedure in the callback to delete. */ - void *clientData) /* The client data in the callback to + ClientData clientData) /* The client data in the callback to * delete. */ { ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); @@ -9071,7 +8206,7 @@ Tcl_DeleteChannelHandler( } /* - * If Tcl_NotifyChannel is about to process this handler, tell it to + * If ChannelHandlerEventProc is about to process this handler, tell it to * process the next one instead - we are going to delete *this* one. */ @@ -9091,7 +8226,7 @@ Tcl_DeleteChannelHandler( } else { prevChPtr->nextPtr = chPtr->nextPtr; } - ckfree(chPtr); + ckfree((char *) chPtr); /* * Recompute the interest list for the channel, so that infinite loops @@ -9142,7 +8277,6 @@ DeleteScriptRecord( if (esPtr == statePtr->scriptRecordPtr) { statePtr->scriptRecordPtr = esPtr->nextPtr; } else { - CLANG_ASSERT(prevEsPtr); prevEsPtr->nextPtr = esPtr->nextPtr; } @@ -9150,7 +8284,7 @@ DeleteScriptRecord( TclChannelEventScriptInvoker, esPtr); TclDecrRefCount(esPtr->scriptPtr); - ckfree(esPtr); + ckfree((char *) esPtr); break; } @@ -9199,12 +8333,12 @@ CreateScriptRecord( makeCH = (esPtr == NULL); if (makeCH) { - esPtr = (EventScriptRecord *)ckalloc(sizeof(EventScriptRecord)); + esPtr = (EventScriptRecord *) ckalloc(sizeof(EventScriptRecord)); } /* * Initialize the structure before calling Tcl_CreateChannelHandler, - * because a reflected channel calling 'chan postevent' aka + * because a reflected channel caling '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]. @@ -9245,24 +8379,20 @@ CreateScriptRecord( void TclChannelEventScriptInvoker( - void *clientData, /* The script+interp record. */ - TCL_UNUSED(int) /*mask*/) + ClientData clientData, /* The script+interp record. */ + int mask) /* Not used. */ { - EventScriptRecord *esPtr = (EventScriptRecord *)clientData; - /* The event script + interpreter to eval it - * in. */ - Channel *chanPtr = esPtr->chanPtr; - /* The channel for which this handler is + Tcl_Interp *interp; /* Interpreter in which to eval the script. */ + Channel *chanPtr; /* The channel for which this handler is * registered. */ - Tcl_Interp *interp = esPtr->interp; - /* Interpreter in which to eval the script. */ - int mask = esPtr->mask; + EventScriptRecord *esPtr; /* The event script + interpreter to eval it + * in. */ int result; /* Result of call to eval script. */ - /* - * Be sure event executed in managed channel (covering bugs similar [f583715154]). - */ - assert(chanPtr->state->managingThread == Tcl_GetCurrentThread()); + esPtr = clientData; + chanPtr = esPtr->chanPtr; + mask = esPtr->mask; + interp = esPtr->interp; /* * We must preserve the interpreter so we can report errors on it later. @@ -9286,7 +8416,7 @@ TclChannelEventScriptInvoker( if (chanPtr->typePtr != NULL) { DeleteScriptRecord(interp, chanPtr, mask); } - Tcl_BackgroundException(interp, result); + TclBackgroundException(interp, result); } TclChannelRelease((Tcl_Channel)chanPtr); Tcl_Release(interp); @@ -9311,9 +8441,10 @@ TclChannelEventScriptInvoker( *---------------------------------------------------------------------- */ + /* ARGSUSED */ int Tcl_FileEventObjCmd( - TCL_UNUSED(void *), + ClientData clientData, /* Not used. */ Tcl_Interp *interp, /* Interpreter in which the channel for which * to create the handler is found. */ int objc, /* Number of arguments. */ @@ -9322,11 +8453,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. */ - const char *chanName; + char *chanName; int modeIndex; /* Index of mode argument. */ int mask; - static const char *const modeOptions[] = {"readable", "writable", NULL}; - static const int maskArray[] = {TCL_READABLE, TCL_WRITABLE}; + static const char *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?"); @@ -9346,8 +8477,8 @@ Tcl_FileEventObjCmd( chanPtr = (Channel *) chan; statePtr = chanPtr->state; if (GotFlag(statePtr, mask) == 0) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf("channel is not %s", - (mask == TCL_READABLE) ? "readable" : "writable")); + Tcl_AppendResult(interp, "channel is not ", + (mask == TCL_READABLE) ? "readable" : "writable", NULL); return TCL_ERROR; } @@ -9357,7 +8488,6 @@ Tcl_FileEventObjCmd( if (objc == 3) { EventScriptRecord *esPtr; - for (esPtr = statePtr->scriptRecordPtr; esPtr != NULL; esPtr = esPtr->nextPtr) { if ((esPtr->interp == interp) && (esPtr->mask == mask)) { @@ -9407,12 +8537,12 @@ Tcl_FileEventObjCmd( static void ZeroTransferTimerProc( - void *clientData) + ClientData clientData) { /* calling CopyData with mask==0 still implies immediate invocation of the * -command callback, and completion of the fcopy. */ - CopyData((CopyState *)clientData, 0); + CopyData(clientData, 0); } /* @@ -9436,26 +8566,12 @@ ZeroTransferTimerProc( *---------------------------------------------------------------------- */ -#if !defined(TCL_NO_DEPRECATED) -int -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); -} -#endif - int TclCopyChannel( Tcl_Interp *interp, /* Current interpreter. */ Tcl_Channel inChan, /* Channel to read from. */ Tcl_Channel outChan, /* Channel to write to. */ - long long toRead, /* Amount of data to copy, or -1 for all. */ + int toRead, /* Amount of data to copy, or -1 for all. */ Tcl_Obj *cmdPtr) /* Pointer to script to execute or NULL. */ { Channel *inPtr = (Channel *) inChan; @@ -9464,22 +8580,21 @@ TclCopyChannel( int readFlags, writeFlags; CopyState *csPtr; int nonBlocking = (cmdPtr) ? CHANNEL_NONBLOCKING : 0; - int moveBytes; inStatePtr = inPtr->state; outStatePtr = outPtr->state; - if (BUSY_STATE(inStatePtr, TCL_READABLE)) { + if (BUSY_STATE(inStatePtr,TCL_READABLE)) { if (interp) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "channel \"%s\" is busy", Tcl_GetChannelName(inChan))); + 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_SetObjResult(interp, Tcl_ObjPrintf( - "channel \"%s\" is busy", Tcl_GetChannelName(outChan))); + Tcl_AppendResult(interp, "channel \"", + Tcl_GetChannelName(outChan), "\" is busy", NULL); } return TCL_ERROR; } @@ -9512,21 +8627,8 @@ TclCopyChannel( * Make sure the output side is unbuffered. */ - ResetFlag(outStatePtr, CHANNEL_LINEBUFFERED); - SetFlag(outStatePtr, CHANNEL_UNBUFFERED); - - /* - * Test for conditions where we know we can just move bytes from input - * channel to output channel with no transformation or even examination - * of the bytes themselves. - */ - - moveBytes = inStatePtr->inEofChar == '\0' /* No eofChar to stop input */ - && inStatePtr->inputTranslation == TCL_TRANSLATE_LF - && outStatePtr->outputTranslation == TCL_TRANSLATE_LF - && inStatePtr->encoding == outStatePtr->encoding - && ENCODING_PROFILE_GET(inStatePtr->inputEncodingFlags) == TCL_ENCODING_PROFILE_TCL8 - && ENCODING_PROFILE_GET(outStatePtr->outputEncodingFlags) == TCL_ENCODING_PROFILE_TCL8; + outStatePtr->flags = (outStatePtr->flags & ~(CHANNEL_LINEBUFFERED)) + | CHANNEL_UNBUFFERED; /* * Allocate a new CopyState to maintain info about the current copy in @@ -9534,14 +8636,14 @@ TclCopyChannel( * completed. */ - csPtr = (CopyState *)ckalloc(offsetof(CopyState, buffer) + 1U + !moveBytes * inStatePtr->bufSize); - csPtr->bufSize = !moveBytes * inStatePtr->bufSize; + csPtr = (CopyState *) 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 = (Tcl_WideInt) 0; + csPtr->total = 0; csPtr->interp = interp; if (cmdPtr) { Tcl_IncrRefCount(cmdPtr); @@ -9551,10 +8653,6 @@ TclCopyChannel( inStatePtr->csPtrR = csPtr; outStatePtr->csPtrW = csPtr; - if (moveBytes) { - return MoveBytes(csPtr); - } - /* * Special handling of -size 0 async transfers, so that the -command is * still called asynchronously. @@ -9589,236 +8687,6 @@ TclCopyChannel( *---------------------------------------------------------------------- */ -static void -MBCallback( - CopyState *csPtr, - Tcl_Obj *errObj) -{ - Tcl_Obj *cmdPtr = Tcl_DuplicateObj(csPtr->cmdPtr); - Tcl_WideInt total = csPtr->total; - Tcl_Interp *interp = csPtr->interp; - int code; - - Tcl_IncrRefCount(cmdPtr); - StopCopy(csPtr); - - /* TODO: What if cmdPtr is not a list?! */ - - Tcl_ListObjAppendElement(NULL, cmdPtr, Tcl_NewWideIntObj(total)); - if (errObj) { - Tcl_ListObjAppendElement(NULL, cmdPtr, errObj); - } - - Tcl_Preserve(interp); - code = Tcl_EvalObjEx(interp, cmdPtr, TCL_EVAL_GLOBAL); - if (code != TCL_OK) { - Tcl_BackgroundException(interp, code); - } - Tcl_Release(interp); - TclDecrRefCount(cmdPtr); -} - -static void -MBError( - CopyState *csPtr, - int mask, - int errorCode) -{ - Tcl_Channel inChan = (Tcl_Channel) csPtr->readPtr; - Tcl_Channel outChan = (Tcl_Channel) csPtr->writePtr; - Tcl_Obj *errObj; - - Tcl_SetErrno(errorCode); - - errObj = Tcl_ObjPrintf( "error %sing \"%s\": %s", - (mask & TCL_READABLE) ? "read" : "writ", - Tcl_GetChannelName((mask & TCL_READABLE) ? inChan : outChan), - Tcl_PosixError(csPtr->interp)); - - if (csPtr->cmdPtr) { - MBCallback(csPtr, errObj); - } else { - Tcl_SetObjResult(csPtr->interp, errObj); - StopCopy(csPtr); - } -} - -static void -MBEvent( - void *clientData, - int mask) -{ - CopyState *csPtr = (CopyState *) clientData; - Tcl_Channel inChan = (Tcl_Channel) csPtr->readPtr; - Tcl_Channel outChan = (Tcl_Channel) csPtr->writePtr; - ChannelState *inStatePtr = csPtr->readPtr->state; - - if (mask & TCL_WRITABLE) { - Tcl_DeleteChannelHandler(inChan, MBEvent, csPtr); - Tcl_DeleteChannelHandler(outChan, MBEvent, csPtr); - switch (MBWrite(csPtr)) { - case TCL_OK: - MBCallback(csPtr, NULL); - break; - case TCL_CONTINUE: - Tcl_CreateChannelHandler(inChan, TCL_READABLE, MBEvent, csPtr); - break; - } - } else if (mask & TCL_READABLE) { - if (TCL_OK == MBRead(csPtr)) { - /* When at least one full buffer is present, stop reading. */ - if (IsBufferFull(inStatePtr->inQueueHead) - || !Tcl_InputBlocked(inChan)) { - Tcl_DeleteChannelHandler(inChan, MBEvent, csPtr); - } - - /* Successful read -- set up to write the bytes we read */ - Tcl_CreateChannelHandler(outChan, TCL_WRITABLE, MBEvent, csPtr); - } - } -} - -static int -MBRead( - CopyState *csPtr) -{ - ChannelState *inStatePtr = csPtr->readPtr->state; - ChannelBuffer *bufPtr = inStatePtr->inQueueHead; - int code; - - if (bufPtr && BytesLeft(bufPtr) > 0) { - return TCL_OK; - } - - code = GetInput(inStatePtr->topChanPtr); - if (code == 0 || GotFlag(inStatePtr, CHANNEL_BLOCKED)) { - return TCL_OK; - } else { - MBError(csPtr, TCL_READABLE, code); - return TCL_ERROR; - } -} - -static int -MBWrite( - CopyState *csPtr) -{ - ChannelState *inStatePtr = csPtr->readPtr->state; - ChannelState *outStatePtr = csPtr->writePtr->state; - ChannelBuffer *bufPtr = inStatePtr->inQueueHead; - ChannelBuffer *tail = NULL; - int code; - Tcl_WideInt inBytes = 0; - - /* Count up number of bytes waiting in the input queue */ - while (bufPtr) { - inBytes += BytesLeft(bufPtr); - tail = bufPtr; - if (csPtr->toRead != -1 && csPtr->toRead < inBytes) { - /* Queue has enough bytes to complete the copy */ - break; - } - bufPtr = bufPtr->nextPtr; - } - - if (bufPtr) { - /* Split the overflowing buffer in two */ - int extra = (int) (inBytes - csPtr->toRead); - /* Note that going with int for extra assumes that inBytes is not too - * much over toRead to require a wide itself. If that gets violated - * then the calculations involving extra must be made wide too. - * - * Noted with Win32/MSVC debug build treating the warning (possible of - * data in long long to int conversion) as error. - */ - - bufPtr = AllocChannelBuffer(extra); - - tail->nextAdded -= extra; - memcpy(InsertPoint(bufPtr), InsertPoint(tail), extra); - bufPtr->nextAdded += extra; - bufPtr->nextPtr = tail->nextPtr; - tail->nextPtr = NULL; - inBytes = csPtr->toRead; - } - - /* Update the byte counts */ - if (csPtr->toRead != -1) { - csPtr->toRead -= inBytes; - } - csPtr->total += inBytes; - - /* Move buffers from input to output channels */ - if (outStatePtr->outQueueTail) { - outStatePtr->outQueueTail->nextPtr = inStatePtr->inQueueHead; - } else { - outStatePtr->outQueueHead = inStatePtr->inQueueHead; - } - outStatePtr->outQueueTail = tail; - inStatePtr->inQueueHead = bufPtr; - if (inStatePtr->inQueueTail == tail) { - inStatePtr->inQueueTail = bufPtr; - } - if (bufPtr == NULL) { - inStatePtr->inQueueTail = NULL; - } - - code = FlushChannel(csPtr->interp, outStatePtr->topChanPtr, 0); - if (code) { - MBError(csPtr, TCL_WRITABLE, code); - return TCL_ERROR; - } - if (csPtr->toRead == 0 || GotFlag(inStatePtr, CHANNEL_EOF)) { - return TCL_OK; - } - return TCL_CONTINUE; -} - -static int -MoveBytes( - CopyState *csPtr) /* State of copy operation. */ -{ - ChannelState *outStatePtr = csPtr->writePtr->state; - ChannelBuffer *bufPtr = outStatePtr->curOutPtr; - int errorCode; - - if (bufPtr && BytesLeft(bufPtr)) { - /* If we start with unflushed bytes in the destination - * channel, flush them out of the way first. */ - - errorCode = FlushChannel(csPtr->interp, outStatePtr->topChanPtr, 0); - if (errorCode != 0) { - MBError(csPtr, TCL_WRITABLE, errorCode); - return TCL_ERROR; - } - } - - if (csPtr->cmdPtr) { - Tcl_Channel inChan = (Tcl_Channel) csPtr->readPtr; - Tcl_CreateChannelHandler(inChan, TCL_READABLE, MBEvent, csPtr); - return TCL_OK; - } - - while (1) { - int code; - - if (TCL_ERROR == MBRead(csPtr)) { - return TCL_ERROR; - } - code = MBWrite(csPtr); - if (code == TCL_OK) { - Tcl_SetObjResult(csPtr->interp, Tcl_NewWideIntObj(csPtr->total)); - StopCopy(csPtr); - return TCL_OK; - } - if (code == TCL_ERROR) { - return TCL_ERROR; - } - /* code == TCL_CONTINUE --> continue the loop */ - } - return TCL_OK; /* Silence compiler warnings */ -} - static int CopyData( CopyState *csPtr, /* State of copy operation. */ @@ -9828,12 +8696,9 @@ CopyData( Tcl_Obj *cmdPtr, *errObj = NULL, *bufObj = NULL, *msg = NULL; Tcl_Channel inChan, outChan; ChannelState *inStatePtr, *outStatePtr; - int result = TCL_OK; - Tcl_Size sizeb; - Tcl_Size sizePart; + int result = TCL_OK, size, sizeb; Tcl_WideInt total; - int size; - const char *buffer; + char *buffer; int inBinary, outBinary, sameEncoding; /* Encoding control */ int underflow; /* Input underflow */ @@ -9855,16 +8720,14 @@ CopyData( inBinary = (inStatePtr->encoding == NULL); outBinary = (outStatePtr->encoding == NULL); - sameEncoding = inStatePtr->encoding == outStatePtr->encoding - && ENCODING_PROFILE_GET(inStatePtr->inputEncodingFlags) == TCL_ENCODING_PROFILE_TCL8 - && ENCODING_PROFILE_GET(outStatePtr->outputEncodingFlags) == TCL_ENCODING_PROFILE_TCL8; + sameEncoding = (inStatePtr->encoding == outStatePtr->encoding); if (!(inBinary || sameEncoding)) { TclNewObj(bufObj); Tcl_IncrRefCount(bufObj); } - while (csPtr->toRead != (Tcl_WideInt) 0) { + while (csPtr->toRead != 0) { /* * Check for unreported background errors. */ @@ -9874,20 +8737,12 @@ CopyData( Tcl_SetErrno(inStatePtr->unreportedError); inStatePtr->unreportedError = 0; goto readError; - } else if (inStatePtr->flags & CHANNEL_ENCODING_ERROR) { - Tcl_SetErrno(EILSEQ); - inStatePtr->flags &= ~CHANNEL_ENCODING_ERROR; - goto readError; } Tcl_GetChannelError(outChan, &msg); if ((outStatePtr->unreportedError != 0) || (msg != NULL)) { Tcl_SetErrno(outStatePtr->unreportedError); outStatePtr->unreportedError = 0; goto writeError; - } else if (outStatePtr->flags & CHANNEL_ENCODING_ERROR) { - Tcl_SetErrno(EILSEQ); - outStatePtr->flags &= ~CHANNEL_ENCODING_ERROR; - goto writeError; } if (cmdPtr && (mask == 0)) { @@ -9896,44 +8751,24 @@ CopyData( * underflow instead to prime the readable fileevent. */ - size = 0; + size = 0; underflow = 1; } else { /* * Read up to bufSize bytes. */ - if ((csPtr->toRead == (Tcl_WideInt) -1) - || (csPtr->toRead > (Tcl_WideInt) csPtr->bufSize)) { + if ((csPtr->toRead == -1) || (csPtr->toRead > csPtr->bufSize)) { sizeb = csPtr->bufSize; } else { - sizeb = (int) csPtr->toRead; + sizeb = csPtr->toRead; } if (inBinary || sameEncoding) { - size = DoRead(inStatePtr->topChanPtr, csPtr->buffer, sizeb, - !GotFlag(inStatePtr, CHANNEL_NONBLOCKING)); + size = DoRead(inStatePtr->topChanPtr, csPtr->buffer, sizeb); } else { size = DoReadChars(inStatePtr->topChanPtr, bufObj, sizeb, - !GotFlag(inStatePtr, CHANNEL_NONBLOCKING) - ,0 /* No append */); - /* - * In case of a recoverable encoding error, any data before - * the error should be written. This data is in the bufObj. - * Program flow for this case: - * - Check, if there are any remaining bytes to write - * - If yes, simulate a successful read to write them out - * - Come back here by the outer loop and read again - * - Do not enter in the if below, as there are no pending - * writes - * - Fail below with a read error - */ - if (size < 0 && Tcl_GetErrno() == EILSEQ) { - Tcl_GetStringFromObj(bufObj, &sizePart); - if (sizePart > 0) { - size = sizePart; - } - } + 0 /* No append */); } underflow = (size >= 0) && (size < sizeb); /* Input underflow */ } @@ -9943,12 +8778,12 @@ CopyData( if (interp) { TclNewObj(errObj); Tcl_AppendStringsToObj(errObj, "error reading \"", - Tcl_GetChannelName(inChan), "\": ", (void *)NULL); + Tcl_GetChannelName(inChan), "\": ", NULL); if (msg != NULL) { Tcl_AppendObjToObj(errObj, msg); } else { Tcl_AppendStringsToObj(errObj, Tcl_PosixError(interp), - (void *)NULL); + NULL); } } if (msg != NULL) { @@ -9966,8 +8801,8 @@ CopyData( if ((size == 0) && Tcl_Eof(inChan) && !(cmdPtr && (mask == 0))) { break; } - if (cmdPtr && (!Tcl_Eof(inChan) || (mask == 0)) && - !(mask & TCL_READABLE)) { + if (((!Tcl_Eof(inChan)) || (cmdPtr && (mask == 0))) && + !(mask & TCL_READABLE)) { if (mask & TCL_WRITABLE) { Tcl_DeleteChannelHandler(outChan, CopyEventProc, csPtr); } @@ -9975,13 +8810,6 @@ CopyData( csPtr); } if (size == 0) { - if (!GotFlag(inStatePtr, CHANNEL_NONBLOCKING)) { - /* - * We allowed a short read. Keep trying. - */ - - continue; - } if (bufObj != NULL) { TclDecrRefCount(bufObj); bufObj = NULL; @@ -10024,12 +8852,12 @@ CopyData( if (interp) { TclNewObj(errObj); Tcl_AppendStringsToObj(errObj, "error writing \"", - Tcl_GetChannelName(outChan), "\": ", (void *)NULL); + Tcl_GetChannelName(outChan), "\": ", NULL); if (msg != NULL) { Tcl_AppendObjToObj(errObj, msg); } else { Tcl_AppendStringsToObj(errObj, Tcl_PosixError(interp), - (void *)NULL); + NULL); } } if (msg != NULL) { @@ -10039,7 +8867,7 @@ CopyData( } /* - * Update the current byte count. Do it now so the count is valid + * (UP) 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. @@ -10065,7 +8893,7 @@ CopyData( * therefore we don't need a writable handler. */ - if (!underflow && GotFlag(outStatePtr, BG_FLUSH_SCHEDULED)) { + if (!underflow && (outStatePtr->flags & BG_FLUSH_SCHEDULED)) { if (!(mask & TCL_WRITABLE)) { if (mask & TCL_READABLE) { Tcl_DeleteChannelHandler(inChan, CopyEventProc, csPtr); @@ -10116,7 +8944,6 @@ 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 @@ -10134,7 +8961,7 @@ CopyData( } code = Tcl_EvalObjEx(interp, cmdPtr, TCL_EVAL_GLOBAL); if (code != TCL_OK) { - Tcl_BackgroundException(interp, code); + TclBackgroundException(interp, code); result = TCL_ERROR; } TclDecrRefCount(cmdPtr); @@ -10160,13 +8987,14 @@ CopyData( * DoRead -- * * Stores up to "bytesToRead" bytes in memory pointed to by "dst". - * These bytes come from reading the channel "chanPtr" and - * performing the configured translations. No encoding conversions - * are applied to the bytes being read. + * These bytes come from reading the channel "chanPtr" and + * performing the configured translations. + * + * No encoding conversions are applied to the bytes being read. * * Results: * The number of bytes actually stored (<= bytesToRead), - * or TCL_INDEX_NONE if there is an error in reading the channel. Use + * or -1 if there is an error in reading the channel. Use * Tcl_GetErrno() to retrieve the error code for the error * that occurred. * @@ -10175,7 +9003,7 @@ CopyData( * - EOF is reached on the channel; or * - the channel is non-blocking, and we've read all we can * without blocking. - * - a channel reading error occurs (and we return TCL_INDEX_NONE) + * - a channel reading error occurs (and we return -1) * * Side effects: * May cause input to be buffered. @@ -10183,17 +9011,16 @@ CopyData( *---------------------------------------------------------------------- */ -static Tcl_Size +static int DoRead( Channel *chanPtr, /* The channel from which to read. */ - char *dst, /* Where to store input read. */ - Tcl_Size bytesToRead, /* Maximum number of bytes to read. */ - int allowShortReads) /* Allow half-blocking (pipes,sockets) */ + char *dst, /* Where to store input read. */ + int bytesToRead) /* Maximum number of bytes to read. */ { ChannelState *statePtr = chanPtr->state; char *p = dst; - assert(bytesToRead >= 0); + assert (bytesToRead >= 0); /* * Early out when we know a read will get the eofchar. @@ -10207,25 +9034,17 @@ DoRead( * too. Keep on keeping on for now. */ - if (GotFlag(statePtr, CHANNEL_ENCODING_ERROR)) { - UpdateInterest(chanPtr); - Tcl_SetErrno(EILSEQ); - return -1; - } 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; @@ -10240,52 +9059,46 @@ 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; ChannelBuffer *bufPtr = statePtr->inQueueHead; /* - * Don't read more data if we have what we need. + * Don't read more data if we have what we need. */ 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; @@ -10300,8 +9113,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) { @@ -10309,16 +9122,16 @@ DoRead( } /* - * 1) We're @EOF because we saw eof char, or there was an encoding error. + * 1) We're @EOF because we saw eof char. */ - if (GotFlag(statePtr, CHANNEL_STICKY_EOF|CHANNEL_ENCODING_ERROR)) { + if (GotFlag(statePtr, CHANNEL_STICKY_EOF)) { break; } /* - * 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); @@ -10326,38 +9139,26 @@ DoRead( assert(BytesLeft(bufPtr) == 1); if (bufPtr->nextPtr == NULL) { - /* - * There's no more buffered data... - */ + /* There's no more buffered data.... */ if (GotFlag(statePtr, CHANNEL_EOF)) { - /* - * ...and there never will be. - */ + /* ...and there never will be. */ *p++ = '\r'; bytesToRead--; bufPtr->nextRemoved++; } else if (GotFlag(statePtr, 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; @@ -10376,14 +9177,14 @@ DoRead( bufPtr = statePtr->inQueueHead; } - if ((GotFlag(statePtr, CHANNEL_NONBLOCKING) || allowShortReads) - && GotFlag(statePtr, CHANNEL_BLOCKED)) { + if (GotFlag(statePtr, CHANNEL_NONBLOCKING|CHANNEL_BLOCKED) + == (CHANNEL_NONBLOCKING|CHANNEL_BLOCKED)) { break; } /* - * 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) @@ -10395,12 +9196,11 @@ DoRead( ResetFlag(statePtr, CHANNEL_BLOCKED); } - assert(!GotFlag(statePtr, CHANNEL_EOF) - || GotFlag(statePtr, CHANNEL_STICKY_EOF) - || GotFlag(statePtr, CHANNEL_ENCODING_ERROR) - || 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); @@ -10426,10 +9226,10 @@ DoRead( static void CopyEventProc( - void *clientData, + ClientData clientData, int mask) { - (void) CopyData((CopyState *)clientData, mask); + (void) CopyData((CopyState *) clientData, mask); } /* @@ -10454,16 +9254,12 @@ StopCopy( CopyState *csPtr) /* State for bg copy to stop . */ { ChannelState *inStatePtr, *outStatePtr; - Tcl_Channel inChan, outChan; - int nonBlocking; if (!csPtr) { return; } - inChan = (Tcl_Channel) csPtr->readPtr; - outChan = (Tcl_Channel) csPtr->writePtr; inStatePtr = csPtr->readPtr->state; outStatePtr = csPtr->writePtr->state; @@ -10471,34 +9267,34 @@ StopCopy( * Restore the old blocking mode and output buffering mode. */ - nonBlocking = csPtr->readFlags & CHANNEL_NONBLOCKING; - if (nonBlocking != GotFlag(inStatePtr, 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; - if (nonBlocking != GotFlag(outStatePtr, CHANNEL_NONBLOCKING)) { + nonBlocking = (csPtr->writeFlags & CHANNEL_NONBLOCKING); + if (nonBlocking != (outStatePtr->flags & CHANNEL_NONBLOCKING)) { SetBlockMode(NULL, csPtr->writePtr, nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING); } } - ResetFlag(outStatePtr, CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED); - SetFlag(outStatePtr, - csPtr->writeFlags & (CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED)); + outStatePtr->flags &= ~(CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED); + outStatePtr->flags |= + csPtr->writeFlags & (CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED); if (csPtr->cmdPtr) { - Tcl_DeleteChannelHandler(inChan, CopyEventProc, csPtr); - if (inChan != outChan) { - Tcl_DeleteChannelHandler(outChan, CopyEventProc, csPtr); + Tcl_DeleteChannelHandler((Tcl_Channel) csPtr->readPtr, CopyEventProc, + csPtr); + if (csPtr->readPtr != csPtr->writePtr) { + Tcl_DeleteChannelHandler((Tcl_Channel) csPtr->writePtr, + CopyEventProc, csPtr); } - Tcl_DeleteChannelHandler(inChan, MBEvent, csPtr); - Tcl_DeleteChannelHandler(outChan, MBEvent, csPtr); TclDecrRefCount(csPtr->cmdPtr); } inStatePtr->csPtrR = NULL; outStatePtr->csPtrW = NULL; - ckfree(csPtr); + ckfree((char *) csPtr); } /* @@ -10539,7 +9335,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; @@ -10594,9 +9390,8 @@ SetBlockMode( */ if (!TclChanCaughtErrorBypass(interp, (Tcl_Channel) chanPtr)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "error setting blocking mode: %s", - Tcl_PosixError(interp))); + Tcl_AppendResult(interp, "error setting blocking mode: ", + Tcl_PosixError(interp), NULL); } } else { /* @@ -10693,11 +9488,10 @@ Tcl_GetChannelNamesEx( } goto done; } - for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch); hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) { - statePtr = ((Channel *) Tcl_GetHashValue(hPtr))->state; + statePtr = ((Channel *) Tcl_GetHashValue(hPtr))->state; if (statePtr->topChanPtr == (Channel *) tsdPtr->stdinChannel) { name = "stdin"; } else if (statePtr->topChanPtr == (Channel *) tsdPtr->stdoutChannel) { @@ -10762,7 +9556,7 @@ Tcl_IsChannelRegistered( chanPtr = ((Channel *) chan)->state->bottomChanPtr; statePtr = chanPtr->state; - hTblPtr = (Tcl_HashTable *)Tcl_GetAssocData(interp, "tclIO", NULL); + hTblPtr = Tcl_GetAssocData(interp, "tclIO", NULL); if (hTblPtr == NULL) { return 0; } @@ -10843,8 +9637,11 @@ Tcl_IsChannelExisting( name = statePtr->channelName; } + /* Bug 2333466. Include \0 in the compare to prevent partial matching + * on prefixes. + */ if ((*chanName == *name) && - (memcmp(name, chanName, chanNameLen + 1) == 0)) { + (memcmp(name, chanName, (size_t) chanNameLen+1) == 0)) { return 1; } } @@ -10896,17 +9693,49 @@ Tcl_ChannelVersion( const Tcl_ChannelType *chanTypePtr) /* Pointer to channel type. */ { -#ifndef TCL_NO_DEPRECATED - if ((chanTypePtr->version < TCL_CHANNEL_VERSION_2) - || (chanTypePtr->version > TCL_CHANNEL_VERSION_5)) { + if (chanTypePtr->version == TCL_CHANNEL_VERSION_2) { + return TCL_CHANNEL_VERSION_2; + } else if (chanTypePtr->version == TCL_CHANNEL_VERSION_3) { + return TCL_CHANNEL_VERSION_3; + } else if (chanTypePtr->version == TCL_CHANNEL_VERSION_4) { + return TCL_CHANNEL_VERSION_4; + } else if (chanTypePtr->version == TCL_CHANNEL_VERSION_5) { + return TCL_CHANNEL_VERSION_5; + } else { /* * In <v2 channel versions, the version field is occupied by the * Tcl_DriverBlockModeProc */ + return TCL_CHANNEL_VERSION_1; } -#endif - return chanTypePtr->version; +} + +/* + *---------------------------------------------------------------------- + * + * HaveVersion -- + * + * Return whether a channel type is (at least) of a given version. + * + * Results: + * True if the minimum version is exceeded by the version actually + * present. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +HaveVersion( + const Tcl_ChannelType *chanTypePtr, + Tcl_ChannelTypeVersion minimumVersion) +{ + Tcl_ChannelTypeVersion actualVersion = Tcl_ChannelVersion(chanTypePtr); + + return (PTR2INT(actualVersion)) >= (PTR2INT(minimumVersion)); } /* @@ -10929,15 +9758,15 @@ Tcl_ChannelBlockModeProc( const Tcl_ChannelType *chanTypePtr) /* Pointer to channel type. */ { -#ifndef TCL_NO_DEPRECATED - if (Tcl_ChannelVersion(chanTypePtr) < TCL_CHANNEL_VERSION_2) { + 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; + + return (Tcl_DriverBlockModeProc *) (chanTypePtr->version); } -#endif - return chanTypePtr->blockModeProc; } /* @@ -10956,7 +9785,6 @@ Tcl_ChannelBlockModeProc( *---------------------------------------------------------------------- */ -#ifndef TCL_NO_DEPRECATED Tcl_DriverCloseProc * Tcl_ChannelCloseProc( const Tcl_ChannelType *chanTypePtr) @@ -10964,7 +9792,6 @@ Tcl_ChannelCloseProc( { return chanTypePtr->closeProc; } -#endif /* *---------------------------------------------------------------------- @@ -11054,7 +9881,6 @@ Tcl_ChannelOutputProc( *---------------------------------------------------------------------- */ -#ifndef TCL_NO_DEPRECATED Tcl_DriverSeekProc * Tcl_ChannelSeekProc( const Tcl_ChannelType *chanTypePtr) @@ -11062,7 +9888,6 @@ Tcl_ChannelSeekProc( { return chanTypePtr->seekProc; } -#endif /* *---------------------------------------------------------------------- @@ -11181,12 +10006,11 @@ Tcl_ChannelFlushProc( const Tcl_ChannelType *chanTypePtr) /* Pointer to channel type. */ { -#ifndef TCL_NO_DEPRECATED - if (Tcl_ChannelVersion(chanTypePtr) < TCL_CHANNEL_VERSION_2) { + if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) { + return chanTypePtr->flushProc; + } else { return NULL; } -#endif - return chanTypePtr->flushProc; } /* @@ -11210,12 +10034,11 @@ Tcl_ChannelHandlerProc( const Tcl_ChannelType *chanTypePtr) /* Pointer to channel type. */ { -#ifndef TCL_NO_DEPRECATED - if (Tcl_ChannelVersion(chanTypePtr) < TCL_CHANNEL_VERSION_2) { + if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) { + return chanTypePtr->handlerProc; + } else { return NULL; } -#endif - return chanTypePtr->handlerProc; } /* @@ -11239,12 +10062,11 @@ Tcl_ChannelWideSeekProc( const Tcl_ChannelType *chanTypePtr) /* Pointer to channel type. */ { -#ifndef TCL_NO_DEPRECATED - if (Tcl_ChannelVersion(chanTypePtr) < TCL_CHANNEL_VERSION_3) { + if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_3)) { + return chanTypePtr->wideSeekProc; + } else { return NULL; } -#endif - return chanTypePtr->wideSeekProc; } /* @@ -11269,12 +10091,11 @@ Tcl_ChannelThreadActionProc( const Tcl_ChannelType *chanTypePtr) /* Pointer to channel type. */ { -#ifndef TCL_NO_DEPRECATED - if (Tcl_ChannelVersion(chanTypePtr) < TCL_CHANNEL_VERSION_4) { + if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_4)) { + return chanTypePtr->threadActionProc; + } else { return NULL; } -#endif - return chanTypePtr->threadActionProc; } /* @@ -11300,17 +10121,15 @@ Tcl_SetChannelErrorInterp( Tcl_Obj *msg) /* Error message to store. */ { Interp *iPtr = (Interp *) interp; - Tcl_Obj *disposePtr = iPtr->chanMsg; - if (msg != NULL) { - iPtr->chanMsg = FixLevelCode(msg); - Tcl_IncrRefCount(iPtr->chanMsg); - } else { + if (iPtr->chanMsg != NULL) { + TclDecrRefCount(iPtr->chanMsg); iPtr->chanMsg = NULL; } - if (disposePtr != NULL) { - TclDecrRefCount(disposePtr); + if (msg != NULL) { + iPtr->chanMsg = FixLevelCode(msg); + Tcl_IncrRefCount(iPtr->chanMsg); } return; } @@ -11338,17 +10157,15 @@ Tcl_SetChannelError( Tcl_Obj *msg) /* Error message to store. */ { ChannelState *statePtr = ((Channel *) chan)->state; - Tcl_Obj *disposePtr = statePtr->chanMsg; - if (msg != NULL) { - statePtr->chanMsg = FixLevelCode(msg); - Tcl_IncrRefCount(statePtr->chanMsg); - } else { + if (statePtr->chanMsg != NULL) { + TclDecrRefCount(statePtr->chanMsg); statePtr->chanMsg = NULL; } - if (disposePtr != NULL) { - TclDecrRefCount(disposePtr); + if (msg != NULL) { + statePtr->chanMsg = FixLevelCode(msg); + Tcl_IncrRefCount(statePtr->chanMsg); } return; } @@ -11376,8 +10193,7 @@ static Tcl_Obj * FixLevelCode( Tcl_Obj *msg) { - int explicitResult, numOptions, lcn; - Tcl_Size lc; + int explicitResult, numOptions, lc, lcn; Tcl_Obj **lv, **lvn; int res, i, j, val, lignore, cignore; int newlevel = -1, newcode = -1; @@ -11390,13 +10206,13 @@ FixLevelCode( * Syntax = (option value)... ?message? * * Bad message syntax causes a panic, because the other side uses - * Tcl_GetReturnOptions and list construction functions to marshal the + * Tcl_GetReturnOptions and list construction functions to marshall the * information. Hence an error means that we've got serious breakage. */ - res = TclListObjGetElements(NULL, msg, &lc, &lv); + res = Tcl_ListObjGetElements(NULL, msg, &lc, &lv); if (res != TCL_OK) { - Tcl_Panic("Tcl_SetChannelError: bad syntax of message"); + Tcl_Panic("Tcl_SetChannelError(Interp): Bad syntax of message"); } explicitResult = (1 == (lc % 2)); @@ -11456,11 +10272,11 @@ FixLevelCode( lcn += 2; } - lvn = (Tcl_Obj **)ckalloc(lcn * sizeof(Tcl_Obj *)); + lvn = (Tcl_Obj **) ckalloc(lcn * sizeof(Tcl_Obj *)); /* - * New level/code information is spliced into the first occurrence of - * -level, -code, further occurrences are ignored. The options cannot be + * New level/code information is spliced into the first occurence of + * -level, -code, further occurences are ignored. The options cannot be * not present, we would not come here. Options which are ok are simply * copied over. */ @@ -11470,7 +10286,7 @@ FixLevelCode( if (0 == strcmp(TclGetString(lv[i]), "-level")) { if (newlevel >= 0) { lvn[j++] = lv[i]; - lvn[j++] = Tcl_NewWideIntObj(newlevel); + lvn[j++] = Tcl_NewIntObj(newlevel); newlevel = -1; lignore = 1; continue; @@ -11480,7 +10296,7 @@ FixLevelCode( } else if (0 == strcmp(TclGetString(lv[i]), "-code")) { if (newcode >= 0) { lvn[j++] = lv[i]; - lvn[j++] = Tcl_NewWideIntObj(newcode); + lvn[j++] = Tcl_NewIntObj(newcode); newcode = -1; cignore = 1; continue; @@ -11509,7 +10325,7 @@ FixLevelCode( msg = Tcl_NewListObj(j, lvn); - ckfree(lvn); + ckfree((char *) lvn); return msg; } @@ -11591,16 +10407,17 @@ Tcl_ChannelTruncateProc( const Tcl_ChannelType *chanTypePtr) /* Pointer to channel type. */ { - if (Tcl_ChannelVersion(chanTypePtr) < TCL_CHANNEL_VERSION_5) { + if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_5)) { + return chanTypePtr->truncateProc; + } else { return NULL; } - return chanTypePtr->truncateProc; } /* *---------------------------------------------------------------------- * - * DupChannelInternalRep -- + * DupChannelIntRep -- * * Initialize the internal representation of a new Tcl_Obj to a copy of * the internal representation of an existing string object. @@ -11616,23 +10433,85 @@ Tcl_ChannelTruncateProc( */ static void -DupChannelInternalRep( - Tcl_Obj *srcPtr, /* Object with internal rep to copy. Must have +DupChannelIntRep( + register Tcl_Obj *srcPtr, /* Object with internal rep to copy. Must have * an internal rep of type "Channel". */ - Tcl_Obj *copyPtr) /* Object with internal rep to set. Must not + register Tcl_Obj *copyPtr) /* Object with internal rep to set. Must not * currently have an internal rep.*/ { - ResolvedChanName *resPtr; + ChannelState *statePtr = GET_CHANNELSTATE(srcPtr); - ChanGetInternalRep(srcPtr, resPtr); - assert(resPtr); - ChanSetInternalRep(copyPtr, resPtr); + SET_CHANNELSTATE(copyPtr, statePtr); + SET_CHANNELINTERP(copyPtr, GET_CHANNELINTERP(srcPtr)); + Tcl_Preserve((ClientData) statePtr); + copyPtr->typePtr = srcPtr->typePtr; } /* *---------------------------------------------------------------------- * - * FreeChannelInternalRep -- + * SetChannelFromAny -- + * + * Create an internal representation of type "Channel" for an object. + * + * Results: + * This operation always succeeds and returns TCL_OK. + * + * Side effects: + * Any old internal reputation for objPtr is freed and the internal + * representation is set to "Channel". + * + *---------------------------------------------------------------------- + */ + +static int +SetChannelFromAny( + Tcl_Interp *interp, /* Used for error reporting if not NULL. */ + register Tcl_Obj *objPtr) /* The object to convert. */ +{ + ChannelState *statePtr; + + if (interp == NULL) { + return TCL_ERROR; + } + if (objPtr->typePtr == &chanObjType) { + /* + * TODO: TAINT Flag and dup'd channel values? + * The channel is valid until any call to DetachChannel occurs. + * Ensure consistency checks are done. + */ + + statePtr = GET_CHANNELSTATE(objPtr); + if (GotFlag(statePtr, CHANNEL_TAINTED|CHANNEL_CLOSED)) { + ResetFlag(statePtr, CHANNEL_TAINTED); + Tcl_Release((ClientData) statePtr); + objPtr->typePtr = NULL; + } else if (interp != GET_CHANNELINTERP(objPtr)) { + Tcl_Release((ClientData) statePtr); + objPtr->typePtr = NULL; + } + } + if (objPtr->typePtr != &chanObjType) { + Tcl_Channel chan = Tcl_GetChannel(interp, TclGetString(objPtr), NULL); + + if (chan == NULL) { + return TCL_ERROR; + } + + TclFreeIntRep(objPtr); + statePtr = ((Channel *)chan)->state; + Tcl_Preserve((ClientData) statePtr); + SET_CHANNELSTATE(objPtr, statePtr); + SET_CHANNELINTERP(objPtr, interp); + objPtr->typePtr = &chanObjType; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * FreeChannelIntRep -- * * Release statePtr storage. * @@ -11646,18 +10525,10 @@ DupChannelInternalRep( */ static void -FreeChannelInternalRep( +FreeChannelIntRep( Tcl_Obj *objPtr) /* Object with internal rep to free. */ { - ResolvedChanName *resPtr; - - ChanGetInternalRep(objPtr, resPtr); - assert(resPtr); - if (resPtr->refCount-- > 1) { - return; - } - Tcl_Release(resPtr->statePtr); - ckfree(resPtr); + Tcl_Release((ClientData) GET_CHANNELSTATE(objPtr)); } #if 0 @@ -11671,10 +10542,10 @@ DumpFlags( char *str, int flags) { + char buf[20]; int i = 0; - char buf[24]; -#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); @@ -11685,7 +10556,6 @@ DumpFlags( ChanFlag('c', CHANNEL_CLOSED); ChanFlag('E', CHANNEL_EOF); ChanFlag('S', CHANNEL_STICKY_EOF); - ChanFlag('U', CHANNEL_ENCODING_ERROR); ChanFlag('B', CHANNEL_BLOCKED); ChanFlag('/', INPUT_SAW_CR); ChanFlag('D', CHANNEL_DEAD); |
