summaryrefslogtreecommitdiffstats
path: root/generic/tclIO.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclIO.c')
-rw-r--r--generic/tclIO.c4746
1 files changed, 1971 insertions, 2775 deletions
diff --git a/generic/tclIO.c b/generic/tclIO.c
index d3842ef..7bc849e 100644
--- a/generic/tclIO.c
+++ b/generic/tclIO.c
@@ -6,6 +6,7 @@
*
* 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
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -16,6 +17,108 @@
#include <assert.h>
/*
+ * For each channel handler registered in a call to Tcl_CreateChannelHandler,
+ * there is one record of the following type. All of records for a specific
+ * channel are chained together in a singly linked list which is stored in
+ * the channel structure.
+ */
+
+typedef struct ChannelHandler {
+ Channel *chanPtr; /* The channel structure for this channel. */
+ int mask; /* Mask of desired events. */
+ Tcl_ChannelProc *proc; /* Procedure to call in the type of
+ * Tcl_CreateChannelHandler. */
+ 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 ChannelHandlerEventProc. There is a potential
+ * problem if a ChannelHandler is deleted while it is the current one, since
+ * 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 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.
+ */
+
+typedef struct NextChannelHandler {
+ ChannelHandler *nextHandlerPtr; /* The next handler to be invoked in
+ * this invocation. */
+ struct NextChannelHandler *nestedHandlerPtr;
+ /* Next nested invocation of
+ * 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.
+ */
+
+typedef struct GetsState {
+ Tcl_Obj *objPtr; /* The object to which UTF-8 characters
+ * will be appended. */
+ char **dstPtr; /* Pointer into objPtr's string rep where
+ * next character should be stored. */
+ Tcl_Encoding encoding; /* The encoding to use to convert raw bytes
+ * to UTF-8. */
+ ChannelBuffer *bufPtr; /* The current buffer of raw bytes being
+ * emptied. */
+ Tcl_EncodingState state; /* The encoding state just before the last
+ * external to UTF-8 conversion in
+ * FilterInputBytes(). */
+ int rawRead; /* The number of bytes removed from bufPtr
+ * in the last call to FilterInputBytes(). */
+ int bytesWrote; /* The number of bytes of UTF-8 data
+ * appended to objPtr during the last call to
+ * FilterInputBytes(). */
+ int charsWrote; /* The corresponding number of UTF-8
+ * characters appended to objPtr during the
+ * last call to FilterInputBytes(). */
+ int totalChars; /* The total number of UTF-8 characters
+ * appended to objPtr so far, just before the
+ * last call to FilterInputBytes(). */
+} GetsState;
+
+/*
+ * The following structure encapsulates the state for a background channel
+ * copy. Note that the data buffer for the copy will be appended to this
+ * structure.
+ */
+
+typedef struct CopyState {
+ struct Channel *readPtr; /* Pointer to input channel. */
+ struct Channel *writePtr; /* Pointer to output channel. */
+ int readFlags; /* Original read channel flags. */
+ int writeFlags; /* Original write channel flags. */
+ 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. */
+ int bufSize; /* Size of appended buffer. */
+ char buffer[1]; /* Copy buffer, this must be the last
+ * field. */
+} CopyState;
+
+/*
* All static variables used in this file are collected into a single instance
* of the following structure. For multi-threaded implementations, there is
* one instance of this structure for each thread.
@@ -44,15 +147,30 @@ typedef struct ThreadSpecificData {
static Tcl_ThreadDataKey dataKey;
/*
+ * Structure to record a close callback. One such record exists for
+ * each close callback registered for a channel.
+ */
+
+typedef struct CloseCallback {
+ Tcl_CloseProc *proc; /* The procedure to call. */
+ ClientData clientData; /* Arbitrary one-word data to pass
+ * to the callback. */
+ struct CloseCallback *nextPtr; /* For chaining close callbacks. */
+} CloseCallback;
+
+/*
* Static functions in this file:
*/
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(ClientData clientData);
+static int ChanRead(Channel *chanPtr, char *dst, int dstSize);
static int CheckChannelErrors(ChannelState *statePtr,
int direction);
-static int CheckFlush(Channel *chanPtr, ChannelBuffer *bufPtr,
- int newlineFlag);
static int CheckForDeadChannel(Tcl_Interp *interp,
ChannelState *statePtr);
static void CheckForStdChannelsBeingClosed(Tcl_Channel chan);
@@ -60,14 +178,7 @@ static void CleanupChannelHandlers(Tcl_Interp *interp,
Channel *chanPtr);
static int CloseChannel(Tcl_Interp *interp, Channel *chanPtr,
int errorCode);
-static int CloseChannelPart(Tcl_Interp *interp, Channel *chanPtr,
- int errorCode, int flags);
-static int CloseWrite(Tcl_Interp *interp, Channel *chanPtr);
static void CommonGetsCleanup(Channel *chanPtr);
-static size_t CopyAndTranslateBuffer(ChannelState *statePtr,
- char *result, size_t space);
-static size_t CopyBuffer(Channel *chanPtr, char *result,
- size_t space);
static int CopyData(CopyState *csPtr, int mask);
static void CopyEventProc(ClientData clientData, int mask);
static void CreateScriptRecord(Tcl_Interp *interp,
@@ -80,19 +191,15 @@ static int DetachChannel(Tcl_Interp *interp, Tcl_Channel chan);
static void DiscardInputQueued(ChannelState *statePtr,
int discardSavedBuffers);
static void DiscardOutputQueued(ChannelState *chanPtr);
-static ssize_t DoRead(Channel *chanPtr, char *srcPtr, size_t slen,
- int allowShortReads);
-static ssize_t DoWrite(Channel *chanPtr, const char *src,
- size_t srcLen);
-static ssize_t DoReadChars(Channel *chan, Tcl_Obj *objPtr,
- size_t toRead, int appendFlag);
-static ssize_t DoWriteChars(Channel *chan, const char *src,
- size_t len);
+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 ssize_t TclGetsObjBinary(Tcl_Channel chan, Tcl_Obj *objPtr);
+static int TclGetsObjBinary(Tcl_Channel chan, Tcl_Obj *objPtr);
+static Tcl_Encoding GetBinaryEncoding();
static void FreeBinaryEncoding(ClientData clientData);
static Tcl_HashTable * GetChannelTable(Tcl_Interp *interp);
static int GetInput(Channel *chanPtr);
@@ -100,32 +207,30 @@ static int HaveVersion(const Tcl_ChannelType *typePtr,
Tcl_ChannelTypeVersion minimumVersion);
static void PeekAhead(Channel *chanPtr, char **dstEndPtr,
GetsState *gsPtr);
-static ssize_t ReadBytes(ChannelState *statePtr, Tcl_Obj *objPtr,
- size_t charsLeft, size_t *offsetPtr);
-static ssize_t ReadChars(ChannelState *statePtr, Tcl_Obj *objPtr,
- size_t charsLeft, size_t *offsetPtr,
- size_t *factorPtr);
+static int ReadBytes(ChannelState *statePtr, Tcl_Obj *objPtr,
+ int charsLeft);
+static int ReadChars(ChannelState *statePtr, Tcl_Obj *objPtr,
+ int charsLeft, int *factorPtr);
static void RecycleBuffer(ChannelState *statePtr,
ChannelBuffer *bufPtr, int mustDiscard);
static int StackSetBlockMode(Channel *chanPtr, int mode);
static int SetBlockMode(Tcl_Interp *interp, Channel *chanPtr,
int mode);
static void StopCopy(CopyState *csPtr);
-static int TranslateInputEOL(ChannelState *statePtr, char *dst,
- const char *src, size_t *dstLenPtr,
- size_t *srcLenPtr);
-static int TranslateOutputEOL(ChannelState *statePtr, char *dst,
- const char *src, size_t *dstLenPtr,
- size_t *srcLenPtr);
+static void TranslateInputEOL(ChannelState *statePtr, char *dst,
+ const char *src, int *dstLenPtr, int *srcLenPtr);
static void UpdateInterest(Channel *chanPtr);
-static ssize_t WriteBytes(Channel *chanPtr, const char *src,
- size_t srcLen);
-static ssize_t WriteChars(Channel *chanPtr, const char *src,
- size_t srcLen);
+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 inline int WillRead(Channel *chanPtr);
+static int WillRead(Channel *chanPtr);
+
+#define WriteChars(chanPtr, src, srcLen) \
+ Write(chanPtr, src, srcLen, chanPtr->state->encoding)
+#define WriteBytes(chanPtr, src, srcLen) \
+ Write(chanPtr, src, srcLen, tclIdentityEncoding)
/*
* Simplifying helper macros. All may use their argument(s) multiple times.
@@ -133,11 +238,11 @@ static inline int WillRead(Channel *chanPtr);
* short description of what the macro does.
*
* --------------------------------------------------------------------------
- * size_t BytesLeft(ChannelBuffer *bufPtr)
+ * int BytesLeft(ChannelBuffer *bufPtr)
*
* Returns the number of bytes of data remaining in the buffer.
*
- * ssize_t SpaceLeft(ChannelBuffer *bufPtr)
+ * int SpaceLeft(ChannelBuffer *bufPtr)
*
* Returns the number of bytes of space remaining at the end of the
* buffer.
@@ -171,21 +276,21 @@ static inline 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)->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.
@@ -214,29 +319,27 @@ static inline int WillRead(Channel *chanPtr);
*/
static void DupChannelIntRep(Tcl_Obj *objPtr, Tcl_Obj *copyPtr);
-static int SetChannelFromAny(Tcl_Interp *interp,
- Tcl_Obj *objPtr);
-static void UpdateStringOfChannel(Tcl_Obj *objPtr);
+static int SetChannelFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
static void FreeChannelIntRep(Tcl_Obj *objPtr);
-static const Tcl_ObjType tclChannelType = {
+static Tcl_ObjType chanObjType = {
"channel", /* name for this type */
FreeChannelIntRep, /* freeIntRepProc */
DupChannelIntRep, /* dupIntRepProc */
- NULL, /* updateStringProc UpdateStringOfChannel */
+ NULL, /* updateStringProc */
NULL /* setFromAnyProc SetChannelFromAny */
};
#define GET_CHANNELSTATE(objPtr) \
- ((ChannelState *) (objPtr)->internalRep.otherValuePtr)
+ ((ChannelState *) (objPtr)->internalRep.twoPtrValue.ptr1)
#define SET_CHANNELSTATE(objPtr, storePtr) \
- ((objPtr)->internalRep.otherValuePtr = (void *) (storePtr))
+ ((objPtr)->internalRep.twoPtrValue.ptr1 = (void *) (storePtr))
#define GET_CHANNELINTERP(objPtr) \
- ((Interp *) (objPtr)->internalRep.twoPtrValue.ptr2)
+ ((Tcl_Interp *) (objPtr)->internalRep.twoPtrValue.ptr2)
#define SET_CHANNELINTERP(objPtr, storePtr) \
((objPtr)->internalRep.twoPtrValue.ptr2 = (void *) (storePtr))
-#define BUSY_STATE(st, fl) \
+#define BUSY_STATE(st,fl) \
((((st)->csPtrR) && ((fl) & TCL_READABLE)) || \
(((st)->csPtrW) && ((fl) & TCL_WRITABLE)))
@@ -245,49 +348,84 @@ static const Tcl_ObjType tclChannelType = {
/*
*---------------------------------------------------------------------------
*
- * ChanClose, ChanRead, ChanSeek, ChanThreadAction, ChanWatch, ChanWrite --
+ * ChanRead --
*
- * 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.
+ * 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().
+ *
+ * Side effects:
+ * The CHANNEL_BLOCKED and CHANNEL_EOF flags of the channel state are
+ * set as appropriate.
+ * On EOF, the inputEncodingFlags are set to perform ending operations
+ * on decoding.
+ * TODO - Is this really the right place for that?
*
*---------------------------------------------------------------------------
*/
-
-static inline int
-ChanClose(
- Channel *chanPtr,
- Tcl_Interp *interp)
-{
- if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) {
- return chanPtr->typePtr->closeProc(chanPtr->instanceData, interp);
- } else {
- return chanPtr->typePtr->close2Proc(chanPtr->instanceData, interp, 0);
- }
-}
-
-static inline int
-ChanCloseHalf(
- Channel *chanPtr,
- Tcl_Interp *interp,
- int flags)
-{
- return chanPtr->typePtr->close2Proc(chanPtr->instanceData, interp, flags);
-}
-
-static inline ssize_t
+static int
ChanRead(
Channel *chanPtr,
char *dst,
- size_t dstSize,
- int *errnoPtr)
+ int dstSize)
{
- if (WillRead(chanPtr)) {
+ 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.
+ */
+ 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;
+ }
+ ResetFlag(chanPtr->state, CHANNEL_BLOCKED | CHANNEL_EOF);
+ chanPtr->state->inputEncodingFlags &= ~TCL_ENCODING_END;
+ if (WillRead(chanPtr) < 0) {
return -1;
}
- return chanPtr->typePtr->inputProc(chanPtr->instanceData, dst, dstSize,
- errnoPtr);
+ bytesRead = chanPtr->typePtr->inputProc(chanPtr->instanceData,
+ dst, dstSize, &result);
+
+ /* 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 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);
+ }
+ } else if (bytesRead == 0) {
+ SetFlag(chanPtr->state, CHANNEL_EOF);
+ chanPtr->state->inputEncodingFlags |= TCL_ENCODING_END;
+ } else if (bytesRead < 0) {
+ if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
+ SetFlag(chanPtr->state, CHANNEL_BLOCKED);
+ result = EAGAIN;
+ }
+ Tcl_SetErrno(result);
+ }
+ return bytesRead;
}
static inline Tcl_WideInt
@@ -317,37 +455,6 @@ ChanSeek(
Tcl_WideAsLong(offset), mode, errnoPtr));
}
-static inline void
-ChanThreadAction(
- Channel *chanPtr,
- int action)
-{
- Tcl_DriverThreadActionProc *threadActionProc =
- Tcl_ChannelThreadActionProc(chanPtr->typePtr);
-
- if (threadActionProc != NULL) {
- threadActionProc(chanPtr->instanceData, action);
- }
-}
-
-static inline void
-ChanWatch(
- Channel *chanPtr,
- int mask)
-{
- chanPtr->typePtr->watchProc(chanPtr->instanceData, mask);
-}
-
-static inline ssize_t
-ChanWrite(
- Channel *chanPtr,
- const char *src,
- size_t srcLen,
- int *errnoPtr)
-{
- return chanPtr->typePtr->outputProc(chanPtr->instanceData, src, srcLen,
- errnoPtr);
-}
/*
*---------------------------------------------------------------------------
@@ -403,19 +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
@@ -434,38 +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) {
/*
- * 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) ||
@@ -498,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
@@ -510,6 +597,7 @@ TclFinalizeIOSubsystem(void)
chanPtr->instanceData = NULL;
SetFlag(statePtr, CHANNEL_DEAD);
}
+ TclChannelRelease((Tcl_Channel)chanPtr);
}
}
@@ -660,7 +748,7 @@ Tcl_CreateCloseHandler(
ChannelState *statePtr = ((Channel *) chan)->state;
CloseCallback *cbPtr;
- cbPtr = ckalloc(sizeof(CloseCallback));
+ cbPtr = (CloseCallback *) ckalloc(sizeof(CloseCallback));
cbPtr->proc = proc;
cbPtr->clientData = clientData;
@@ -703,11 +791,14 @@ Tcl_DeleteCloseHandler(
if ((cbPtr->proc == proc) && (cbPtr->clientData == clientData)) {
if (cbPrevPtr == NULL) {
statePtr->closeCbPtr = cbPtr->nextPtr;
+ } else {
+ cbPrevPtr->nextPtr = cbPtr->nextPtr;
}
- ckfree(cbPtr);
+ ckfree((char *) cbPtr);
break;
+ } else {
+ cbPrevPtr = cbPtr;
}
- cbPrevPtr = cbPtr;
}
}
@@ -739,7 +830,7 @@ GetChannelTable(
hTblPtr = Tcl_GetAssocData(interp, "tclIO", NULL);
if (hTblPtr == NULL) {
- hTblPtr = 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);
@@ -828,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;
}
@@ -855,7 +946,7 @@ DeleteChannelTable(
}
Tcl_DeleteHashTable(hTblPtr);
- ckfree(hTblPtr);
+ ckfree((char *) hTblPtr);
}
/*
@@ -1041,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", TCL_STRLEN));
+ Tcl_AppendResult(interp, "Illegal recursive call to close "
+ "through close-handler of channel", NULL);
}
return TCL_ERROR;
}
@@ -1069,16 +1159,7 @@ Tcl_UnregisterChannel(
*/
if (statePtr->refCount <= 0) {
- /*
- * Ensure that if there is another buffer, it gets flushed whether or
- * not we are doing a background flush.
- */
-
- if ((statePtr->curOutPtr != NULL) &&
- IsBufferReady(statePtr->curOutPtr)) {
- SetFlag(statePtr, BUFFER_READY);
- }
- Tcl_Preserve(statePtr);
+ Tcl_Preserve((ClientData)statePtr);
if (!GotFlag(statePtr, BG_FLUSH_SCHEDULED)) {
/*
* We don't want to re-enter Tcl_Close().
@@ -1087,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;
}
@@ -1278,8 +1359,8 @@ 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_AppendResult(interp, "can not find channel named \"", chanName,
+ "\"", NULL);
Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "CHANNEL", chanName, NULL);
return NULL;
}
@@ -1293,7 +1374,7 @@ Tcl_GetChannel(
chanPtr = Tcl_GetHashValue(hPtr);
chanPtr = chanPtr->state->bottomChanPtr;
if (modePtr != NULL) {
- *modePtr = chanPtr->state->flags & (TCL_READABLE|TCL_WRITABLE);
+ *modePtr = GotFlag(chanPtr->state, TCL_READABLE|TCL_WRITABLE);
}
return (Tcl_Channel) chanPtr;
@@ -1338,10 +1419,10 @@ TclGetChannelFromObj(
}
statePtr = GET_CHANNELSTATE(objPtr);
- *channelPtr = (Tcl_Channel) statePtr->bottomChanPtr;
+ *channelPtr = (Tcl_Channel) (statePtr->bottomChanPtr);
if (modePtr != NULL) {
- *modePtr = statePtr->flags & (TCL_READABLE|TCL_WRITABLE);
+ *modePtr = GotFlag(statePtr, TCL_READABLE|TCL_WRITABLE);
}
return TCL_OK;
@@ -1365,7 +1446,7 @@ 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. */
ClientData instanceData, /* Instance specific data. */
int mask) /* TCL_READABLE & TCL_WRITABLE to indicate if
@@ -1375,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);
/*
@@ -1388,15 +1468,15 @@ Tcl_CreateChannel(
* as well.
*/
- assert(sizeof(Tcl_ChannelTypeVersion) == sizeof(Tcl_DriverBlockModeProc *));
+ assert(sizeof(Tcl_ChannelTypeVersion) == sizeof(Tcl_DriverBlockModeProc*));
/*
* JH: We could subsequently memset these to 0 to avoid the numerous
* assignments to 0/NULL below.
*/
- chanPtr = ckalloc(sizeof(Channel));
- statePtr = ckalloc(sizeof(ChannelState));
+ chanPtr = (Channel *) ckalloc(sizeof(Channel));
+ statePtr = (ChannelState *) ckalloc(sizeof(ChannelState));
chanPtr->state = statePtr;
chanPtr->instanceData = instanceData;
@@ -1408,20 +1488,14 @@ Tcl_CreateChannel(
*/
if (chanName != NULL) {
- unsigned len = strlen(chanName) + 1;
+ char *tmp = ckalloc((unsigned) (strlen(chanName) + 1));
- /*
- * Make sure we allocate at least 7 bytes, so it fits for "stdout"
- * later.
- */
-
- tmp = ckalloc((len < 7) ? 7 : len);
+ statePtr->channelName = tmp;
strcpy(tmp, chanName);
} else {
- tmp = ckalloc(7);
- tmp[0] = '\0';
+ Tcl_Panic("Tcl_CreateChannel: NULL channel name");
}
- statePtr->channelName = tmp;
+
statePtr->flags = mask;
/*
@@ -1473,11 +1547,7 @@ Tcl_CreateChannel(
statePtr->timer = NULL;
statePtr->csPtrR = NULL;
statePtr->csPtrW = NULL;
-
statePtr->outputStage = NULL;
- if ((statePtr->encoding != NULL) && GotFlag(statePtr, TCL_WRITABLE)) {
- statePtr->outputStage = ckalloc(statePtr->bufSize + 2);
- }
/*
* As we are creating the channel, it is obviously the top for now.
@@ -1489,6 +1559,7 @@ Tcl_CreateChannel(
chanPtr->upChanPtr = NULL;
chanPtr->inQueueHead = NULL;
chanPtr->inQueueTail = NULL;
+ chanPtr->refCount = 0;
/*
* TIP #219, Tcl Channel Reflection API
@@ -1523,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);
}
@@ -1570,8 +1638,7 @@ 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. */
ClientData instanceData, /* Instance specific data for the new
* channel. */
@@ -1582,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
@@ -1599,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;
}
@@ -1619,11 +1686,11 @@ Tcl_StackChannel(
* --+---+---+---+----+
*/
- if ((mask & (statePtr->flags & (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;
}
@@ -1642,13 +1709,16 @@ Tcl_StackChannel(
statePtr->csPtrR = NULL;
statePtr->csPtrW = NULL;
+ /*
+ * TODO: Examine what can go wrong if Tcl_Flush() call disturbs
+ * the stacking state of this channel during its operations.
+ */
if (Tcl_Flush((Tcl_Channel) prevChanPtr) != TCL_OK) {
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;
}
@@ -1673,23 +1743,23 @@ Tcl_StackChannel(
*/
if (((mask & TCL_READABLE) != 0) && (statePtr->inQueueHead != NULL)) {
+
/*
- * Remark: It is possible that the channel buffers contain data from
- * some earlier push-backs.
+ * When statePtr->inQueueHead is not NULL, we know
+ * prevChanPtr->inQueueHead must be NULL.
*/
- statePtr->inQueueTail->nextPtr = prevChanPtr->inQueueHead;
- prevChanPtr->inQueueHead = statePtr->inQueueHead;
+ assert(prevChanPtr->inQueueHead == NULL);
+ assert(prevChanPtr->inQueueTail == NULL);
- if (prevChanPtr->inQueueTail == NULL) {
- prevChanPtr->inQueueTail = statePtr->inQueueTail;
- }
+ prevChanPtr->inQueueHead = statePtr->inQueueHead;
+ prevChanPtr->inQueueTail = statePtr->inQueueTail;
statePtr->inQueueHead = NULL;
statePtr->inQueueTail = NULL;
}
- chanPtr = ckalloc(sizeof(Channel));
+ chanPtr = (Channel *) ckalloc(sizeof(Channel));
/*
* Save some of the current state into the new structure, reinitialize the
@@ -1705,6 +1775,7 @@ Tcl_StackChannel(
chanPtr->upChanPtr = NULL;
chanPtr->inQueueHead = NULL;
chanPtr->inQueueTail = NULL;
+ chanPtr->refCount = 0;
/*
* Place new block at the head of a possibly existing list of previously
@@ -1725,10 +1796,48 @@ 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;
}
+
+void
+TclChannelPreserve(
+ Tcl_Channel chan)
+{
+ ((Channel *)chan)->refCount++;
+}
+
+void
+TclChannelRelease(
+ Tcl_Channel chan)
+{
+ Channel *chanPtr = (Channel *) chan;
+
+ if (chanPtr->refCount == 0) {
+ Tcl_Panic("Channel released more than preserved");
+ }
+ if (--chanPtr->refCount) {
+ return;
+ }
+ if (chanPtr->typePtr == NULL) {
+ ckfree((char *)chanPtr);
+ }
+}
+
+static void
+ChannelFree(
+ Channel *chanPtr)
+{
+ if (chanPtr->refCount == 0) {
+ ckfree((char *)chanPtr);
+ return;
+ }
+ chanPtr->typePtr = NULL;
+}
/*
*----------------------------------------------------------------------
@@ -1756,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.
@@ -1766,9 +1876,16 @@ Tcl_UnstackChannel(
if (chanPtr->downChanPtr != NULL) {
/*
* Instead of manipulating the per-thread / per-interp list/hashtable
- * of registered channels we wind down the state of the
- * transformation, and then restore the state of underlying channel
- * into the old structure.
+ * of registered channels we wind down the state of the transformation,
+ * and then restore the state of underlying channel into the old
+ * structure.
+ */
+
+ /*
+ * 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
+ * value we're holding on to will not be the right thing.
*/
Channel *downChanPtr = chanPtr->downChanPtr;
@@ -1801,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;
}
@@ -1853,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;
@@ -1867,15 +1988,17 @@ Tcl_UnstackChannel(
* Close and free the channel driver state.
*/
- result = ChanClose(chanPtr, interp);
- chanPtr->typePtr = NULL;
+ if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) {
+ result = (chanPtr->typePtr->closeProc)(chanPtr->instanceData,
+ interp);
+ } else {
+ result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData,
+ interp, 0);
+ }
- /*
- * AK: Tcl_NotifyChannel may hold a reference to this block of memory
- */
+ ChannelFree(chanPtr);
- Tcl_EventuallyFree(chanPtr, TCL_DYNAMIC);
- UpdateInterest(downChanPtr);
+ UpdateInterest(statePtr->topChanPtr);
if (result != 0) {
Tcl_SetErrno(result);
@@ -2043,7 +2166,7 @@ Tcl_GetChannelThread(
*----------------------------------------------------------------------
*/
-const Tcl_ChannelType *
+Tcl_ChannelType *
Tcl_GetChannelType(
Tcl_Channel chan) /* The channel to return type for. */
{
@@ -2078,7 +2201,7 @@ Tcl_GetChannelMode(
ChannelState *statePtr = ((Channel *) chan)->state;
/* State of actual channel. */
- return (statePtr->flags & (TCL_READABLE | TCL_WRITABLE));
+ return GotFlag(statePtr, TCL_READABLE | TCL_WRITABLE);
}
/*
@@ -2102,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;
}
@@ -2137,13 +2260,15 @@ Tcl_GetChannelHandle(
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;
}
@@ -2182,13 +2307,38 @@ AllocChannelBuffer(
int n;
n = length + CHANNELBUFFER_HEADER_SIZE + BUFFER_PADDING + BUFFER_PADDING;
- bufPtr = ckalloc(n);
+ bufPtr = (ChannelBuffer *) ckalloc((unsigned) n);
bufPtr->nextAdded = BUFFER_PADDING;
bufPtr->nextRemoved = BUFFER_PADDING;
bufPtr->bufLength = length + BUFFER_PADDING;
bufPtr->nextPtr = NULL;
+ bufPtr->refCount = 1;
return bufPtr;
}
+
+static void
+PreserveChannelBuffer(
+ ChannelBuffer *bufPtr)
+{
+ bufPtr->refCount++;
+}
+
+static void
+ReleaseChannelBuffer(
+ ChannelBuffer *bufPtr)
+{
+ if (--bufPtr->refCount) {
+ return;
+ }
+ ckfree((char *) bufPtr);
+}
+
+static int
+IsShared(
+ ChannelBuffer *bufPtr)
+{
+ return bufPtr->refCount > 1;
+}
/*
*----------------------------------------------------------------------
@@ -2219,20 +2369,23 @@ RecycleBuffer(
/*
* Do we have to free the buffer to the OS?
*/
+ if (IsShared(bufPtr)) {
+ mustDiscard = 1;
+ }
if (mustDiscard) {
- ckfree(bufPtr);
+ ReleaseChannelBuffer(bufPtr);
return;
}
/*
- * Only save buffers which are at least as big as the requested buffersize
- * for the channel. This is to honor dynamic changes of the buffersize
+ * Only save buffers which have the requested buffersize for the
+ * channel. This is to honor dynamic changes of the buffersize
* made by the user.
*/
- if ((bufPtr->bufLength - BUFFER_PADDING) < statePtr->bufSize) {
- ckfree(bufPtr);
+ if ((bufPtr->bufLength - BUFFER_PADDING) != statePtr->bufSize) {
+ ReleaseChannelBuffer(bufPtr);
return;
}
@@ -2267,7 +2420,7 @@ RecycleBuffer(
* If we reached this code we return the buffer to the OS.
*/
- ckfree(bufPtr);
+ ReleaseChannelBuffer(bufPtr);
return;
keepBuffer:
@@ -2305,6 +2458,11 @@ DiscardOutputQueued(
}
statePtr->outQueueHead = NULL;
statePtr->outQueueTail = NULL;
+ bufPtr = statePtr->curOutPtr;
+ if (bufPtr && BytesLeft(bufPtr)) {
+ statePtr->curOutPtr = NULL;
+ RecycleBuffer(statePtr, bufPtr, 0);
+ }
}
/*
@@ -2329,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", TCL_STRLEN));
+ 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;
}
/*
@@ -2346,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
@@ -2372,8 +2529,6 @@ FlushChannel(
ChannelState *statePtr = chanPtr->state;
/* State of the channel stack. */
ChannelBuffer *bufPtr; /* Iterates over buffered output queue. */
- int toWrite; /* Amount of output data in current buffer
- * available to be written. */
int written; /* Amount of output data actually written in
* current round. */
int errorCode = 0; /* Stores POSIX error codes from channel
@@ -2393,63 +2548,61 @@ FlushChannel(
}
/*
- * Loop over the queued buffers and attempt to flush as much as possible
- * of the queued output to the channel.
- */
-
- Tcl_Preserve(chanPtr);
- while (1) {
- /*
- * If the queue is empty and there is a ready current buffer, OR if
- * the current buffer is full, then move the current buffer to the
- * queue.
- */
-
- if (((statePtr->curOutPtr != NULL) &&
- IsBufferFull(statePtr->curOutPtr))
- || (GotFlag(statePtr, BUFFER_READY) &&
- (statePtr->outQueueHead == NULL))) {
- ResetFlag(statePtr, BUFFER_READY);
- statePtr->curOutPtr->nextPtr = NULL;
- if (statePtr->outQueueHead == NULL) {
- statePtr->outQueueHead = statePtr->curOutPtr;
- } else {
- statePtr->outQueueTail->nextPtr = statePtr->curOutPtr;
- }
- statePtr->outQueueTail = statePtr->curOutPtr;
- statePtr->curOutPtr = NULL;
+ * 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.
+ * 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.
+ */
+
+ bufPtr = statePtr->curOutPtr;
+ if (bufPtr && BytesLeft(bufPtr) && /* Keep empties off queue */
+ (statePtr->outQueueHead == NULL || IsBufferFull(bufPtr)
+ || !GotFlag(statePtr, CHANNEL_NONBLOCKING))) {
+ if (statePtr->outQueueHead == NULL) {
+ statePtr->outQueueHead = bufPtr;
+ } else {
+ statePtr->outQueueTail->nextPtr = bufPtr;
}
- bufPtr = statePtr->outQueueHead;
+ statePtr->outQueueTail = bufPtr;
+ statePtr->curOutPtr = NULL;
+ }
- /*
- * If we are not being called from an async flush and an async flush
- * is active, we just return without producing any output.
- */
+ assert(!IsBufferFull(statePtr->curOutPtr));
- if (!calledFromAsyncFlush && GotFlag(statePtr, BG_FLUSH_SCHEDULED)) {
- errorCode = 0;
- goto done;
- }
+ /*
+ * If we are not being called from an async flush and an async flush
+ * is active, we just return without producing any output.
+ */
- /*
- * If the output queue is still empty, break out of the while loop.
- */
+ if (!calledFromAsyncFlush && GotFlag(statePtr, BG_FLUSH_SCHEDULED)) {
+ return 0;
+ }
- if (bufPtr == NULL) {
- break; /* Out of the "while (1)". */
- }
+ /*
+ * Loop over the queued buffers and attempt to flush as much as possible
+ * of the queued output to the channel.
+ */
+
+ TclChannelPreserve((Tcl_Channel)chanPtr);
+ while (statePtr->outQueueHead) {
+ bufPtr = statePtr->outQueueHead;
/*
* Produce the output on the channel.
*/
- toWrite = BytesLeft(bufPtr);
- if (toWrite == 0) {
- written = 0;
- } else {
- written = ChanWrite(chanPtr, RemovePoint(bufPtr), toWrite,
- &errorCode);
- }
+ PreserveChannelBuffer(bufPtr);
+ written = (chanPtr->typePtr->outputProc)(chanPtr->instanceData,
+ RemovePoint(bufPtr), BytesLeft(bufPtr), &errorCode);
/*
* If the write failed completely attempt to start the asynchronous
@@ -2464,6 +2617,7 @@ FlushChannel(
if (errorCode == EINTR) {
errorCode = 0;
+ ReleaseChannelBuffer(bufPtr);
continue;
}
@@ -2485,6 +2639,7 @@ FlushChannel(
UpdateInterest(chanPtr);
}
errorCode = 0;
+ ReleaseChannelBuffer(bufPtr);
break;
}
@@ -2530,9 +2685,14 @@ FlushChannel(
Tcl_SetErrno(errorCode);
if (interp != NULL && !TclChanCaughtErrorBypass(interp,
(Tcl_Channel) chanPtr)) {
- Tcl_SetObjResult(interp,
- Tcl_NewStringObj(Tcl_PosixError(interp),
- TCL_STRLEN));
+ /*
+ * 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);
}
/*
@@ -2547,14 +2707,15 @@ FlushChannel(
*/
DiscardOutputQueued(statePtr);
- continue;
+ ReleaseChannelBuffer(bufPtr);
+ break;
} else {
+ /* TODO: Consider detecting and reacting to short writes
+ * on blocking channels. Ought not happen. See iocmd-24.2. */
wroteSome = 1;
}
- if (!IsBufferEmpty(bufPtr)) {
- bufPtr->nextRemoved += written;
- }
+ bufPtr->nextRemoved += written;
/*
* If this buffer is now empty, recycle it.
@@ -2567,7 +2728,8 @@ FlushChannel(
}
RecycleBuffer(statePtr, bufPtr, 0);
}
- } /* Closes "while (1)". */
+ ReleaseChannelBuffer(bufPtr);
+ } /* Closes "while". */
/*
* If we wrote some data while flushing in the background, we are done.
@@ -2581,7 +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 {
+ /* 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. */
}
}
@@ -2596,25 +2765,9 @@ 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:
- Tcl_Release(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
return errorCode;
}
@@ -2668,7 +2821,7 @@ CloseChannel(
*/
if (statePtr->curOutPtr != NULL) {
- ckfree(statePtr->curOutPtr);
+ ReleaseChannelBuffer(statePtr->curOutPtr);
statePtr->curOutPtr = NULL;
}
@@ -2689,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);
}
/*
@@ -2700,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;
@@ -2717,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
@@ -2726,15 +2884,11 @@ CloseChannel(
if (chanPtr == statePtr->bottomChanPtr) {
if (statePtr->channelName != NULL) {
- ckfree(statePtr->channelName);
+ ckfree((char *) statePtr->channelName);
statePtr->channelName = NULL;
}
Tcl_FreeEncoding(statePtr->encoding);
- if (statePtr->outputStage != NULL) {
- ckfree(statePtr->outputStage);
- statePtr->outputStage = NULL;
- }
}
/*
@@ -2756,7 +2910,7 @@ CloseChannel(
statePtr->chanMsg = NULL;
}
if (interp) {
- Tcl_SetChannelErrorInterp(interp, statePtr->unreportedMsg);
+ Tcl_SetChannelErrorInterp(interp,statePtr->unreportedMsg);
}
}
if (errorCode == 0) {
@@ -2784,9 +2938,9 @@ CloseChannel(
statePtr->topChanPtr = downChanPtr;
downChanPtr->upChanPtr = NULL;
- chanPtr->typePtr = NULL;
- Tcl_EventuallyFree(chanPtr, TCL_DYNAMIC);
+ ChannelFree(chanPtr);
+
return Tcl_Close(interp, (Tcl_Channel) downChanPtr);
}
@@ -2794,13 +2948,11 @@ CloseChannel(
* There is only the TOP Channel, so we free the remaining pointers we
* have and then ourselves. Since this is the last of the channels in the
* stack, make sure to free the ChannelState structure associated with it.
- * We use Tcl_EventuallyFree to allow for any last references.
*/
- chanPtr->typePtr = NULL;
+ ChannelFree(chanPtr);
Tcl_EventuallyFree(statePtr, TCL_DYNAMIC);
- Tcl_EventuallyFree(chanPtr, TCL_DYNAMIC);
return errorCode;
}
@@ -2841,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
@@ -2867,7 +3020,11 @@ CutChannel(
* TIP #218, Channel Thread Actions
*/
- ChanThreadAction((Channel *) chan, TCL_CHANNEL_THREAD_REMOVE);
+ threadActionProc = Tcl_ChannelThreadActionProc(Tcl_GetChannelType(chan));
+ if (threadActionProc != NULL) {
+ (*threadActionProc)(Tcl_GetChannelInstanceData(chan),
+ TCL_CHANNEL_THREAD_REMOVE);
+ }
}
void
@@ -2882,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
@@ -2909,8 +3067,13 @@ 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;
}
}
@@ -2947,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");
@@ -2967,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
@@ -2978,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");
@@ -2999,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;
}
}
@@ -3069,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", TCL_STRLEN));
+ Tcl_AppendResult(interp, "Illegal recursive call to close "
+ "through close-handler of channel", NULL);
}
return TCL_ERROR;
}
@@ -3084,10 +3257,18 @@ Tcl_Close(
stickyError = 0;
- if ((statePtr->encoding != NULL) && (statePtr->curOutPtr != NULL)
- && (CheckChannelErrors(statePtr, TCL_WRITABLE) == 0)) {
- statePtr->outputEncodingFlags |= TCL_ENCODING_END;
- if (WriteChars(chanPtr, "", 0) < 0) {
+ if (GotFlag(statePtr, TCL_WRITABLE) && (statePtr->encoding != NULL)
+ && !(statePtr->outputEncodingFlags & TCL_ENCODING_START)) {
+
+ int code = CheckChannelErrors(statePtr, TCL_WRITABLE);
+
+ if (code == 0) {
+ statePtr->outputEncodingFlags |= TCL_ENCODING_END;
+ code = WriteChars(chanPtr, "", 0);
+ statePtr->outputEncodingFlags &= ~TCL_ENCODING_END;
+ statePtr->outputEncodingFlags |= TCL_ENCODING_START;
+ }
+ if (code < 0) {
stickyError = Tcl_GetErrno();
}
@@ -3099,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;
@@ -3115,27 +3296,19 @@ Tcl_Close(
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);
/*
- * Ensure that the last output buffer will be flushed.
- */
-
- if ((statePtr->curOutPtr != NULL) && IsBufferReady(statePtr->curOutPtr)) {
- SetFlag(statePtr, BUFFER_READY);
- }
-
- /*
* If this channel supports it, close the read side, since we don't need
* it anymore and this will help avoid deadlocks on some channel types.
*/
if (chanPtr->typePtr->closeProc == TCL_CLOSE2PROC) {
- result = chanPtr->typePtr->close2Proc(chanPtr->instanceData, interp,
+ result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, interp,
TCL_CLOSE_READ);
} else {
result = 0;
@@ -3172,361 +3345,23 @@ Tcl_Close(
Tcl_SetErrno(stickyError);
if (interp != NULL) {
Tcl_SetObjResult(interp,
- Tcl_NewStringObj(Tcl_PosixError(interp), TCL_STRLEN));
+ Tcl_NewStringObj(Tcl_PosixError(interp), -1));
}
- flushcode = -1;
- }
- if ((flushcode != 0) || (result != 0)) {
return TCL_ERROR;
}
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_CloseEx --
- *
- * Closes one side of a channel, read or write.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * Closes one direction of the channel.
- *
- * NOTE:
- * Tcl_CloseEx closes the specified direction of the channel as far as
- * the user is concerned. The channel keeps existing however. You cannot
- * calls this function to close the last possible direction of the
- * channel. Use Tcl_Close for that.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
-int
-Tcl_CloseEx(
- Tcl_Interp *interp, /* Interpreter for errors. */
- Tcl_Channel chan, /* The channel being closed. May still be used
- * by some interpreter. */
- int flags) /* Flags telling us which side to close. */
-{
- Channel *chanPtr; /* The real IO channel. */
- ChannelState *statePtr; /* State of real IO channel. */
-
- if (chan == NULL) {
- return TCL_OK;
- }
-
- /* TODO: assert flags validity ? */
-
- chanPtr = (Channel *) chan;
- statePtr = chanPtr->state;
-
- /*
- * Does the channel support half-close anyway? Error if not.
- */
-
- if (!chanPtr->typePtr->close2Proc) {
- Tcl_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",
- TCL_STRLEN));
- return TCL_ERROR;
- }
-
- /*
- * Check direction against channel mode. It is an error if we try to close
- * a direction not supported by the channel (already closed, or never
- * opened for that direction).
- */
-
- if (!(statePtr->flags & (TCL_READABLE | TCL_WRITABLE) & flags)) {
- const char *msg;
-
- if (flags & TCL_CLOSE_READ) {
- msg = "read";
- } else {
- msg = "write";
- }
- Tcl_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 (statePtr->flags & CHANNEL_INCLOSE) {
- if (interp) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "illegal recursive call to close through close-handler"
- " of channel", TCL_STRLEN));
- }
- return TCL_ERROR;
- }
-
- if (flags & TCL_CLOSE_READ) {
- /*
- * Call the finalization code directly. There are no events to handle,
- * there cannot be for the read-side.
- */
-
- return CloseChannelPart(interp, chanPtr, 0, flags);
- } else if (flags & TCL_CLOSE_WRITE) {
- if ((statePtr->curOutPtr != NULL) &&
- IsBufferReady(statePtr->curOutPtr)) {
- SetFlag(statePtr, BUFFER_READY);
- }
- Tcl_Preserve(statePtr);
- if (!GotFlag(statePtr, BG_FLUSH_SCHEDULED)) {
- /*
- * We don't want to re-enter CloseWrite().
- */
-
- if (!GotFlag(statePtr, CHANNEL_CLOSEDWRITE)) {
- if (CloseWrite(interp, chanPtr) != TCL_OK) {
- SetFlag(statePtr, CHANNEL_CLOSEDWRITE);
- Tcl_Release(statePtr);
- return TCL_ERROR;
- }
- }
- }
- SetFlag(statePtr, CHANNEL_CLOSEDWRITE);
- Tcl_Release(statePtr);
- }
-
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * CloseWrite --
- *
- * Closes the write side a channel.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * Closes the write side of the channel.
- *
- * NOTE:
- * CloseWrite removes the channel as far as the user is concerned.
- * However, the ooutput data structures may continue to exist for a while
- * longer if it has a background flush scheduled. The device itself is
- * eventually closed and the channel structures modified, in
- * CloseChannelPart, below.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-CloseWrite(
- Tcl_Interp *interp, /* Interpreter for errors. */
- Channel *chanPtr) /* The channel whose write side is being
- * closed. May still be used by some
- * interpreter */
-{
- /* Notes: clear-channel-handlers - write side only ? or keep around, just
- * not called. */
- /* No close cllbacks are run - channel is still open (read side) */
-
- ChannelState *statePtr = chanPtr->state;
- /* State of real IO channel. */
- int flushcode;
- int result = 0;
-
- /*
- * Ensure that the last output buffer will be flushed.
- */
-
- if ((statePtr->curOutPtr != NULL) && IsBufferReady(statePtr->curOutPtr)) {
- SetFlag(statePtr, BUFFER_READY);
- }
-
- /*
- * The call to FlushChannel will flush any queued output and invoke the
- * close function of the channel driver, or it will set up the channel to
- * be flushed and closed asynchronously.
- */
-
- SetFlag(statePtr, CHANNEL_CLOSEDWRITE);
-
- flushcode = FlushChannel(interp, chanPtr, 0);
-
/*
- * TIP #219.
- * Capture error messages put by the driver into the bypass area and put
- * them into the regular interpreter result.
- *
- * Notes: Due to the assertion of CHANNEL_CLOSEDWRITE in the flags
- * FlushChannel() has called CloseChannelPart(). While we can still access
- * "chan" (no structures were freed), the only place which may still
- * contain a message is the interpreter itself, and "CloseChannelPart" made
- * sure to lift any channel message it generated into it. Hence the NULL
- * argument in the call below.
+ * Bug 97069ea11a: set error message if a flush code is set and no error
+ * message set up to now.
*/
-
- if (TclChanCaughtErrorBypass(interp, NULL)) {
- result = EINVAL;
+ if (flushcode != 0 && interp != NULL
+ && 0 == Tcl_GetCharLength(Tcl_GetObjResult(interp)) ) {
+ Tcl_SetErrno(flushcode);
+ Tcl_SetObjResult(interp,
+ Tcl_NewStringObj(Tcl_PosixError(interp), -1));
}
-
if ((flushcode != 0) || (result != 0)) {
return TCL_ERROR;
}
-
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * CloseChannelPart --
- *
- * Utility procedure to close a channel partially and free associated
- * resources. If the channel was stacked it will never be run (The higher
- * level forbid this). If the channel was not stacked, then we will free
- * all the bits of the chosen side (read, or write) for the TOP channel.
- *
- * Results:
- * Error code from an unreported error or the driver close2 operation.
- *
- * Side effects:
- * May free memory, may change the value of errno.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-CloseChannelPart(
- Tcl_Interp *interp, /* Interpreter for errors. */
- Channel *chanPtr, /* The channel being closed. May still be used
- * by some interpreter. */
- int errorCode, /* Status of operation so far. */
- int flags) /* Flags telling us which side to close. */
-{
- ChannelState *statePtr; /* State of real IO channel. */
- int result; /* Of calling the close2proc. */
-
- statePtr = chanPtr->state;
-
- if (flags & TCL_CLOSE_READ) {
- /*
- * No more input can be consumed so discard any leftover input.
- */
-
- DiscardInputQueued(statePtr, 1);
- } else if (flags & TCL_CLOSE_WRITE) {
- /*
- * The caller guarantees that there are no more buffers queued for
- * output.
- */
-
- if (statePtr->outQueueHead != NULL) {
- Tcl_Panic("ClosechanHalf, closed write-side of channel: "
- "queued output left");
- }
-
- /*
- * If the EOF character is set in the channel, append that to the
- * output device.
- */
-
- if ((statePtr->outEofChar != 0) && GotFlag(statePtr, TCL_WRITABLE)) {
- int dummy;
- char c = (char) statePtr->outEofChar;
-
- (void) ChanWrite(chanPtr, &c, 1, &dummy);
- }
-
- /*
- * TIP #219, Tcl Channel Reflection API.
- * Move a leftover error message in the channel bypass into the
- * interpreter bypass. Just clear it if there is no interpreter.
- */
-
- if (statePtr->chanMsg != NULL) {
- if (interp != NULL) {
- Tcl_SetChannelErrorInterp(interp, statePtr->chanMsg);
- }
- TclDecrRefCount(statePtr->chanMsg);
- statePtr->chanMsg = NULL;
- }
- }
-
- /*
- * Finally do what is asked of us. Close and free the channel driver state
- * for the chosen side of the channel. This may leave a TIP #219 error
- * message in the interp.
- */
-
- result = ChanCloseHalf(chanPtr, interp, flags);
-
- /*
- * If we are being called synchronously, report either any latent error on
- * the channel or the current error.
- */
-
- if (statePtr->unreportedError != 0) {
- errorCode = statePtr->unreportedError;
-
- /*
- * TIP #219, Tcl Channel Reflection API.
- * Move an error message found in the unreported area into the regular
- * bypass (interp). This kills any message in the channel bypass area.
- */
-
- if (statePtr->chanMsg != NULL) {
- TclDecrRefCount(statePtr->chanMsg);
- statePtr->chanMsg = NULL;
- }
- if (interp) {
- Tcl_SetChannelErrorInterp(interp, statePtr->unreportedMsg);
- }
- }
- if (errorCode == 0) {
- errorCode = result;
- if (errorCode != 0) {
- Tcl_SetErrno(errorCode);
- }
- }
-
- /*
- * TIP #219.
- * Capture error messages put by the driver into the bypass area and put
- * them into the regular interpreter result. See also the bottom of
- * CloseWrite().
- */
-
- if (TclChanCaughtErrorBypass(interp, (Tcl_Channel) chanPtr)) {
- result = EINVAL;
- }
-
- if (result != 0) {
- return TCL_ERROR;
- }
-
- /*
- * Remove the closed side from the channel mode/flags.
- */
-
- ResetFlag(statePtr, flags & (TCL_READABLE | TCL_WRITABLE));
return TCL_OK;
}
@@ -3592,7 +3427,7 @@ Tcl_ClearChannelHandlers(
for (chPtr = statePtr->chPtr; chPtr != NULL; chPtr = chNext) {
chNext = chPtr->nextPtr;
- ckfree(chPtr);
+ ckfree((char *) chPtr);
}
statePtr->chPtr = NULL;
@@ -3619,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;
}
@@ -3648,11 +3483,11 @@ Tcl_ClearChannelHandlers(
*----------------------------------------------------------------------
*/
-ssize_t
+int
Tcl_Write(
Tcl_Channel chan, /* The channel to buffer output for. */
const char *src, /* Data to queue in output buffer. */
- size_t srcLen) /* Length of data in bytes, or TCL_STRLEN for
+ int srcLen) /* Length of data in bytes, or < 0 for
* strlen(). */
{
/*
@@ -3669,10 +3504,13 @@ Tcl_Write(
return -1;
}
- if (srcLen == TCL_STRLEN) {
+ if (srcLen < 0) {
srcLen = strlen(src);
}
- return DoWrite(chanPtr, src, srcLen);
+ if (WriteBytes(chanPtr, src, srcLen) < 0) {
+ return -1;
+ }
+ return srcLen;
}
/*
@@ -3699,11 +3537,11 @@ Tcl_Write(
*----------------------------------------------------------------------
*/
-ssize_t
+int
Tcl_WriteRaw(
Tcl_Channel chan, /* The channel to buffer output for. */
const char *src, /* Data to queue in output buffer. */
- size_t srcLen) /* Length of data in bytes, or TCL_STRLEN for
+ int srcLen) /* Length of data in bytes, or < 0 for
* strlen(). */
{
Channel *chanPtr = ((Channel *) chan);
@@ -3715,7 +3553,7 @@ Tcl_WriteRaw(
return -1;
}
- if (srcLen == TCL_STRLEN) {
+ if (srcLen < 0) {
srcLen = strlen(src);
}
@@ -3724,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);
}
@@ -3755,90 +3595,48 @@ Tcl_WriteRaw(
*----------------------------------------------------------------------
*/
-ssize_t
+int
Tcl_WriteChars(
Tcl_Channel chan, /* The channel to buffer output for. */
const char *src, /* UTF-8 characters to queue in output
* buffer. */
- size_t len) /* Length of string in bytes, or TCL_STRLEN
- * for strlen(). */
+ int len) /* Length of string in bytes, or < 0 for
+ * strlen(). */
{
- ChannelState *statePtr; /* State info for channel */
-
- statePtr = ((Channel *) chan)->state;
+ Channel *chanPtr = (Channel *) chan;
+ ChannelState *statePtr = chanPtr->state; /* State info for channel */
+ int result;
+ Tcl_Obj *objPtr;
if (CheckChannelErrors(statePtr, TCL_WRITABLE) != 0) {
return -1;
}
- return DoWriteChars((Channel *) chan, src, len);
-}
-
-/*
- *---------------------------------------------------------------------------
- *
- * DoWriteChars --
- *
- * Takes a sequence of UTF-8 characters and converts them for output
- * using the channel's current encoding, may queue the buffer for output
- * if it gets full, and also remembers whether the current buffer is
- * ready e.g. if it contains a newline and we are in line buffering mode.
- * Compensates stacking, i.e. will redirect the data from the specified
- * channel to the topmost channel in a stack.
- *
- * Results:
- * 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
- * channel.
- *
- *----------------------------------------------------------------------
- */
-
-static ssize_t
-DoWriteChars(
- Channel *chanPtr, /* The channel to buffer output for. */
- const char *src, /* UTF-8 characters to queue in output
- * buffer. */
- size_t len) /* Length of string in bytes, or TCL_STRLEN
- * for strlen(). */
-{
- /*
- * Always use the topmost channel of the stack
- */
-
- ChannelState *statePtr; /* State info for channel */
-
- statePtr = chanPtr->state;
chanPtr = statePtr->topChanPtr;
- if (len == TCL_STRLEN) {
+ if (len < 0) {
len = strlen(src);
}
- if (statePtr->encoding == NULL) {
- /*
- * 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 eg [puts] for the \n) could
- * be extended to more efficient translation of the src string.
- */
-
- int result;
+ if (statePtr->encoding) {
+ return WriteChars(chanPtr, src, len);
+ }
- if ((len == 1) && (UCHAR(*src) < 0xC0)) {
- result = WriteBytes(chanPtr, src, len);
- } else {
- Tcl_Obj *objPtr = Tcl_NewStringObj(src, len);
+ /*
+ * 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 eg [puts] for the \n) could be extended to more efficient
+ * translation of the src string.
+ */
- src = (char *) Tcl_GetByteArrayFromObj(objPtr, &len);
- result = WriteBytes(chanPtr, src, len);
- TclDecrRefCount(objPtr);
- }
- return result;
+ if ((len == 1) && (UCHAR(*src) < 0xC0)) {
+ return WriteBytes(chanPtr, src, len);
}
- return WriteChars(chanPtr, src, len);
+
+ objPtr = Tcl_NewStringObj(src, len);
+ src = (char *) Tcl_GetByteArrayFromObj(objPtr, &len);
+ result = WriteBytes(chanPtr, src, len);
+ TclDecrRefCount(objPtr);
+ return result;
}
/*
@@ -3866,7 +3664,7 @@ DoWriteChars(
*----------------------------------------------------------------------
*/
-ssize_t
+int
Tcl_WriteObj(
Tcl_Channel chan, /* The channel to buffer output for. */
Tcl_Obj *objPtr) /* The object to write. */
@@ -3877,8 +3675,8 @@ Tcl_WriteObj(
Channel *chanPtr;
ChannelState *statePtr; /* State info for channel */
- const char *src;
- size_t srcLen;
+ char *src;
+ int srcLen;
statePtr = ((Channel *) chan)->state;
chanPtr = statePtr->topChanPtr;
@@ -3895,33 +3693,41 @@ Tcl_WriteObj(
}
}
-static void
-WillWrite(
- Channel *chanPtr)
+static void WillWrite(Channel *chanPtr)
{
int inputBuffered;
- if ((chanPtr->typePtr->seekProc != NULL) &&
- ((inputBuffered = Tcl_InputBuffered((Tcl_Channel) chanPtr)) > 0)){
+ if ((chanPtr->typePtr->seekProc != NULL)
+ && ((inputBuffered = Tcl_InputBuffered((Tcl_Channel) chanPtr)) > 0)) {
int ignore;
-
DiscardInputQueued(chanPtr->state, 0);
- ChanSeek(chanPtr, -inputBuffered, SEEK_CUR, &ignore);
+ ChanSeek(chanPtr, - inputBuffered, SEEK_CUR, &ignore);
}
}
-static inline int
-WillRead(
- Channel *chanPtr)
+static int WillRead(Channel *chanPtr)
{
+ if (chanPtr->typePtr == NULL) {
+ /* Prevent read attempts on a closed channel */
+ DiscardInputQueued(chanPtr->state, 0);
+ Tcl_SetErrno(EINVAL);
+ return -1;
+ }
if ((chanPtr->typePtr->seekProc != NULL)
- && (Tcl_OutputBuffered((Tcl_Channel) chanPtr) > 0)) {
- if ((chanPtr->state->curOutPtr != NULL)
- && IsBufferReady(chanPtr->state->curOutPtr)) {
- SetFlag(chanPtr->state, BUFFER_READY);
- }
+ && (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.
+ */
+
if (FlushChannel(NULL, chanPtr, 0) != 0) {
- return 1;
+ return -1;
}
}
return 0;
@@ -3930,111 +3736,9 @@ WillRead(
/*
*----------------------------------------------------------------------
*
- * WriteBytes --
- *
- * Write a sequence of bytes into an output buffer, may queue the buffer
- * for output if it gets full, and also remembers whether the current
- * buffer is ready e.g. if it contains a newline and we are in line
- * buffering mode.
- *
- * Results:
- * 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
- * channel.
+ * Write --
*
- *----------------------------------------------------------------------
- */
-
-static ssize_t
-WriteBytes(
- Channel *chanPtr, /* The channel to buffer output for. */
- const char *src, /* Bytes to write. */
- size_t srcLen) /* Number of bytes to write. */
-{
- ChannelState *statePtr = chanPtr->state;
- /* State info for channel */
- ChannelBuffer *bufPtr;
- char *dst;
- int translate;
- size_t dstMax, sawLF, savedLF, total, dstLen, toWrite;
-
- if (srcLen) {
- WillWrite(chanPtr);
- }
-
- total = 0;
- sawLF = 0;
- savedLF = 0;
- translate = GotFlag(statePtr, CHANNEL_LINEBUFFERED)
- || (statePtr->outputTranslation != TCL_TRANSLATE_LF);
-
- /*
- * Loop over all bytes in src, storing them in output buffer with proper
- * EOL translation.
- */
-
- while (srcLen + savedLF > 0) {
- bufPtr = statePtr->curOutPtr;
- if (bufPtr == NULL) {
- bufPtr = AllocChannelBuffer(statePtr->bufSize);
- statePtr->curOutPtr = bufPtr;
- }
- dst = InsertPoint(bufPtr);
- dstMax = SpaceLeft(bufPtr);
- dstLen = dstMax;
-
- toWrite = dstLen;
- if (toWrite > srcLen) {
- toWrite = srcLen;
- }
-
- if (translate) {
- if (savedLF) {
- /*
- * A '\n' was left over from last call to TranslateOutputEOL()
- * and we need to store it in this buffer. If the channel is
- * line-based, we will need to flush it.
- */
-
- *dst++ = '\n';
- dstLen--;
- sawLF++;
- }
- if (TranslateOutputEOL(statePtr, dst, src, &dstLen, &toWrite)) {
- sawLF++;
- }
- dstLen += savedLF;
- savedLF = 0;
- if (dstLen > dstMax) {
- savedLF = 1;
- dstLen = dstMax;
- }
- } else {
- memcpy(dst, src, toWrite);
- dstLen = toWrite;
- }
-
- bufPtr->nextAdded += dstLen;
- if (CheckFlush(chanPtr, bufPtr, sawLF) != 0) {
- return -1;
- }
- total += dstLen;
- src += toWrite;
- srcLen -= toWrite;
- sawLF = 0;
- }
- return total;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * WriteChars --
- *
- * Convert UTF-8 bytes to the channel's external encoding and write the
+ * Convert srcLen bytes starting at src according to encoding and write
* produced bytes into an output buffer, may queue the buffer for output
* if it gets full, and also remembers whether the current buffer is
* ready e.g. if it contains a newline and we are in line buffering mode.
@@ -4050,382 +3754,176 @@ WriteBytes(
*----------------------------------------------------------------------
*/
-static ssize_t
-WriteChars(
+static int
+Write(
Channel *chanPtr, /* The channel to buffer output for. */
const char *src, /* UTF-8 string to write. */
- size_t 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 */
- ChannelBuffer *bufPtr;
- char *dst, *stage;
- int savedLF, sawLF, endEncoding, result, consumedSomething, translate;
- size_t saved;
- size_t total, dstLen, dstWrote, stageLen, stageMax, stageRead, toWrite;
- Tcl_Encoding encoding;
- char safe[BUFFER_PADDING];
+ char *nextNewLine = NULL;
+ int endEncoding, saved = 0, total = 0, flushed = 0, needNlFlush = 0;
if (srcLen) {
WillWrite(chanPtr);
}
- total = 0;
- sawLF = 0;
- savedLF = 0;
- saved = 0;
- encoding = statePtr->encoding;
-
/*
* Write the terminated escape sequence even if srcLen is 0.
*/
endEncoding = ((statePtr->outputEncodingFlags & TCL_ENCODING_END) != 0);
- translate = GotFlag(statePtr, CHANNEL_LINEBUFFERED)
- || (statePtr->outputTranslation != TCL_TRANSLATE_LF);
-
- /*
- * Loop over all UTF-8 characters in src, storing them in staging buffer
- * with proper EOL translation.
- */
+ if (GotFlag(statePtr, CHANNEL_LINEBUFFERED)
+ || (statePtr->outputTranslation != TCL_TRANSLATE_LF)) {
+ nextNewLine = memchr(src, '\n', srcLen);
+ }
- consumedSomething = 1;
- while (consumedSomething && (srcLen + savedLF + endEncoding > 0)) {
- consumedSomething = 0;
- stage = statePtr->outputStage;
- stageMax = statePtr->bufSize;
- stageLen = stageMax;
+ while (srcLen + saved + endEncoding > 0) {
+ ChannelBuffer *bufPtr;
+ char *dst, safe[BUFFER_PADDING];
+ int result, srcRead, dstLen, dstWrote, srcLimit = srcLen;
- toWrite = stageLen;
- if (toWrite > srcLen) {
- toWrite = srcLen;
+ if (nextNewLine) {
+ srcLimit = nextNewLine - src;
}
-
- if (translate) {
- if (savedLF) {
- /*
- * A '\n' was left over from last call to TranslateOutputEOL()
- * and we need to store it in the staging buffer. If the
- * channel is line-based, we will need to flush the output
- * buffer (after translating the staging buffer).
- */
-
- *stage++ = '\n';
- stageLen--;
- sawLF++;
- }
- if (TranslateOutputEOL(statePtr, stage, src, &stageLen,
- &toWrite)) {
- sawLF++;
- }
-
- stage -= savedLF;
- stageLen += savedLF;
- savedLF = 0;
-
- if (stageLen > stageMax) {
- savedLF = 1;
- stageLen = stageMax;
- }
- } else {
- memcpy(stage, src, toWrite);
- stageLen = toWrite;
+
+ /* Get space to write into */
+ bufPtr = statePtr->curOutPtr;
+ if (bufPtr == NULL) {
+ bufPtr = AllocChannelBuffer(statePtr->bufSize);
+ statePtr->curOutPtr = bufPtr;
}
- src += toWrite;
- srcLen -= toWrite;
-
- /*
- * Loop over all UTF-8 characters in staging buffer, converting them
- * to external encoding, storing them in output buffer.
- */
-
- while (stageLen + saved + endEncoding > 0) {
- bufPtr = statePtr->curOutPtr;
- if (bufPtr == NULL) {
- bufPtr = AllocChannelBuffer(statePtr->bufSize);
- statePtr->curOutPtr = bufPtr;
- }
- dst = InsertPoint(bufPtr);
- dstLen = SpaceLeft(bufPtr);
-
- if (saved != 0) {
- /*
- * Here's some translated bytes left over from the last buffer
- * that we need to stick at the beginning of this buffer.
- */
-
- memcpy(dst, safe, saved);
- bufPtr->nextAdded += saved;
- dst += saved;
- dstLen -= saved;
- saved = 0;
- }
-
- result = Tcl_UtfToExternal(NULL, encoding, stage, stageLen,
- statePtr->outputEncodingFlags,
- &statePtr->outputEncodingState, dst,
- dstLen + BUFFER_PADDING, &stageRead, &dstWrote, NULL);
-
+ if (saved) {
/*
- * Fix for [Bug 506297], reported by Martin Forssen
- * <ruric@users.sourceforge.net>.
- *
- * The encoding chosen in the script exposing the bug writes out
- * three intro characters when TCL_ENCODING_START is set, but does
- * not consume any input as TCL_ENCODING_END is cleared. As some
- * output was generated the enclosing loop calls UtfToExternal
- * again, again with START set. Three more characters in the out
- * and still no use of input ... To break this infinite loop we
- * remove TCL_ENCODING_START from the set of flags after the first
- * call (no condition is required, the later calls remove an unset
- * flag, which is a no-op). This causes the subsequent calls to
- * UtfToExternal to consume and convert the actual input.
+ * Here's some translated bytes left over from the last buffer
+ * that we need to stick at the beginning of this buffer.
*/
- statePtr->outputEncodingFlags &= ~TCL_ENCODING_START;
+ memcpy(InsertPoint(bufPtr), safe, (size_t) saved);
+ bufPtr->nextAdded += saved;
+ saved = 0;
+ }
+ PreserveChannelBuffer(bufPtr);
+ dst = InsertPoint(bufPtr);
+ dstLen = SpaceLeft(bufPtr);
+
+ result = Tcl_UtfToExternal(NULL, encoding, src, srcLimit,
+ statePtr->outputEncodingFlags,
+ &statePtr->outputEncodingState, dst,
+ dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL);
+
+ /* See chan-io-1.[89]. Tcl Bug 506297. */
+ statePtr->outputEncodingFlags &= ~TCL_ENCODING_START;
+
+ if ((result != TCL_OK) && (srcRead + dstWrote == 0)) {
+ /* We're reading from invalid/incomplete UTF-8 */
+ ReleaseChannelBuffer(bufPtr);
+ if (total == 0) {
+ Tcl_SetErrno(EINVAL);
+ return -1;
+ }
+ break;
+ }
- /*
- * The following code must be executed only when result is not 0.
- */
+ bufPtr->nextAdded += dstWrote;
+ src += srcRead;
+ srcLen -= srcRead;
+ total += dstWrote;
+ dst += dstWrote;
+ dstLen -= dstWrote;
- if ((result != 0) && (stageRead + dstWrote == 0)) {
- /*
- * We have an incomplete UTF-8 character at the end of the
- * staging buffer. It will get moved to the beginning of the
- * staging buffer followed by more bytes from src.
- */
+ if (src == nextNewLine && dstLen > 0) {
+ static char crln[3] = "\r\n";
+ char *nl = NULL;
+ int nlLen = 0;
- src -= stageLen;
- srcLen += stageLen;
- stageLen = 0;
- savedLF = 0;
+ switch (statePtr->outputTranslation) {
+ case TCL_TRANSLATE_LF:
+ nl = crln + 1;
+ nlLen = 1;
+ break;
+ case TCL_TRANSLATE_CR:
+ nl = crln;
+ nlLen = 1;
+ break;
+ case TCL_TRANSLATE_CRLF:
+ nl = crln;
+ nlLen = 2;
+ break;
+ default:
+ Tcl_Panic("unknown output translation requested");
break;
}
- bufPtr->nextAdded += dstWrote;
- 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.
- */
+
+ result |= Tcl_UtfToExternal(NULL, encoding, nl, nlLen,
+ statePtr->outputEncodingFlags,
+ &statePtr->outputEncodingState, dst,
+ dstLen + BUFFER_PADDING, &srcRead, &dstWrote, NULL);
- saved = -SpaceLeft(bufPtr);
- memcpy(safe, dst + dstLen, saved);
- bufPtr->nextAdded = bufPtr->bufLength;
- }
- if (CheckFlush(chanPtr, bufPtr, sawLF) != 0) {
- return -1;
- }
+ assert (srcRead == nlLen);
+ bufPtr->nextAdded += dstWrote;
+ src++;
+ srcLen--;
total += dstWrote;
- stage += stageRead;
- stageLen -= stageRead;
- sawLF = 0;
-
- consumedSomething = 1;
+ dst += dstWrote;
+ dstLen -= dstWrote;
+ nextNewLine = memchr(src, '\n', srcLen);
+ needNlFlush = 1;
+ }
+ if (IsBufferOverflowing(bufPtr)) {
/*
- * If all translated characters are written to the buffer,
- * endEncoding is set to 0 because the escape sequence may be
- * output.
+ * 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.
*/
- if ((stageLen + saved == 0) && (result == 0)) {
- endEncoding = 0;
- }
+ saved = -SpaceLeft(bufPtr);
+ memcpy(safe, dst + dstLen, (size_t) saved);
+ bufPtr->nextAdded = bufPtr->bufLength;
}
- }
-
- /*
- * If nothing was written and it happened because there was no progress in
- * the UTF conversion, we throw an error.
- */
- if (!consumedSomething && (total == 0)) {
- Tcl_SetErrno(EINVAL);
- return -1;
- }
- return total;
-}
-
-/*
- *---------------------------------------------------------------------------
- *
- * TranslateOutputEOL --
- *
- * Helper function for WriteBytes() and WriteChars(). Converts the '\n'
- * characters in the source buffer into the appropriate EOL form
- * specified by the output translation mode.
- *
- * EOL translation stops either when the source buffer is empty or the
- * output buffer is full.
- *
- * When converting to CRLF mode and there is only 1 byte left in the
- * output buffer, this routine stores the '\r' in the last byte and then
- * stores the '\n' in the byte just past the end of the buffer. The
- * caller is responsible for passing in a buffer that is large enough to
- * hold the extra byte.
- *
- * Results:
- * The return value is 1 if a '\n' was translated from the source buffer,
- * or 0 otherwise -- this can be used by the caller to decide to flush a
- * line-based channel even though the channel buffer is not full.
- *
- * *dstLenPtr is filled with how many bytes of the output buffer were
- * used. As mentioned above, this can be one more that the output
- * buffer's specified length if a CRLF was stored.
- *
- * *srcLenPtr is filled with how many bytes of the source buffer were
- * consumed.
- *
- * Side effects:
- * It may be obvious, but bears mentioning that when converting in CRLF
- * mode (which requires two bytes of storage in the output buffer), the
- * number of bytes consumed from the source buffer will be less than the
- * number of bytes stored in the output buffer.
- *
- *---------------------------------------------------------------------------
- */
-
-static int
-TranslateOutputEOL(
- ChannelState *statePtr, /* Channel being read, for translation and
- * buffering modes. */
- char *dst, /* Output buffer filled with UTF-8 chars by
- * applying appropriate EOL translation to
- * source characters. */
- const char *src, /* Source UTF-8 characters. */
- size_t *dstLenPtr, /* On entry, the maximum length of output
- * buffer in bytes. On exit, the number of
- * bytes actually used in output buffer. */
- size_t *srcLenPtr) /* On entry, the length of source buffer. On
- * exit, the number of bytes read from the
- * source buffer. */
-{
- char *dstEnd;
- int srcLen, newlineFound;
-
- newlineFound = 0;
- srcLen = *srcLenPtr;
-
- switch (statePtr->outputTranslation) {
- case TCL_TRANSLATE_LF:
- for (dstEnd = dst + srcLen; dst < dstEnd; ) {
- if (*src == '\n') {
- newlineFound = 1;
- }
- *dst++ = *src++;
+ if ((srcLen + saved == 0) && (result == TCL_OK)) {
+ endEncoding = 0;
}
- *dstLenPtr = srcLen;
- break;
- case TCL_TRANSLATE_CR:
- for (dstEnd = dst + srcLen; dst < dstEnd;) {
- if (*src == '\n') {
- *dst++ = '\r';
- newlineFound = 1;
- src++;
- } else {
- *dst++ = *src++;
- }
- }
- *dstLenPtr = srcLen;
- break;
- case TCL_TRANSLATE_CRLF: {
- /*
- * Since this causes the number of bytes to grow, we start off trying
- * to put 'srcLen' bytes into the output buffer, but allow it to store
- * more bytes, as long as there's still source bytes and room in the
- * output buffer.
- */
-
- char *dstStart, *dstMax;
- const char *srcStart;
-
- dstStart = dst;
- dstMax = dst + *dstLenPtr;
-
- srcStart = src;
-
- if (srcLen < *dstLenPtr) {
- dstEnd = dst + srcLen;
- } else {
- dstEnd = dst + *dstLenPtr;
- }
- while (dst < dstEnd) {
- if (*src == '\n') {
- if (dstEnd < dstMax) {
- dstEnd++;
- }
- *dst++ = '\r';
- newlineFound = 1;
- }
- *dst++ = *src++;
- }
- *srcLenPtr = src - srcStart;
- *dstLenPtr = dst - dstStart;
- break;
- }
- default:
- break;
- }
- return newlineFound;
-}
-
-/*
- *---------------------------------------------------------------------------
- *
- * CheckFlush --
- *
- * Helper function for WriteBytes() and WriteChars(). If the channel
- * buffer is ready to be flushed, flush it.
- *
- * Results:
- * The return value is -1 if there was a problem flushing the channel
- * buffer, or 0 otherwise.
- *
- * Side effects:
- * The buffer will be recycled if it is flushed.
- *
- *---------------------------------------------------------------------------
- */
-static int
-CheckFlush(
- Channel *chanPtr, /* Channel being read, for buffering mode. */
- ChannelBuffer *bufPtr, /* Channel buffer to possibly flush. */
- int newlineFlag) /* Non-zero if a the channel buffer contains a
- * newline. */
-{
- ChannelState *statePtr = chanPtr->state;
- /* State info for channel */
-
- /*
- * The current buffer is ready for output:
- * 1. if it is full.
- * 2. if it contains a newline and this channel is line-buffered.
- * 3. if it contains any output and this channel is unbuffered.
- */
-
- if (!GotFlag(statePtr, BUFFER_READY)) {
if (IsBufferFull(bufPtr)) {
- SetFlag(statePtr, BUFFER_READY);
- } else if (GotFlag(statePtr, CHANNEL_LINEBUFFERED)) {
- if (newlineFlag != 0) {
- SetFlag(statePtr, BUFFER_READY);
+ if (FlushChannel(NULL, chanPtr, 0) != 0) {
+ ReleaseChannelBuffer(bufPtr);
+ return -1;
}
- } else if (GotFlag(statePtr, CHANNEL_UNBUFFERED)) {
- SetFlag(statePtr, BUFFER_READY);
- }
- }
- if (GotFlag(statePtr, BUFFER_READY)) {
+ flushed += statePtr->bufSize;
+
+ /*
+ * We just flushed. So if we have needNlFlush set to record
+ * that we need to flush because theres a (translated) newline
+ * in the buffer, that's likely not true any more. But there
+ * is a tricky exception. If we have saved bytes that did not
+ * really get flushed and those bytes came from a translation
+ * of a newline as the last thing taken from the src array,
+ * then needNlFlush needs to remain set to flag that the
+ * next buffer still needs a newline flush.
+ */
+ if (needNlFlush && (saved == 0 || src[-1] != '\n')) {
+ needNlFlush = 0;
+ }
+ }
+ ReleaseChannelBuffer(bufPtr);
+ }
+ if ((flushed < total) && (GotFlag(statePtr, CHANNEL_UNBUFFERED) ||
+ (needNlFlush && GotFlag(statePtr, CHANNEL_LINEBUFFERED)))) {
if (FlushChannel(NULL, chanPtr, 0) != 0) {
return -1;
}
}
- return 0;
+
+ return total;
}
/*
@@ -4447,7 +3945,7 @@ CheckFlush(
*---------------------------------------------------------------------------
*/
-ssize_t
+int
Tcl_Gets(
Tcl_Channel chan, /* Channel from which to read. */
Tcl_DString *lineRead) /* The line read will be appended to this
@@ -4456,12 +3954,14 @@ Tcl_Gets(
* for managing the storage. */
{
Tcl_Obj *objPtr;
- ssize_t 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;
@@ -4490,7 +3990,7 @@ Tcl_Gets(
*---------------------------------------------------------------------------
*/
-ssize_t
+int
Tcl_GetsObj(
Tcl_Channel chan, /* Channel from which to read. */
Tcl_Obj *objPtr) /* The line read will be appended to this
@@ -4501,15 +4001,28 @@ Tcl_GetsObj(
ChannelState *statePtr = chanPtr->state;
/* State info for channel */
ChannelBuffer *bufPtr;
- int inEofChar, skip, oldFlags;
- size_t copiedTotal, oldLength, oldRemoved;
+ int inEofChar, skip, copiedTotal, oldLength, oldFlags, oldRemoved;
Tcl_Encoding encoding;
char *dst, *dstEnd, *eol, *eof;
Tcl_EncodingState oldState;
if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
- copiedTotal = -1;
- goto done;
+ return -1;
+ }
+
+ /*
+ * If we're sitting ready to read the eofchar, there's no need to
+ * do it.
+ */
+
+ if (GotFlag(statePtr, CHANNEL_STICKY_EOF)) {
+ SetFlag(statePtr, CHANNEL_EOF);
+ assert( statePtr->inputEncodingFlags & TCL_ENCODING_END );
+ assert( !GotFlag(statePtr, CHANNEL_BLOCKED|INPUT_SAW_CR) );
+
+ /* TODO: Do we need this? */
+ UpdateInterest(chanPtr);
+ return -1;
}
/*
@@ -4529,6 +4042,7 @@ Tcl_GetsObj(
*/
chanPtr = statePtr->topChanPtr;
+ TclChannelPreserve((Tcl_Channel)chanPtr);
bufPtr = statePtr->inQueueHead;
encoding = statePtr->encoding;
@@ -4552,16 +4066,7 @@ Tcl_GetsObj(
*/
if (encoding == NULL) {
- ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
-
- if (tsdPtr->binaryEncoding == NULL) {
- tsdPtr->binaryEncoding = Tcl_GetEncoding(NULL, "iso8859-1");
- Tcl_CreateThreadExitHandler(FreeBinaryEncoding, NULL);
- }
- encoding = tsdPtr->binaryEncoding;
- if (encoding == NULL) {
- Tcl_Panic("attempted gets on binary channel where no iso8859-1 encoding available");
- }
+ encoding = GetBinaryEncoding();
}
/*
@@ -4586,6 +4091,7 @@ Tcl_GetsObj(
eof = NULL;
inEofChar = statePtr->inEofChar;
+ ResetFlag(statePtr, CHANNEL_BLOCKED);
while (1) {
if (dst >= dstEnd) {
if (FilterInputBytes(chanPtr, &gs) != 0) {
@@ -4678,7 +4184,7 @@ Tcl_GetsObj(
*/
char tmp[1 + TCL_UTF_MAX];
- size_t rawRead;
+ int rawRead;
bufPtr = gs.bufPtr;
Tcl_ExternalToUtf(NULL, gs.encoding, RemovePoint(bufPtr),
@@ -4737,6 +4243,7 @@ Tcl_GetsObj(
dstEnd = eof;
SetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF);
statePtr->inputEncodingFlags |= TCL_ENCODING_END;
+ ResetFlag(statePtr, CHANNEL_BLOCKED|INPUT_SAW_CR);
}
if (GotFlag(statePtr, CHANNEL_EOF)) {
skip = 0;
@@ -4750,6 +4257,7 @@ Tcl_GetsObj(
Tcl_SetObjLength(objPtr, oldLength);
CommonGetsCleanup(chanPtr);
copiedTotal = -1;
+ ResetFlag(statePtr, CHANNEL_BLOCKED);
goto done;
}
goto gotEOL;
@@ -4771,7 +4279,11 @@ Tcl_GetsObj(
* self-modifying reflected transforms.
*/
- chanPtr = statePtr->topChanPtr;
+ if (chanPtr != statePtr->topChanPtr) {
+ TclChannelRelease((Tcl_Channel)chanPtr);
+ chanPtr = statePtr->topChanPtr;
+ TclChannelPreserve((Tcl_Channel)chanPtr);
+ }
bufPtr = gs.bufPtr;
if (bufPtr == NULL) {
@@ -4805,16 +4317,18 @@ Tcl_GetsObj(
* Regenerate the top channel, in case it was changed due to
* self-modifying reflected transforms.
*/
-
- chanPtr = statePtr->topChanPtr;
-
+ if (chanPtr != statePtr->topChanPtr) {
+ TclChannelRelease((Tcl_Channel)chanPtr);
+ chanPtr = statePtr->topChanPtr;
+ TclChannelPreserve((Tcl_Channel)chanPtr);
+ }
bufPtr = statePtr->inQueueHead;
- if (bufPtr == NULL) {
- Tcl_Panic("Tcl_GetsObj: restore reached with bufPtr==NULL");
+ if (bufPtr != NULL) {
+ bufPtr->nextRemoved = oldRemoved;
+ bufPtr = bufPtr->nextPtr;
}
- bufPtr->nextRemoved = oldRemoved;
- for (bufPtr = bufPtr->nextPtr; bufPtr != NULL; bufPtr = bufPtr->nextPtr) {
+ for ( ; bufPtr != NULL; bufPtr = bufPtr->nextPtr) {
bufPtr->nextRemoved = BUFFER_PADDING;
}
CommonGetsCleanup(chanPtr);
@@ -4843,14 +4357,24 @@ Tcl_GetsObj(
*/
done:
+ assert(!GotFlag(statePtr, CHANNEL_EOF)
+ || GotFlag(statePtr, CHANNEL_STICKY_EOF)
+ || Tcl_InputBuffered((Tcl_Channel)chanPtr) == 0);
+
+ assert( !(GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED)
+ == (CHANNEL_EOF|CHANNEL_BLOCKED)) );
+
/*
* Regenerate the top channel, in case it was changed due to
* self-modifying reflected transforms.
*/
-
- chanPtr = statePtr->topChanPtr;
-
+ if (chanPtr != statePtr->topChanPtr) {
+ TclChannelRelease((Tcl_Channel)chanPtr);
+ chanPtr = statePtr->topChanPtr;
+ TclChannelPreserve((Tcl_Channel)chanPtr);
+ }
UpdateInterest(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
return copiedTotal;
}
@@ -4863,6 +4387,11 @@ 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.
+ *
* Results:
* Number of characters accumulated in the object or -1 if error,
* blocked, or EOF. If -1, use Tcl_GetErrno() to retrieve the POSIX error
@@ -4877,7 +4406,7 @@ Tcl_GetsObj(
*---------------------------------------------------------------------------
*/
-static ssize_t
+static int
TclGetsObjBinary(
Tcl_Channel chan, /* Channel from which to read. */
Tcl_Obj *objPtr) /* The line read will be appended to this
@@ -4887,8 +4416,8 @@ TclGetsObjBinary(
ChannelState *statePtr = chanPtr->state;
/* State info for channel */
ChannelBuffer *bufPtr;
- int inEofChar, skip, oldFlags, eolChar;
- size_t rawLen, byteLen, copiedTotal, oldLength, oldRemoved;
+ int inEofChar, skip, copiedTotal, oldLength, oldFlags, oldRemoved;
+ int rawLen, byteLen, eolChar;
unsigned char *dst, *dstEnd, *eol, *eof, *byteArray;
/*
@@ -4896,6 +4425,7 @@ TclGetsObjBinary(
*/
chanPtr = statePtr->topChanPtr;
+ TclChannelPreserve((Tcl_Channel)chanPtr);
bufPtr = statePtr->inQueueHead;
@@ -4916,13 +4446,10 @@ 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
@@ -4942,17 +4469,24 @@ TclGetsObjBinary(
* hasn't seen EOL. Need to read more bytes from the channel
* device. Side effect is to allocate another channel buffer.
*/
-
- if (GotFlag(statePtr, CHANNEL_BLOCKED)) {
- if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) {
- goto restore;
- }
- ResetFlag(statePtr, CHANNEL_BLOCKED);
- }
if (GetInput(chanPtr) != 0) {
goto restore;
}
bufPtr = statePtr->inQueueTail;
+ if (bufPtr == NULL) {
+ goto restore;
+ }
+ } 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 */
+
+ assert ( !GotFlag(statePtr, CHANNEL_STICKY_EOF) );
+ assert ( !GotFlag(statePtr, CHANNEL_EOF) );
}
dst = (unsigned char *) RemovePoint(bufPtr);
@@ -4994,6 +4528,7 @@ TclGetsObjBinary(
SetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF);
statePtr->inputEncodingFlags |= TCL_ENCODING_END;
+ ResetFlag(statePtr, CHANNEL_BLOCKED|INPUT_SAW_CR);
}
if (GotFlag(statePtr, CHANNEL_EOF)) {
skip = 0;
@@ -5007,10 +4542,15 @@ TclGetsObjBinary(
byteArray = Tcl_SetByteArrayLength(objPtr, oldLength);
CommonGetsCleanup(chanPtr);
copiedTotal = -1;
+ ResetFlag(statePtr, CHANNEL_BLOCKED);
goto done;
}
goto gotEOL;
}
+ if (GotFlag(statePtr, CHANNEL_BLOCKED|CHANNEL_NONBLOCKING)
+ == (CHANNEL_BLOCKED|CHANNEL_NONBLOCKING)) {
+ goto restore;
+ }
/*
* Copy bytes from the channel buffer to the ByteArray.
@@ -5019,7 +4559,7 @@ TclGetsObjBinary(
rawLen = dstEnd - dst;
byteArray = Tcl_SetByteArrayLength(objPtr, byteLen + rawLen);
- memcpy(byteArray + byteLen, dst, rawLen);
+ memcpy(byteArray + byteLen, dst, (size_t) rawLen);
byteLen += rawLen;
}
@@ -5036,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;
@@ -5065,12 +4605,12 @@ TclGetsObjBinary(
restore:
bufPtr = statePtr->inQueueHead;
- if (bufPtr == NULL) {
- Tcl_Panic("TclGetsObjBinary: restore reached with bufPtr==NULL");
+ if (bufPtr) {
+ bufPtr->nextRemoved = oldRemoved;
+ bufPtr = bufPtr->nextPtr;
}
- bufPtr->nextRemoved = oldRemoved;
- for (bufPtr = bufPtr->nextPtr; bufPtr != NULL; bufPtr = bufPtr->nextPtr) {
+ for ( ; bufPtr != NULL; bufPtr = bufPtr->nextPtr) {
bufPtr->nextRemoved = BUFFER_PADDING;
}
CommonGetsCleanup(chanPtr);
@@ -5098,7 +4638,13 @@ 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)) );
UpdateInterest(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
return copiedTotal;
}
@@ -5127,6 +4673,21 @@ FreeBinaryEncoding(
tsdPtr->binaryEncoding = NULL;
}
}
+
+static Tcl_Encoding
+GetBinaryEncoding()
+{
+ ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
+
+ if (tsdPtr->binaryEncoding == NULL) {
+ tsdPtr->binaryEncoding = Tcl_GetEncoding(NULL, "iso8859-1");
+ Tcl_CreateThreadExitHandler(FreeBinaryEncoding, NULL);
+ }
+ if (tsdPtr->binaryEncoding == NULL) {
+ Tcl_Panic("binary encoding is not available");
+ }
+ return tsdPtr->binaryEncoding;
+}
/*
*---------------------------------------------------------------------------
@@ -5160,9 +4721,8 @@ FilterInputBytes(
ChannelState *statePtr = chanPtr->state;
/* State info for channel */
ChannelBuffer *bufPtr;
- char *raw, *rawStart, *dst;
- int result;
- size_t offset, toRead, dstNeeded, spaceLeft, rawLen;
+ char *raw, *dst;
+ int offset, toRead, dstNeeded, spaceLeft, result, rawLen;
Tcl_Obj *objPtr;
#define ENCODING_LINESIZE 20 /* Lower bound on how many bytes to convert at
* a time. Since we don't know a priori how
@@ -5195,13 +4755,11 @@ FilterInputBytes(
*/
read:
- if (GotFlag(statePtr, CHANNEL_BLOCKED)) {
- if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) {
- gsPtr->charsWrote = 0;
- gsPtr->rawRead = 0;
- return -1;
- }
- ResetFlag(statePtr, CHANNEL_BLOCKED);
+ if (GotFlag(statePtr, CHANNEL_NONBLOCKING|CHANNEL_BLOCKED)
+ == (CHANNEL_NONBLOCKING|CHANNEL_BLOCKED)) {
+ gsPtr->charsWrote = 0;
+ gsPtr->rawRead = 0;
+ return -1;
}
if (GetInput(chanPtr) != 0) {
gsPtr->charsWrote = 0;
@@ -5210,6 +4768,22 @@ FilterInputBytes(
}
bufPtr = statePtr->inQueueTail;
gsPtr->bufPtr = bufPtr;
+ if (bufPtr == NULL) {
+ gsPtr->charsWrote = 0;
+ gsPtr->rawRead = 0;
+ return -1;
+ }
+ } 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 */
+
+ assert ( !GotFlag(statePtr, CHANNEL_STICKY_EOF) );
+ assert ( !GotFlag(statePtr, CHANNEL_EOF) );
}
/*
@@ -5218,8 +4792,7 @@ FilterInputBytes(
* string rep if we need more space.
*/
- rawStart = RemovePoint(bufPtr);
- raw = rawStart;
+ raw = RemovePoint(bufPtr);
rawLen = BytesLeft(bufPtr);
dst = *gsPtr->dstPtr;
@@ -5231,7 +4804,7 @@ FilterInputBytes(
dstNeeded = toRead * TCL_UTF_MAX;
spaceLeft = objPtr->length - offset;
if (dstNeeded > spaceLeft) {
- size_t length = offset + ((offset < dstNeeded) ? dstNeeded : offset);
+ int length = offset + ((offset < dstNeeded) ? dstNeeded : offset);
if (Tcl_AttemptSetObjLength(objPtr, length) == 0) {
length = offset + dstNeeded;
@@ -5266,7 +4839,7 @@ FilterInputBytes(
*/
ChannelBuffer *nextPtr;
- size_t extra;
+ int extra;
nextPtr = bufPtr->nextPtr;
if (!IsBufferFull(bufPtr)) {
@@ -5286,7 +4859,7 @@ FilterInputBytes(
} else {
/*
* There are no more cached raw bytes left. See if we can get
- * some more.
+ * some more, but avoid blocking on a non-blocking channel.
*/
goto read;
@@ -5299,7 +4872,7 @@ FilterInputBytes(
}
extra = rawLen - gsPtr->rawRead;
memcpy(nextPtr->buf + (BUFFER_PADDING - extra),
- raw + gsPtr->rawRead, extra);
+ raw + gsPtr->rawRead, (size_t) extra);
nextPtr->nextRemoved -= extra;
bufPtr->nextAdded -= extra;
}
@@ -5442,12 +5015,13 @@ CommonGetsCleanup(
nextPtr = bufPtr->nextPtr;
for ( ; nextPtr != NULL; nextPtr = bufPtr->nextPtr) {
- size_t extra;
+ int extra;
extra = SpaceLeft(bufPtr);
if (extra > 0) {
memcpy(InsertPoint(bufPtr),
- nextPtr->buf + (BUFFER_PADDING - extra), extra);
+ nextPtr->buf + BUFFER_PADDING - extra,
+ (size_t) extra);
bufPtr->nextAdded += extra;
nextPtr->nextRemoved = BUFFER_PADDING;
}
@@ -5478,11 +5052,11 @@ CommonGetsCleanup(
*----------------------------------------------------------------------
*/
-ssize_t
+int
Tcl_Read(
Tcl_Channel chan, /* The channel from which to read. */
char *dst, /* Where to store input read. */
- size_t bytesToRead) /* Maximum number of bytes to read. */
+ int bytesToRead) /* Maximum number of bytes to read. */
{
Channel *chanPtr = (Channel *) chan;
ChannelState *statePtr = chanPtr->state;
@@ -5498,7 +5072,7 @@ Tcl_Read(
return -1;
}
- return DoRead(chanPtr, dst, bytesToRead, 0);
+ return DoRead(chanPtr, dst, bytesToRead);
}
/*
@@ -5523,139 +5097,90 @@ Tcl_Read(
*----------------------------------------------------------------------
*/
-ssize_t
+int
Tcl_ReadRaw(
Tcl_Channel chan, /* The channel from which to read. */
- char *bufPtr, /* Where to store input read. */
- size_t bytesToRead) /* Maximum number of bytes to read. */
+ char *readBuf, /* Where to store input read. */
+ int bytesToRead) /* Maximum number of bytes to read. */
{
Channel *chanPtr = (Channel *) chan;
ChannelState *statePtr = chanPtr->state;
/* State info for channel */
- ssize_t nread;
- size_t copied, copiedNow;
- int result;
-
- /*
- * The check below does too much because it will reject a call to this
- * function with a channel which is part of an 'fcopy'. But we have to
- * allow this here or else the chaining in the transformation drivers will
- * fail with 'file busy' error instead of retrieving and transforming the
- * data to copy.
- *
- * We let the check procedure now believe that there is no fcopy in
- * progress. A better solution than this might be an additional flag
- * argument to switch off specific checks.
- */
+ int copied = 0;
+ assert(bytesToRead > 0);
if (CheckChannelErrors(statePtr, TCL_READABLE | CHANNEL_RAW_MODE) != 0) {
return -1;
}
- /*
- * Check for information in the push-back buffers. If there is some, use
- * it. Go to the driver only if there is none (anymore) and the caller
- * requests more bytes.
- */
+ /* First read bytes from the push-back buffers. */
- for (copied = 0; copied < bytesToRead; copied += copiedNow) {
- copiedNow = CopyBuffer(chanPtr, bufPtr + copied,
- bytesToRead - copied);
- if (copiedNow == 0) {
- if (GotFlag(statePtr, CHANNEL_EOF)) {
- goto done;
- }
- if (GotFlag(statePtr, CHANNEL_BLOCKED)) {
- if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) {
- goto done;
- }
- ResetFlag(statePtr, CHANNEL_BLOCKED);
- }
+ while (chanPtr->inQueueHead && bytesToRead > 0) {
+ ChannelBuffer *bufPtr = chanPtr->inQueueHead;
+ int bytesInBuffer = BytesLeft(bufPtr);
+ int toCopy = (bytesInBuffer < bytesToRead) ? bytesInBuffer
+ : bytesToRead;
-#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
- /*
- * [Bug 943274]. Better emulation of non-blocking channels for
- * channels without BlockModeProc, by keeping track of true
- * fileevents generated by the OS == Data waiting and reading if
- * and only if we are sure to have data.
- */
+ /* Copy the current chunk into the read buffer. */
- if (GotFlag(statePtr, CHANNEL_NONBLOCKING) &&
- (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) &&
- !GotFlag(statePtr, CHANNEL_HAS_MORE_DATA)) {
- /*
- * We bypass the driver; it would block as no data is
- * available.
- */
+ memcpy(readBuf, RemovePoint(bufPtr), (size_t) toCopy);
+ bufPtr->nextRemoved += toCopy;
+ copied += toCopy;
+ readBuf += toCopy;
+ bytesToRead -= toCopy;
- nread = -1;
- result = EWOULDBLOCK;
- } else
-#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
- {
- /*
- * Now go to the driver to get as much as is possible to fill
- * the remaining request. Do all the error handling by
- * ourselves. The code was stolen from 'GetInput' and slightly
- * adapted (different return value here).
- *
- * The case of 'bytesToRead == 0' at this point cannot happen.
- */
+ /* If the current buffer is empty recycle it. */
- nread = ChanRead(chanPtr, bufPtr + copied,
- bytesToRead - copied, &result);
+ if (IsBufferEmpty(bufPtr)) {
+ chanPtr->inQueueHead = bufPtr->nextPtr;
+ if (chanPtr->inQueueHead == NULL) {
+ chanPtr->inQueueTail = NULL;
}
+ RecycleBuffer(chanPtr->state, bufPtr, 0);
+ }
+ }
- if (nread > 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 (nread < ((ssize_t) bytesToRead - (ssize_t) copied)) {
- SetFlag(statePtr, CHANNEL_BLOCKED);
- }
-
-#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
- if (nread <= (bytesToRead - copied)) {
- /*
- * [Bug 943274] We have read the available data, clear
- * flag.
- */
+ /*
+ * 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.
+ */
- ResetFlag(statePtr, CHANNEL_HAS_MORE_DATA);
- }
-#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
- } else if (nread == 0) {
- SetFlag(statePtr, CHANNEL_EOF);
- statePtr->inputEncodingFlags |= TCL_ENCODING_END;
-
- } else if (nread < 0) {
- if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
- if (copied > 0) {
- /*
- * Information that was copied earlier has precedence
- * over EAGAIN/WOULDBLOCK handling.
- */
+ if (copied) {
+ return copied;
+ }
- return copied;
- }
+ /* This test not needed. */
+ if (bytesToRead > 0) {
- SetFlag(statePtr, CHANNEL_BLOCKED);
- result = EAGAIN;
- }
+ int nread = ChanRead(chanPtr, readBuf, bytesToRead);
- Tcl_SetErrno(result);
- return -1;
+ 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.
+ */
+ if (!GotFlag(statePtr, CHANNEL_BLOCKED) || copied == 0) {
+ copied = -1;
}
-
- return copied + nread;
+ } else {
+ /*
+ * nread == 0. Driver is at EOF. Let that state filter up.
+ */
}
}
-
- done:
return copied;
}
@@ -5681,13 +5206,13 @@ Tcl_ReadRaw(
*---------------------------------------------------------------------------
*/
-ssize_t
+int
Tcl_ReadChars(
Tcl_Channel chan, /* The channel to read. */
Tcl_Obj *objPtr, /* Input data is stored in this object. */
- size_t toRead, /* Maximum number of characters to store, or
- * TCL_STRLEN to read all available data (up
- * to EOF or when channel blocks). */
+ 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,
* the data will replace the existing contents
@@ -5737,13 +5262,13 @@ Tcl_ReadChars(
*---------------------------------------------------------------------------
*/
-static ssize_t
+static int
DoReadChars(
Channel *chanPtr, /* The channel to read. */
Tcl_Obj *objPtr, /* Input data is stored in this object. */
- size_t toRead, /* Maximum number of characters to store, or
- * TCL_STRLEN to read all available data (up
- * to EOF or when channel blocks). */
+ 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,
* the data will replace the existing contents
@@ -5752,22 +5277,18 @@ DoReadChars(
ChannelState *statePtr = chanPtr->state;
/* State info for channel */
ChannelBuffer *bufPtr;
- size_t offset, factor;
- ssize_t copied, copiedNow;
- int result;
- Tcl_Encoding encoding;
+ int copied, copiedNow, result;
+ Tcl_Encoding encoding = statePtr->encoding;
+ int binaryMode;
#define UTF_EXPANSION_FACTOR 1024
+ int factor = UTF_EXPANSION_FACTOR;
- /*
- * This operation should occur at the top of a channel stack.
- */
-
- chanPtr = statePtr->topChanPtr;
- encoding = statePtr->encoding;
- factor = UTF_EXPANSION_FACTOR;
+ binaryMode = (encoding == NULL)
+ && (statePtr->inputTranslation == TCL_TRANSLATE_LF)
+ && (statePtr->inEofChar == '\0');
if (appendFlag == 0) {
- if (encoding == NULL) {
+ if (binaryMode) {
Tcl_SetByteArrayLength(objPtr, 0);
} else {
Tcl_SetObjLength(objPtr, 0);
@@ -5776,27 +5297,63 @@ DoReadChars(
* 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);
}
- offset = 0;
- } else {
- if (encoding == NULL) {
- Tcl_GetByteArrayFromObj(objPtr, &offset);
- } else {
- TclGetStringFromObj(objPtr, &offset);
+ }
+
+ /*
+ * 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.
+ */
+
+ if (GotFlag(statePtr, CHANNEL_STICKY_EOF)) {
+ SetFlag(statePtr, CHANNEL_EOF);
+ assert( statePtr->inputEncodingFlags & TCL_ENCODING_END );
+ assert( !GotFlag(statePtr, CHANNEL_BLOCKED|INPUT_SAW_CR) );
+
+ /* TODO: We don't need this call? */
+ UpdateInterest(chanPtr);
+ return 0;
+ }
+
+ /* Special handling for zero-char read request. */
+ 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: We don't need this call? */
+ UpdateInterest(chanPtr);
+ return 0;
}
- for (copied = 0; toRead > 0; ) {
+ /*
+ * This operation should occur at the top of a channel stack.
+ */
+
+ chanPtr = statePtr->topChanPtr;
+ TclChannelPreserve((Tcl_Channel)chanPtr);
+
+ /* 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; (unsigned) toRead > 0; ) {
copiedNow = -1;
if (statePtr->inQueueHead != NULL) {
- if (encoding == NULL) {
- copiedNow = ReadBytes(statePtr, objPtr, toRead, &offset);
+ if (binaryMode) {
+ copiedNow = ReadBytes(statePtr, objPtr, toRead);
} else {
- copiedNow = ReadChars(statePtr, objPtr, toRead, &offset,
- &factor);
+ copiedNow = ReadChars(statePtr, objPtr, toRead, &factor);
}
/*
@@ -5805,8 +5362,9 @@ DoReadChars(
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) {
@@ -5815,53 +5373,62 @@ DoReadChars(
}
}
- if (copiedNow == -1) {
+ if (copiedNow < 0) {
if (GotFlag(statePtr, CHANNEL_EOF)) {
break;
}
- if (GotFlag(statePtr, CHANNEL_BLOCKED)) {
- if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) {
- break;
- }
- ResetFlag(statePtr, CHANNEL_BLOCKED);
+ if (GotFlag(statePtr, CHANNEL_NONBLOCKING|CHANNEL_BLOCKED)
+ == (CHANNEL_NONBLOCKING|CHANNEL_BLOCKED)) {
+ break;
}
result = GetInput(chanPtr);
+ if (chanPtr != statePtr->topChanPtr) {
+ TclChannelRelease((Tcl_Channel)chanPtr);
+ chanPtr = statePtr->topChanPtr;
+ TclChannelPreserve((Tcl_Channel)chanPtr);
+ }
if (result != 0) {
- if (result == EAGAIN) {
- break;
+ if (!GotFlag(statePtr, CHANNEL_BLOCKED)) {
+ copied = -1;
}
- copied = -1;
- goto done;
+ break;
}
} else {
copied += copiedNow;
- if (toRead != TCL_STRLEN) {
- toRead -= copiedNow;
- }
+ toRead -= copiedNow;
}
}
- ResetFlag(statePtr, CHANNEL_BLOCKED);
- if (encoding == NULL) {
- Tcl_SetByteArrayLength(objPtr, offset);
- } else {
- Tcl_SetObjLength(objPtr, offset);
- }
-
/*
- * Update the notifier state so we don't block while there is still data
- * in the buffers.
+ * 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);
+ }
- done:
/*
* 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;
+ TclChannelPreserve((Tcl_Channel)chanPtr);
+ }
- chanPtr = statePtr->topChanPtr;
-
+ /*
+ * Update the notifier state so we don't block while there is still data
+ * in the buffers.
+ */
+ assert(!GotFlag(statePtr, CHANNEL_EOF)
+ || GotFlag(statePtr, CHANNEL_STICKY_EOF)
+ || Tcl_InputBuffered((Tcl_Channel)chanPtr) == 0);
+ assert( !(GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED)
+ == (CHANNEL_EOF|CHANNEL_BLOCKED)) );
UpdateInterest(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
return copied;
}
@@ -5879,18 +5446,16 @@ DoReadChars(
* allocated to hold data read from the channel as needed.
*
* Results:
- * The return value is the number of bytes appended to the object and
- * *offsetPtr is filled with the total number of bytes in the object
- * (greater than the return value if there were already bytes in the
- * object).
+ * The return value is the number of bytes appended to the object, or
+ * -1 to indicate that zero bytes were read due to an EOF.
*
* Side effects:
- * None.
+ * The storage of bytes in objPtr can cause (re-)allocation of memory.
*
*---------------------------------------------------------------------------
*/
-static ssize_t
+static int
ReadBytes(
ChannelState *statePtr, /* State of the channel to read. */
Tcl_Obj *objPtr, /* Input data is appended to this ByteArray
@@ -5898,72 +5463,22 @@ ReadBytes(
* been allocated to hold data, not how many
* bytes of data have been stored in the
* object. */
- size_t bytesToRead, /* Maximum number of bytes to store, or
- * TCL_STRLEN 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 bytes available in the
- * first buffer, only the bytes from the first
- * buffer are returned. */
- size_t *offsetPtr) /* On input, contains how many bytes of objPtr
- * have been used to hold data. On output,
- * filled with how many bytes are now being
- * used. */
-{
- size_t toRead, srcLen, offset, length, srcRead, dstWrote;
- ChannelBuffer *bufPtr;
- char *src, *dst;
-
- offset = *offsetPtr;
-
- bufPtr = statePtr->inQueueHead;
- src = RemovePoint(bufPtr);
- srcLen = BytesLeft(bufPtr);
-
- toRead = bytesToRead;
- if (toRead == TCL_STRLEN || toRead > srcLen) {
- toRead = srcLen;
- }
-
- dst = (char *) Tcl_GetByteArrayFromObj(objPtr, &length);
- if (offset + toRead + 1 > length) {
- /*
- * Double the existing size of the object or make enough room to hold
- * all the characters we may get from the source buffer, whichever is
- * larger.
- */
-
- length = offset * 2;
- if (offset < toRead) {
- length = offset + toRead + 1;
- }
- dst = (char *) Tcl_SetByteArrayLength(objPtr, length);
- }
- dst += offset;
-
- if (GotFlag(statePtr, INPUT_NEED_NL)) {
- ResetFlag(statePtr, INPUT_NEED_NL);
- if ((srcLen == 0) || (*src != '\n')) {
- *dst = '\r';
- *offsetPtr += 1;
- return 1;
- }
- *dst++ = '\n';
- src++;
- srcLen--;
- toRead--;
- }
+ 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
+ * bytes available in the first buffer, only
+ * the bytes from the first buffer are
+ * returned. */
+{
+ ChannelBuffer *bufPtr = statePtr->inQueueHead;
+ int srcLen = BytesLeft(bufPtr);
+ int toRead = bytesToRead>srcLen || bytesToRead<0 ? srcLen : bytesToRead;
- srcRead = srcLen;
- dstWrote = toRead;
- if (TranslateInputEOL(statePtr, dst, src, &dstWrote, &srcRead)) {
- if (dstWrote == 0) {
- return -1;
- }
- }
- bufPtr->nextRemoved += srcRead;
- *offsetPtr += dstWrote;
- return dstWrote;
+ TclAppendBytesToByteArray(objPtr, (unsigned char *) RemovePoint(bufPtr),
+ toRead);
+ bufPtr->nextRemoved += toRead;
+ return toRead;
}
/*
@@ -5993,276 +5508,379 @@ ReadBytes(
*---------------------------------------------------------------------------
*/
-static ssize_t
+static int
ReadChars(
ChannelState *statePtr, /* State of channel to read. */
Tcl_Obj *objPtr, /* Input data is appended to this object.
* objPtr->length is how much space has been
* allocated to hold data, not how many bytes
* of data have been stored in the object. */
- size_t charsToRead, /* Maximum number of characters to store, or
- * TCL_STRLEN to get all available characters.
+ int charsToRead, /* Maximum number of characters to store, or
+ * -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. */
- size_t *offsetPtr, /* On input, contains how many bytes of objPtr
- * have been used to hold data. On output,
- * filled with how many bytes are now being
- * used. */
- size_t *factorPtr) /* On input, contains a guess of how many
+ * 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
+ * second buffer. */
+ int *factorPtr) /* On input, contains a guess of how many
* bytes need to be allocated to hold the
* result of converting N source bytes to
* UTF-8. On output, contains another guess
* based on the data seen so far. */
{
- size_t toRead, factor, offset, spaceLeft, srcLen, dstNeeded;
- size_t srcRead, dstWrote, numChars, dstRead;
- ChannelBuffer *bufPtr;
- char *src, *dst;
- Tcl_EncodingState oldState;
- int encEndFlagSuppressed = 0;
-
- factor = *factorPtr;
- offset = *offsetPtr;
+ Tcl_Encoding encoding = statePtr->encoding? statePtr->encoding
+ : GetBinaryEncoding();
+ Tcl_EncodingState savedState = statePtr->inputEncodingState;
+ ChannelBuffer *bufPtr = statePtr->inQueueHead;
+ int savedIEFlags = statePtr->inputEncodingFlags;
+ int savedFlags = statePtr->flags;
+ char *dst, *src = RemovePoint(bufPtr);
+ int dstLimit, numBytes, srcLen = BytesLeft(bufPtr);
- bufPtr = statePtr->inQueueHead;
- src = RemovePoint(bufPtr);
- 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.
+ */
- toRead = charsToRead;
- if (toRead == TCL_STRLEN || toRead > srcLen) {
- toRead = srcLen;
- }
+ 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 dstNeeded = TCL_UTF_MAX - 1 + toRead * factor / UTF_EXPANSION_FACTOR;
- dstNeeded = TCL_UTF_MAX - 1 + toRead * factor / UTF_EXPANSION_FACTOR;
- spaceLeft = objPtr->length - offset;
-
- if (dstNeeded > spaceLeft) {
- /*
- * Double the existing size of the object or make enough room to hold
- * all the characters we want from the source buffer, whichever is
- * larger.
- */
-
- int length = offset + ((offset < dstNeeded) ? dstNeeded : offset);
-
- if (Tcl_AttemptSetObjLength(objPtr, length) == 0) {
- length = offset + dstNeeded;
- if (Tcl_AttemptSetObjLength(objPtr, length) == 0) {
- dstNeeded = TCL_UTF_MAX - 1 + toRead;
- length = offset + dstNeeded;
- Tcl_SetObjLength(objPtr, length);
- }
- }
- spaceLeft = length - offset;
- }
+ (void) TclGetStringFromObj(objPtr, &numBytes);
+ Tcl_AppendToObj(objPtr, NULL, dstNeeded);
if (toRead == srcLen) {
- /*
- * Want to convert the whole buffer in one pass. If we have enough
- * space, convert it using all available space in object rather than
- * using the factor.
- */
-
- dstNeeded = spaceLeft;
+ unsigned int size;
+ dst = TclGetStringStorage(objPtr, &size) + numBytes;
+ dstNeeded = size - numBytes;
+ } else {
+ dst = TclGetString(objPtr) + numBytes;
}
- dst = objPtr->bytes + offset;
/*
- * [Bug 1462248]: The cause of the crash reported in this bug is this:
- *
- * - ReadChars, called with a single buffer, with a incomplete
- * multi-byte character at the end (only the first byte of it).
- * - Encoding translation fails, asks for more data
- * - Data is read, and eof is reached, TCL_ENCODING_END (TEE) is set.
- * - ReadChar is called again, converts the first buffer, but due to TEE
- * it does not check for incomplete multi-byte data, and the character
- * just after the end of the first buffer is a valid completion of the
- * multi-byte header in the actual buffer. The conversion reads more
- * characters from the buffer then present. This causes nextRemoved to
- * overshoot nextAdded and the next reads compute a negative srcLen,
- * cause further translations to fail, causing copying of data into the
- * next buffer using bad arguments, causing the mecpy for to eventually
- * fail.
- *
- * In the end it is a memory access bug spiraling out of control if the
- * conditions are _just so_. And ultimate cause is that TEE is given to a
- * conversion where it should not. TEE signals that this is the last
- * buffer. Except in our case it is not.
- *
- * My solution is to suppress TEE if the first buffer is not the last. We
- * will eventually need it given that EOF has been reached, but not right
- * now. This is what the new flag "endEncSuppressFlag" is for.
+ * 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 bug in 'Tcl_Utf2UtfProc' where it read from memory behind the
- * actual buffer has been fixed as well, and fixes the problem with the
- * crash too, but this would still allow the generic layer to
- * accidentially break a multi-byte sequence if the conditions are just
- * right, because again the ExternalToUtf would be successful where it
- * should not.
+ * 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.
*/
- if ((statePtr->inputEncodingFlags & TCL_ENCODING_END) &&
- (bufPtr->nextPtr != NULL)) {
+ dstLimit = dstNeeded + 1;
+ while (1) {
+ int dstDecoded, dstRead, dstWrote, srcRead, numChars, code;
+
/*
- * TEE is set for a buffer which is not the last. Squash it for now,
- * and restore it later, before yielding control to our caller.
+ * 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.
*/
- statePtr->inputEncodingFlags &= ~TCL_ENCODING_END;
- encEndFlagSuppressed = 1;
- }
+ assert(bufPtr->nextPtr == NULL || BytesLeft(bufPtr->nextPtr) == 0
+ || (statePtr->inputEncodingFlags & TCL_ENCODING_END) == 0);
+
+ code = Tcl_ExternalToUtf(NULL, encoding, src, srcLen,
+ statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
+ dst, dstLimit, &srcRead, &dstDecoded, &numChars);
- oldState = statePtr->inputEncodingState;
- if (GotFlag(statePtr, INPUT_NEED_NL)) {
/*
- * We want a '\n' because the last character we saw was '\r'.
+ * 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.
*/
- ResetFlag(statePtr, INPUT_NEED_NL);
- Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
- statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
- dst, TCL_UTF_MAX + 1, &srcRead, &dstWrote, &numChars);
- if ((dstWrote > 0) && (*dst == '\n')) {
+ dstWrote = dstLimit;
+ dstRead = dstDecoded;
+ TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead);
+
+ if (dstRead < dstDecoded) {
+
/*
- * The next char was a '\n'. Consume it and produce a '\n'.
+ * The encoding transformation produced bytes that the
+ * translation transformation did not consume. Why did
+ * this happen?
*/
- bufPtr->nextRemoved += srcRead;
- } else {
+ if (statePtr->inEofChar && dst[dstRead] == statePtr->inEofChar) {
+ /*
+ * 1) There's an eof char set on the channel, and
+ * 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.
+ *
+ * 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.
+ */
+ 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.
+ */
+
+ dstLimit = dstRead + TCL_UTF_MAX;
+ statePtr->flags = savedFlags;
+ statePtr->inputEncodingFlags = savedIEFlags;
+ statePtr->inputEncodingState = savedState;
+ continue;
+ }
+ }
+
/*
- * The next char was not a '\n'. Produce a '\r'.
+ * 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.
*/
- *dst = '\r';
- }
- statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
- *offsetPtr += 1;
+ assert(dst[dstRead] == '\r');
+ assert(statePtr->inputTranslation == TCL_TRANSLATE_CRLF);
- if (encEndFlagSuppressed) {
- statePtr->inputEncodingFlags |= TCL_ENCODING_END;
- }
- return 1;
- }
+ 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.
+ */
- Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
- statePtr->inputEncodingFlags, &statePtr->inputEncodingState, dst,
- dstNeeded + 1, &srcRead, &dstWrote, &numChars);
+ dstLimit = dstRead + TCL_UTF_MAX;
+ statePtr->flags = savedFlags;
+ statePtr->inputEncodingFlags = savedIEFlags;
+ statePtr->inputEncodingState = savedState;
+ continue;
+ }
- if (encEndFlagSuppressed) {
- statePtr->inputEncodingFlags |= TCL_ENCODING_END;
- }
+ assert(dstWrote == 0);
+ assert(dstRead == 0);
- if (srcRead == 0) {
- /*
- * Not enough bytes in src buffer to make a complete char. Copy the
- * bytes to the next buffer to make a new contiguous string, then tell
- * the caller to fill the buffer with more bytes.
- */
+ /*
+ * 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?
+ */
- ChannelBuffer *nextPtr;
+ if (code != TCL_OK) {
+ char buffer[TCL_UTF_MAX + 2];
+ int read, decoded, count;
+
+ /*
+ * Didn't get everything the buffer could offer
+ */
+
+ statePtr->flags = savedFlags;
+ statePtr->inputEncodingFlags = savedIEFlags;
+ statePtr->inputEncodingState = savedState;
+
+ assert(bufPtr->nextPtr == NULL
+ || BytesLeft(bufPtr->nextPtr) == 0 || 0 ==
+ (statePtr->inputEncodingFlags & TCL_ENCODING_END));
+
+ Tcl_ExternalToUtf(NULL, encoding, src, srcLen,
+ statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
+ buffer, TCL_UTF_MAX + 2, &read, &decoded, &count);
+
+ if (count == 2) {
+ if (buffer[1] == '\n') {
+ /* \r\n translate to \n */
+ dst[0] = '\n';
+ bufPtr->nextRemoved += read;
+ } else {
+ dst[0] = '\r';
+ bufPtr->nextRemoved += srcRead;
+ }
+
+ dst[1] = '\0';
+ statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
+
+ Tcl_SetObjLength(objPtr, numBytes + 1);
+ return 1;
+ }
+
+ } else if (GotFlag(statePtr, CHANNEL_EOF)) {
- nextPtr = bufPtr->nextPtr;
- if (nextPtr == NULL) {
- if (srcLen > 0) {
/*
- * There isn't enough data in the buffers to complete the next
- * character, so we need to wait for more data before the next
- * file event can be delivered. [Bug 478856]
- *
- * The exception to this is if the input buffer was completely
- * empty before we tried to convert its contents. Nothing in,
- * nothing out, and no incomplete character data. The
- * conversion before the current one was complete.
+ * The bare \r is the only char and we will never read
+ * a subsequent char to make the determination.
*/
- SetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
+ dst[0] = '\r';
+ bufPtr->nextRemoved = bufPtr->nextAdded;
+ Tcl_SetObjLength(objPtr, numBytes + 1);
+ return 1;
}
- return -1;
+
+ /*
+ * Revise the dstRead value so that the numChars calc
+ * below correctly computes zero characters read.
+ */
+
+ dstRead = numChars;
+
+ /* FALL THROUGH - get more data (dstWrote == 0) */
}
- /*
- * 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'.
- *
- * 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.
+ /*
+ * 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.
*/
- if (nextPtr->nextRemoved < srcLen) {
- Tcl_Panic("Buffer Underflow, BUFFER_PADDING not enough");
- }
+ numChars -= (dstRead - dstWrote);
- nextPtr->nextRemoved -= srcLen;
- memcpy(RemovePoint(nextPtr), src, srcLen);
- RecycleBuffer(statePtr, bufPtr, 0);
- statePtr->inQueueHead = nextPtr;
- return ReadChars(statePtr, objPtr, charsToRead, offsetPtr, factorPtr);
- }
+ if (charsToRead > 0 && numChars > charsToRead) {
- dstRead = dstWrote;
- if (TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead)) {
- /*
- * Hit EOF char. How many bytes of src correspond to where the EOF was
- * located in dst? Run the conversion again with an output buffer just
- * big enough to hold the data so we can get the correct value for
- * srcRead.
- */
+ /*
+ * 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!
+ */
- if (dstWrote == 0) {
- return -1;
+ dstLimit = Tcl_UtfAtIndex(dst, charsToRead) + TCL_UTF_MAX - dst;
+ statePtr->flags = savedFlags;
+ statePtr->inputEncodingFlags = savedIEFlags;
+ statePtr->inputEncodingState = savedState;
+ continue;
}
- statePtr->inputEncodingState = oldState;
- Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
- statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
- dst, dstRead + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars);
- TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead);
- }
- /*
- * The number of characters that we got may be less than the number that
- * we started with because "\r\n" sequences may have been turned into just
- * '\n' in dst.
- */
+ if (dstWrote == 0) {
+ ChannelBuffer *nextPtr;
- numChars -= dstRead - dstWrote;
+ /* We were not able to read any chars. */
- if (numChars > toRead) {
- /*
- * Got too many chars.
- */
+ assert (numChars == 0);
- const char *eof = Tcl_UtfAtIndex(dst, toRead);
+ /*
+ * 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.
+ */
- statePtr->inputEncodingState = oldState;
- Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
- statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
- dst, eof - dst + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars);
- dstRead = dstWrote;
- TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead);
- numChars -= (dstRead - dstWrote);
- }
- statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
+ 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.
+ */
+
+ nextPtr = bufPtr->nextPtr;
+
+ if (nextPtr == NULL) {
+ if (srcLen > 0) {
+ SetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
+ }
+ Tcl_SetObjLength(objPtr, numBytes);
+ return -1;
+ }
+
+ /*
+ * 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'.
+ *
+ * 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.
+ */
- bufPtr->nextRemoved += srcRead;
- if (dstWrote > srcRead + 1) {
- *factorPtr = dstWrote * UTF_EXPANSION_FACTOR / srcRead;
+ if (nextPtr->nextRemoved - srcLen < 0) {
+ Tcl_Panic("Buffer Underflow, BUFFER_PADDING not enough");
+ }
+
+ nextPtr->nextRemoved -= srcLen;
+ memcpy(RemovePoint(nextPtr), src, (size_t) srcLen);
+ RecycleBuffer(statePtr, bufPtr, 0);
+ statePtr->inQueueHead = nextPtr;
+ Tcl_SetObjLength(objPtr, numBytes);
+ return ReadChars(statePtr, objPtr, charsToRead, factorPtr);
+ }
+
+ statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
+
+ consume:
+ bufPtr->nextRemoved += srcRead;
+ /*
+ * 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;
+ }
+ Tcl_SetObjLength(objPtr, numBytes + dstWrote);
+ return numChars;
}
- *offsetPtr += dstWrote;
- return numChars;
}
/*
@@ -6283,7 +5901,7 @@ ReadChars(
*---------------------------------------------------------------------------
*/
-static int
+static void
TranslateInputEOL(
ChannelState *statePtr, /* Channel being read, for EOL translation and
* EOF character. */
@@ -6291,133 +5909,139 @@ TranslateInputEOL(
* appropriate EOL translation to source
* characters. */
const char *srcStart, /* Source characters. */
- size_t *dstLenPtr, /* On entry, the maximum length of output
- * buffer in bytes; must be <= *srcLenPtr. On
- * exit, the number of bytes actually used in
- * output buffer. */
- size_t *srcLenPtr) /* On entry, the length of source buffer. On
+ int *dstLenPtr, /* On entry, the maximum length of output
+ * buffer in bytes. On exit, the number of
+ * bytes actually used in output buffer. */
+ int *srcLenPtr) /* On entry, the length of source buffer. On
* exit, the number of bytes read from the
* source buffer. */
{
- size_t dstLen, srcLen, inEofChar;
- const char *eof;
+ const char *eof = NULL;
+ int dstLen = *dstLenPtr;
+ int srcLen = *srcLenPtr;
+ int inEofChar = statePtr->inEofChar;
- dstLen = *dstLenPtr;
+ /*
+ * 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. */
+ srcLen = dstLen;
+ }
+ break;
+ default:
+ /* In other modes, at most 2 src bytes become a dst byte. */
+ if (srcLen/2 > dstLen) {
+ srcLen = 2 * dstLen;
+ }
+ break;
+ }
- eof = NULL;
- inEofChar = statePtr->inEofChar;
if (inEofChar != '\0') {
/*
- * Find EOF in translated buffer then compress out the EOL. The source
- * buffer may be much longer than the destination buffer - we only
- * want to return EOF if the EOF has been copied to the destination
- * buffer.
+ * Make sure we do not read past any logical end of channel input
+ * created by the presence of the input eof char.
*/
- const char *src, *srcMax = srcStart + *srcLenPtr;
-
- for (src = srcStart; src < srcMax; src++) {
- if (*src == inEofChar) {
- eof = src;
- srcLen = src - srcStart;
- if (srcLen < dstLen) {
- dstLen = srcLen;
- }
- *srcLenPtr = srcLen;
- break;
- }
+ if ((eof = memchr(srcStart, inEofChar, srcLen))) {
+ srcLen = eof - srcStart;
}
}
+
switch (statePtr->inputTranslation) {
case TCL_TRANSLATE_LF:
+ case TCL_TRANSLATE_CR:
if (dstStart != srcStart) {
- memcpy(dstStart, srcStart, dstLen);
+ memcpy(dstStart, srcStart, (size_t) srcLen);
}
- srcLen = dstLen;
- break;
- case TCL_TRANSLATE_CR: {
- char *dst, *dstEnd;
+ if (statePtr->inputTranslation == TCL_TRANSLATE_CR) {
+ char *dst = dstStart;
+ char *dstEnd = dstStart + srcLen;
- if (dstStart != srcStart) {
- memcpy(dstStart, srcStart, dstLen);
- }
- dstEnd = dstStart + dstLen;
- for (dst = dstStart; dst < dstEnd; dst++) {
- if (*dst == '\r') {
- *dst = '\n';
+ while ((dst = memchr(dst, '\r', dstEnd - dst))) {
+ *dst++ = '\n';
}
}
- srcLen = dstLen;
+ dstLen = srcLen;
break;
- }
case TCL_TRANSLATE_CRLF: {
- char *dst;
- const char *src, *srcEnd, *srcMax;
-
- dst = dstStart;
- src = srcStart;
- srcEnd = srcStart + dstLen;
- srcMax = srcStart + *srcLenPtr;
-
- for ( ; src < srcEnd; ) {
- if (*src == '\r') {
- src++;
- if (src >= srcMax) {
- SetFlag(statePtr, INPUT_NEED_NL);
- } else if (*src == '\n') {
- *dst++ = *src++;
- } else {
+ const char *crFound, *src = srcStart;
+ char *dst = dstStart;
+ int lesser = (dstLen < srcLen) ? dstLen : srcLen;
+
+ while ((crFound = memchr(src, '\r', lesser))) {
+ int numBytes = crFound - src;
+ memmove(dst, src, numBytes);
+
+ dst += numBytes; dstLen -= numBytes;
+ src += numBytes; srcLen -= numBytes;
+ if (srcLen == 1) {
+ /* valid src bytes end in \r */
+ if (eof) {
*dst++ = '\r';
+ src++; srcLen--;
+ } else {
+ lesser = 0;
+ break;
}
+ } else if (src[1] == '\n') {
+ *dst++ = '\n';
+ src += 2; srcLen -= 2;
} else {
- *dst++ = *src++;
+ *dst++ = '\r';
+ src++; srcLen--;
}
+ dstLen--;
+ lesser = (dstLen < srcLen) ? dstLen : srcLen;
}
- srcLen = src - srcStart;
- dstLen = dst - dstStart;
+ memmove(dst, src, lesser);
+ srcLen = src + lesser - srcStart;
+ dstLen = dst + lesser - dstStart;
break;
}
case TCL_TRANSLATE_AUTO: {
- char *dst;
- const char *src, *srcEnd, *srcMax;
-
- dst = dstStart;
- src = srcStart;
- srcEnd = srcStart + dstLen;
- srcMax = srcStart + *srcLenPtr;
+ const char *crFound, *src = srcStart;
+ char *dst = dstStart;
+ int lesser;
- if (GotFlag(statePtr, INPUT_SAW_CR) && (src < srcMax)) {
- if (*src == '\n') {
- src++;
- }
+ if (GotFlag(statePtr, INPUT_SAW_CR) && srcLen) {
+ if (*src == '\n') { src++; srcLen--; }
ResetFlag(statePtr, INPUT_SAW_CR);
}
- for ( ; src < srcEnd; ) {
- if (*src == '\r') {
- src++;
- if (src >= srcMax) {
- SetFlag(statePtr, INPUT_SAW_CR);
- } else if (*src == '\n') {
- if (srcEnd < srcMax) {
- srcEnd++;
- }
- src++;
- }
- *dst++ = '\n';
- } else {
- *dst++ = *src++;
+ lesser = (dstLen < srcLen) ? dstLen : srcLen;
+ while ((crFound = memchr(src, '\r', lesser))) {
+ int numBytes = crFound - src;
+ memmove(dst, src, numBytes);
+
+ dst[numBytes] = '\n';
+ dst += numBytes + 1; dstLen -= numBytes + 1;
+ src += numBytes + 1; srcLen -= numBytes + 1;
+ if (srcLen == 0) {
+ SetFlag(statePtr, INPUT_SAW_CR);
+ } else if (*src == '\n') {
+ src++; srcLen--;
}
+ lesser = (dstLen < srcLen) ? dstLen : srcLen;
}
- srcLen = src - srcStart;
- dstLen = dst - dstStart;
+ memmove(dst, src, lesser);
+ srcLen = src + lesser - srcStart;
+ dstLen = dst + lesser - dstStart;
break;
}
default:
- return 0;
+ Tcl_Panic("unknown input translation %d", statePtr->inputTranslation);
}
*dstLenPtr = dstLen;
+ *srcLenPtr = srcLen;
- if ((eof != NULL) && (srcStart + srcLen >= eof)) {
+ if (srcStart + srcLen == eof) {
/*
* EOF character was seen in EOL translated range. Leave current file
* position pointing at the EOF character, but don't store the EOF
@@ -6426,12 +6050,8 @@ TranslateInputEOL(
SetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF);
statePtr->inputEncodingFlags |= TCL_ENCODING_END;
- ResetFlag(statePtr, INPUT_SAW_CR | INPUT_NEED_NL);
- return 1;
+ ResetFlag(statePtr, CHANNEL_BLOCKED|INPUT_SAW_CR);
}
-
- *srcLenPtr = srcLen;
- return 0;
}
/*
@@ -6451,11 +6071,11 @@ TranslateInputEOL(
*----------------------------------------------------------------------
*/
-ssize_t
+int
Tcl_Ungets(
Tcl_Channel chan, /* The channel for which to add the input. */
const char *str, /* The input itself. */
- size_t length, /* 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. */
{
@@ -6463,7 +6083,6 @@ Tcl_Ungets(
ChannelState *statePtr; /* State of actual channel. */
ChannelBuffer *bufPtr; /* Buffer to contain the data. */
int flags;
- ssize_t len = (ssize_t) length;
chanPtr = (Channel *) chan;
statePtr = chanPtr->state;
@@ -6486,16 +6105,15 @@ Tcl_Ungets(
statePtr->flags = flags;
/*
- * If we have encountered a sticky EOF, just punt without storing (sticky
- * EOF is set if we have seen the input eofChar, to prevent reading beyond
- * the eofChar). Otherwise, clear the EOF flags, and clear the BLOCKED
- * bit. We want to discover these conditions anew in each operation.
+ * Clear the EOF flags, and clear the BLOCKED bit.
*/
-
- if (GotFlag(statePtr, CHANNEL_STICKY_EOF)) {
- goto done;
+
+ if (GotFlag(statePtr, CHANNEL_EOF)) {
+ statePtr->inputEncodingFlags |= TCL_ENCODING_START;
}
- ResetFlag(statePtr, CHANNEL_BLOCKED | CHANNEL_EOF);
+ ResetFlag(statePtr,
+ CHANNEL_BLOCKED | CHANNEL_STICKY_EOF | CHANNEL_EOF | INPUT_SAW_CR);
+ statePtr->inputEncodingFlags &= ~TCL_ENCODING_END;
bufPtr = AllocChannelBuffer(len);
memcpy(InsertPoint(bufPtr), str, (size_t) len);
@@ -6560,14 +6178,6 @@ Tcl_Flush(
return -1;
}
- /*
- * Force current output buffer to be output also.
- */
-
- if ((statePtr->curOutPtr != NULL) && IsBufferReady(statePtr->curOutPtr)) {
- SetFlag(statePtr, BUFFER_READY);
- }
-
result = FlushChannel(NULL, chanPtr, 0);
if (result != 0) {
return TCL_ERROR;
@@ -6618,7 +6228,7 @@ DiscardInputQueued(
*/
if (discardSavedBuffers && statePtr->saveInBufPtr != NULL) {
- ckfree(statePtr->saveInBufPtr);
+ ReleaseChannelBuffer(statePtr->saveInBufPtr);
statePtr->saveInBufPtr = NULL;
}
}
@@ -6630,6 +6240,9 @@ DiscardInputQueued(
*
* Reads input data from a device into a channel buffer.
*
+ * IMPORTANT! This routine is only called on a chanPtr argument
+ * that is the top channel of a stack!
+ *
* Results:
* The return value is the Posix error code if an error occurred while
* reading from the file, or 0 otherwise.
@@ -6644,13 +6257,21 @@ static int
GetInput(
Channel *chanPtr) /* Channel to read input from. */
{
- size_t toRead; /* How much to read? */
+ int toRead; /* How much to read? */
int result; /* Of calling driver. */
- ssize_t nread; /* How much was read from channel? */
+ int nread; /* How much was read from channel? */
ChannelBuffer *bufPtr; /* New buffer to add to input queue. */
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) );
+
/*
* Prevent reading from a dead channel -- a channel that has been closed
* but not yet deallocated, which can happen if the exit handler for
@@ -6663,20 +6284,34 @@ 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.
+ */
+
+ /*
* 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
* placed in the area when it was stacked.
- *
- * Two possibilities for the state: No buffers in it, or a single empty
- * buffer. In the latter case we can recycle it now.
*/
if (chanPtr->inQueueHead != NULL) {
- if (statePtr->inQueueHead != NULL) {
- RecycleBuffer(statePtr, statePtr->inQueueHead, 0);
- statePtr->inQueueHead = NULL;
- }
+
+ /* TODO: Tests to cover this. */
+ assert(statePtr->inQueueHead == NULL);
statePtr->inQueueHead = chanPtr->inQueueHead;
statePtr->inQueueTail = chanPtr->inQueueTail;
@@ -6697,21 +6332,21 @@ GetInput(
*/
bufPtr = statePtr->inQueueTail;
- if ((bufPtr != NULL) && !IsBufferFull(bufPtr)) {
- toRead = SpaceLeft(bufPtr);
- } else {
+
+ if ((bufPtr == NULL) || IsBufferFull(bufPtr)) {
bufPtr = statePtr->saveInBufPtr;
statePtr->saveInBufPtr = NULL;
/*
* Check the actual buffersize against the requested buffersize.
- * Buffers which are smaller than requested are squashed. This is done
+ * 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 - BUFFER_PADDING < statePtr->bufSize)) {
- ckfree(bufPtr);
+ && (bufPtr->bufLength - BUFFER_PADDING != statePtr->bufSize)) {
+ ReleaseChannelBuffer(bufPtr);
bufPtr = NULL;
}
@@ -6720,21 +6355,8 @@ GetInput(
}
bufPtr->nextPtr = NULL;
- /*
- * SF #427196: Use the actual size of the buffer to determine the
- * number of bytes to read from the channel and not the size for new
- * buffers. They can be different if the buffersize was changed
- * between reads.
- *
- * Note: This affects performance negatively if the buffersize was
- * extended but this small buffer is reused for all subsequent reads.
- * The system never uses buffers with the requested bigger size in
- * that case. An adjunct patch could try and delete all unused buffers
- * it encounters and which are smaller than the formally requested
- * buffersize.
- */
-
toRead = SpaceLeft(bufPtr);
+ assert(toRead == statePtr->bufSize);
if (statePtr->inQueueTail == NULL) {
statePtr->inQueueHead = bufPtr;
@@ -6742,75 +6364,22 @@ GetInput(
statePtr->inQueueTail->nextPtr = bufPtr;
}
statePtr->inQueueTail = bufPtr;
+ } else {
+ toRead = SpaceLeft(bufPtr);
}
- /*
- * If EOF is set, we should avoid calling the driver because on some
- * platforms it is impossible to read from a device after EOF.
- */
-
- if (GotFlag(statePtr, CHANNEL_EOF)) {
- return 0;
- }
-
-#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
- /*
- * [Bug 943274]: Better emulation of non-blocking channels for channels
- * without BlockModeProc, by keeping track of true fileevents generated by
- * the OS == Data waiting and reading if and only if we are sure to have
- * data.
- */
-
- if (GotFlag(statePtr, CHANNEL_NONBLOCKING) &&
- (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) &&
- !GotFlag(statePtr, CHANNEL_HAS_MORE_DATA)) {
- /*
- * Bypass the driver, it would block, as no data is available
- */
+ PreserveChannelBuffer(bufPtr);
+ nread = ChanRead(chanPtr, InsertPoint(bufPtr), toRead);
- nread = -1;
- result = EWOULDBLOCK;
- } else
-#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
- {
- nread = ChanRead(chanPtr, InsertPoint(bufPtr), toRead, &result);
- }
-
- if (nread > 0) {
+ if (nread < 0) {
+ result = Tcl_GetErrno();
+ } else {
+ result = 0;
bufPtr->nextAdded += nread;
-
- /*
- * 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 (nread < toRead) {
- SetFlag(statePtr, CHANNEL_BLOCKED);
- }
-
-#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
- if (nread <= toRead) {
- /*
- * [Bug 943274]: We have read the available data, clear flag.
- */
-
- ResetFlag(statePtr, CHANNEL_HAS_MORE_DATA);
- }
-#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
- } else if (nread == 0) {
- SetFlag(statePtr, CHANNEL_EOF);
- statePtr->inputEncodingFlags |= TCL_ENCODING_END;
- } else if (nread < 0) {
- if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
- SetFlag(statePtr, CHANNEL_BLOCKED);
- result = EAGAIN;
- }
- Tcl_SetErrno(result);
- return result;
}
- return 0;
+
+ ReleaseChannelBuffer(bufPtr);
+ return result;
}
/*
@@ -6914,8 +6483,12 @@ Tcl_Seek(
* point. Also clear CR related flags.
*/
- ResetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED |
- INPUT_SAW_CR);
+ if (GotFlag(statePtr, CHANNEL_EOF)) {
+ statePtr->inputEncodingFlags |= TCL_ENCODING_START;
+ }
+ statePtr->flags &=
+ ~(CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED | INPUT_SAW_CR);
+ statePtr->inputEncodingFlags &= ~TCL_ENCODING_END;
/*
* If the channel is in asynchronous output mode, switch it back to
@@ -6938,15 +6511,6 @@ Tcl_Seek(
}
/*
- * If there is data buffered in statePtr->curOutPtr then mark the channel
- * as ready to flush before invoking FlushChannel.
- */
-
- if ((statePtr->curOutPtr != NULL) && IsBufferReady(statePtr->curOutPtr)) {
- SetFlag(statePtr, BUFFER_READY);
- }
-
- /*
* If the flush fails we cannot recover the original position. In that
* case the seek is not attempted because we do not know where the access
* position is - instead we return the error. FlushChannel has already
@@ -6959,10 +6523,23 @@ 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 (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);
}
@@ -7057,13 +6634,25 @@ 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, Tcl_LongAsWide(0), SEEK_CUR, &result);
+ 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 Tcl_LongAsWide(-1);
@@ -7078,6 +6667,48 @@ 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.
@@ -7113,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.
@@ -7130,7 +6761,7 @@ Tcl_TruncateChannel(
WillWrite(chanPtr);
- if (WillRead(chanPtr)) {
+ if (WillRead(chanPtr) < 0) {
return TCL_ERROR;
}
@@ -7210,7 +6841,7 @@ CheckChannelErrors(
* Fail if the channel is not opened for desired operation.
*/
- if ((statePtr->flags & direction) == 0) {
+ if (GotFlag(statePtr, direction) == 0) {
Tcl_SetErrno(EACCES);
return -1;
}
@@ -7229,17 +6860,7 @@ CheckChannelErrors(
}
if (direction == TCL_READABLE) {
- /*
- * If we have not encountered a sticky EOF, clear the EOF bit (sticky
- * EOF is set if we have seen the input eofChar, to prevent reading
- * beyond the eofChar). Also, always clear the BLOCKED bit. We want to
- * discover these conditions anew in each operation.
- */
-
- if (!GotFlag(statePtr, CHANNEL_STICKY_EOF)) {
- ResetFlag(statePtr, CHANNEL_EOF);
- }
- ResetFlag(statePtr, CHANNEL_BLOCKED | CHANNEL_NEED_MORE_DATA);
+ ResetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
}
return 0;
@@ -7268,9 +6889,7 @@ Tcl_Eof(
ChannelState *statePtr = ((Channel *) chan)->state;
/* State of real channel structure. */
- return (GotFlag(statePtr, CHANNEL_STICKY_EOF) ||
- (GotFlag(statePtr, CHANNEL_EOF) &&
- (Tcl_InputBuffered(chan) == 0))) ? 1 : 0;
+ return GotFlag(statePtr, CHANNEL_EOF) ? 1 : 0;
}
/*
@@ -7449,20 +7068,32 @@ 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;
+
+ if (statePtr->bufSize == sz) {
+ return;
+ }
statePtr->bufSize = sz;
- if (statePtr->outputStage != NULL) {
- ckfree(statePtr->outputStage);
- statePtr->outputStage = NULL;
+ /*
+ * If bufsize changes, need to get rid of old utility buffer.
+ */
+
+ if (statePtr->saveInBufPtr != NULL) {
+ RecycleBuffer(statePtr, statePtr->saveInBufPtr, 1);
+ statePtr->saveInBufPtr = NULL;
}
- if ((statePtr->encoding != NULL) && GotFlag(statePtr, TCL_WRITABLE)) {
- statePtr->outputStage = ckalloc(statePtr->bufSize + 2);
+ if ((statePtr->inQueueHead != NULL)
+ && (statePtr->inQueueHead->nextPtr == NULL)
+ && IsBufferEmpty(statePtr->inQueueHead)) {
+ RecycleBuffer(statePtr, statePtr->inQueueHead, 1);
+ statePtr->inQueueHead = NULL;
+ statePtr->inQueueTail = NULL;
}
}
@@ -7530,36 +7161,33 @@ Tcl_BadChannelOption(
* standard generic options. Can be NULL for
* generic options only. */
{
- static const char *genericopt =
- "blocking buffering buffersize encoding eofchar translation";
-
if (interp != NULL) {
+ const char *genericopt =
+ "blocking buffering buffersize encoding eofchar translation";
const char **argv;
- size_t argc, i;
+ int argc, i;
Tcl_DString ds;
- Tcl_Obj *errObj;
Tcl_DStringInit(&ds);
- Tcl_DStringAppend(&ds, genericopt, TCL_STRLEN);
+ Tcl_DStringAppend(&ds, genericopt, -1);
if (optionList && (*optionList)) {
- TclDStringAppendLiteral(&ds, " ");
- Tcl_DStringAppend(&ds, optionList, TCL_STRLEN);
+ Tcl_DStringAppend(&ds, " ", 1);
+ Tcl_DStringAppend(&ds, optionList, -1);
}
if (Tcl_SplitList(interp, Tcl_DStringValue(&ds),
&argc, &argv) != TCL_OK) {
Tcl_Panic("malformed option list in channel driver");
}
Tcl_ResetResult(interp);
- errObj = Tcl_ObjPrintf("bad option \"%s\": should be one of ",
- 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;
@@ -7621,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;
}
@@ -7783,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.
@@ -7826,7 +7454,7 @@ Tcl_SetChannelOption(
ChannelState *statePtr = chanPtr->state;
/* State info for channel */
size_t len; /* Length of optionName string. */
- size_t argc;
+ int argc;
const char **argv;
/*
@@ -7835,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", TCL_STRLEN));
+ Tcl_AppendResult(interp, "unable to set channel options: "
+ "background copy in progress", NULL);
}
return TCL_ERROR;
}
@@ -7876,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);
@@ -7885,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", TCL_STRLEN));
- 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")) {
@@ -7899,6 +7528,7 @@ Tcl_SetChannelOption(
return TCL_ERROR;
}
Tcl_SetChannelBufferSize(chan, newBufferSize);
+ return TCL_OK;
} else if (HaveOpt(2, "-encoding")) {
Tcl_Encoding encoding;
@@ -7916,7 +7546,8 @@ Tcl_SetChannelOption(
* iso2022, the terminated escape sequence must write to the buffer.
*/
- if ((statePtr->encoding != NULL) && (statePtr->curOutPtr != NULL)
+ if ((statePtr->encoding != NULL)
+ && !(statePtr->outputEncodingFlags & TCL_ENCODING_START)
&& (CheckChannelErrors(statePtr, TCL_WRITABLE) == 0)) {
statePtr->outputEncodingFlags |= TCL_ENCODING_END;
WriteChars(chanPtr, "", 0);
@@ -7929,6 +7560,7 @@ Tcl_SetChannelOption(
statePtr->outputEncodingFlags = TCL_ENCODING_START;
ResetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
UpdateInterest(chanPtr);
+ return TCL_OK;
} else if (HaveOpt(2, "-eofchar")) {
if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
return TCL_ERROR;
@@ -7940,14 +7572,12 @@ Tcl_SetChannelOption(
int outIndex = (argc - 1);
int inValue = (int) argv[0][0];
int outValue = (int) argv[outIndex][0];
-
if (inValue & 0x80 || outValue & 0x80) {
if (interp) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "bad value for -eofchar: must be non-NUL ASCII"
- " character", TCL_STRLEN));
+ 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)) {
@@ -7958,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_STRLEN));
+ " one, or two elements", NULL);
}
- ckfree(argv);
+ ckfree((char *) argv);
return TCL_ERROR;
}
if (argv != NULL) {
- ckfree(argv);
+ ckfree((char *) argv);
}
/*
@@ -7975,7 +7605,13 @@ Tcl_SetChannelOption(
* ahead'. Ditto for blocked.
*/
- ResetFlag(statePtr, CHANNEL_EOF|CHANNEL_STICKY_EOF|CHANNEL_BLOCKED);
+ if (GotFlag(statePtr, CHANNEL_EOF)) {
+ statePtr->inputEncodingFlags |= TCL_ENCODING_START;
+ }
+ statePtr->flags &=
+ ~(CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED);
+ statePtr->inputEncodingFlags &= ~TCL_ENCODING_END;
+
return TCL_OK;
} else if (HaveOpt(1, "-translation")) {
const char *readMode, *writeMode;
@@ -7992,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", TCL_STRLEN));
+ " 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) {
@@ -8022,12 +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",
- TCL_STRLEN));
+ 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;
}
@@ -8073,51 +7708,24 @@ 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",
- TCL_STRLEN));
+ 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);
}
- /*
- * If bufsize changes, need to get rid of old utility buffer.
- */
-
- if (statePtr->saveInBufPtr != NULL) {
- RecycleBuffer(statePtr, statePtr->saveInBufPtr, 1);
- statePtr->saveInBufPtr = NULL;
- }
- if ((statePtr->inQueueHead != NULL)
- && (statePtr->inQueueHead->nextPtr == NULL)
- && IsBufferEmpty(statePtr->inQueueHead)) {
- RecycleBuffer(statePtr, statePtr->inQueueHead, 1);
- statePtr->inQueueHead = NULL;
- statePtr->inQueueTail = NULL;
- }
-
- /*
- * If encoding or bufsize changes, need to update output staging buffer.
- */
-
- if (statePtr->outputStage != NULL) {
- ckfree(statePtr->outputStage);
- statePtr->outputStage = NULL;
- }
- if ((statePtr->encoding != NULL) && GotFlag(statePtr, TCL_WRITABLE)) {
- statePtr->outputStage = ckalloc(statePtr->bufSize + 2);
- }
return TCL_OK;
}
@@ -8168,7 +7776,7 @@ CleanupChannelHandlers(
TclChannelEventScriptInvoker, sPtr);
TclDecrRefCount(sPtr->scriptPtr);
- ckfree(sPtr);
+ ckfree((char *) sPtr);
} else {
prevPtr = sPtr;
}
@@ -8209,21 +7817,6 @@ Tcl_NotifyChannel(
Channel *upChanPtr;
const Tcl_ChannelType *upTypePtr;
-#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
- /*
- * [SF Tcl Bug 943274] For a non-blocking channel without blockmodeproc we
- * keep track of actual input coming from the OS so that we can do a
- * credible imitation of non-blocking behaviour.
- */
-
- if ((mask & TCL_READABLE) &&
- GotFlag(statePtr, CHANNEL_NONBLOCKING) &&
- (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) &&
- !GotFlag(statePtr, CHANNEL_TIMER_FEV)) {
- SetFlag(statePtr, CHANNEL_HAS_MORE_DATA);
- }
-#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
-
/*
* In contrast to the other API functions this procedure walks towards the
* top of a stack and not down from it.
@@ -8236,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);
}
/*
@@ -8273,7 +7866,7 @@ Tcl_NotifyChannel(
* Preserve the channel struct in case the script closes it.
*/
- Tcl_Preserve(channel);
+ TclChannelPreserve((Tcl_Channel)channel);
Tcl_Preserve(statePtr);
/*
@@ -8283,8 +7876,9 @@ Tcl_NotifyChannel(
*/
if (GotFlag(statePtr, BG_FLUSH_SCHEDULED) && (mask & TCL_WRITABLE)) {
- FlushChannel(NULL, chanPtr, 1);
- mask &= ~TCL_WRITABLE;
+ if (0 == FlushChannel(NULL, chanPtr, 1)) {
+ mask &= ~TCL_WRITABLE;
+ }
}
/*
@@ -8304,7 +7898,7 @@ Tcl_NotifyChannel(
if ((chPtr->mask & mask) != 0) {
nh.nextHandlerPtr = chPtr->nextPtr;
- chPtr->proc(chPtr->clientData, mask);
+ (*(chPtr->proc))(chPtr->clientData, chPtr->mask & mask);
chPtr = nh.nextHandlerPtr;
} else {
chPtr = chPtr->nextPtr;
@@ -8318,11 +7912,16 @@ Tcl_NotifyChannel(
*/
if (chanPtr->typePtr != NULL) {
+ /*
+ * TODO: This call may not be needed. If a handler induced a
+ * change in interest, that handler should have made its own
+ * UpdateInterest() call, one would think.
+ */
UpdateInterest(chanPtr);
}
Tcl_Release(statePtr);
- Tcl_Release(channel);
+ TclChannelRelease(channel);
tsdPtr->nestedHandlerPtr = nh.nestedHandlerPtr;
}
@@ -8352,6 +7951,11 @@ UpdateInterest(
/* State info for channel */
int mask = statePtr->interestMask;
+ if (chanPtr->typePtr == NULL) {
+ /* Do not update interest on a closed channel */
+ return;
+ }
+
/*
* If there are flushed buffers waiting to be written, then we need to
* watch for the channel to become writable.
@@ -8415,12 +8019,12 @@ UpdateInterest(
mask &= ~TCL_EXCEPTION;
if (!statePtr->timer) {
- statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME,
- ChannelTimerProc, chanPtr);
+ statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,
+ chanPtr);
}
}
}
- ChanWatch(chanPtr, mask);
+ (chanPtr->typePtr->watchProc)(chanPtr->instanceData, mask);
}
/*
@@ -8457,31 +8061,9 @@ ChannelTimerProc(
* before UpdateInterest gets called by Tcl_NotifyChannel.
*/
- statePtr->timer = Tcl_CreateTimerHandler(SYNTHETIC_EVENT_TIME,
- ChannelTimerProc,chanPtr);
-
-#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
- /*
- * Set the TIMER flag to notify the higher levels that the driver
- * might have no data for us. We do this only if we are in
- * non-blocking mode and the driver has no BlockModeProc because only
- * then we really don't know if the driver will block or not. A
- * similar test is done in "PeekAhead".
- */
-
- if (GotFlag(statePtr, CHANNEL_NONBLOCKING) &&
- (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL)) {
- SetFlag(statePtr, CHANNEL_TIMER_FEV);
- }
-#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
-
+ statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,chanPtr);
Tcl_Preserve(statePtr);
Tcl_NotifyChannel((Tcl_Channel) chanPtr, TCL_READABLE);
-
-#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
- ResetFlag(statePtr, CHANNEL_TIMER_FEV);
-#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
-
Tcl_Release(statePtr);
} else {
statePtr->timer = NULL;
@@ -8540,7 +8122,7 @@ Tcl_CreateChannelHandler(
}
}
if (chPtr == NULL) {
- chPtr = ckalloc(sizeof(ChannelHandler));
+ chPtr = (ChannelHandler *) ckalloc(sizeof(ChannelHandler));
chPtr->mask = 0;
chPtr->proc = proc;
chPtr->clientData = clientData;
@@ -8644,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
@@ -8695,7 +8277,6 @@ DeleteScriptRecord(
if (esPtr == statePtr->scriptRecordPtr) {
statePtr->scriptRecordPtr = esPtr->nextPtr;
} else {
- CLANG_ASSERT(prevEsPtr);
prevEsPtr->nextPtr = esPtr->nextPtr;
}
@@ -8703,7 +8284,7 @@ DeleteScriptRecord(
TclChannelEventScriptInvoker, esPtr);
TclDecrRefCount(esPtr->scriptPtr);
- ckfree(esPtr);
+ ckfree((char *) esPtr);
break;
}
@@ -8752,12 +8333,12 @@ CreateScriptRecord(
makeCH = (esPtr == NULL);
if (makeCH) {
- esPtr = 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].
@@ -8820,7 +8401,7 @@ TclChannelEventScriptInvoker(
*/
Tcl_Preserve(interp);
- Tcl_Preserve(chanPtr);
+ TclChannelPreserve((Tcl_Channel)chanPtr);
result = Tcl_EvalObjEx(interp, esPtr->scriptPtr, TCL_EVAL_GLOBAL);
/*
@@ -8835,9 +8416,9 @@ TclChannelEventScriptInvoker(
if (chanPtr->typePtr != NULL) {
DeleteScriptRecord(interp, chanPtr, mask);
}
- Tcl_BackgroundException(interp, result);
+ TclBackgroundException(interp, result);
}
- Tcl_Release(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
Tcl_Release(interp);
}
@@ -8866,17 +8447,17 @@ Tcl_FileEventObjCmd(
ClientData clientData, /* Not used. */
Tcl_Interp *interp, /* Interpreter in which the channel for which
* to create the handler is found. */
- size_t objc, /* Number of arguments. */
+ int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
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?");
@@ -8895,9 +8476,9 @@ Tcl_FileEventObjCmd(
}
chanPtr = (Channel *) chan;
statePtr = chanPtr->state;
- if ((statePtr->flags & mask) == 0) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf("channel is not %s",
- (mask == TCL_READABLE) ? "readable" : "writable"));
+ if (GotFlag(statePtr, mask) == 0) {
+ Tcl_AppendResult(interp, "channel is not ",
+ (mask == TCL_READABLE) ? "readable" : "writable", NULL);
return TCL_ERROR;
}
@@ -8907,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)) {
@@ -8991,7 +8571,7 @@ TclCopyChannel(
Tcl_Interp *interp, /* Current interpreter. */
Tcl_Channel inChan, /* Channel to read from. */
Tcl_Channel outChan, /* Channel to write to. */
- Tcl_WideInt toRead, /* Amount of data to copy, or -1 for all. */
+ 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;
@@ -9004,17 +8584,17 @@ TclCopyChannel(
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;
}
@@ -9047,8 +8627,8 @@ TclCopyChannel(
* Make sure the output side is unbuffered.
*/
- outStatePtr->flags = (outStatePtr->flags & ~CHANNEL_LINEBUFFERED)
- | CHANNEL_UNBUFFERED;
+ outStatePtr->flags = (outStatePtr->flags & ~(CHANNEL_LINEBUFFERED))
+ | CHANNEL_UNBUFFERED;
/*
* Allocate a new CopyState to maintain info about the current copy in
@@ -9056,14 +8636,14 @@ TclCopyChannel(
* completed.
*/
- csPtr = ckalloc(sizeof(CopyState) + 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);
@@ -9116,11 +8696,9 @@ CopyData(
Tcl_Obj *cmdPtr, *errObj = NULL, *bufObj = NULL, *msg = NULL;
Tcl_Channel inChan, outChan;
ChannelState *inStatePtr, *outStatePtr;
- int result = TCL_OK;
- ssize_t size, sizeb;
- size_t sizeout;
+ int result = TCL_OK, size, sizeb;
Tcl_WideInt total;
- const char *buffer;
+ char *buffer;
int inBinary, outBinary, sameEncoding;
/* Encoding control */
int underflow; /* Input underflow */
@@ -9149,7 +8727,7 @@ CopyData(
Tcl_IncrRefCount(bufObj);
}
- while (csPtr->toRead != (Tcl_WideInt) 0) {
+ while (csPtr->toRead != 0) {
/*
* Check for unreported background errors.
*/
@@ -9173,26 +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 = (size_t) 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,
- 0 /* No append */);
+ 0 /* No append */);
}
underflow = (size >= 0) && (size < sizeb); /* Input underflow */
}
@@ -9225,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);
}
@@ -9248,15 +8824,15 @@ CopyData(
if (inBinary || sameEncoding) {
buffer = csPtr->buffer;
- sizeout = size;
+ sizeb = size;
} else {
- buffer = TclGetStringFromObj(bufObj, &sizeout);
+ buffer = TclGetStringFromObj(bufObj, &sizeb);
}
if (outBinary || sameEncoding) {
- sizeb = DoWrite(outStatePtr->topChanPtr, buffer, sizeout);
+ sizeb = WriteBytes(outStatePtr->topChanPtr, buffer, sizeb);
} else {
- sizeb = DoWriteChars(outStatePtr->topChanPtr, buffer, sizeout);
+ sizeb = WriteChars(outStatePtr->topChanPtr, buffer, sizeb);
}
/*
@@ -9291,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.
@@ -9317,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);
@@ -9368,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
@@ -9386,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);
@@ -9411,12 +8986,24 @@ CopyData(
*
* DoRead --
*
- * Reads a given number of bytes from a channel. No encoding conversions
- * are applied to the bytes being read.
+ * 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.
*
* Results:
- * The number of characters read, or -1 on error. Use Tcl_GetErrno() to
- * retrieve the error code for the error that occurred.
+ * The number of bytes actually stored (<= bytesToRead),
+ * or -1 if there is an error in reading the channel. Use
+ * Tcl_GetErrno() to retrieve the error code for the error
+ * that occurred.
+ *
+ * The number of bytes stored can be less than the number
+ * requested when
+ * - 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 -1)
*
* Side effects:
* May cause input to be buffered.
@@ -9424,541 +9011,199 @@ CopyData(
*----------------------------------------------------------------------
*/
-static ssize_t
+static int
DoRead(
Channel *chanPtr, /* The channel from which to read. */
- char *bufPtr, /* Where to store input read. */
- size_t toRead, /* 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;
- /* State info for channel */
- size_t copied; /* How many characters were copied into the
- * result string? */
- size_t copiedNow; /* How many characters were copied from the
- * current input buffer? */
- int result; /* Of calling GetInput. */
-
- /*
- * If we have not encountered a sticky EOF, clear the EOF bit. Either way
- * clear the BLOCKED bit. We want to discover these anew during each
- * operation.
- */
-
- if (!GotFlag(statePtr, CHANNEL_STICKY_EOF)) {
- ResetFlag(statePtr, CHANNEL_EOF);
- }
- ResetFlag(statePtr, CHANNEL_BLOCKED | CHANNEL_NEED_MORE_DATA);
-
- for (copied = 0; copied < toRead; copied += copiedNow) {
- copiedNow = CopyAndTranslateBuffer(statePtr, bufPtr + copied,
- toRead - copied);
- if (copiedNow == 0) {
- if (GotFlag(statePtr, CHANNEL_EOF)) {
- goto done;
- }
- if (GotFlag(statePtr, CHANNEL_BLOCKED)) {
- if (GotFlag(statePtr, CHANNEL_NONBLOCKING)) {
- goto done;
- }
- ResetFlag(statePtr, CHANNEL_BLOCKED);
- }
- result = GetInput(chanPtr);
- if (result != 0) {
- if (result != EAGAIN) {
- copied = -1;
- }
- goto done;
- }
- } else if (allowShortReads) {
- copied += copiedNow;
- break;
- }
- }
+ char *p = dst;
- ResetFlag(statePtr, CHANNEL_BLOCKED);
+ assert (bytesToRead >= 0);
/*
- * Update the notifier state so we don't block while there is still data
- * in the buffers.
+ * Early out when we know a read will get the eofchar.
+ *
+ * NOTE: This seems to be a bug. The special handling for
+ * a zero-char read request ought to come first. As coded
+ * the EOF due to eofchar has distinguishing behavior from
+ * the EOF due to reported EOF on the underlying device, and
+ * that seems undesirable. However recent history indicates
+ * that new inconsistent behavior in a patchlevel has problems
+ * too. Keep on keeping on for now.
*/
- done:
- UpdateInterest(chanPtr);
- return copied;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * CopyAndTranslateBuffer --
- *
- * Copy at most one buffer of input to the result space, doing eol
- * translations according to mode in effect currently.
- *
- * Results:
- * Number of bytes stored in the result buffer (as opposed to the number
- * of bytes read from the channel). May return zero if no input is
- * available to be translated.
- *
- * Side effects:
- * Consumes buffered input. May deallocate one buffer.
- *
- *----------------------------------------------------------------------
- */
-
-static size_t
-CopyAndTranslateBuffer(
- ChannelState *statePtr, /* Channel state from which to read input. */
- char *result, /* Where to store the copied input. */
- size_t space) /* How many bytes are available in result to
- * store the copied input? */
-{
- ChannelBuffer *bufPtr; /* The buffer from which to copy bytes. */
- size_t bytesInBuffer; /* How many bytes are available to be copied
- * in the current input buffer? */
- size_t copied; /* How many characters were already copied
- * into the destination space? */
- size_t i; /* Iterates over the copied input looking for
- * the input eofChar. */
-
- /*
- * If there is no input at all, return zero. The invariant is that either
- * there is no buffer in the queue, or if the first buffer is empty, it is
- * also the last buffer (and thus there is no input in the queue). Note
- * also that if the buffer is empty, we leave it in the queue.
- */
+ if (GotFlag(statePtr, CHANNEL_STICKY_EOF)) {
+ SetFlag(statePtr, CHANNEL_EOF);
+ assert( statePtr->inputEncodingFlags & TCL_ENCODING_END );
+ assert( !GotFlag(statePtr, CHANNEL_BLOCKED|INPUT_SAW_CR) );
- if (statePtr->inQueueHead == NULL) {
+ /* TODO: Don't need this call */
+ UpdateInterest(chanPtr);
return 0;
}
- bufPtr = statePtr->inQueueHead;
- bytesInBuffer = BytesLeft(bufPtr);
- copied = 0;
- switch (statePtr->inputTranslation) {
- case TCL_TRANSLATE_LF:
- if (bytesInBuffer == 0) {
- return 0;
+ /* Special handling for zero-char read request. */
+ if (bytesToRead == 0) {
+ if (GotFlag(statePtr, CHANNEL_EOF)) {
+ statePtr->inputEncodingFlags |= TCL_ENCODING_START;
}
+ ResetFlag(statePtr, CHANNEL_BLOCKED|CHANNEL_EOF);
+ statePtr->inputEncodingFlags &= ~TCL_ENCODING_END;
+ /* TODO: Don't need this call */
+ UpdateInterest(chanPtr);
+ return 0;
+ }
+ TclChannelPreserve((Tcl_Channel)chanPtr);
+ while (bytesToRead) {
/*
- * Copy the current chunk into the result buffer.
+ * Each pass through the loop is intended to process up to
+ * one channel buffer.
*/
- if (bytesInBuffer < space) {
- space = bytesInBuffer;
- }
- memcpy(result, RemovePoint(bufPtr), space);
- bufPtr->nextRemoved += space;
- copied = space;
- break;
- case TCL_TRANSLATE_CR: {
- char *end;
-
- if (bytesInBuffer == 0) {
- return 0;
- }
+ int bytesRead, bytesWritten;
+ ChannelBuffer *bufPtr = statePtr->inQueueHead;
/*
- * Copy the current chunk into the result buffer, then replace all \r
- * with \n.
+ * Don't read more data if we have what we need.
*/
- if (bytesInBuffer < space) {
- space = bytesInBuffer;
- }
- memcpy(result, RemovePoint(bufPtr), space);
- bufPtr->nextRemoved += space;
- copied = space;
+ 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 */
+ int code;
- for (end = result + copied; result < end; result++) {
- if (*result == '\r') {
- *result = '\n';
- }
- }
- break;
- }
- case TCL_TRANSLATE_CRLF: {
- char *src, *end, *dst;
- int curByte;
+ moreData:
+ code = GetInput(chanPtr);
+ bufPtr = statePtr->inQueueHead;
- /*
- * If there is a held-back "\r" at EOF, produce it now.
- */
+ assert (bufPtr != NULL);
- if (bytesInBuffer == 0) {
- if ((statePtr->flags & (INPUT_SAW_CR | CHANNEL_EOF)) ==
- (INPUT_SAW_CR | CHANNEL_EOF)) {
- result[0] = '\r';
- ResetFlag(statePtr, INPUT_SAW_CR);
- return 1;
+ if (GotFlag(statePtr, CHANNEL_EOF|CHANNEL_BLOCKED)) {
+ /* Further reads cannot do any more */
+ break;
}
- return 0;
- }
- /*
- * Copy the current chunk and replace "\r\n" with "\n" (but not
- * standalone "\r"!).
- */
-
- if (bytesInBuffer < space) {
- space = bytesInBuffer;
- }
- memcpy(result, RemovePoint(bufPtr), space);
- bufPtr->nextRemoved += space;
- copied = space;
-
- end = result + copied;
- dst = result;
- for (src = result; src < end; src++) {
- curByte = *src;
- if (curByte == '\n') {
- ResetFlag(statePtr, INPUT_SAW_CR);
- } else if (GotFlag(statePtr, INPUT_SAW_CR)) {
- ResetFlag(statePtr, INPUT_SAW_CR);
- *dst = '\r';
- dst++;
- }
- if (curByte == '\r') {
- SetFlag(statePtr, INPUT_SAW_CR);
- } else {
- *dst = (char) curByte;
- dst++;
+ if (code) {
+ /* Read error */
+ UpdateInterest(chanPtr);
+ TclChannelRelease((Tcl_Channel)chanPtr);
+ return -1;
}
- }
- copied = dst - result;
- break;
- }
- case TCL_TRANSLATE_AUTO: {
- char *src, *end, *dst;
- int curByte;
- if (bytesInBuffer == 0) {
- return 0;
+ assert (IsBufferFull(bufPtr));
}
- /*
- * Loop over the current buffer, converting "\r" and "\r\n" to "\n".
- */
-
- if (bytesInBuffer < space) {
- space = bytesInBuffer;
- }
- memcpy(result, RemovePoint(bufPtr), space);
- bufPtr->nextRemoved += space;
- copied = space;
+ assert (bufPtr != NULL);
- end = result + copied;
- dst = result;
- for (src = result; src < end; src++) {
- curByte = *src;
- if (curByte == '\r') {
- SetFlag(statePtr, INPUT_SAW_CR);
- *dst = '\n';
- dst++;
- } else {
- if ((curByte != '\n') || !GotFlag(statePtr, INPUT_SAW_CR)) {
- *dst = (char) curByte;
- dst++;
- }
- ResetFlag(statePtr, INPUT_SAW_CR);
- }
- }
- copied = dst - result;
- break;
- }
- default:
- Tcl_Panic("unknown eol translation mode");
- }
+ bytesRead = BytesLeft(bufPtr);
+ bytesWritten = bytesToRead;
- /*
- * If an in-stream EOF character is set for this channel, check that the
- * input we copied so far does not contain the EOF char. If it does, copy
- * only up to and excluding that character.
- */
+ TranslateInputEOL(statePtr, p, RemovePoint(bufPtr),
+ &bytesWritten, &bytesRead);
+ bufPtr->nextRemoved += bytesRead;
+ p += bytesWritten;
+ bytesToRead -= bytesWritten;
- if (statePtr->inEofChar != 0) {
- for (i = 0; i < copied; i++) {
- if (result[i] == (char) statePtr->inEofChar) {
- /*
- * Set sticky EOF so that no further input is presented to the
- * caller.
- */
+ if (!IsBufferEmpty(bufPtr)) {
+ /*
+ * Buffer is not empty. How can that be?
+ *
+ * 0) We stopped early because we got all the bytes
+ * we were seeking. That's fine.
+ */
- SetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF);
- statePtr->inputEncodingFlags |= TCL_ENCODING_END;
- copied = i;
+ if (bytesToRead == 0) {
break;
}
- }
- }
-
- /*
- * If the current buffer is empty recycle it.
- */
-
- if (IsBufferEmpty(bufPtr)) {
- statePtr->inQueueHead = bufPtr->nextPtr;
- if (statePtr->inQueueHead == NULL) {
- statePtr->inQueueTail = NULL;
- }
- RecycleBuffer(statePtr, bufPtr, 0);
- }
-
- /*
- * Return the number of characters copied into the result buffer. This may
- * be different from the number of bytes consumed, because of EOL
- * translations.
- */
-
- return copied;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * CopyBuffer --
- *
- * Copy at most one buffer of input to the result space.
- *
- * Results:
- * Number of bytes stored in the result buffer. May return zero if no
- * input is available.
- *
- * Side effects:
- * Consumes buffered input. May deallocate one buffer.
- *
- *----------------------------------------------------------------------
- */
-
-static size_t
-CopyBuffer(
- Channel *chanPtr, /* Channel from which to read input. */
- char *result, /* Where to store the copied input. */
- size_t space) /* How many bytes are available in result to
- * store the copied input? */
-{
- ChannelBuffer *bufPtr; /* The buffer from which to copy bytes. */
- size_t bytesInBuffer; /* How many bytes are available to be copied
- * in the current input buffer? */
- size_t copied; /* How many characters were already copied
- * into the destination space? */
- /*
- * If there is no input at all, return zero. The invariant is that either
- * there is no buffer in the queue, or if the first buffer is empty, it is
- * also the last buffer (and thus there is no input in the queue). Note
- * also that if the buffer is empty, we don't leave it in the queue, but
- * recycle it.
- */
-
- if (chanPtr->inQueueHead == NULL) {
- return 0;
- }
- bufPtr = chanPtr->inQueueHead;
- bytesInBuffer = BytesLeft(bufPtr);
-
- copied = 0;
-
- if (bytesInBuffer == 0) {
- RecycleBuffer(chanPtr->state, bufPtr, 0);
- chanPtr->inQueueHead = NULL;
- chanPtr->inQueueTail = NULL;
- return 0;
- }
-
- /*
- * Copy the current chunk into the result buffer.
- */
-
- if (bytesInBuffer < space) {
- space = bytesInBuffer;
- }
-
- memcpy(result, RemovePoint(bufPtr), space);
- bufPtr->nextRemoved += space;
- copied = space;
-
- /*
- * We don't care about in-stream EOF characters here as the data read here
- * may still flow through one or more transformations, i.e. is not in its
- * final state yet.
- */
-
- /*
- * If the current buffer is empty recycle it.
- */
-
- if (IsBufferEmpty(bufPtr)) {
- chanPtr->inQueueHead = bufPtr->nextPtr;
- if (chanPtr->inQueueHead == NULL) {
- chanPtr->inQueueTail = NULL;
- }
- RecycleBuffer(chanPtr->state, bufPtr, 0);
- }
+ /*
+ * 1) We're @EOF because we saw eof char.
+ */
- /*
- * Return the number of characters copied into the result buffer.
- */
+ if (GotFlag(statePtr, CHANNEL_STICKY_EOF)) {
+ break;
+ }
- return copied;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DoWrite --
- *
- * Puts a sequence of characters into an output buffer, may queue the
- * buffer for output if it gets full, and also remembers whether the
- * current buffer is ready e.g. if it contains a newline and we are in
- * line buffering mode.
- *
- * Results:
- * 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
- * channel.
- *
- *----------------------------------------------------------------------
- */
+ /*
+ * 2) The buffer holds a \r while in CRLF translation,
+ * followed by the end of the buffer.
+ */
-static ssize_t
-DoWrite(
- Channel *chanPtr, /* The channel to buffer output for. */
- const char *src, /* Data to write. */
- size_t srcLen) /* Number of bytes to write. */
-{
- ChannelState *statePtr = chanPtr->state;
- /* State info for channel */
- ChannelBuffer *outBufPtr; /* Current output buffer. */
- int foundNewline; /* Did we find a newline in output? */
- char *dPtr;
- const char *sPtr; /* Search variables for newline. */
- int crsent; /* In CRLF eol translation mode, remember the
- * fact that a CR was output to the channel
- * without its following NL. */
- size_t i; /* Loop index for newline search. */
- size_t destCopied; /* How many bytes were used in this
- * destination buffer to hold the output? */
- size_t totalDestCopied; /* How many bytes total were copied to the
- * channel buffer? */
- size_t srcCopied; /* How many bytes were copied from the source
- * string? */
- char *destPtr; /* Where in line to copy to? */
+ assert(statePtr->inputTranslation == TCL_TRANSLATE_CRLF);
+ assert(RemovePoint(bufPtr)[0] == '\r');
+ assert(BytesLeft(bufPtr) == 1);
- /*
- * If we are in network (or windows) translation mode, record the fact
- * that we have not yet sent a CR to the channel.
- */
+ if (bufPtr->nextPtr == NULL) {
+ /* There's no more buffered data.... */
- crsent = 0;
+ if (GotFlag(statePtr, CHANNEL_EOF)) {
+ /* ...and there never will be. */
- /*
- * Loop filling buffers and flushing them until all output has been
- * consumed.
- */
+ *p++ = '\r';
+ bytesToRead--;
+ bufPtr->nextRemoved++;
+ } else if (GotFlag(statePtr, CHANNEL_BLOCKED)) {
+ /* ...and we cannot get more now. */
+ SetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
+ break;
+ } else {
+ /* ... so we need to get some. */
+ goto moreData;
+ }
+ }
- srcCopied = 0;
- totalDestCopied = 0;
+ if (bufPtr->nextPtr) {
+ /* There's a next buffer. Shift orphan \r to it. */
- while (srcLen > 0) {
- /*
- * Make sure there is a current output buffer to accept output.
- */
+ ChannelBuffer *nextPtr = bufPtr->nextPtr;
- if (statePtr->curOutPtr == NULL) {
- statePtr->curOutPtr = AllocChannelBuffer(statePtr->bufSize);
+ nextPtr->nextRemoved -= 1;
+ RemovePoint(nextPtr)[0] = '\r';
+ bufPtr->nextRemoved++;
+ }
}
- outBufPtr = statePtr->curOutPtr;
-
- destCopied = SpaceLeft(outBufPtr);
- if (destCopied > srcLen) {
- destCopied = srcLen;
+ if (IsBufferEmpty(bufPtr)) {
+ statePtr->inQueueHead = bufPtr->nextPtr;
+ if (statePtr->inQueueHead == NULL) {
+ statePtr->inQueueTail = NULL;
+ }
+ RecycleBuffer(statePtr, bufPtr, 0);
+ bufPtr = statePtr->inQueueHead;
}
- destPtr = InsertPoint(outBufPtr);
- switch (statePtr->outputTranslation) {
- case TCL_TRANSLATE_LF:
- srcCopied = destCopied;
- memcpy(destPtr, src, destCopied);
- break;
- case TCL_TRANSLATE_CR:
- srcCopied = destCopied;
- memcpy(destPtr, src, destCopied);
- for (dPtr = destPtr; dPtr < destPtr + destCopied; dPtr++) {
- if (*dPtr == '\n') {
- *dPtr = '\r';
- }
- }
+ if (GotFlag(statePtr, CHANNEL_NONBLOCKING|CHANNEL_BLOCKED)
+ == (CHANNEL_NONBLOCKING|CHANNEL_BLOCKED)) {
break;
- case TCL_TRANSLATE_CRLF:
- for (srcCopied = 0, dPtr = destPtr, sPtr = src;
- dPtr < destPtr + destCopied;
- dPtr++, sPtr++, srcCopied++) {
- if (*sPtr == '\n') {
- if (crsent) {
- *dPtr = '\n';
- crsent = 0;
- } else {
- *dPtr = '\r';
- crsent = 1;
- sPtr--, srcCopied--;
- }
- } else {
- *dPtr = *sPtr;
- }
- }
- break;
- case TCL_TRANSLATE_AUTO:
- Tcl_Panic("Tcl_Write: AUTO output translation mode not supported");
- default:
- Tcl_Panic("Tcl_Write: unknown output translation mode");
}
/*
- * The current buffer is ready for output if it is full, or if it
- * contains a newline and this channel is line-buffered, or if it
- * contains any output and this channel is unbuffered.
+ * When there's no buffered data to read, and we're at EOF,
+ * escape to the caller.
*/
- outBufPtr->nextAdded += destCopied;
- if (!GotFlag(statePtr, BUFFER_READY)) {
- if (IsBufferFull(outBufPtr)) {
- SetFlag(statePtr, BUFFER_READY);
- } else if (GotFlag(statePtr, CHANNEL_LINEBUFFERED)) {
- for (sPtr = src, i = 0, foundNewline = 0;
- (i < srcCopied) && (!foundNewline);
- i++, sPtr++) {
- if (*sPtr == '\n') {
- foundNewline = 1;
- break;
- }
- }
- if (foundNewline) {
- SetFlag(statePtr, BUFFER_READY);
- }
- } else if (GotFlag(statePtr, CHANNEL_UNBUFFERED)) {
- SetFlag(statePtr, BUFFER_READY);
- }
- }
-
- totalDestCopied += srcCopied;
- src += srcCopied;
- srcLen -= srcCopied;
-
- if (GotFlag(statePtr, BUFFER_READY)) {
- if (FlushChannel(NULL, chanPtr, 0) != 0) {
- return -1;
- }
+ if (GotFlag(statePtr, CHANNEL_EOF)
+ && (bufPtr == NULL || IsBufferEmpty(bufPtr))) {
+ break;
}
- } /* Closes "while" */
+ }
+ if (bytesToRead == 0) {
+ ResetFlag(statePtr, CHANNEL_BLOCKED);
+ }
- return totalDestCopied;
+ 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);
}
/*
@@ -9984,7 +9229,7 @@ CopyEventProc(
ClientData clientData,
int mask)
{
- (void) CopyData(clientData, mask);
+ (void) CopyData((CopyState *) clientData, mask);
}
/*
@@ -10022,19 +9267,19 @@ StopCopy(
* Restore the old blocking mode and output buffering mode.
*/
- nonBlocking = csPtr->readFlags & CHANNEL_NONBLOCKING;
+ nonBlocking = (csPtr->readFlags & CHANNEL_NONBLOCKING);
if (nonBlocking != (inStatePtr->flags & CHANNEL_NONBLOCKING)) {
SetBlockMode(NULL, csPtr->readPtr,
nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
}
if (csPtr->readPtr != csPtr->writePtr) {
- nonBlocking = csPtr->writeFlags & CHANNEL_NONBLOCKING;
+ nonBlocking = (csPtr->writeFlags & CHANNEL_NONBLOCKING);
if (nonBlocking != (outStatePtr->flags & CHANNEL_NONBLOCKING)) {
SetBlockMode(NULL, csPtr->writePtr,
nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
}
}
- ResetFlag(outStatePtr, CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED);
+ outStatePtr->flags &= ~(CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED);
outStatePtr->flags |=
csPtr->writeFlags & (CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED);
@@ -10049,7 +9294,7 @@ StopCopy(
}
inStatePtr->csPtrR = NULL;
outStatePtr->csPtrW = NULL;
- ckfree(csPtr);
+ ckfree((char *) csPtr);
}
/*
@@ -10078,16 +9323,19 @@ StackSetBlockMode(
{
int result = 0;
Tcl_DriverBlockModeProc *blockModeProc;
+ ChannelState *statePtr = chanPtr->state;
/*
* Start at the top of the channel stack
+ * TODO: Examine what can go wrong when blockModeProc calls
+ * disturb the stacking state of the channel.
*/
- chanPtr = chanPtr->state->topChanPtr;
+ chanPtr = statePtr->topChanPtr;
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;
@@ -10142,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 {
/*
@@ -10236,16 +9483,15 @@ Tcl_GetChannelNamesEx(
&& (pattern[2] == 'd'))) {
if ((Tcl_FindHashEntry(hTblPtr, pattern) != NULL)
&& (Tcl_ListObjAppendElement(interp, resultPtr,
- Tcl_NewStringObj(pattern, TCL_STRLEN)) != TCL_OK)) {
+ Tcl_NewStringObj(pattern, -1)) != TCL_OK)) {
goto error;
}
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) {
@@ -10263,7 +9509,7 @@ Tcl_GetChannelNamesEx(
if (((pattern == NULL) || Tcl_StringMatch(name, pattern)) &&
(Tcl_ListObjAppendElement(interp, resultPtr,
- Tcl_NewStringObj(name, TCL_STRLEN)) != TCL_OK)) {
+ Tcl_NewStringObj(name, -1)) != TCL_OK)) {
error:
TclDecrRefCount(resultPtr);
return TCL_ERROR;
@@ -10391,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, (size_t) chanNameLen + 1) == 0)) {
+ (memcmp(name, chanName, (size_t) chanNameLen+1) == 0)) {
return 1;
}
}
@@ -10511,13 +9760,13 @@ Tcl_ChannelBlockModeProc(
{
if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) {
return chanTypePtr->blockModeProc;
- }
-
- /*
- * The v1 structure had the blockModeProc in a different place.
- */
+ } else {
+ /*
+ * The v1 structure had the blockModeProc in a different place.
+ */
- return (Tcl_DriverBlockModeProc *) chanTypePtr->version;
+ return (Tcl_DriverBlockModeProc *) (chanTypePtr->version);
+ }
}
/*
@@ -10759,8 +10008,9 @@ Tcl_ChannelFlushProc(
{
if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) {
return chanTypePtr->flushProc;
+ } else {
+ return NULL;
}
- return NULL;
}
/*
@@ -10786,8 +10036,9 @@ Tcl_ChannelHandlerProc(
{
if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) {
return chanTypePtr->handlerProc;
+ } else {
+ return NULL;
}
- return NULL;
}
/*
@@ -10813,8 +10064,9 @@ Tcl_ChannelWideSeekProc(
{
if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_3)) {
return chanTypePtr->wideSeekProc;
+ } else {
+ return NULL;
}
- return NULL;
}
/*
@@ -10841,8 +10093,9 @@ Tcl_ChannelThreadActionProc(
{
if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_4)) {
return chanTypePtr->threadActionProc;
+ } else {
+ return NULL;
}
- return NULL;
}
/*
@@ -10940,9 +10193,10 @@ static Tcl_Obj *
FixLevelCode(
Tcl_Obj *msg)
{
- size_t explicitResult, numOptions, lc, lcn, i, j;
+ int explicitResult, numOptions, lc, lcn;
Tcl_Obj **lv, **lvn;
- int res, val, lignore, cignore, newlevel = -1, newcode = -1;
+ int res, i, j, val, lignore, cignore;
+ int newlevel = -1, newcode = -1;
/* ASSERT msg != NULL */
@@ -10958,10 +10212,10 @@ FixLevelCode(
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 = (lc % 2);
+ explicitResult = (1 == (lc % 2));
numOptions = lc - explicitResult;
/*
@@ -11018,7 +10272,7 @@ FixLevelCode(
lcn += 2;
}
- lvn = ckalloc(lcn * sizeof(Tcl_Obj *));
+ lvn = (Tcl_Obj **) ckalloc(lcn * sizeof(Tcl_Obj *));
/*
* New level/code information is spliced into the first occurence of
@@ -11071,7 +10325,7 @@ FixLevelCode(
msg = Tcl_NewListObj(j, lvn);
- ckfree(lvn);
+ ckfree((char *) lvn);
return msg;
}
@@ -11155,8 +10409,9 @@ Tcl_ChannelTruncateProc(
{
if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_5)) {
return chanTypePtr->truncateProc;
+ } else {
+ return NULL;
}
- return NULL;
}
/*
@@ -11185,12 +10440,11 @@ DupChannelIntRep(
* currently have an internal rep.*/
{
ChannelState *statePtr = GET_CHANNELSTATE(srcPtr);
- Interp *interpPtr = GET_CHANNELINTERP(srcPtr);
SET_CHANNELSTATE(copyPtr, statePtr);
- SET_CHANNELINTERP(copyPtr, interpPtr);
- Tcl_Preserve(statePtr);
- copyPtr->typePtr = &tclChannelType;
+ SET_CHANNELINTERP(copyPtr, GET_CHANNELINTERP(srcPtr));
+ Tcl_Preserve((ClientData) statePtr);
+ copyPtr->typePtr = srcPtr->typePtr;
}
/*
@@ -11216,53 +10470,40 @@ SetChannelFromAny(
register Tcl_Obj *objPtr) /* The object to convert. */
{
ChannelState *statePtr;
- Interp *interpPtr;
if (interp == NULL) {
return TCL_ERROR;
}
- if (objPtr->typePtr == &tclChannelType) {
+ 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);
- interpPtr = GET_CHANNELINTERP(objPtr);
if (GotFlag(statePtr, CHANNEL_TAINTED|CHANNEL_CLOSED)) {
ResetFlag(statePtr, CHANNEL_TAINTED);
- Tcl_Release(statePtr);
- UpdateStringOfChannel(objPtr);
+ Tcl_Release((ClientData) statePtr);
objPtr->typePtr = NULL;
- } else if (interpPtr != (Interp*) interp) {
- Tcl_Release(statePtr);
- UpdateStringOfChannel(objPtr);
+ } else if (interp != GET_CHANNELINTERP(objPtr)) {
+ Tcl_Release((ClientData) statePtr);
objPtr->typePtr = NULL;
}
}
- if (objPtr->typePtr != &tclChannelType) {
- Tcl_Channel chan;
-
- /*
- * We need a valid string with which to check for a valid channel, but
- * make sure not to free internal rep until validated. [Bug 1847044]
- */
+ if (objPtr->typePtr != &chanObjType) {
+ Tcl_Channel chan = Tcl_GetChannel(interp, TclGetString(objPtr), NULL);
- if ((objPtr->typePtr != NULL) && (objPtr->bytes == NULL)) {
- objPtr->typePtr->updateStringProc(objPtr);
- }
-
- chan = Tcl_GetChannel(interp, objPtr->bytes, NULL);
if (chan == NULL) {
return TCL_ERROR;
}
TclFreeIntRep(objPtr);
- statePtr = ((Channel *) chan)->state;
- Tcl_Preserve(statePtr);
+ statePtr = ((Channel *)chan)->state;
+ Tcl_Preserve((ClientData) statePtr);
SET_CHANNELSTATE(objPtr, statePtr);
SET_CHANNELINTERP(objPtr, interp);
- objPtr->typePtr = &tclChannelType;
+ objPtr->typePtr = &chanObjType;
}
return TCL_OK;
}
@@ -11270,45 +10511,6 @@ SetChannelFromAny(
/*
*----------------------------------------------------------------------
*
- * UpdateStringOfChannel --
- *
- * Update the string representation for an object whose internal
- * representation is "Channel".
- *
- * Results:
- * None.
- *
- * Side effects:
- * The object's string may be set by converting its Unicode represention
- * to UTF format.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-UpdateStringOfChannel(
- Tcl_Obj *objPtr) /* Object with string rep to update. */
-{
- if (objPtr->bytes == NULL) {
- ChannelState *statePtr = GET_CHANNELSTATE(objPtr);
- const char *name = statePtr->channelName;
-
- if (name) {
- size_t len = strlen(name);
-
- objPtr->bytes = ckalloc(len + 1);
- objPtr->length = len;
- memcpy(objPtr->bytes, name, len);
- } else {
- objPtr->bytes = tclEmptyStringRep;
- objPtr->length = 0;
- }
- }
-}
-
-/*
- *----------------------------------------------------------------------
- *
* FreeChannelIntRep --
*
* Release statePtr storage.
@@ -11326,8 +10528,7 @@ static void
FreeChannelIntRep(
Tcl_Obj *objPtr) /* Object with internal rep to free. */
{
- Tcl_Release(GET_CHANNELSTATE(objPtr));
- objPtr->typePtr = NULL;
+ Tcl_Release((ClientData) GET_CHANNELSTATE(objPtr));
}
#if 0
@@ -11344,27 +10545,21 @@ DumpFlags(
char buf[20];
int i = 0;
-#define ChanFlag(chr, bit) (buf[i++] = ((flags & (bit)) ? (chr) : '_'))
+#define ChanFlag(chr,bit) (buf[i++] = ((flags & (bit)) ? (chr) : '_'))
ChanFlag('r', TCL_READABLE);
ChanFlag('w', TCL_WRITABLE);
ChanFlag('n', CHANNEL_NONBLOCKING);
ChanFlag('l', CHANNEL_LINEBUFFERED);
ChanFlag('u', CHANNEL_UNBUFFERED);
- ChanFlag('R', BUFFER_READY);
ChanFlag('F', BG_FLUSH_SCHEDULED);
ChanFlag('c', CHANNEL_CLOSED);
ChanFlag('E', CHANNEL_EOF);
ChanFlag('S', CHANNEL_STICKY_EOF);
ChanFlag('B', CHANNEL_BLOCKED);
ChanFlag('/', INPUT_SAW_CR);
- ChanFlag('*', INPUT_NEED_NL);
ChanFlag('D', CHANNEL_DEAD);
ChanFlag('R', CHANNEL_RAW_MODE);
-#ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
- ChanFlag('T', CHANNEL_TIMER_FEV);
- ChanFlag('H', CHANNEL_HAS_MORE_DATA);
-#endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
ChanFlag('x', CHANNEL_INCLOSE);
buf[i] ='\0';
@@ -11380,5 +10575,6 @@ DumpFlags(
* c-basic-offset: 4
* fill-column: 78
* tab-width: 8
+ * indent-tabs-mode: nil
* End:
*/