tclIO.c

Go to the documentation of this file.
00001 /*
00002  * tclIO.c --
00003  *
00004  *      This file provides the generic portions (those that are the same on
00005  *      all platforms and for all channel types) of Tcl's IO facilities.
00006  *
00007  * Copyright (c) 1998-2000 Ajuba Solutions
00008  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
00009  *
00010  * See the file "license.terms" for information on usage and redistribution of
00011  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00012  *
00013  * RCS: @(#) $Id: tclIO.c,v 1.137 2008/01/20 21:16:15 hobbs Exp $
00014  */
00015 
00016 #include "tclInt.h"
00017 #include "tclIO.h"
00018 #include <assert.h>
00019 
00020 /*
00021  * All static variables used in this file are collected into a single instance
00022  * of the following structure. For multi-threaded implementations, there is
00023  * one instance of this structure for each thread.
00024  *
00025  * Notice that different structures with the same name appear in other files.
00026  * The structure defined below is used in this file only.
00027  */
00028 
00029 typedef struct ThreadSpecificData {
00030     NextChannelHandler *nestedHandlerPtr;
00031                                 /* This variable holds the list of nested
00032                                  * ChannelHandlerEventProc invocations. */
00033     ChannelState *firstCSPtr;   /* List of all channels currently open,
00034                                  * indexed by ChannelState, as only one
00035                                  * ChannelState exists per set of stacked
00036                                  * channels. */
00037     Tcl_Channel stdinChannel;   /* Static variable for the stdin channel. */
00038     int stdinInitialized;
00039     Tcl_Channel stdoutChannel;  /* Static variable for the stdout channel. */
00040     int stdoutInitialized;
00041     Tcl_Channel stderrChannel;  /* Static variable for the stderr channel. */
00042     int stderrInitialized;
00043     Tcl_Encoding binaryEncoding;
00044 } ThreadSpecificData;
00045 
00046 static Tcl_ThreadDataKey dataKey;
00047 
00048 /*
00049  * Static functions in this file:
00050  */
00051 
00052 static ChannelBuffer *  AllocChannelBuffer(int length);
00053 static void             ChannelTimerProc(ClientData clientData);
00054 static int              CheckChannelErrors(ChannelState *statePtr,
00055                             int direction);
00056 static int              CheckFlush(Channel *chanPtr, ChannelBuffer *bufPtr,
00057                             int newlineFlag);
00058 static int              CheckForDeadChannel(Tcl_Interp *interp,
00059                             ChannelState *statePtr);
00060 static void             CheckForStdChannelsBeingClosed(Tcl_Channel chan);
00061 static void             CleanupChannelHandlers(Tcl_Interp *interp,
00062                             Channel *chanPtr);
00063 static int              CloseChannel(Tcl_Interp *interp, Channel *chanPtr,
00064                             int errorCode);
00065 static void             CommonGetsCleanup(Channel *chanPtr);
00066 static int              CopyAndTranslateBuffer(ChannelState *statePtr,
00067                             char *result, int space);
00068 static int              CopyBuffer(Channel *chanPtr, char *result, int space);
00069 static int              CopyData(CopyState *csPtr, int mask);
00070 static void             CopyEventProc(ClientData clientData, int mask);
00071 static void             CreateScriptRecord(Tcl_Interp *interp,
00072                             Channel *chanPtr, int mask, Tcl_Obj *scriptPtr);
00073 static void             DeleteChannelTable(ClientData clientData,
00074                             Tcl_Interp *interp);
00075 static void             DeleteScriptRecord(Tcl_Interp *interp,
00076                             Channel *chanPtr, int mask);
00077 static int              DetachChannel(Tcl_Interp *interp, Tcl_Channel chan);
00078 static void             DiscardInputQueued(ChannelState *statePtr,
00079                             int discardSavedBuffers);
00080 static void             DiscardOutputQueued(ChannelState *chanPtr);
00081 static int              DoRead(Channel *chanPtr, char *srcPtr, int slen);
00082 static int              DoWrite(Channel *chanPtr, const char *src, int srcLen);
00083 static int              DoReadChars(Channel *chan, Tcl_Obj *objPtr, int toRead,
00084                             int appendFlag);
00085 static int              DoWriteChars(Channel *chan, const char *src, int len);
00086 static int              FilterInputBytes(Channel *chanPtr,
00087                             GetsState *statePtr);
00088 static int              FlushChannel(Tcl_Interp *interp, Channel *chanPtr,
00089                             int calledFromAsyncFlush);
00090 static int              TclGetsObjBinary(Tcl_Channel chan, Tcl_Obj *objPtr);
00091 static void             FreeBinaryEncoding(ClientData clientData);
00092 static Tcl_HashTable *  GetChannelTable(Tcl_Interp *interp);
00093 static int              GetInput(Channel *chanPtr);
00094 static int              HaveVersion(const Tcl_ChannelType *typePtr,
00095                             Tcl_ChannelTypeVersion minimumVersion);
00096 static void             PeekAhead(Channel *chanPtr, char **dstEndPtr,
00097                             GetsState *gsPtr);
00098 static int              ReadBytes(ChannelState *statePtr, Tcl_Obj *objPtr,
00099                             int charsLeft, int *offsetPtr);
00100 static int              ReadChars(ChannelState *statePtr, Tcl_Obj *objPtr,
00101                             int charsLeft, int *offsetPtr, int *factorPtr);
00102 static void             RecycleBuffer(ChannelState *statePtr,
00103                             ChannelBuffer *bufPtr, int mustDiscard);
00104 static int              StackSetBlockMode(Channel *chanPtr, int mode);
00105 static int              SetBlockMode(Tcl_Interp *interp, Channel *chanPtr,
00106                             int mode);
00107 static void             StopCopy(CopyState *csPtr);
00108 static int              TranslateInputEOL(ChannelState *statePtr, char *dst,
00109                             const char *src, int *dstLenPtr, int *srcLenPtr);
00110 static int              TranslateOutputEOL(ChannelState *statePtr, char *dst,
00111                             const char *src, int *dstLenPtr, int *srcLenPtr);
00112 static void             UpdateInterest(Channel *chanPtr);
00113 static int              WriteBytes(Channel *chanPtr, const char *src,
00114                             int srcLen);
00115 static int              WriteChars(Channel *chanPtr, const char *src,
00116                             int srcLen);
00117 static Tcl_Obj *        FixLevelCode(Tcl_Obj *msg);
00118 static void             SpliceChannel(Tcl_Channel chan);
00119 static void             CutChannel(Tcl_Channel chan);
00120 
00121 /*
00122  * Simplifying helper macros. All may use their argument(s) multiple times.
00123  * The ANSI C "prototypes" for the macros are listed below, together with a
00124  * short description of what the macro does.
00125  *
00126  * --------------------------------------------------------------------------
00127  * int BytesLeft(ChannelBuffer *bufPtr)
00128  *
00129  *      Returns the number of bytes of data remaining in the buffer.
00130  *
00131  * int SpaceLeft(ChannelBuffer *bufPtr)
00132  *
00133  *      Returns the number of bytes of space remaining at the end of the
00134  *      buffer.
00135  *
00136  * int IsBufferReady(ChannelBuffer *bufPtr)
00137  *
00138  *      Returns whether a buffer has bytes available within it.
00139  *
00140  * int IsBufferEmpty(ChannelBuffer *bufPtr)
00141  *
00142  *      Returns whether a buffer is entirely empty. Note that this is not the
00143  *      inverse of the above operation; trying to merge the two seems to lead
00144  *      to occasional crashes...
00145  *
00146  * int IsBufferFull(ChannelBuffer *bufPtr)
00147  *
00148  *      Returns whether more data can be added to a buffer.
00149  *
00150  * int IsBufferOverflowing(ChannelBuffer *bufPtr)
00151  *
00152  *      Returns whether a buffer has more data in it than it should.
00153  *
00154  * char *InsertPoint(ChannelBuffer *bufPtr)
00155  *
00156  *      Returns a pointer to where characters should be added to the buffer.
00157  *
00158  * char *RemovePoint(ChannelBuffer *bufPtr)
00159  *
00160  *      Returns a pointer to where characters should be removed from the
00161  *      buffer.
00162  * --------------------------------------------------------------------------
00163  */
00164 
00165 #define BytesLeft(bufPtr) ((bufPtr)->nextAdded - (bufPtr)->nextRemoved)
00166 
00167 #define SpaceLeft(bufPtr) ((bufPtr)->bufLength - (bufPtr)->nextAdded)
00168 
00169 #define IsBufferReady(bufPtr) ((bufPtr)->nextAdded > (bufPtr)->nextRemoved)
00170 
00171 #define IsBufferEmpty(bufPtr) ((bufPtr)->nextAdded == (bufPtr)->nextRemoved)
00172 
00173 #define IsBufferFull(bufPtr) ((bufPtr)->nextAdded >= (bufPtr)->bufLength)
00174 
00175 #define IsBufferOverflowing(bufPtr) ((bufPtr)->nextAdded > (bufPtr)->bufLength)
00176 
00177 #define InsertPoint(bufPtr) ((bufPtr)->buf + (bufPtr)->nextAdded)
00178 
00179 #define RemovePoint(bufPtr) ((bufPtr)->buf + (bufPtr)->nextRemoved)
00180 
00181 /*
00182  * For working with channel state flag bits.
00183  */
00184 
00185 #define SetFlag(statePtr, flag)         ((statePtr)->flags |= (flag))
00186 #define ResetFlag(statePtr, flag)       ((statePtr)->flags &= ~(flag))
00187 
00188 /*
00189  * Macro for testing whether a string (in optionName, length len) matches a
00190  * value (prefix matching rules). Arguments are the minimum length to match
00191  * and the value to match against. (Can't use Tcl_GetIndexFromObj as this is
00192  * used in a situation where no objects are available.)
00193  */
00194 
00195 #define HaveOpt(minLength, nameString) \
00196         ((len > (minLength)) && (optionName[1] == (nameString)[1]) \
00197                 && (strncmp(optionName, (nameString), len) == 0))
00198 
00199 /*
00200  * The ChannelObjType type.  We actually store the ChannelState structure
00201  * as that lives longest and we want to return the bottomChanPtr when
00202  * requested (consistent with Tcl_GetChannel).  The setFromAny and
00203  * updateString can be NULL as they should not be called.
00204  */
00205 
00206 static void             DupChannelIntRep(Tcl_Obj *objPtr, Tcl_Obj *copyPtr);
00207 static int              SetChannelFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
00208 static void             UpdateStringOfChannel(Tcl_Obj *objPtr);
00209 static void             FreeChannelIntRep(Tcl_Obj *objPtr);
00210 
00211 static Tcl_ObjType tclChannelType = {
00212     "channel",                  /* name for this type */
00213     FreeChannelIntRep,          /* freeIntRepProc */
00214     DupChannelIntRep,           /* dupIntRepProc */
00215     NULL,                       /* updateStringProc UpdateStringOfChannel */
00216     NULL                        /* setFromAnyProc SetChannelFromAny */
00217 };
00218 
00219 #define GET_CHANNELSTATE(objPtr) \
00220     ((ChannelState *) (objPtr)->internalRep.otherValuePtr)
00221 #define SET_CHANNELSTATE(objPtr, storePtr) \
00222     ((objPtr)->internalRep.otherValuePtr = (void *) (storePtr))
00223 
00224 
00225 /*
00226  *---------------------------------------------------------------------------
00227  *
00228  * TclInitIOSubsystem --
00229  *
00230  *      Initialize all resources used by this subsystem on a per-process
00231  *      basis.
00232  *
00233  * Results:
00234  *      None.
00235  *
00236  * Side effects:
00237  *      Depends on the memory subsystems.
00238  *
00239  *---------------------------------------------------------------------------
00240  */
00241 
00242 void
00243 TclInitIOSubsystem(void)
00244 {
00245     /*
00246      * By fetching thread local storage we take care of allocating it for each
00247      * thread.
00248      */
00249 
00250     (void) TCL_TSD_INIT(&dataKey);
00251 }
00252 
00253 /*
00254  *-------------------------------------------------------------------------
00255  *
00256  * TclFinalizeIOSubsystem --
00257  *
00258  *      Releases all resources used by this subsystem on a per-process basis.
00259  *      Closes all extant channels that have not already been closed because
00260  *      they were not owned by any interp.
00261  *
00262  * Results:
00263  *      None.
00264  *
00265  * Side effects:
00266  *      Depends on encoding and memory subsystems.
00267  *
00268  *-------------------------------------------------------------------------
00269  */
00270 
00271         /* ARGSUSED */
00272 void
00273 TclFinalizeIOSubsystem(void)
00274 {
00275     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00276     Channel *chanPtr = NULL;    /* Iterates over open channels. */
00277     ChannelState *statePtr;     /* State of channel stack */
00278     int active = 1;             /* Flag == 1 while there's still work to do */
00279 
00280     /*
00281      * Walk all channel state structures known to this thread and close
00282      * corresponding channels.
00283      */
00284 
00285     while (active) {
00286         /*
00287          * Iterate through the open channel list, and find the first channel
00288          * that isn't dead. We start from the head of the list each time,
00289          * because the close action on one channel can close others.
00290          */
00291 
00292         active = 0;
00293         for (statePtr = tsdPtr->firstCSPtr;
00294                 statePtr != NULL;
00295                 statePtr = statePtr->nextCSPtr) {
00296             chanPtr = statePtr->topChanPtr;
00297             if (!(statePtr->flags & CHANNEL_DEAD)) {
00298                 active = 1;
00299                 break;
00300             }
00301         }
00302 
00303         /*
00304          * We've found a live channel. Close it.
00305          */
00306 
00307         if (active) {
00308             /*
00309              * Set the channel back into blocking mode to ensure that we wait
00310              * for all data to flush out.
00311              */
00312 
00313             (void) Tcl_SetChannelOption(NULL, (Tcl_Channel) chanPtr,
00314                     "-blocking", "on");
00315 
00316             if ((chanPtr == (Channel *) tsdPtr->stdinChannel) ||
00317                     (chanPtr == (Channel *) tsdPtr->stdoutChannel) ||
00318                     (chanPtr == (Channel *) tsdPtr->stderrChannel)) {
00319                 /*
00320                  * Decrement the refcount which was earlier artificially
00321                  * bumped up to keep the channel from being closed.
00322                  */
00323 
00324                 statePtr->refCount--;
00325             }
00326 
00327             if (statePtr->refCount <= 0) {
00328                 /*
00329                  * Close it only if the refcount indicates that the channel is
00330                  * not referenced from any interpreter. If it is, that
00331                  * interpreter will close the channel when it gets destroyed.
00332                  */
00333 
00334                 (void) Tcl_Close(NULL, (Tcl_Channel) chanPtr);
00335             } else {
00336                 /*
00337                  * The refcount is greater than zero, so flush the channel.
00338                  */
00339 
00340                 Tcl_Flush((Tcl_Channel) chanPtr);
00341 
00342                 /*
00343                  * Call the device driver to actually close the underlying
00344                  * device for this channel.
00345                  */
00346 
00347                 if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) {
00348                     (chanPtr->typePtr->closeProc)(chanPtr->instanceData, NULL);
00349                 } else {
00350                     (chanPtr->typePtr->close2Proc)(chanPtr->instanceData,
00351                             NULL, 0);
00352                 }
00353 
00354                 /*
00355                  * Finally, we clean up the fields in the channel data
00356                  * structure since all of them have been deleted already. We
00357                  * mark the channel with CHANNEL_DEAD to prevent any further
00358                  * IO operations on it.
00359                  */
00360 
00361                 chanPtr->instanceData = NULL;
00362                 SetFlag(statePtr, CHANNEL_DEAD);
00363             }
00364         }
00365     }
00366 
00367     TclpFinalizeSockets();
00368     TclpFinalizePipes();
00369 }
00370 
00371 /*
00372  *----------------------------------------------------------------------
00373  *
00374  * Tcl_SetStdChannel --
00375  *
00376  *      This function is used to change the channels that are used for
00377  *      stdin/stdout/stderr in new interpreters.
00378  *
00379  * Results:
00380  *      None
00381  *
00382  * Side effects:
00383  *      None.
00384  *
00385  *----------------------------------------------------------------------
00386  */
00387 
00388 void
00389 Tcl_SetStdChannel(
00390     Tcl_Channel channel,
00391     int type)                   /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
00392 {
00393     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00394     switch (type) {
00395     case TCL_STDIN:
00396         tsdPtr->stdinInitialized = 1;
00397         tsdPtr->stdinChannel = channel;
00398         break;
00399     case TCL_STDOUT:
00400         tsdPtr->stdoutInitialized = 1;
00401         tsdPtr->stdoutChannel = channel;
00402         break;
00403     case TCL_STDERR:
00404         tsdPtr->stderrInitialized = 1;
00405         tsdPtr->stderrChannel = channel;
00406         break;
00407     }
00408 }
00409 
00410 /*
00411  *----------------------------------------------------------------------
00412  *
00413  * Tcl_GetStdChannel --
00414  *
00415  *      Returns the specified standard channel.
00416  *
00417  * Results:
00418  *      Returns the specified standard channel, or NULL.
00419  *
00420  * Side effects:
00421  *      May cause the creation of a standard channel and the underlying file.
00422  *
00423  *----------------------------------------------------------------------
00424  */
00425 
00426 Tcl_Channel
00427 Tcl_GetStdChannel(
00428     int type)                   /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
00429 {
00430     Tcl_Channel channel = NULL;
00431     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00432 
00433     /*
00434      * If the channels were not created yet, create them now and store them in
00435      * the static variables.
00436      */
00437 
00438     switch (type) {
00439     case TCL_STDIN:
00440         if (!tsdPtr->stdinInitialized) {
00441             tsdPtr->stdinChannel = TclpGetDefaultStdChannel(TCL_STDIN);
00442             tsdPtr->stdinInitialized = 1;
00443 
00444             /*
00445              * Artificially bump the refcount to ensure that the channel is
00446              * only closed on exit.
00447              *
00448              * NOTE: Must only do this if stdinChannel is not NULL. It can be
00449              * NULL in situations where Tcl is unable to connect to the
00450              * standard input.
00451              */
00452 
00453             if (tsdPtr->stdinChannel != NULL) {
00454                 Tcl_RegisterChannel(NULL, tsdPtr->stdinChannel);
00455             }
00456         }
00457         channel = tsdPtr->stdinChannel;
00458         break;
00459     case TCL_STDOUT:
00460         if (!tsdPtr->stdoutInitialized) {
00461             tsdPtr->stdoutChannel = TclpGetDefaultStdChannel(TCL_STDOUT);
00462             tsdPtr->stdoutInitialized = 1;
00463             if (tsdPtr->stdoutChannel != NULL) {
00464                 Tcl_RegisterChannel(NULL, tsdPtr->stdoutChannel);
00465             }
00466         }
00467         channel = tsdPtr->stdoutChannel;
00468         break;
00469     case TCL_STDERR:
00470         if (!tsdPtr->stderrInitialized) {
00471             tsdPtr->stderrChannel = TclpGetDefaultStdChannel(TCL_STDERR);
00472             tsdPtr->stderrInitialized = 1;
00473             if (tsdPtr->stderrChannel != NULL) {
00474                 Tcl_RegisterChannel(NULL, tsdPtr->stderrChannel);
00475             }
00476         }
00477         channel = tsdPtr->stderrChannel;
00478         break;
00479     }
00480     return channel;
00481 }
00482 
00483 /*
00484  *----------------------------------------------------------------------
00485  *
00486  * Tcl_CreateCloseHandler
00487  *
00488  *      Creates a close callback which will be called when the channel is
00489  *      closed.
00490  *
00491  * Results:
00492  *      None.
00493  *
00494  * Side effects:
00495  *      Causes the callback to be called in the future when the channel will
00496  *      be closed.
00497  *
00498  *----------------------------------------------------------------------
00499  */
00500 
00501 void
00502 Tcl_CreateCloseHandler(
00503     Tcl_Channel chan,           /* The channel for which to create the close
00504                                  * callback. */
00505     Tcl_CloseProc *proc,        /* The callback routine to call when the
00506                                  * channel will be closed. */
00507     ClientData clientData)      /* Arbitrary data to pass to the close
00508                                  * callback. */
00509 {
00510     ChannelState *statePtr;
00511     CloseCallback *cbPtr;
00512 
00513     statePtr = ((Channel *) chan)->state;
00514 
00515     cbPtr = (CloseCallback *) ckalloc(sizeof(CloseCallback));
00516     cbPtr->proc = proc;
00517     cbPtr->clientData = clientData;
00518 
00519     cbPtr->nextPtr = statePtr->closeCbPtr;
00520     statePtr->closeCbPtr = cbPtr;
00521 }
00522 
00523 /*
00524  *----------------------------------------------------------------------
00525  *
00526  * Tcl_DeleteCloseHandler --
00527  *
00528  *      Removes a callback that would have been called on closing the channel.
00529  *      If there is no matching callback then this function has no effect.
00530  *
00531  * Results:
00532  *      None.
00533  *
00534  * Side effects:
00535  *      The callback will not be called in the future when the channel is
00536  *      eventually closed.
00537  *
00538  *----------------------------------------------------------------------
00539  */
00540 
00541 void
00542 Tcl_DeleteCloseHandler(
00543     Tcl_Channel chan,           /* The channel for which to cancel the close
00544                                  * callback. */
00545     Tcl_CloseProc *proc,        /* The procedure for the callback to
00546                                  * remove. */
00547     ClientData clientData)      /* The callback data for the callback to
00548                                  * remove. */
00549 {
00550     ChannelState *statePtr;
00551     CloseCallback *cbPtr, *cbPrevPtr;
00552 
00553     statePtr = ((Channel *) chan)->state;
00554     for (cbPtr = statePtr->closeCbPtr, cbPrevPtr = NULL;
00555             cbPtr != NULL; cbPtr = cbPtr->nextPtr) {
00556         if ((cbPtr->proc == proc) && (cbPtr->clientData == clientData)) {
00557             if (cbPrevPtr == NULL) {
00558                 statePtr->closeCbPtr = cbPtr->nextPtr;
00559             }
00560             ckfree((char *) cbPtr);
00561             break;
00562         } else {
00563             cbPrevPtr = cbPtr;
00564         }
00565     }
00566 }
00567 
00568 /*
00569  *----------------------------------------------------------------------
00570  *
00571  * GetChannelTable --
00572  *
00573  *      Gets and potentially initializes the channel table for an interpreter.
00574  *      If it is initializing the table it also inserts channels for stdin,
00575  *      stdout and stderr if the interpreter is trusted.
00576  *
00577  * Results:
00578  *      A pointer to the hash table created, for use by the caller.
00579  *
00580  * Side effects:
00581  *      Initializes the channel table for an interpreter. May create channels
00582  *      for stdin, stdout and stderr.
00583  *
00584  *----------------------------------------------------------------------
00585  */
00586 
00587 static Tcl_HashTable *
00588 GetChannelTable(
00589     Tcl_Interp *interp)
00590 {
00591     Tcl_HashTable *hTblPtr;     /* Hash table of channels. */
00592     Tcl_Channel stdinChan, stdoutChan, stderrChan;
00593 
00594     hTblPtr = Tcl_GetAssocData(interp, "tclIO", NULL);
00595     if (hTblPtr == NULL) {
00596         hTblPtr = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable));
00597         Tcl_InitHashTable(hTblPtr, TCL_STRING_KEYS);
00598         Tcl_SetAssocData(interp, "tclIO",
00599                 (Tcl_InterpDeleteProc *) DeleteChannelTable, hTblPtr);
00600 
00601         /*
00602          * If the interpreter is trusted (not "safe"), insert channels for
00603          * stdin, stdout and stderr (possibly creating them in the process).
00604          */
00605 
00606         if (Tcl_IsSafe(interp) == 0) {
00607             stdinChan = Tcl_GetStdChannel(TCL_STDIN);
00608             if (stdinChan != NULL) {
00609                 Tcl_RegisterChannel(interp, stdinChan);
00610             }
00611             stdoutChan = Tcl_GetStdChannel(TCL_STDOUT);
00612             if (stdoutChan != NULL) {
00613                 Tcl_RegisterChannel(interp, stdoutChan);
00614             }
00615             stderrChan = Tcl_GetStdChannel(TCL_STDERR);
00616             if (stderrChan != NULL) {
00617                 Tcl_RegisterChannel(interp, stderrChan);
00618             }
00619         }
00620     }
00621     return hTblPtr;
00622 }
00623 
00624 /*
00625  *----------------------------------------------------------------------
00626  *
00627  * DeleteChannelTable --
00628  *
00629  *      Deletes the channel table for an interpreter, closing any open
00630  *      channels whose refcount reaches zero. This procedure is invoked when
00631  *      an interpreter is deleted, via the AssocData cleanup mechanism.
00632  *
00633  * Results:
00634  *      None.
00635  *
00636  * Side effects:
00637  *      Deletes the hash table of channels. May close channels. May flush
00638  *      output on closed channels. Removes any channeEvent handlers that were
00639  *      registered in this interpreter.
00640  *
00641  *----------------------------------------------------------------------
00642  */
00643 
00644 static void
00645 DeleteChannelTable(
00646     ClientData clientData,      /* The per-interpreter data structure. */
00647     Tcl_Interp *interp)         /* The interpreter being deleted. */
00648 {
00649     Tcl_HashTable *hTblPtr;     /* The hash table. */
00650     Tcl_HashSearch hSearch;     /* Search variable. */
00651     Tcl_HashEntry *hPtr;        /* Search variable. */
00652     Channel *chanPtr;           /* Channel being deleted. */
00653     ChannelState *statePtr;     /* State of Channel being deleted. */
00654     EventScriptRecord *sPtr, *prevPtr, *nextPtr;
00655                                 /* Variables to loop over all channel events
00656                                  * registered, to delete the ones that refer
00657                                  * to the interpreter being deleted. */
00658 
00659     /*
00660      * Delete all the registered channels - this will close channels whose
00661      * refcount reaches zero.
00662      */
00663 
00664     hTblPtr = clientData;
00665     for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch); hPtr != NULL;
00666             hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch)) {
00667         chanPtr = Tcl_GetHashValue(hPtr);
00668         statePtr = chanPtr->state;
00669 
00670         /*
00671          * Remove any fileevents registered in this interpreter.
00672          */
00673 
00674         for (sPtr = statePtr->scriptRecordPtr, prevPtr = NULL;
00675                 sPtr != NULL; sPtr = nextPtr) {
00676             nextPtr = sPtr->nextPtr;
00677             if (sPtr->interp == interp) {
00678                 if (prevPtr == NULL) {
00679                     statePtr->scriptRecordPtr = nextPtr;
00680                 } else {
00681                     prevPtr->nextPtr = nextPtr;
00682                 }
00683 
00684                 Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
00685                         TclChannelEventScriptInvoker, (ClientData) sPtr);
00686 
00687                 TclDecrRefCount(sPtr->scriptPtr);
00688                 ckfree((char *) sPtr);
00689             } else {
00690                 prevPtr = sPtr;
00691             }
00692         }
00693 
00694         /*
00695          * Cannot call Tcl_UnregisterChannel because that procedure calls
00696          * Tcl_GetAssocData to get the channel table, which might already be
00697          * inaccessible from the interpreter structure. Instead, we emulate
00698          * the behavior of Tcl_UnregisterChannel directly here.
00699          */
00700 
00701         Tcl_DeleteHashEntry(hPtr);
00702         SetFlag(statePtr, CHANNEL_TAINTED);
00703         statePtr->refCount--;
00704         if (statePtr->refCount <= 0) {
00705             if (!(statePtr->flags & BG_FLUSH_SCHEDULED)) {
00706                 (void) Tcl_Close(interp, (Tcl_Channel) chanPtr);
00707             }
00708         }
00709 
00710     }
00711     Tcl_DeleteHashTable(hTblPtr);
00712     ckfree((char *) hTblPtr);
00713 }
00714 
00715 /*
00716  *----------------------------------------------------------------------
00717  *
00718  * CheckForStdChannelsBeingClosed --
00719  *
00720  *      Perform special handling for standard channels being closed. When
00721  *      given a standard channel, if the refcount is now 1, it means that the
00722  *      last reference to the standard channel is being explicitly closed. Now
00723  *      bump the refcount artificially down to 0, to ensure the normal
00724  *      handling of channels being closed will occur. Also reset the static
00725  *      pointer to the channel to NULL, to avoid dangling references.
00726  *
00727  * Results:
00728  *      None.
00729  *
00730  * Side effects:
00731  *      Manipulates the refcount on standard channels. May smash the global
00732  *      static pointer to a standard channel.
00733  *
00734  *----------------------------------------------------------------------
00735  */
00736 
00737 static void
00738 CheckForStdChannelsBeingClosed(
00739     Tcl_Channel chan)
00740 {
00741     ChannelState *statePtr = ((Channel *) chan)->state;
00742     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00743 
00744     if ((chan == tsdPtr->stdinChannel) && (tsdPtr->stdinInitialized)) {
00745         if (statePtr->refCount < 2) {
00746             statePtr->refCount = 0;
00747             tsdPtr->stdinChannel = NULL;
00748             return;
00749         }
00750     } else if ((chan == tsdPtr->stdoutChannel)
00751             && (tsdPtr->stdoutInitialized)) {
00752         if (statePtr->refCount < 2) {
00753             statePtr->refCount = 0;
00754             tsdPtr->stdoutChannel = NULL;
00755             return;
00756         }
00757     } else if ((chan == tsdPtr->stderrChannel)
00758             && (tsdPtr->stderrInitialized)) {
00759         if (statePtr->refCount < 2) {
00760             statePtr->refCount = 0;
00761             tsdPtr->stderrChannel = NULL;
00762             return;
00763         }
00764     }
00765 }
00766 
00767 /*
00768  *----------------------------------------------------------------------
00769  *
00770  * Tcl_IsStandardChannel --
00771  *
00772  *      Test if the given channel is a standard channel. No attempt is made to
00773  *      check if the channel or the standard channels are initialized or
00774  *      otherwise valid.
00775  *
00776  * Results:
00777  *      Returns 1 if true, 0 if false.
00778  *
00779  * Side effects:
00780  *      None.
00781  *
00782  *----------------------------------------------------------------------
00783  */
00784 
00785 int
00786 Tcl_IsStandardChannel(
00787     Tcl_Channel chan)           /* Channel to check. */
00788 {
00789     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00790 
00791     if ((chan == tsdPtr->stdinChannel)
00792             || (chan == tsdPtr->stdoutChannel)
00793             || (chan == tsdPtr->stderrChannel)) {
00794         return 1;
00795     } else {
00796         return 0;
00797     }
00798 }
00799 
00800 /*
00801  *----------------------------------------------------------------------
00802  *
00803  * Tcl_RegisterChannel --
00804  *
00805  *      Adds an already-open channel to the channel table of an interpreter.
00806  *      If the interpreter passed as argument is NULL, it only increments the
00807  *      channel refCount.
00808  *
00809  * Results:
00810  *      None.
00811  *
00812  * Side effects:
00813  *      May increment the reference count of a channel.
00814  *
00815  *----------------------------------------------------------------------
00816  */
00817 
00818 void
00819 Tcl_RegisterChannel(
00820     Tcl_Interp *interp,         /* Interpreter in which to add the channel. */
00821     Tcl_Channel chan)           /* The channel to add to this interpreter
00822                                  * channel table. */
00823 {
00824     Tcl_HashTable *hTblPtr;     /* Hash table of channels. */
00825     Tcl_HashEntry *hPtr;        /* Search variable. */
00826     int isNew;                  /* Is the hash entry new or does it exist? */
00827     Channel *chanPtr;           /* The actual channel. */
00828     ChannelState *statePtr;     /* State of the actual channel. */
00829 
00830     /*
00831      * Always (un)register bottom-most channel in the stack. This makes
00832      * management of the channel list easier because no manipulation is
00833      * necessary during (un)stack operation.
00834      */
00835 
00836     chanPtr = ((Channel *) chan)->state->bottomChanPtr;
00837     statePtr = chanPtr->state;
00838 
00839     if (statePtr->channelName == NULL) {
00840         Tcl_Panic("Tcl_RegisterChannel: channel without name");
00841     }
00842     if (interp != NULL) {
00843         hTblPtr = GetChannelTable(interp);
00844         hPtr = Tcl_CreateHashEntry(hTblPtr, statePtr->channelName, &isNew);
00845         if (!isNew) {
00846             if (chan == Tcl_GetHashValue(hPtr)) {
00847                 return;
00848             }
00849 
00850             Tcl_Panic("Tcl_RegisterChannel: duplicate channel names");
00851         }
00852         Tcl_SetHashValue(hPtr, chanPtr);
00853     }
00854     statePtr->refCount++;
00855 }
00856 
00857 /*
00858  *----------------------------------------------------------------------
00859  *
00860  * Tcl_UnregisterChannel --
00861  *
00862  *      Deletes the hash entry for a channel associated with an interpreter.
00863  *      If the interpreter given as argument is NULL, it only decrements the
00864  *      reference count. (This all happens in the Tcl_DetachChannel helper
00865  *      function).
00866  *
00867  *      Finally, if the reference count of the channel drops to zero, it is
00868  *      deleted.
00869  *
00870  * Results:
00871  *      A standard Tcl result.
00872  *
00873  * Side effects:
00874  *      Calls Tcl_DetachChannel which deletes the hash entry for a channel
00875  *      associated with an interpreter.
00876  *
00877  *      May delete the channel, which can have a variety of consequences,
00878  *      especially if we are forced to close the channel.
00879  *
00880  *----------------------------------------------------------------------
00881  */
00882 
00883 int
00884 Tcl_UnregisterChannel(
00885     Tcl_Interp *interp,         /* Interpreter in which channel is defined. */
00886     Tcl_Channel chan)           /* Channel to delete. */
00887 {
00888     ChannelState *statePtr;     /* State of the real channel. */
00889 
00890     statePtr = ((Channel *) chan)->state->bottomChanPtr->state;
00891 
00892     if (statePtr->flags & CHANNEL_INCLOSE) {
00893         if (interp != NULL) {
00894             Tcl_AppendResult(interp, "Illegal recursive call to close "
00895                     "through close-handler of channel", NULL);
00896         }
00897         return TCL_ERROR;
00898     }
00899 
00900     if (DetachChannel(interp, chan) != TCL_OK) {
00901         return TCL_OK;
00902     }
00903 
00904     statePtr = ((Channel *) chan)->state->bottomChanPtr->state;
00905 
00906     /*
00907      * Perform special handling for standard channels being closed. If the
00908      * refCount is now 1 it means that the last reference to the standard
00909      * channel is being explicitly closed, so bump the refCount down
00910      * artificially to 0. This will ensure that the channel is actually
00911      * closed, below. Also set the static pointer to NULL for the channel.
00912      */
00913 
00914     CheckForStdChannelsBeingClosed(chan);
00915 
00916     /*
00917      * If the refCount reached zero, close the actual channel.
00918      */
00919 
00920     if (statePtr->refCount <= 0) {
00921         /*
00922          * Ensure that if there is another buffer, it gets flushed whether or
00923          * not we are doing a background flush.
00924          */
00925 
00926         if ((statePtr->curOutPtr != NULL) &&
00927                 IsBufferReady(statePtr->curOutPtr)) {
00928             SetFlag(statePtr, BUFFER_READY);
00929         }
00930         Tcl_Preserve((ClientData)statePtr);
00931         if (!(statePtr->flags & BG_FLUSH_SCHEDULED)) {
00932             /*
00933              * We don't want to re-enter Tcl_Close().
00934              */
00935 
00936             if (!(statePtr->flags & CHANNEL_CLOSED)) {
00937                 if (Tcl_Close(interp, chan) != TCL_OK) {
00938                     SetFlag(statePtr, CHANNEL_CLOSED);
00939                     Tcl_Release((ClientData)statePtr);
00940                     return TCL_ERROR;
00941                 }
00942             }
00943         }
00944         SetFlag(statePtr, CHANNEL_CLOSED);
00945         Tcl_Release((ClientData)statePtr);
00946     }
00947     return TCL_OK;
00948 }
00949 
00950 /*
00951  *----------------------------------------------------------------------
00952  *
00953  * Tcl_DetachChannel --
00954  *
00955  *      Deletes the hash entry for a channel associated with an interpreter.
00956  *      If the interpreter given as argument is NULL, it only decrements the
00957  *      reference count. Even if the ref count drops to zero, the channel is
00958  *      NOT closed or cleaned up. This allows a channel to be detached from an
00959  *      interpreter and left in the same state it was in when it was
00960  *      originally returned by 'Tcl_OpenFileChannel', for example.
00961  *
00962  *      This function cannot be used on the standard channels, and will return
00963  *      TCL_ERROR if that is attempted.
00964  *
00965  *      This function should only be necessary for special purposes in which
00966  *      you need to generate a pristine channel from one that has already been
00967  *      used. All ordinary purposes will almost always want to use
00968  *      Tcl_UnregisterChannel instead.
00969  *
00970  *      Provided the channel is not attached to any other interpreter, it can
00971  *      then be closed with Tcl_Close, rather than with Tcl_UnregisterChannel.
00972  *
00973  * Results:
00974  *      A standard Tcl result. If the channel is not currently registered with
00975  *      the given interpreter, TCL_ERROR is returned, otherwise TCL_OK.
00976  *      However no error messages are left in the interp's result.
00977  *
00978  * Side effects:
00979  *      Deletes the hash entry for a channel associated with an interpreter.
00980  *
00981  *----------------------------------------------------------------------
00982  */
00983 
00984 int
00985 Tcl_DetachChannel(
00986     Tcl_Interp *interp,         /* Interpreter in which channel is defined. */
00987     Tcl_Channel chan)           /* Channel to delete. */
00988 {
00989     if (Tcl_IsStandardChannel(chan)) {
00990         return TCL_ERROR;
00991     }
00992 
00993     return DetachChannel(interp, chan);
00994 }
00995 
00996 /*
00997  *----------------------------------------------------------------------
00998  *
00999  * DetachChannel --
01000  *
01001  *      Deletes the hash entry for a channel associated with an interpreter.
01002  *      If the interpreter given as argument is NULL, it only decrements the
01003  *      reference count. Even if the ref count drops to zero, the channel is
01004  *      NOT closed or cleaned up. This allows a channel to be detached from an
01005  *      interpreter and left in the same state it was in when it was
01006  *      originally returned by 'Tcl_OpenFileChannel', for example.
01007  *
01008  * Results:
01009  *      A standard Tcl result. If the channel is not currently registered with
01010  *      the given interpreter, TCL_ERROR is returned, otherwise TCL_OK.
01011  *      However no error messages are left in the interp's result.
01012  *
01013  * Side effects:
01014  *      Deletes the hash entry for a channel associated with an interpreter.
01015  *
01016  *----------------------------------------------------------------------
01017  */
01018 
01019 static int
01020 DetachChannel(
01021     Tcl_Interp *interp,         /* Interpreter in which channel is defined. */
01022     Tcl_Channel chan)           /* Channel to delete. */
01023 {
01024     Tcl_HashTable *hTblPtr;     /* Hash table of channels. */
01025     Tcl_HashEntry *hPtr;        /* Search variable. */
01026     Channel *chanPtr;           /* The real IO channel. */
01027     ChannelState *statePtr;     /* State of the real channel. */
01028 
01029     /*
01030      * Always (un)register bottom-most channel in the stack. This makes
01031      * management of the channel list easier because no manipulation is
01032      * necessary during (un)stack operation.
01033      */
01034 
01035     chanPtr = ((Channel *) chan)->state->bottomChanPtr;
01036     statePtr = chanPtr->state;
01037 
01038     if (interp != NULL) {
01039         hTblPtr = Tcl_GetAssocData(interp, "tclIO", NULL);
01040         if (hTblPtr == NULL) {
01041             return TCL_ERROR;
01042         }
01043         hPtr = Tcl_FindHashEntry(hTblPtr, statePtr->channelName);
01044         if (hPtr == NULL) {
01045             return TCL_ERROR;
01046         }
01047         if ((Channel *) Tcl_GetHashValue(hPtr) != chanPtr) {
01048             return TCL_ERROR;
01049         }
01050         Tcl_DeleteHashEntry(hPtr);
01051         SetFlag(statePtr, CHANNEL_TAINTED);
01052 
01053         /*
01054          * Remove channel handlers that refer to this interpreter, so that
01055          * they will not be present if the actual close is delayed and more
01056          * events happen on the channel. This may occur if the channel is
01057          * shared between several interpreters, or if the channel has async
01058          * flushing active.
01059          */
01060 
01061         CleanupChannelHandlers(interp, chanPtr);
01062     }
01063 
01064     statePtr->refCount--;
01065 
01066     return TCL_OK;
01067 }
01068 
01069 /*
01070  *---------------------------------------------------------------------------
01071  *
01072  * Tcl_GetChannel --
01073  *
01074  *      Finds an existing Tcl_Channel structure by name in a given
01075  *      interpreter. This function is public because it is used by
01076  *      channel-type-specific functions.
01077  *
01078  * Results:
01079  *      A Tcl_Channel or NULL on failure. If failed, interp's result object
01080  *      contains an error message. *modePtr is filled with the modes in which
01081  *      the channel was opened.
01082  *
01083  * Side effects:
01084  *      None.
01085  *
01086  *---------------------------------------------------------------------------
01087  */
01088 
01089 Tcl_Channel
01090 Tcl_GetChannel(
01091     Tcl_Interp *interp,         /* Interpreter in which to find or create the
01092                                  * channel. */
01093     const char *chanName,       /* The name of the channel. */
01094     int *modePtr)               /* Where to store the mode in which the
01095                                  * channel was opened? Will contain an ORed
01096                                  * combination of TCL_READABLE and
01097                                  * TCL_WRITABLE, if non-NULL. */
01098 {
01099     Channel *chanPtr;           /* The actual channel. */
01100     Tcl_HashTable *hTblPtr;     /* Hash table of channels. */
01101     Tcl_HashEntry *hPtr;        /* Search variable. */
01102     const char *name;           /* Translated name. */
01103 
01104     /*
01105      * Substitute "stdin", etc. Note that even though we immediately find the
01106      * channel using Tcl_GetStdChannel, we still need to look it up in the
01107      * specified interpreter to ensure that it is present in the channel
01108      * table. Otherwise, safe interpreters would always have access to the
01109      * standard channels.
01110      */
01111 
01112     name = chanName;
01113     if ((chanName[0] == 's') && (chanName[1] == 't')) {
01114         chanPtr = NULL;
01115         if (strcmp(chanName, "stdin") == 0) {
01116             chanPtr = (Channel *) Tcl_GetStdChannel(TCL_STDIN);
01117         } else if (strcmp(chanName, "stdout") == 0) {
01118             chanPtr = (Channel *) Tcl_GetStdChannel(TCL_STDOUT);
01119         } else if (strcmp(chanName, "stderr") == 0) {
01120             chanPtr = (Channel *) Tcl_GetStdChannel(TCL_STDERR);
01121         }
01122         if (chanPtr != NULL) {
01123             name = chanPtr->state->channelName;
01124         }
01125     }
01126 
01127     hTblPtr = GetChannelTable(interp);
01128     hPtr = Tcl_FindHashEntry(hTblPtr, name);
01129     if (hPtr == NULL) {
01130         Tcl_AppendResult(interp, "can not find channel named \"", chanName,
01131                 "\"", NULL);
01132         Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "CHANNEL", chanName, NULL);
01133         return NULL;
01134     }
01135 
01136     /*
01137      * Always return bottom-most channel in the stack. This one lives the
01138      * longest - other channels may go away unnoticed. The other APIs
01139      * compensate where necessary to retrieve the topmost channel again.
01140      */
01141 
01142     chanPtr = Tcl_GetHashValue(hPtr);
01143     chanPtr = chanPtr->state->bottomChanPtr;
01144     if (modePtr != NULL) {
01145         *modePtr = (chanPtr->state->flags & (TCL_READABLE|TCL_WRITABLE));
01146     }
01147 
01148     return (Tcl_Channel) chanPtr;
01149 }
01150 
01151 /*
01152  *---------------------------------------------------------------------------
01153  *
01154  * TclGetChannelFromObj --
01155  *
01156  *      Finds an existing Tcl_Channel structure by name in a given
01157  *      interpreter. This function is public because it is used by
01158  *      channel-type-specific functions.
01159  *
01160  * Results:
01161  *      A Tcl_Channel or NULL on failure. If failed, interp's result object
01162  *      contains an error message. *modePtr is filled with the modes in which
01163  *      the channel was opened.
01164  *
01165  * Side effects:
01166  *      None.
01167  *
01168  *---------------------------------------------------------------------------
01169  */
01170 
01171 int
01172 TclGetChannelFromObj(
01173     Tcl_Interp *interp,         /* Interpreter in which to find or create the
01174                                  * channel. */
01175     Tcl_Obj *objPtr,
01176     Tcl_Channel *channelPtr,
01177     int *modePtr,               /* Where to store the mode in which the
01178                                  * channel was opened? Will contain an ORed
01179                                  * combination of TCL_READABLE and
01180                                  * TCL_WRITABLE, if non-NULL. */
01181     int flags)
01182 {
01183     ChannelState *statePtr;
01184 
01185     if (SetChannelFromAny(interp, objPtr) != TCL_OK) {
01186         return TCL_ERROR;
01187     }
01188 
01189     statePtr = GET_CHANNELSTATE(objPtr);
01190     *channelPtr = (Tcl_Channel) (statePtr->bottomChanPtr);
01191 
01192     if (modePtr != NULL) {
01193         *modePtr = (statePtr->flags & (TCL_READABLE|TCL_WRITABLE));
01194     }
01195 
01196     return TCL_OK;
01197 }
01198 
01199 /*
01200  *----------------------------------------------------------------------
01201  *
01202  * Tcl_CreateChannel --
01203  *
01204  *      Creates a new entry in the hash table for a Tcl_Channel record.
01205  *
01206  * Results:
01207  *      Returns the new Tcl_Channel.
01208  *
01209  * Side effects:
01210  *      Creates a new Tcl_Channel instance and inserts it into the hash table.
01211  *
01212  *----------------------------------------------------------------------
01213  */
01214 
01215 Tcl_Channel
01216 Tcl_CreateChannel(
01217     Tcl_ChannelType *typePtr, /* The channel type record. */
01218     const char *chanName,       /* Name of channel to record. */
01219     ClientData instanceData,    /* Instance specific data. */
01220     int mask)                   /* TCL_READABLE & TCL_WRITABLE to indicate if
01221                                  * the channel is readable, writable. */
01222 {
01223     Channel *chanPtr;           /* The channel structure newly created. */
01224     ChannelState *statePtr;     /* The stack-level independent state info for
01225                                  * the channel. */
01226     const char *name;
01227     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
01228 
01229     /*
01230      * With the change of the Tcl_ChannelType structure to use a version in
01231      * 8.3.2+, we have to make sure that our assumption that the structure
01232      * remains a binary compatible size is true.
01233      *
01234      * If this assertion fails on some system, then it can be removed only if
01235      * the user recompiles code with older channel drivers in the new system
01236      * as well.
01237      */
01238 
01239     assert(sizeof(Tcl_ChannelTypeVersion) == sizeof(Tcl_DriverBlockModeProc*));
01240 
01241     /*
01242      * JH: We could subsequently memset these to 0 to avoid the numerous
01243      * assignments to 0/NULL below.
01244      */
01245 
01246     chanPtr = (Channel *) ckalloc(sizeof(Channel));
01247     statePtr = (ChannelState *) ckalloc(sizeof(ChannelState));
01248     chanPtr->state = statePtr;
01249 
01250     chanPtr->instanceData = instanceData;
01251     chanPtr->typePtr = typePtr;
01252 
01253     /*
01254      * Set all the bits that are part of the stack-independent state
01255      * information for the channel.
01256      */
01257 
01258     if (chanName != NULL) {
01259         char *tmp = ckalloc((unsigned) (strlen(chanName) + 1));
01260 
01261         statePtr->channelName = tmp;
01262         strcpy(tmp, chanName);
01263     } else {
01264         Tcl_Panic("Tcl_CreateChannel: NULL channel name");
01265     }
01266 
01267     statePtr->flags = mask;
01268 
01269     /*
01270      * Set the channel to system default encoding.
01271      *
01272      * Note the strange bit of protection taking place here. If the system
01273      * encoding name is reported back as "binary", something weird is
01274      * happening. Tcl provides no "binary" encoding, so someone else has
01275      * provided one. We ignore it so as not to interfere with the "magic"
01276      * interpretation that Tcl_Channels give to the "-encoding binary" option.
01277      */
01278 
01279     statePtr->encoding = NULL;
01280     name = Tcl_GetEncodingName(NULL);
01281     if (strcmp(name, "binary") != 0) {
01282         statePtr->encoding = Tcl_GetEncoding(NULL, name);
01283     }
01284     statePtr->inputEncodingState  = NULL;
01285     statePtr->inputEncodingFlags  = TCL_ENCODING_START;
01286     statePtr->outputEncodingState = NULL;
01287     statePtr->outputEncodingFlags = TCL_ENCODING_START;
01288 
01289     /*
01290      * Set the channel up initially in AUTO input translation mode to accept
01291      * "\n", "\r" and "\r\n". Output translation mode is set to a platform
01292      * specific default value. The eofChar is set to 0 for both input and
01293      * output, so that Tcl does not look for an in-file EOF indicator (e.g.
01294      * ^Z) and does not append an EOF indicator to files.
01295      */
01296 
01297     statePtr->inputTranslation  = TCL_TRANSLATE_AUTO;
01298     statePtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
01299     statePtr->inEofChar         = 0;
01300     statePtr->outEofChar        = 0;
01301 
01302     statePtr->unreportedError   = 0;
01303     statePtr->refCount          = 0;
01304     statePtr->closeCbPtr        = NULL;
01305     statePtr->curOutPtr         = NULL;
01306     statePtr->outQueueHead      = NULL;
01307     statePtr->outQueueTail      = NULL;
01308     statePtr->saveInBufPtr      = NULL;
01309     statePtr->inQueueHead       = NULL;
01310     statePtr->inQueueTail       = NULL;
01311     statePtr->chPtr             = NULL;
01312     statePtr->interestMask      = 0;
01313     statePtr->scriptRecordPtr   = NULL;
01314     statePtr->bufSize           = CHANNELBUFFER_DEFAULT_SIZE;
01315     statePtr->timer             = NULL;
01316     statePtr->csPtr             = NULL;
01317 
01318     statePtr->outputStage       = NULL;
01319     if ((statePtr->encoding != NULL) && (statePtr->flags & TCL_WRITABLE)) {
01320         statePtr->outputStage = (char *)
01321                 ckalloc((unsigned) (statePtr->bufSize + 2));
01322     }
01323 
01324     /*
01325      * As we are creating the channel, it is obviously the top for now.
01326      */
01327 
01328     statePtr->topChanPtr        = chanPtr;
01329     statePtr->bottomChanPtr     = chanPtr;
01330     chanPtr->downChanPtr        = NULL;
01331     chanPtr->upChanPtr          = NULL;
01332     chanPtr->inQueueHead        = NULL;
01333     chanPtr->inQueueTail        = NULL;
01334 
01335     /*
01336      * TIP #219, Tcl Channel Reflection API
01337      */
01338 
01339     statePtr->chanMsg           = NULL;
01340     statePtr->unreportedMsg     = NULL;
01341 
01342     /*
01343      * Link the channel into the list of all channels; create an on-exit
01344      * handler if there is not one already, to close off all the channels in
01345      * the list on exit.
01346      *
01347      * JH: Could call Tcl_SpliceChannel, but need to avoid NULL check.
01348      *
01349      * TIP #218.
01350      * AK: Just initialize the field to NULL before invoking Tcl_SpliceChannel
01351      *     We need Tcl_SpliceChannel, for the threadAction calls. There is no
01352      *     real reason to duplicate all of this.
01353      * NOTE: All drivers using thread actions now have to perform their TSD
01354      *       manipulation only in their thread action proc. Doing it when
01355      *       creating their instance structures will collide with the thread
01356      *       action activity and lead to damaged lists.
01357      */
01358 
01359     statePtr->nextCSPtr = NULL;
01360     SpliceChannel((Tcl_Channel) chanPtr);
01361 
01362     /*
01363      * Install this channel in the first empty standard channel slot, if the
01364      * channel was previously closed explicitly.
01365      */
01366 
01367     if ((tsdPtr->stdinChannel == NULL) && (tsdPtr->stdinInitialized == 1)) {
01368         Tcl_SetStdChannel((Tcl_Channel) chanPtr, TCL_STDIN);
01369         Tcl_RegisterChannel(NULL, (Tcl_Channel) chanPtr);
01370     } else if ((tsdPtr->stdoutChannel == NULL) &&
01371             (tsdPtr->stdoutInitialized == 1)) {
01372         Tcl_SetStdChannel((Tcl_Channel) chanPtr, TCL_STDOUT);
01373         Tcl_RegisterChannel(NULL, (Tcl_Channel) chanPtr);
01374     } else if ((tsdPtr->stderrChannel == NULL) &&
01375             (tsdPtr->stderrInitialized == 1)) {
01376         Tcl_SetStdChannel((Tcl_Channel) chanPtr, TCL_STDERR);
01377         Tcl_RegisterChannel(NULL, (Tcl_Channel) chanPtr);
01378     }
01379     return (Tcl_Channel) chanPtr;
01380 }
01381 
01382 /*
01383  *----------------------------------------------------------------------
01384  *
01385  * Tcl_StackChannel --
01386  *
01387  *      Replaces an entry in the hash table for a Tcl_Channel record. The
01388  *      replacement is a new channel with same name, it supercedes the
01389  *      replaced channel. Input and output of the superceded channel is now
01390  *      going through the newly created channel and allows the arbitrary
01391  *      filtering/manipulation of the dataflow.
01392  *
01393  *      Andreas Kupries <a.kupries@westend.com>, 12/13/1998 "Trf-Patch for
01394  *      filtering channels"
01395  *
01396  * Results:
01397  *      Returns the new Tcl_Channel, which actually contains the saved
01398  *      information about prevChan.
01399  *
01400  * Side effects:
01401  *      A new channel structure is allocated and linked below the existing
01402  *      channel. The channel operations and client data of the existing
01403  *      channel are copied down to the newly created channel, and the current
01404  *      channel has its operations replaced by the new typePtr.
01405  *
01406  *----------------------------------------------------------------------
01407  */
01408 
01409 Tcl_Channel
01410 Tcl_StackChannel(
01411     Tcl_Interp *interp,         /* The interpreter we are working in */
01412     Tcl_ChannelType *typePtr,   /* The channel type record for the new
01413                                  * channel. */
01414     ClientData instanceData,    /* Instance specific data for the new
01415                                  * channel. */
01416     int mask,                   /* TCL_READABLE & TCL_WRITABLE to indicate if
01417                                  * the channel is readable, writable. */
01418     Tcl_Channel prevChan)       /* The channel structure to replace */
01419 {
01420     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
01421     Channel *chanPtr, *prevChanPtr;
01422     ChannelState *statePtr;
01423     Tcl_DriverThreadActionProc *threadActionProc;
01424 
01425     /*
01426      * Find the given channel (prevChan) in the list of all channels. If we do
01427      * not find it, then it was never registered correctly.
01428      *
01429      * This operation should occur at the top of a channel stack.
01430      */
01431 
01432     statePtr = (ChannelState *) tsdPtr->firstCSPtr;
01433     prevChanPtr = ((Channel *) prevChan)->state->topChanPtr;
01434 
01435     while ((statePtr != NULL) && (statePtr->topChanPtr != prevChanPtr)) {
01436         statePtr = statePtr->nextCSPtr;
01437     }
01438 
01439     if (statePtr == NULL) {
01440         if (interp) {
01441             Tcl_AppendResult(interp, "couldn't find state for channel \"",
01442                     Tcl_GetChannelName(prevChan), "\"", NULL);
01443         }
01444         return NULL;
01445     }
01446 
01447     /*
01448      * Here we check if the given "mask" matches the "flags" of the already
01449      * existing channel.
01450      *
01451      *    | - | R | W | RW |
01452      *  --+---+---+---+----+    <=>  0 != (chan->mask & prevChan->mask)
01453      *  - |   |   |   |    |
01454      *  R |   | + |   | +  |    The superceding channel is allowed to restrict
01455      *  W |   |   | + | +  |    the capabilities of the superceded one!
01456      *  RW|   | + | + | +  |
01457      *  --+---+---+---+----+
01458      */
01459 
01460     if ((mask & (statePtr->flags & (TCL_READABLE | TCL_WRITABLE))) == 0) {
01461         if (interp) {
01462             Tcl_AppendResult(interp,
01463                     "reading and writing both disallowed for channel \"",
01464                     Tcl_GetChannelName(prevChan), "\"", NULL);
01465         }
01466         return NULL;
01467     }
01468 
01469     /*
01470      * Flush the buffers. This ensures that any data still in them at this
01471      * time is not handled by the new transformation. Restrict this to
01472      * writable channels. Take care to hide a possible bg-copy in progress
01473      * from Tcl_Flush and the CheckForChannelErrors inside.
01474      */
01475 
01476     if ((mask & TCL_WRITABLE) != 0) {
01477         CopyState *csPtr;
01478 
01479         csPtr = statePtr->csPtr;
01480         statePtr->csPtr = NULL;
01481 
01482         if (Tcl_Flush((Tcl_Channel) prevChanPtr) != TCL_OK) {
01483             statePtr->csPtr = csPtr;
01484             if (interp) {
01485                 Tcl_AppendResult(interp, "could not flush channel \"",
01486                         Tcl_GetChannelName(prevChan), "\"", NULL);
01487             }
01488             return NULL;
01489         }
01490 
01491         statePtr->csPtr = csPtr;
01492     }
01493 
01494     /*
01495      * Discard any input in the buffers. They are not yet read by the user of
01496      * the channel, so they have to go through the new transformation before
01497      * reading. As the buffers contain the untransformed form their contents
01498      * are not only useless but actually distorts our view of the system.
01499      *
01500      * To preserve the information without having to read them again and to
01501      * avoid problems with the location in the channel (seeking might be
01502      * impossible) we move the buffers from the common state structure into
01503      * the channel itself. We use the buffers in the channel below the new
01504      * transformation to hold the data. In the future this allows us to write
01505      * transformations which pre-read data and push the unused part back when
01506      * they are going away.
01507      */
01508 
01509     if (((mask & TCL_READABLE) != 0) && (statePtr->inQueueHead != NULL)) {
01510         /*
01511          * Remark: It is possible that the channel buffers contain data from
01512          * some earlier push-backs.
01513          */
01514 
01515         statePtr->inQueueTail->nextPtr = prevChanPtr->inQueueHead;
01516         prevChanPtr->inQueueHead = statePtr->inQueueHead;
01517 
01518         if (prevChanPtr->inQueueTail == NULL) {
01519             prevChanPtr->inQueueTail = statePtr->inQueueTail;
01520         }
01521 
01522         statePtr->inQueueHead = NULL;
01523         statePtr->inQueueTail = NULL;
01524     }
01525 
01526     chanPtr = (Channel *) ckalloc(sizeof(Channel));
01527 
01528     /*
01529      * Save some of the current state into the new structure, reinitialize the
01530      * parts which will stay with the transformation.
01531      *
01532      * Remarks:
01533      */
01534 
01535     chanPtr->state              = statePtr;
01536     chanPtr->instanceData       = instanceData;
01537     chanPtr->typePtr            = typePtr;
01538     chanPtr->downChanPtr        = prevChanPtr;
01539     chanPtr->upChanPtr          = NULL;
01540     chanPtr->inQueueHead        = NULL;
01541     chanPtr->inQueueTail        = NULL;
01542 
01543     /*
01544      * Place new block at the head of a possibly existing list of previously
01545      * stacked channels.
01546      */
01547 
01548     prevChanPtr->upChanPtr      = chanPtr;
01549     statePtr->topChanPtr        = chanPtr;
01550 
01551     /*
01552      * TIP #218, Channel Thread Actions.
01553      *
01554      * We call the thread actions for the new channel directly. We _cannot_
01555      * use SpliceChannel, because the (thread-)global list of all channels
01556      * always contains the _ChannelState_ for a stack of channels, not the
01557      * individual channels. And SpliceChannel would not only call the thread
01558      * actions, but also add the shared ChannelState to this list a second
01559      * time, mangling it.
01560      */
01561 
01562     threadActionProc = Tcl_ChannelThreadActionProc(chanPtr->typePtr);
01563     if (threadActionProc != NULL) {
01564         (*threadActionProc)(chanPtr->instanceData, TCL_CHANNEL_THREAD_INSERT);
01565     }
01566 
01567     return (Tcl_Channel) chanPtr;
01568 }
01569 
01570 /*
01571  *----------------------------------------------------------------------
01572  *
01573  * Tcl_UnstackChannel --
01574  *
01575  *      Unstacks an entry in the hash table for a Tcl_Channel record. This is
01576  *      the reverse to 'Tcl_StackChannel'.
01577  *
01578  * Results:
01579  *      A standard Tcl result.
01580  *
01581  * Side effects:
01582  *      If TCL_ERROR is returned, the posix error code will be set with
01583  *      Tcl_SetErrno. May leave a message in interp result as well.
01584  *
01585  *----------------------------------------------------------------------
01586  */
01587 
01588 int
01589 Tcl_UnstackChannel(
01590     Tcl_Interp *interp,         /* The interpreter we are working in */
01591     Tcl_Channel chan)           /* The channel to unstack */
01592 {
01593     Channel *chanPtr = (Channel *) chan;
01594     ChannelState *statePtr = chanPtr->state;
01595     int result = 0;
01596     Tcl_DriverThreadActionProc *threadActionProc;
01597 
01598     /*
01599      * This operation should occur at the top of a channel stack.
01600      */
01601 
01602     chanPtr = statePtr->topChanPtr;
01603 
01604     if (chanPtr->downChanPtr != NULL) {
01605         /*
01606          * Instead of manipulating the per-thread / per-interp list/hashtable
01607          * of registered channels we wind down the state of the transformation,
01608          * and then restore the state of underlying channel into the old
01609          * structure.
01610          */
01611 
01612         Channel *downChanPtr = chanPtr->downChanPtr;
01613 
01614         /*
01615          * Flush the buffers. This ensures that any data still in them at this
01616          * time _is_ handled by the transformation we are unstacking right
01617          * now. Restrict this to writable channels. Take care to hide a
01618          * possible bg-copy in progress from Tcl_Flush and the
01619          * CheckForChannelErrors inside.
01620          */
01621 
01622         if (statePtr->flags & TCL_WRITABLE) {
01623             CopyState *csPtr;
01624 
01625             csPtr = statePtr->csPtr;
01626             statePtr->csPtr = NULL;
01627 
01628             if (Tcl_Flush((Tcl_Channel) chanPtr) != TCL_OK) {
01629                 statePtr->csPtr = csPtr;
01630 
01631                 /*
01632                  * TIP #219, Tcl Channel Reflection API.
01633                  * Move error messages put by the driver into the chan/ip
01634                  * bypass area into the regular interpreter result. Fall back
01635                  * to the regular message if nothing was found in the
01636                  * bypasses.
01637                  */
01638 
01639                 if (!TclChanCaughtErrorBypass(interp, chan) && interp) {
01640                     Tcl_AppendResult(interp, "could not flush channel \"",
01641                             Tcl_GetChannelName((Tcl_Channel) chanPtr), "\"",
01642                             NULL);
01643                 }
01644                 return TCL_ERROR;
01645             }
01646 
01647             statePtr->csPtr = csPtr;
01648         }
01649 
01650         /*
01651          * Anything in the input queue and the push-back buffers of the
01652          * transformation going away is transformed data, but not yet read. As
01653          * unstacking means that the caller does not want to see transformed
01654          * data any more we have to discard these bytes. To avoid writing an
01655          * analogue to 'DiscardInputQueued' we move the information in the
01656          * push back buffers to the input queue and then call
01657          * 'DiscardInputQueued' on that.
01658          */
01659 
01660         if ((((statePtr->flags & TCL_READABLE) != 0)) &&
01661                 ((statePtr->inQueueHead != NULL) ||
01662                 (chanPtr->inQueueHead != NULL))) {
01663 
01664             if ((statePtr->inQueueHead != NULL) &&
01665                     (chanPtr->inQueueHead != NULL)) {
01666                 statePtr->inQueueTail->nextPtr = chanPtr->inQueueHead;
01667                 statePtr->inQueueTail = chanPtr->inQueueTail;
01668                 statePtr->inQueueHead = statePtr->inQueueTail;
01669 
01670             } else if (chanPtr->inQueueHead != NULL) {
01671                 statePtr->inQueueHead = chanPtr->inQueueHead;
01672                 statePtr->inQueueTail = chanPtr->inQueueTail;
01673             }
01674 
01675             chanPtr->inQueueHead = NULL;
01676             chanPtr->inQueueTail = NULL;
01677 
01678             DiscardInputQueued(statePtr, 0);
01679         }
01680 
01681         /*
01682          * TIP #218, Channel Thread Actions.
01683          *
01684          * We call the thread actions for the new channel directly. We
01685          * _cannot_ use CutChannel, because the (thread-)global list of all
01686          * channels always contains the _ChannelState_ for a stack of
01687          * channels, not the individual channels. And SpliceChannel would not
01688          * only call the thread actions, but also remove the shared
01689          * ChannelState from this list despite there being more channels for
01690          * the state which are still active.
01691          */
01692 
01693         threadActionProc = Tcl_ChannelThreadActionProc(chanPtr->typePtr);
01694         if (threadActionProc != NULL) {
01695             (*threadActionProc)(chanPtr->instanceData,
01696                     TCL_CHANNEL_THREAD_REMOVE);
01697         }
01698 
01699         statePtr->topChanPtr = downChanPtr;
01700         downChanPtr->upChanPtr = NULL;
01701 
01702         /*
01703          * Leave this link intact for closeproc
01704          *  chanPtr->downChanPtr = NULL;
01705          */
01706 
01707         /*
01708          * Close and free the channel driver state.
01709          */
01710 
01711         if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) {
01712             result = (chanPtr->typePtr->closeProc)(chanPtr->instanceData,
01713                     interp);
01714         } else {
01715             result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData,
01716                     interp, 0);
01717         }
01718 
01719         chanPtr->typePtr = NULL;
01720 
01721         /*
01722          * AK: Tcl_NotifyChannel may hold a reference to this block of memory
01723          */
01724 
01725         Tcl_EventuallyFree(chanPtr, TCL_DYNAMIC);
01726         UpdateInterest(downChanPtr);
01727 
01728         if (result != 0) {
01729             Tcl_SetErrno(result);
01730 
01731             /*
01732              * TIP #219, Tcl Channel Reflection API.
01733              * Move error messages put by the driver into the chan/ip bypass
01734              * area into the regular interpreter result.
01735              */
01736 
01737             TclChanCaughtErrorBypass(interp, chan);
01738             return TCL_ERROR;
01739         }
01740     } else {
01741         /*
01742          * This channel does not cover another one. Simply do a close, if
01743          * necessary.
01744          */
01745 
01746         if (statePtr->refCount <= 0) {
01747             if (Tcl_Close(interp, chan) != TCL_OK) {
01748                 /*
01749                  * TIP #219, Tcl Channel Reflection API.
01750                  * "TclChanCaughtErrorBypass" is not required here, it was
01751                  * done already by "Tcl_Close".
01752                  */
01753 
01754                 return TCL_ERROR;
01755             }
01756         }
01757 
01758         /*
01759          * TIP #218, Channel Thread Actions.
01760          * Not required in this branch, this is done by Tcl_Close. If
01761          * Tcl_Close is not called then the ChannelState is still active in
01762          * the thread and no action has to be taken either.
01763          */
01764     }
01765 
01766     return TCL_OK;
01767 }
01768 
01769 /*
01770  *----------------------------------------------------------------------
01771  *
01772  * Tcl_GetStackedChannel --
01773  *
01774  *      Determines whether the specified channel is stacked upon another.
01775  *
01776  * Results:
01777  *      NULL if the channel is not stacked upon another one, or a reference to
01778  *      the channel it is stacked upon. This reference can be used in queries,
01779  *      but modification is not allowed.
01780  *
01781  * Side effects:
01782  *      None.
01783  *
01784  *----------------------------------------------------------------------
01785  */
01786 
01787 Tcl_Channel
01788 Tcl_GetStackedChannel(
01789     Tcl_Channel chan)
01790 {
01791     Channel *chanPtr = (Channel *) chan;
01792                                 /* The actual channel. */
01793 
01794     return (Tcl_Channel) chanPtr->downChanPtr;
01795 }
01796 
01797 /*
01798  *----------------------------------------------------------------------
01799  *
01800  * Tcl_GetTopChannel --
01801  *
01802  *      Returns the top channel of a channel stack.
01803  *
01804  * Results:
01805  *      NULL if the channel is not stacked upon another one, or a reference to
01806  *      the channel it is stacked upon. This reference can be used in queries,
01807  *      but modification is not allowed.
01808  *
01809  * Side effects:
01810  *      None.
01811  *
01812  *----------------------------------------------------------------------
01813  */
01814 
01815 Tcl_Channel
01816 Tcl_GetTopChannel(
01817     Tcl_Channel chan)
01818 {
01819     Channel *chanPtr = (Channel *) chan;
01820                                 /* The actual channel. */
01821 
01822     return (Tcl_Channel) chanPtr->state->topChanPtr;
01823 }
01824 
01825 /*
01826  *----------------------------------------------------------------------
01827  *
01828  * Tcl_GetChannelInstanceData --
01829  *
01830  *      Returns the client data associated with a channel.
01831  *
01832  * Results:
01833  *      The client data.
01834  *
01835  * Side effects:
01836  *      None.
01837  *
01838  *----------------------------------------------------------------------
01839  */
01840 
01841 ClientData
01842 Tcl_GetChannelInstanceData(
01843     Tcl_Channel chan)           /* Channel for which to return client data. */
01844 {
01845     Channel *chanPtr = (Channel *) chan;
01846                                 /* The actual channel. */
01847 
01848     return chanPtr->instanceData;
01849 }
01850 
01851 /*
01852  *----------------------------------------------------------------------
01853  *
01854  * Tcl_GetChannelThread --
01855  *
01856  *      Given a channel structure, returns the thread managing it. TIP #10
01857  *
01858  * Results:
01859  *      Returns the id of the thread managing the channel.
01860  *
01861  * Side effects:
01862  *      None.
01863  *
01864  *----------------------------------------------------------------------
01865  */
01866 
01867 Tcl_ThreadId
01868 Tcl_GetChannelThread(
01869     Tcl_Channel chan)           /* The channel to return the managing thread
01870                                  * for. */
01871 {
01872     Channel *chanPtr = (Channel *) chan;
01873                                 /* The actual channel. */
01874 
01875     return chanPtr->state->managingThread;
01876 }
01877 
01878 /*
01879  *----------------------------------------------------------------------
01880  *
01881  * Tcl_GetChannelType --
01882  *
01883  *      Given a channel structure, returns the channel type structure.
01884  *
01885  * Results:
01886  *      Returns a pointer to the channel type structure.
01887  *
01888  * Side effects:
01889  *      None.
01890  *
01891  *----------------------------------------------------------------------
01892  */
01893 
01894 Tcl_ChannelType *
01895 Tcl_GetChannelType(
01896     Tcl_Channel chan)           /* The channel to return type for. */
01897 {
01898     Channel *chanPtr = (Channel *) chan;
01899                                 /* The actual channel. */
01900 
01901     return chanPtr->typePtr;
01902 }
01903 
01904 /*
01905  *----------------------------------------------------------------------
01906  *
01907  * Tcl_GetChannelMode --
01908  *
01909  *      Computes a mask indicating whether the channel is open for reading and
01910  *      writing.
01911  *
01912  * Results:
01913  *      An OR-ed combination of TCL_READABLE and TCL_WRITABLE.
01914  *
01915  * Side effects:
01916  *      None.
01917  *
01918  *----------------------------------------------------------------------
01919  */
01920 
01921 int
01922 Tcl_GetChannelMode(
01923     Tcl_Channel chan)           /* The channel for which the mode is being
01924                                  * computed. */
01925 {
01926     ChannelState *statePtr = ((Channel *) chan)->state;
01927                                 /* State of actual channel. */
01928 
01929     return (statePtr->flags & (TCL_READABLE | TCL_WRITABLE));
01930 }
01931 
01932 /*
01933  *----------------------------------------------------------------------
01934  *
01935  * Tcl_GetChannelName --
01936  *
01937  *      Returns the string identifying the channel name.
01938  *
01939  * Results:
01940  *      The string containing the channel name. This memory is owned by the
01941  *      generic layer and should not be modified by the caller.
01942  *
01943  * Side effects:
01944  *      None.
01945  *
01946  *----------------------------------------------------------------------
01947  */
01948 
01949 const char *
01950 Tcl_GetChannelName(
01951     Tcl_Channel chan)           /* The channel for which to return the name. */
01952 {
01953     ChannelState *statePtr;     /* State of actual channel. */
01954 
01955     statePtr = ((Channel *) chan)->state;
01956     return statePtr->channelName;
01957 }
01958 
01959 /*
01960  *----------------------------------------------------------------------
01961  *
01962  * Tcl_GetChannelHandle --
01963  *
01964  *      Returns an OS handle associated with a channel.
01965  *
01966  * Results:
01967  *      Returns TCL_OK and places the handle in handlePtr, or returns
01968  *      TCL_ERROR on failure.
01969  *
01970  * Side effects:
01971  *      None.
01972  *
01973  *----------------------------------------------------------------------
01974  */
01975 
01976 int
01977 Tcl_GetChannelHandle(
01978     Tcl_Channel chan,           /* The channel to get file from. */
01979     int direction,              /* TCL_WRITABLE or TCL_READABLE. */
01980     ClientData *handlePtr)      /* Where to store handle */
01981 {
01982     Channel *chanPtr;           /* The actual channel. */
01983     ClientData handle;
01984     int result;
01985 
01986     chanPtr = ((Channel *) chan)->state->bottomChanPtr;
01987     result = (chanPtr->typePtr->getHandleProc)(chanPtr->instanceData,
01988             direction, &handle);
01989     if (handlePtr) {
01990         *handlePtr = handle;
01991     }
01992     return result;
01993 }
01994 
01995 /*
01996  *---------------------------------------------------------------------------
01997  *
01998  * AllocChannelBuffer --
01999  *
02000  *      A channel buffer has BUFFER_PADDING bytes extra at beginning to hold
02001  *      any bytes of a native-encoding character that got split by the end of
02002  *      the previous buffer and need to be moved to the beginning of the next
02003  *      buffer to make a contiguous string so it can be converted to UTF-8.
02004  *
02005  *      A channel buffer has BUFFER_PADDING bytes extra at the end to hold any
02006  *      bytes of a native-encoding character (generated from a UTF-8
02007  *      character) that overflow past the end of the buffer and need to be
02008  *      moved to the next buffer.
02009  *
02010  * Results:
02011  *      A newly allocated channel buffer.
02012  *
02013  * Side effects:
02014  *      None.
02015  *
02016  *---------------------------------------------------------------------------
02017  */
02018 
02019 static ChannelBuffer *
02020 AllocChannelBuffer(
02021     int length)                 /* Desired length of channel buffer. */
02022 {
02023     ChannelBuffer *bufPtr;
02024     int n;
02025 
02026     n = length + CHANNELBUFFER_HEADER_SIZE + BUFFER_PADDING + BUFFER_PADDING;
02027     bufPtr = (ChannelBuffer *) ckalloc((unsigned) n);
02028     bufPtr->nextAdded   = BUFFER_PADDING;
02029     bufPtr->nextRemoved = BUFFER_PADDING;
02030     bufPtr->bufLength   = length + BUFFER_PADDING;
02031     bufPtr->nextPtr     = NULL;
02032     return bufPtr;
02033 }
02034 
02035 /*
02036  *----------------------------------------------------------------------
02037  *
02038  * RecycleBuffer --
02039  *
02040  *      Helper function to recycle input and output buffers. Ensures that two
02041  *      input buffers are saved (one in the input queue and another in the
02042  *      saveInBufPtr field) and that curOutPtr is set to a buffer. Only if
02043  *      these conditions are met is the buffer freed to the OS.
02044  *
02045  * Results:
02046  *      None.
02047  *
02048  * Side effects:
02049  *      May free a buffer to the OS.
02050  *
02051  *----------------------------------------------------------------------
02052  */
02053 
02054 static void
02055 RecycleBuffer(
02056     ChannelState *statePtr,     /* ChannelState in which to recycle buffers. */
02057     ChannelBuffer *bufPtr,      /* The buffer to recycle. */
02058     int mustDiscard)            /* If nonzero, free the buffer to the OS,
02059                                  * always. */
02060 {
02061     /*
02062      * Do we have to free the buffer to the OS?
02063      */
02064 
02065     if (mustDiscard) {
02066         ckfree((char *) bufPtr);
02067         return;
02068     }
02069 
02070     /*
02071      * Only save buffers which are at least as big as the requested buffersize
02072      * for the channel. This is to honor dynamic changes of the buffersize
02073      * made by the user.
02074      */
02075 
02076     if ((bufPtr->bufLength - BUFFER_PADDING) < statePtr->bufSize) {
02077         ckfree((char *) bufPtr);
02078         return;
02079     }
02080 
02081     /*
02082      * Only save buffers for the input queue if the channel is readable.
02083      */
02084 
02085     if (statePtr->flags & TCL_READABLE) {
02086         if (statePtr->inQueueHead == NULL) {
02087             statePtr->inQueueHead = bufPtr;
02088             statePtr->inQueueTail = bufPtr;
02089             goto keepBuffer;
02090         }
02091         if (statePtr->saveInBufPtr == NULL) {
02092             statePtr->saveInBufPtr = bufPtr;
02093             goto keepBuffer;
02094         }
02095     }
02096 
02097     /*
02098      * Only save buffers for the output queue if the channel is writable.
02099      */
02100 
02101     if (statePtr->flags & TCL_WRITABLE) {
02102         if (statePtr->curOutPtr == NULL) {
02103             statePtr->curOutPtr = bufPtr;
02104             goto keepBuffer;
02105         }
02106     }
02107 
02108     /*
02109      * If we reached this code we return the buffer to the OS.
02110      */
02111 
02112     ckfree((char *) bufPtr);
02113     return;
02114 
02115   keepBuffer:
02116     bufPtr->nextRemoved = BUFFER_PADDING;
02117     bufPtr->nextAdded = BUFFER_PADDING;
02118     bufPtr->nextPtr = NULL;
02119 }
02120 
02121 /*
02122  *----------------------------------------------------------------------
02123  *
02124  * DiscardOutputQueued --
02125  *
02126  *      Discards all output queued in the output queue of a channel.
02127  *
02128  * Results:
02129  *      None.
02130  *
02131  * Side effects:
02132  *      Recycles buffers.
02133  *
02134  *----------------------------------------------------------------------
02135  */
02136 
02137 static void
02138 DiscardOutputQueued(
02139     ChannelState *statePtr)     /* ChannelState for which to discard output. */
02140 {
02141     ChannelBuffer *bufPtr;
02142 
02143     while (statePtr->outQueueHead != NULL) {
02144         bufPtr = statePtr->outQueueHead;
02145         statePtr->outQueueHead = bufPtr->nextPtr;
02146         RecycleBuffer(statePtr, bufPtr, 0);
02147     }
02148     statePtr->outQueueHead = NULL;
02149     statePtr->outQueueTail = NULL;
02150 }
02151 
02152 /*
02153  *----------------------------------------------------------------------
02154  *
02155  * CheckForDeadChannel --
02156  *
02157  *      This function checks is a given channel is Dead (a channel that has
02158  *      been closed but not yet deallocated.)
02159  *
02160  * Results:
02161  *      True (1) if channel is Dead, False (0) if channel is Ok
02162  *
02163  * Side effects:
02164  *      None
02165  *
02166  *----------------------------------------------------------------------
02167  */
02168 
02169 static int
02170 CheckForDeadChannel(
02171     Tcl_Interp *interp,         /* For error reporting (can be NULL) */
02172     ChannelState *statePtr)     /* The channel state to check. */
02173 {
02174     if (statePtr->flags & CHANNEL_DEAD) {
02175         Tcl_SetErrno(EINVAL);
02176         if (interp) {
02177             Tcl_AppendResult(interp,
02178                     "unable to access channel: invalid channel", NULL);
02179         }
02180         return 1;
02181     }
02182     return 0;
02183 }
02184 
02185 /*
02186  *----------------------------------------------------------------------
02187  *
02188  * FlushChannel --
02189  *
02190  *      This function flushes as much of the queued output as is possible
02191  *      now. If calledFromAsyncFlush is nonzero, it is being called in an
02192  *      event handler to flush channel output asynchronously.
02193  *
02194  * Results:
02195  *      0 if successful, else the error code that was returned by the channel
02196  *      type operation. May leave a message in the interp result.
02197  *
02198  * Side effects:
02199  *      May produce output on a channel. May block indefinitely if the channel
02200  *      is synchronous. May schedule an async flush on the channel. May
02201  *      recycle memory for buffers in the output queue.
02202  *
02203  *----------------------------------------------------------------------
02204  */
02205 
02206 static int
02207 FlushChannel(
02208     Tcl_Interp *interp,         /* For error reporting during close. */
02209     Channel *chanPtr,           /* The channel to flush on. */
02210     int calledFromAsyncFlush)   /* If nonzero then we are being called from an
02211                                  * asynchronous flush callback. */
02212 {
02213     ChannelState *statePtr = chanPtr->state;
02214                                 /* State of the channel stack. */
02215     ChannelBuffer *bufPtr;      /* Iterates over buffered output queue. */
02216     int toWrite;                /* Amount of output data in current buffer
02217                                  * available to be written. */
02218     int written;                /* Amount of output data actually written in
02219                                  * current round. */
02220     int errorCode = 0;          /* Stores POSIX error codes from channel
02221                                  * driver operations. */
02222     int wroteSome = 0;          /* Set to one if any data was written to the
02223                                  * driver. */
02224 
02225     /*
02226      * Prevent writing on a dead channel -- a channel that has been closed but
02227      * not yet deallocated. This can occur if the exit handler for the channel
02228      * deallocation runs before all channels are deregistered in all
02229      * interpreters.
02230      */
02231 
02232     if (CheckForDeadChannel(interp, statePtr)) {
02233         return -1;
02234     }
02235 
02236     /*
02237      * Loop over the queued buffers and attempt to flush as much as possible
02238      * of the queued output to the channel.
02239      */
02240 
02241     while (1) {
02242         /*
02243          * If the queue is empty and there is a ready current buffer, OR if
02244          * the current buffer is full, then move the current buffer to the
02245          * queue.
02246          */
02247 
02248         if (((statePtr->curOutPtr != NULL) &&
02249                 IsBufferFull(statePtr->curOutPtr))
02250                 || ((statePtr->flags & BUFFER_READY) &&
02251                         (statePtr->outQueueHead == NULL))) {
02252             ResetFlag(statePtr, BUFFER_READY);
02253             statePtr->curOutPtr->nextPtr = NULL;
02254             if (statePtr->outQueueHead == NULL) {
02255                 statePtr->outQueueHead = statePtr->curOutPtr;
02256             } else {
02257                 statePtr->outQueueTail->nextPtr = statePtr->curOutPtr;
02258             }
02259             statePtr->outQueueTail = statePtr->curOutPtr;
02260             statePtr->curOutPtr = NULL;
02261         }
02262         bufPtr = statePtr->outQueueHead;
02263 
02264         /*
02265          * If we are not being called from an async flush and an async flush
02266          * is active, we just return without producing any output.
02267          */
02268 
02269         if ((!calledFromAsyncFlush) &&
02270                 (statePtr->flags & BG_FLUSH_SCHEDULED)) {
02271             return 0;
02272         }
02273 
02274         /*
02275          * If the output queue is still empty, break out of the while loop.
02276          */
02277 
02278         if (bufPtr == NULL) {
02279             break;      /* Out of the "while (1)". */
02280         }
02281 
02282         /*
02283          * Produce the output on the channel.
02284          */
02285 
02286         toWrite = BytesLeft(bufPtr);
02287         written = (chanPtr->typePtr->outputProc)(chanPtr->instanceData,
02288                 RemovePoint(bufPtr), toWrite, &errorCode);
02289 
02290         /*
02291          * If the write failed completely attempt to start the asynchronous
02292          * flush mechanism and break out of this loop - do not attempt to
02293          * write any more output at this time.
02294          */
02295 
02296         if (written < 0) {
02297             /*
02298              * If the last attempt to write was interrupted, simply retry.
02299              */
02300 
02301             if (errorCode == EINTR) {
02302                 errorCode = 0;
02303                 continue;
02304             }
02305 
02306             /*
02307              * If the channel is non-blocking and we would have blocked, start
02308              * a background flushing handler and break out of the loop.
02309              */
02310 
02311             if ((errorCode == EWOULDBLOCK) || (errorCode == EAGAIN)) {
02312                 /*
02313                  * This used to check for CHANNEL_NONBLOCKING, and panic if
02314                  * the channel was blocking. However, it appears that setting
02315                  * stdin to -blocking 0 has some effect on the stdout when
02316                  * it's a tty channel (dup'ed underneath)
02317                  */
02318 
02319                 if (!(statePtr->flags & BG_FLUSH_SCHEDULED)) {
02320                     SetFlag(statePtr, BG_FLUSH_SCHEDULED);
02321                     UpdateInterest(chanPtr);
02322                 }
02323                 errorCode = 0;
02324                 break;
02325             }
02326 
02327             /*
02328              * Decide whether to report the error upwards or defer it.
02329              */
02330 
02331             if (calledFromAsyncFlush) {
02332                 /*
02333                  * TIP #219, Tcl Channel Reflection API.
02334                  * When defering the error copy a message from the bypass into
02335                  * the unreported area. Or discard it if the new error is to be
02336                  * ignored in favor of an earlier defered error.
02337                  */
02338 
02339                 Tcl_Obj *msg = statePtr->chanMsg;
02340 
02341                 if (statePtr->unreportedError == 0) {
02342                     statePtr->unreportedError = errorCode;
02343                     statePtr->unreportedMsg = msg;
02344                     if (msg != NULL) {
02345                         Tcl_IncrRefCount(msg);
02346                     }
02347                 } else {
02348                     /*
02349                      * An old unreported error is kept, and this error thrown
02350                      * away.
02351                      */
02352 
02353                     statePtr->chanMsg = NULL;
02354                     if (msg != NULL) {
02355                         TclDecrRefCount(msg);
02356                     }
02357                 }
02358             } else {
02359                 /*
02360                  * TIP #219, Tcl Channel Reflection API.
02361                  * Move error messages put by the driver into the chan bypass
02362                  * area into the regular interpreter result. Fall back to the
02363                  * regular message if nothing was found in the bypasses.
02364                  */
02365 
02366                 Tcl_SetErrno(errorCode);
02367                 if (interp != NULL && !TclChanCaughtErrorBypass(interp,
02368                         (Tcl_Channel) chanPtr)) {
02369                     /*
02370                      * Casting away const here is safe because the
02371                      * TCL_VOLATILE flag guarantees const treatment of the
02372                      * Posix error string.
02373                      */
02374 
02375                     Tcl_SetResult(interp, (char *) Tcl_PosixError(interp),
02376                             TCL_VOLATILE);
02377                 }
02378 
02379                 /*
02380                  * An unreportable bypassed message is kept, for the caller of
02381                  * Tcl_Seek, Tcl_Write, etc.
02382                  */
02383             }
02384 
02385             /*
02386              * When we get an error we throw away all the output currently
02387              * queued.
02388              */
02389 
02390             DiscardOutputQueued(statePtr);
02391             continue;
02392         } else {
02393             wroteSome = 1;
02394         }
02395 
02396         bufPtr->nextRemoved += written;
02397 
02398         /*
02399          * If this buffer is now empty, recycle it.
02400          */
02401 
02402         if (IsBufferEmpty(bufPtr)) {
02403             statePtr->outQueueHead = bufPtr->nextPtr;
02404             if (statePtr->outQueueHead == NULL) {
02405                 statePtr->outQueueTail = NULL;
02406             }
02407             RecycleBuffer(statePtr, bufPtr, 0);
02408         }
02409     }   /* Closes "while (1)". */
02410 
02411     /*
02412      * If we wrote some data while flushing in the background, we are done.
02413      * We can't finish the background flush until we run out of data and the
02414      * channel becomes writable again. This ensures that all of the pending
02415      * data has been flushed at the system level.
02416      */
02417 
02418     if (statePtr->flags & BG_FLUSH_SCHEDULED) {
02419         if (wroteSome) {
02420             return errorCode;
02421         } else if (statePtr->outQueueHead == NULL) {
02422             ResetFlag(statePtr, BG_FLUSH_SCHEDULED);
02423             (chanPtr->typePtr->watchProc)(chanPtr->instanceData,
02424                     statePtr->interestMask);
02425         }
02426     }
02427 
02428     /*
02429      * If the channel is flagged as closed, delete it when the refCount drops
02430      * to zero, the output queue is empty and there is no output in the
02431      * current output buffer.
02432      */
02433 
02434     if ((statePtr->flags & CHANNEL_CLOSED) && (statePtr->refCount <= 0) &&
02435             (statePtr->outQueueHead == NULL) &&
02436             ((statePtr->curOutPtr == NULL) ||
02437             IsBufferEmpty(statePtr->curOutPtr))) {
02438         return CloseChannel(interp, chanPtr, errorCode);
02439     }
02440     return errorCode;
02441 }
02442 
02443 /*
02444  *----------------------------------------------------------------------
02445  *
02446  * CloseChannel --
02447  *
02448  *      Utility procedure to close a channel and free associated resources.
02449  *
02450  *      If the channel was stacked, then the it will copy the necessary
02451  *      elements of the NEXT channel into the TOP channel, in essence
02452  *      unstacking the channel. The NEXT channel will then be freed.
02453  *
02454  *      If the channel was not stacked, then we will free all the bits for the
02455  *      TOP channel, including the data structure itself.
02456  *
02457  * Results:
02458  *      Error code from an unreported error or the driver close operation.
02459  *
02460  * Side effects:
02461  *      May close the actual channel, may free memory, may change the value of
02462  *      errno.
02463  *
02464  *----------------------------------------------------------------------
02465  */
02466 
02467 static int
02468 CloseChannel(
02469     Tcl_Interp *interp,         /* For error reporting. */
02470     Channel *chanPtr,           /* The channel to close. */
02471     int errorCode)              /* Status of operation so far. */
02472 {
02473     int result = 0;             /* Of calling driver close operation. */
02474     ChannelState *statePtr;     /* State of the channel stack. */
02475     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
02476 
02477     if (chanPtr == NULL) {
02478         return result;
02479     }
02480     statePtr = chanPtr->state;
02481 
02482     /*
02483      * No more input can be consumed so discard any leftover input.
02484      */
02485 
02486     DiscardInputQueued(statePtr, 1);
02487 
02488     /*
02489      * Discard a leftover buffer in the current output buffer field.
02490      */
02491 
02492     if (statePtr->curOutPtr != NULL) {
02493         ckfree((char *) statePtr->curOutPtr);
02494         statePtr->curOutPtr = NULL;
02495     }
02496 
02497     /*
02498      * The caller guarantees that there are no more buffers queued for output.
02499      */
02500 
02501     if (statePtr->outQueueHead != NULL) {
02502         Tcl_Panic("TclFlush, closed channel: queued output left");
02503     }
02504 
02505     /*
02506      * If the EOF character is set in the channel, append that to the output
02507      * device.
02508      */
02509 
02510     if ((statePtr->outEofChar != 0) && (statePtr->flags & TCL_WRITABLE)) {
02511         int dummy;
02512         char c = (char) statePtr->outEofChar;
02513 
02514         (chanPtr->typePtr->outputProc)(chanPtr->instanceData, &c, 1, &dummy);
02515     }
02516 
02517     /*
02518      * TIP #219, Tcl Channel Reflection API.
02519      * Move a leftover error message in the channel bypass into the
02520      * interpreter bypass. Just clear it if there is no interpreter.
02521      */
02522 
02523     if (statePtr->chanMsg != NULL) {
02524         if (interp != NULL) {
02525             Tcl_SetChannelErrorInterp(interp,statePtr->chanMsg);
02526         }
02527         TclDecrRefCount(statePtr->chanMsg);
02528         statePtr->chanMsg = NULL;
02529     }
02530 
02531     /*
02532      * Remove this channel from of the list of all channels.
02533      */
02534 
02535     CutChannel((Tcl_Channel) chanPtr);
02536 
02537     /*
02538      * Close and free the channel driver state.
02539      * This may leave a TIP #219 error message in the interp.
02540      */
02541 
02542     if (chanPtr->typePtr->closeProc != TCL_CLOSE2PROC) {
02543         result = (chanPtr->typePtr->closeProc)(chanPtr->instanceData, interp);
02544     } else {
02545         result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData,
02546                 interp, 0);
02547     }
02548 
02549     /*
02550      * Some resources can be cleared only if the bottom channel in a stack is
02551      * closed. All the other channels in the stack are not allowed to remove.
02552      */
02553 
02554     if (chanPtr == statePtr->bottomChanPtr) {
02555         if (statePtr->channelName != NULL) {
02556             ckfree((char *) statePtr->channelName);
02557             statePtr->channelName = NULL;
02558         }
02559 
02560         Tcl_FreeEncoding(statePtr->encoding);
02561         if (statePtr->outputStage != NULL) {
02562             ckfree((char *) statePtr->outputStage);
02563             statePtr->outputStage = NULL;
02564         }
02565     }
02566 
02567     /*
02568      * If we are being called synchronously, report either any latent error on
02569      * the channel or the current error.
02570      */
02571 
02572     if (statePtr->unreportedError != 0) {
02573         errorCode = statePtr->unreportedError;
02574 
02575         /*
02576          * TIP #219, Tcl Channel Reflection API.
02577          * Move an error message found in the unreported area into the regular
02578          * bypass (interp). This kills any message in the channel bypass area.
02579          */
02580 
02581         if (statePtr->chanMsg != NULL) {
02582             TclDecrRefCount(statePtr->chanMsg);
02583             statePtr->chanMsg = NULL;
02584         }
02585         if (interp) {
02586             Tcl_SetChannelErrorInterp(interp,statePtr->unreportedMsg);
02587         }
02588     }
02589     if (errorCode == 0) {
02590         errorCode = result;
02591         if (errorCode != 0) {
02592             Tcl_SetErrno(errorCode);
02593         }
02594     }
02595 
02596     /*
02597      * Cancel any outstanding timer.
02598      */
02599 
02600     Tcl_DeleteTimerHandler(statePtr->timer);
02601 
02602     /*
02603      * Mark the channel as deleted by clearing the type structure.
02604      */
02605 
02606     if (chanPtr->downChanPtr != NULL) {
02607         Channel *downChanPtr = chanPtr->downChanPtr;
02608 
02609         statePtr->nextCSPtr = tsdPtr->firstCSPtr;
02610         tsdPtr->firstCSPtr = statePtr;
02611 
02612         statePtr->topChanPtr = downChanPtr;
02613         downChanPtr->upChanPtr = NULL;
02614         chanPtr->typePtr = NULL;
02615 
02616         Tcl_EventuallyFree(chanPtr, TCL_DYNAMIC);
02617         return Tcl_Close(interp, (Tcl_Channel) downChanPtr);
02618     }
02619 
02620     /*
02621      * There is only the TOP Channel, so we free the remaining pointers we
02622      * have and then ourselves. Since this is the last of the channels in the
02623      * stack, make sure to free the ChannelState structure associated with it.
02624      * We use Tcl_EventuallyFree to allow for any last references.
02625      */
02626 
02627     chanPtr->typePtr = NULL;
02628 
02629     Tcl_EventuallyFree(statePtr, TCL_DYNAMIC);
02630     Tcl_EventuallyFree(chanPtr, TCL_DYNAMIC);
02631 
02632     return errorCode;
02633 }
02634 
02635 /*
02636  *----------------------------------------------------------------------
02637  *
02638  * Tcl_CutChannel --
02639  * CutChannel --
02640  *
02641  *      Removes a channel from the (thread-)global list of all channels (in
02642  *      that thread). This is actually the statePtr for the stack of channel.
02643  *
02644  * Results:
02645  *      Nothing.
02646  *
02647  * Side effects:
02648  *      Resets the field 'nextCSPtr' of the specified channel state to NULL.
02649  *
02650  * NOTE:
02651  *      The channel to cut out of the list must not be referenced in any
02652  *      interpreter. This is something this procedure cannot check (despite
02653  *      the refcount) because the caller usually wants fiddle with the channel
02654  *      (like transfering it to a different thread) and thus keeps the
02655  *      refcount artifically high to prevent its destruction.
02656  *
02657  *----------------------------------------------------------------------
02658  */
02659 
02660 static void
02661 CutChannel(
02662     Tcl_Channel chan)           /* The channel being removed. Must not be
02663                                  * referenced in any interpreter. */
02664 {
02665     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
02666     ChannelState *prevCSPtr;    /* Preceding channel state in list of all
02667                                  * states - used to splice a channel out of
02668                                  * the list on close. */
02669     ChannelState *statePtr = ((Channel *) chan)->state;
02670                                 /* State of the channel stack. */
02671     Tcl_DriverThreadActionProc *threadActionProc;
02672 
02673     /*
02674      * Remove this channel from of the list of all channels (in the current
02675      * thread).
02676      */
02677 
02678     if (tsdPtr->firstCSPtr && (statePtr == tsdPtr->firstCSPtr)) {
02679         tsdPtr->firstCSPtr = statePtr->nextCSPtr;
02680     } else {
02681         for (prevCSPtr = tsdPtr->firstCSPtr;
02682                 prevCSPtr && (prevCSPtr->nextCSPtr != statePtr);
02683                 prevCSPtr = prevCSPtr->nextCSPtr) {
02684             /* Empty loop body. */
02685         }
02686         if (prevCSPtr == NULL) {
02687             Tcl_Panic("FlushChannel: damaged channel list");
02688         }
02689         prevCSPtr->nextCSPtr = statePtr->nextCSPtr;
02690     }
02691 
02692     statePtr->nextCSPtr = NULL;
02693 
02694     /*
02695      * TIP #218, Channel Thread Actions
02696      */
02697 
02698     threadActionProc = Tcl_ChannelThreadActionProc(Tcl_GetChannelType(chan));
02699     if (threadActionProc != NULL) {
02700         (*threadActionProc)(Tcl_GetChannelInstanceData(chan),
02701                 TCL_CHANNEL_THREAD_REMOVE);
02702     }
02703 }
02704 
02705 void
02706 Tcl_CutChannel(
02707     Tcl_Channel chan)           /* The channel being added. Must not be
02708                                  * referenced in any interpreter. */
02709 {
02710     Channel *chanPtr = ((Channel *) chan)->state->bottomChanPtr;
02711     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
02712     ChannelState *prevCSPtr;    /* Preceding channel state in list of all
02713                                  * states - used to splice a channel out of
02714                                  * the list on close. */
02715     ChannelState *statePtr = chanPtr->state;
02716                                 /* State of the channel stack. */
02717     Tcl_DriverThreadActionProc *threadActionProc;
02718 
02719     /*
02720      * Remove this channel from of the list of all channels (in the current
02721      * thread).
02722      */
02723 
02724     if (tsdPtr->firstCSPtr && (statePtr == tsdPtr->firstCSPtr)) {
02725         tsdPtr->firstCSPtr = statePtr->nextCSPtr;
02726     } else {
02727         for (prevCSPtr = tsdPtr->firstCSPtr;
02728                 prevCSPtr && (prevCSPtr->nextCSPtr != statePtr);
02729                 prevCSPtr = prevCSPtr->nextCSPtr) {
02730             /* Empty loop body. */
02731         }
02732         if (prevCSPtr == NULL) {
02733             Tcl_Panic("FlushChannel: damaged channel list");
02734         }
02735         prevCSPtr->nextCSPtr = statePtr->nextCSPtr;
02736     }
02737 
02738     statePtr->nextCSPtr = NULL;
02739 
02740     /*
02741      * TIP #218, Channel Thread Actions
02742      * For all transformations and the base channel.
02743      */
02744 
02745     while (chanPtr) {
02746         threadActionProc = Tcl_ChannelThreadActionProc(chanPtr->typePtr);
02747         if (threadActionProc != NULL) {
02748             (*threadActionProc)(chanPtr->instanceData,
02749                     TCL_CHANNEL_THREAD_REMOVE);
02750         }
02751         chanPtr= chanPtr->upChanPtr;
02752     }
02753 }
02754 
02755 /*
02756  *----------------------------------------------------------------------
02757  *
02758  * Tcl_SpliceChannel --
02759  * SpliceChannel --
02760  *
02761  *      Adds a channel to the (thread-)global list of all channels (in that
02762  *      thread). Expects that the field 'nextChanPtr' in the channel is set to
02763  *      NULL.
02764  *
02765  * Results:
02766  *      Nothing.
02767  *
02768  * Side effects:
02769  *      Nothing.
02770  *
02771  * NOTE:
02772  *      The channel to splice into the list must not be referenced in any
02773  *      interpreter. This is something this procedure cannot check (despite
02774  *      the refcount) because the caller usually wants figgle with the channel
02775  *      (like transfering it to a different thread) and thus keeps the
02776  *      refcount artifically high to prevent its destruction.
02777  *
02778  *----------------------------------------------------------------------
02779  */
02780 
02781 static void
02782 SpliceChannel(
02783     Tcl_Channel chan)           /* The channel being added. Must not be
02784                                  * referenced in any interpreter. */
02785 {
02786     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
02787     ChannelState *statePtr = ((Channel *) chan)->state;
02788     Tcl_DriverThreadActionProc *threadActionProc;
02789 
02790     if (statePtr->nextCSPtr != NULL) {
02791         Tcl_Panic("SpliceChannel: trying to add channel used in different list");
02792     }
02793 
02794     statePtr->nextCSPtr = tsdPtr->firstCSPtr;
02795     tsdPtr->firstCSPtr = statePtr;
02796 
02797     /*
02798      * TIP #10. Mark the current thread as the new one managing this channel.
02799      *          Note: 'Tcl_GetCurrentThread' returns sensible values even for
02800      *          a non-threaded core.
02801      */
02802 
02803     statePtr->managingThread = Tcl_GetCurrentThread();
02804 
02805     /*
02806      * TIP #218, Channel Thread Actions
02807      */
02808 
02809     threadActionProc = Tcl_ChannelThreadActionProc(Tcl_GetChannelType(chan));
02810     if (threadActionProc != NULL) {
02811         (*threadActionProc) (Tcl_GetChannelInstanceData(chan),
02812                 TCL_CHANNEL_THREAD_INSERT);
02813     }
02814 }
02815 
02816 void
02817 Tcl_SpliceChannel(
02818     Tcl_Channel chan)           /* The channel being added. Must not be
02819                                  * referenced in any interpreter. */
02820 {
02821     Channel *chanPtr = ((Channel *) chan)->state->bottomChanPtr;
02822     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
02823     ChannelState *statePtr = chanPtr->state;
02824     Tcl_DriverThreadActionProc *threadActionProc;
02825 
02826     if (statePtr->nextCSPtr != NULL) {
02827         Tcl_Panic("SpliceChannel: trying to add channel used in different list");
02828     }
02829 
02830     statePtr->nextCSPtr = tsdPtr->firstCSPtr;
02831     tsdPtr->firstCSPtr = statePtr;
02832 
02833     /*
02834      * TIP #10. Mark the current thread as the new one managing this channel.
02835      *          Note: 'Tcl_GetCurrentThread' returns sensible values even for
02836      *          a non-threaded core.
02837      */
02838 
02839     statePtr->managingThread = Tcl_GetCurrentThread();
02840 
02841     /*
02842      * TIP #218, Channel Thread Actions
02843      * For all transformations and the base channel.
02844      */
02845 
02846     while (chanPtr) {
02847         threadActionProc = Tcl_ChannelThreadActionProc(chanPtr->typePtr);
02848         if (threadActionProc != NULL) {
02849             (*threadActionProc)(chanPtr->instanceData,
02850                     TCL_CHANNEL_THREAD_INSERT);
02851         }
02852         chanPtr= chanPtr->upChanPtr;
02853     }
02854 }
02855 
02856 /*
02857  *----------------------------------------------------------------------
02858  *
02859  * Tcl_Close --
02860  *
02861  *      Closes a channel.
02862  *
02863  * Results:
02864  *      A standard Tcl result.
02865  *
02866  * Side effects:
02867  *      Closes the channel if this is the last reference.
02868  *
02869  * NOTE:
02870  *      Tcl_Close removes the channel as far as the user is concerned.
02871  *      However, it may continue to exist for a while longer if it has a
02872  *      background flush scheduled. The device itself is eventually closed and
02873  *      the channel record removed, in CloseChannel, above.
02874  *
02875  *----------------------------------------------------------------------
02876  */
02877 
02878         /* ARGSUSED */
02879 int
02880 Tcl_Close(
02881     Tcl_Interp *interp,         /* Interpreter for errors. */
02882     Tcl_Channel chan)           /* The channel being closed. Must not be
02883                                  * referenced in any interpreter. */
02884 {
02885     CloseCallback *cbPtr;       /* Iterate over close callbacks for this
02886                                  * channel. */
02887     Channel *chanPtr;           /* The real IO channel. */
02888     ChannelState *statePtr;     /* State of real IO channel. */
02889     int result;                 /* Of calling FlushChannel. */
02890     int flushcode;
02891 
02892     if (chan == NULL) {
02893         return TCL_OK;
02894     }
02895 
02896     /*
02897      * Perform special handling for standard channels being closed. If the
02898      * refCount is now 1 it means that the last reference to the standard
02899      * channel is being explicitly closed, so bump the refCount down
02900      * artificially to 0. This will ensure that the channel is actually
02901      * closed, below. Also set the static pointer to NULL for the channel.
02902      */
02903 
02904     CheckForStdChannelsBeingClosed(chan);
02905 
02906     /*
02907      * This operation should occur at the top of a channel stack.
02908      */
02909 
02910     chanPtr = (Channel *) chan;
02911     statePtr = chanPtr->state;
02912     chanPtr = statePtr->topChanPtr;
02913 
02914     if (statePtr->refCount > 0) {
02915         Tcl_Panic("called Tcl_Close on channel with refCount > 0");
02916     }
02917 
02918     if (statePtr->flags & CHANNEL_INCLOSE) {
02919         if (interp) {
02920             Tcl_AppendResult(interp, "Illegal recursive call to close "
02921                     "through close-handler of channel", NULL);
02922         }
02923         return TCL_ERROR;
02924     }
02925     SetFlag(statePtr, CHANNEL_INCLOSE);
02926 
02927     /*
02928      * When the channel has an escape sequence driven encoding such as
02929      * iso2022, the terminated escape sequence must write to the buffer.
02930      */
02931 
02932     if ((statePtr->encoding != NULL) && (statePtr->curOutPtr != NULL)
02933             && (CheckChannelErrors(statePtr, TCL_WRITABLE) == 0)) {
02934         statePtr->outputEncodingFlags |= TCL_ENCODING_END;
02935         WriteChars(chanPtr, "", 0);
02936 
02937         /*
02938          * TIP #219, Tcl Channel Reflection API.
02939          * Move an error message found in the channel bypass into the
02940          * interpreter bypass. Just clear it if there is no interpreter.
02941          */
02942 
02943         if (statePtr->chanMsg != NULL) {
02944             if (interp != NULL) {
02945                 Tcl_SetChannelErrorInterp(interp,statePtr->chanMsg);
02946             }
02947             TclDecrRefCount(statePtr->chanMsg);
02948             statePtr->chanMsg = NULL;
02949         }
02950     }
02951 
02952     Tcl_ClearChannelHandlers(chan);
02953 
02954     /*
02955      * Invoke the registered close callbacks and delete their records.
02956      */
02957 
02958     while (statePtr->closeCbPtr != NULL) {
02959         cbPtr = statePtr->closeCbPtr;
02960         statePtr->closeCbPtr = cbPtr->nextPtr;
02961         (cbPtr->proc)(cbPtr->clientData);
02962         ckfree((char *) cbPtr);
02963     }
02964 
02965     ResetFlag(statePtr, CHANNEL_INCLOSE);
02966 
02967     /*
02968      * Ensure that the last output buffer will be flushed.
02969      */
02970 
02971     if ((statePtr->curOutPtr != NULL) && IsBufferReady(statePtr->curOutPtr)) {
02972         SetFlag(statePtr, BUFFER_READY);
02973     }
02974 
02975     /*
02976      * If this channel supports it, close the read side, since we don't need
02977      * it anymore and this will help avoid deadlocks on some channel types.
02978      */
02979 
02980     if (chanPtr->typePtr->closeProc == TCL_CLOSE2PROC) {
02981         result = (chanPtr->typePtr->close2Proc)(chanPtr->instanceData, interp,
02982                 TCL_CLOSE_READ);
02983     } else {
02984         result = 0;
02985     }
02986 
02987     /*
02988      * The call to FlushChannel will flush any queued output and invoke the
02989      * close function of the channel driver, or it will set up the channel to
02990      * be flushed and closed asynchronously.
02991      */
02992 
02993     SetFlag(statePtr, CHANNEL_CLOSED);
02994 
02995     flushcode = FlushChannel(interp, chanPtr, 0);
02996 
02997     /*
02998      * TIP #219.
02999      * Capture error messages put by the driver into the bypass area and put
03000      * them into the regular interpreter result.
03001      *
03002      * Notes: Due to the assertion of CHANNEL_CLOSED in the flags
03003      * FlushChannel() has called CloseChannel() and thus freed all the channel
03004      * structures. We must not try to access "chan" anymore, hence the NULL
03005      * argument in the call below. The only place which may still contain a
03006      * message is the interpreter itself, and "CloseChannel" made sure to lift
03007      * any channel message it generated into it.
03008      */
03009 
03010     if (TclChanCaughtErrorBypass(interp, NULL)) {
03011         result = EINVAL;
03012     }
03013 
03014     if ((flushcode != 0) || (result != 0)) {
03015         return TCL_ERROR;
03016     }
03017     return TCL_OK;
03018 }
03019 
03020 /*
03021  *----------------------------------------------------------------------
03022  *
03023  * Tcl_ClearChannelHandlers --
03024  *
03025  *      Removes all channel handlers and event scripts from the channel,
03026  *      cancels all background copies involving the channel and any interest
03027  *      in events.
03028  *
03029  * Results:
03030  *      None.
03031  *
03032  * Side effects:
03033  *      See above. Deallocates memory.
03034  *
03035  *----------------------------------------------------------------------
03036  */
03037 
03038 void
03039 Tcl_ClearChannelHandlers(
03040     Tcl_Channel channel)
03041 {
03042     ChannelHandler *chPtr, *chNext;     /* Iterate over channel handlers. */
03043     EventScriptRecord *ePtr, *eNextPtr; /* Iterate over eventscript records. */
03044     Channel *chanPtr;                   /* The real IO channel. */
03045     ChannelState *statePtr;             /* State of real IO channel. */
03046     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
03047     NextChannelHandler *nhPtr;
03048 
03049     /*
03050      * This operation should occur at the top of a channel stack.
03051      */
03052 
03053     chanPtr = (Channel *) channel;
03054     statePtr = chanPtr->state;
03055     chanPtr = statePtr->topChanPtr;
03056 
03057     /*
03058      * Cancel any outstanding timer.
03059      */
03060 
03061     Tcl_DeleteTimerHandler(statePtr->timer);
03062 
03063     /*
03064      * Remove any references to channel handlers for this channel that may be
03065      * about to be invoked.
03066      */
03067 
03068     for (nhPtr = tsdPtr->nestedHandlerPtr; nhPtr != NULL;
03069             nhPtr = nhPtr->nestedHandlerPtr) {
03070         if (nhPtr->nextHandlerPtr &&
03071                 (nhPtr->nextHandlerPtr->chanPtr == chanPtr)) {
03072             nhPtr->nextHandlerPtr = NULL;
03073         }
03074     }
03075 
03076     /*
03077      * Remove all the channel handler records attached to the channel itself.
03078      */
03079 
03080     for (chPtr = statePtr->chPtr; chPtr != NULL; chPtr = chNext) {
03081         chNext = chPtr->nextPtr;
03082         ckfree((char *) chPtr);
03083     }
03084     statePtr->chPtr = NULL;
03085 
03086     /*
03087      * Cancel any pending copy operation.
03088      */
03089 
03090     StopCopy(statePtr->csPtr);
03091 
03092     /*
03093      * Must set the interest mask now to 0, otherwise infinite loops
03094      * will occur if Tcl_DoOneEvent is called before the channel is
03095      * finally deleted in FlushChannel. This can happen if the channel
03096      * has a background flush active.
03097      */
03098 
03099     statePtr->interestMask = 0;
03100 
03101     /*
03102      * Remove any EventScript records for this channel.
03103      */
03104 
03105     for (ePtr = statePtr->scriptRecordPtr; ePtr != NULL; ePtr = eNextPtr) {
03106         eNextPtr = ePtr->nextPtr;
03107         TclDecrRefCount(ePtr->scriptPtr);
03108         ckfree((char *) ePtr);
03109     }
03110     statePtr->scriptRecordPtr = NULL;
03111 }
03112 
03113 /*
03114  *----------------------------------------------------------------------
03115  *
03116  * Tcl_Write --
03117  *
03118  *      Puts a sequence of bytes into an output buffer, may queue the buffer
03119  *      for output if it gets full, and also remembers whether the current
03120  *      buffer is ready e.g. if it contains a newline and we are in line
03121  *      buffering mode. Compensates stacking, i.e. will redirect the data from
03122  *      the specified channel to the topmost channel in a stack.
03123  *
03124  *      No encoding conversions are applied to the bytes being read.
03125  *
03126  * Results:
03127  *      The number of bytes written or -1 in case of error. If -1,
03128  *      Tcl_GetErrno will return the error code.
03129  *
03130  * Side effects:
03131  *      May buffer up output and may cause output to be produced on the
03132  *      channel.
03133  *
03134  *----------------------------------------------------------------------
03135  */
03136 
03137 int
03138 Tcl_Write(
03139     Tcl_Channel chan,           /* The channel to buffer output for. */
03140     const char *src,            /* Data to queue in output buffer. */
03141     int srcLen)                 /* Length of data in bytes, or < 0 for
03142                                  * strlen(). */
03143 {
03144     /*
03145      * Always use the topmost channel of the stack
03146      */
03147 
03148     Channel *chanPtr;
03149     ChannelState *statePtr;     /* State info for channel */
03150 
03151     statePtr = ((Channel *) chan)->state;
03152     chanPtr = statePtr->topChanPtr;
03153 
03154     if (CheckChannelErrors(statePtr, TCL_WRITABLE) != 0) {
03155         return -1;
03156     }
03157 
03158     if (srcLen < 0) {
03159         srcLen = strlen(src);
03160     }
03161     return DoWrite(chanPtr, src, srcLen);
03162 }
03163 
03164 /*
03165  *----------------------------------------------------------------------
03166  *
03167  * Tcl_WriteRaw --
03168  *
03169  *      Puts a sequence of bytes into an output buffer, may queue the buffer
03170  *      for output if it gets full, and also remembers whether the current
03171  *      buffer is ready e.g. if it contains a newline and we are in line
03172  *      buffering mode. Writes directly to the driver of the channel, does not
03173  *      compensate for stacking.
03174  *
03175  *      No encoding conversions are applied to the bytes being read.
03176  *
03177  * Results:
03178  *      The number of bytes written or -1 in case of error. If -1,
03179  *      Tcl_GetErrno will return the error code.
03180  *
03181  * Side effects:
03182  *      May buffer up output and may cause output to be produced on the
03183  *      channel.
03184  *
03185  *----------------------------------------------------------------------
03186  */
03187 
03188 int
03189 Tcl_WriteRaw(
03190     Tcl_Channel chan,           /* The channel to buffer output for. */
03191     const char *src,            /* Data to queue in output buffer. */
03192     int srcLen)                 /* Length of data in bytes, or < 0 for
03193                                  * strlen(). */
03194 {
03195     Channel *chanPtr = ((Channel *) chan);
03196     ChannelState *statePtr = chanPtr->state;
03197                                 /* State info for channel */
03198     int errorCode, written;
03199 
03200     if (CheckChannelErrors(statePtr, TCL_WRITABLE | CHANNEL_RAW_MODE) != 0) {
03201         return -1;
03202     }
03203 
03204     if (srcLen < 0) {
03205         srcLen = strlen(src);
03206     }
03207 
03208     /*
03209      * Go immediately to the driver, do all the error handling by ourselves.
03210      * The code was stolen from 'FlushChannel'.
03211      */
03212 
03213     written = (chanPtr->typePtr->outputProc) (chanPtr->instanceData,
03214             src, srcLen, &errorCode);
03215 
03216     if (written < 0) {
03217         Tcl_SetErrno(errorCode);
03218     }
03219 
03220     return written;
03221 }
03222 
03223 /*
03224  *---------------------------------------------------------------------------
03225  *
03226  * Tcl_WriteChars --
03227  *
03228  *      Takes a sequence of UTF-8 characters and converts them for output
03229  *      using the channel's current encoding, may queue the buffer for output
03230  *      if it gets full, and also remembers whether the current buffer is
03231  *      ready e.g. if it contains a newline and we are in line buffering
03232  *      mode. Compensates stacking, i.e. will redirect the data from the
03233  *      specified channel to the topmost channel in a stack.
03234  *
03235  * Results:
03236  *      The number of bytes written or -1 in case of error. If -1,
03237  *      Tcl_GetErrno will return the error code.
03238  *
03239  * Side effects:
03240  *      May buffer up output and may cause output to be produced on the
03241  *      channel.
03242  *
03243  *----------------------------------------------------------------------
03244  */
03245 
03246 int
03247 Tcl_WriteChars(
03248     Tcl_Channel chan,           /* The channel to buffer output for. */
03249     const char *src,            /* UTF-8 characters to queue in output
03250                                  * buffer. */
03251     int len)                    /* Length of string in bytes, or < 0 for
03252                                  * strlen(). */
03253 {
03254     ChannelState *statePtr;     /* State info for channel */
03255 
03256     statePtr = ((Channel *) chan)->state;
03257 
03258     if (CheckChannelErrors(statePtr, TCL_WRITABLE) != 0) {
03259         return -1;
03260     }
03261 
03262     return DoWriteChars((Channel *) chan, src, len);
03263 }
03264 
03265 /*
03266  *---------------------------------------------------------------------------
03267  *
03268  * DoWriteChars --
03269  *
03270  *      Takes a sequence of UTF-8 characters and converts them for output
03271  *      using the channel's current encoding, may queue the buffer for output
03272  *      if it gets full, and also remembers whether the current buffer is
03273  *      ready e.g. if it contains a newline and we are in line buffering mode.
03274  *      Compensates stacking, i.e. will redirect the data from the specified
03275  *      channel to the topmost channel in a stack.
03276  *
03277  * Results:
03278  *      The number of bytes written or -1 in case of error. If -1,
03279  *      Tcl_GetErrno will return the error code.
03280  *
03281  * Side effects:
03282  *      May buffer up output and may cause output to be produced on the
03283  *      channel.
03284  *
03285  *----------------------------------------------------------------------
03286  */
03287 
03288 static int
03289 DoWriteChars(
03290     Channel *chanPtr,           /* The channel to buffer output for. */
03291     const char *src,            /* UTF-8 characters to queue in output
03292                                  * buffer. */
03293     int len)                    /* Length of string in bytes, or < 0 for
03294                                  * strlen(). */
03295 {
03296     /*
03297      * Always use the topmost channel of the stack
03298      */
03299 
03300     ChannelState *statePtr;     /* State info for channel */
03301 
03302     statePtr = chanPtr->state;
03303     chanPtr = statePtr->topChanPtr;
03304 
03305     if (len < 0) {
03306         len = strlen(src);
03307     }
03308     if (statePtr->encoding == NULL) {
03309         /*
03310          * Inefficient way to convert UTF-8 to byte-array, but the code
03311          * parallels the way it is done for objects.
03312          * Special case for 1-byte (used by eg [puts] for the \n) could
03313          * be extended to more efficient translation of the src string.
03314          */
03315 
03316         int result;
03317 
03318         if ((len == 1) && (UCHAR(*src) < 0xC0)) {
03319             result = WriteBytes(chanPtr, src, len);
03320         } else {
03321             Tcl_Obj *objPtr = Tcl_NewStringObj(src, len);
03322             src = (char *) Tcl_GetByteArrayFromObj(objPtr, &len);
03323             result = WriteBytes(chanPtr, src, len);
03324             TclDecrRefCount(objPtr);
03325         }
03326         return result;
03327     }
03328     return WriteChars(chanPtr, src, len);
03329 }
03330 
03331 /*
03332  *---------------------------------------------------------------------------
03333  *
03334  * Tcl_WriteObj --
03335  *
03336  *      Takes the Tcl object and queues its contents for output. If the
03337  *      encoding of the channel is NULL, takes the byte-array representation
03338  *      of the object and queues those bytes for output. Otherwise, takes the
03339  *      characters in the UTF-8 (string) representation of the object and
03340  *      converts them for output using the channel's current encoding. May
03341  *      flush internal buffers to output if one becomes full or is ready for
03342  *      some other reason, e.g. if it contains a newline and the channel is in
03343  *      line buffering mode.
03344  *
03345  * Results:
03346  *      The number of bytes written or -1 in case of error. If -1,
03347  *      Tcl_GetErrno() will return the error code.
03348  *
03349  * Side effects:
03350  *      May buffer up output and may cause output to be produced on the
03351  *      channel.
03352  *
03353  *----------------------------------------------------------------------
03354  */
03355 
03356 int
03357 Tcl_WriteObj(
03358     Tcl_Channel chan,           /* The channel to buffer output for. */
03359     Tcl_Obj *objPtr)            /* The object to write. */
03360 {
03361     /*
03362      * Always use the topmost channel of the stack
03363      */
03364 
03365     Channel *chanPtr;
03366     ChannelState *statePtr;     /* State info for channel */
03367     char *src;
03368     int srcLen;
03369 
03370     statePtr = ((Channel *) chan)->state;
03371     chanPtr = statePtr->topChanPtr;
03372 
03373     if (CheckChannelErrors(statePtr, TCL_WRITABLE) != 0) {
03374         return -1;
03375     }
03376     if (statePtr->encoding == NULL) {
03377         src = (char *) Tcl_GetByteArrayFromObj(objPtr, &srcLen);
03378         return WriteBytes(chanPtr, src, srcLen);
03379     } else {
03380         src = TclGetStringFromObj(objPtr, &srcLen);
03381         return WriteChars(chanPtr, src, srcLen);
03382     }
03383 }
03384 
03385 /*
03386  *----------------------------------------------------------------------
03387  *
03388  * WriteBytes --
03389  *
03390  *      Write a sequence of bytes into an output buffer, may queue the buffer
03391  *      for output if it gets full, and also remembers whether the current
03392  *      buffer is ready e.g. if it contains a newline and we are in line
03393  *      buffering mode.
03394  *
03395  * Results:
03396  *      The number of bytes written or -1 in case of error. If -1,
03397  *      Tcl_GetErrno will return the error code.
03398  *
03399  * Side effects:
03400  *      May buffer up output and may cause output to be produced on the
03401  *      channel.
03402  *
03403  *----------------------------------------------------------------------
03404  */
03405 
03406 static int
03407 WriteBytes(
03408     Channel *chanPtr,           /* The channel to buffer output for. */
03409     const char *src,            /* Bytes to write. */
03410     int srcLen)                 /* Number of bytes to write. */
03411 {
03412     ChannelState *statePtr = chanPtr->state;
03413                                 /* State info for channel */
03414     ChannelBuffer *bufPtr;
03415     char *dst;
03416     int dstMax, sawLF, savedLF, total, dstLen, toWrite, translate;
03417 
03418     total = 0;
03419     sawLF = 0;
03420     savedLF = 0;
03421     translate = (statePtr->flags & CHANNEL_LINEBUFFERED)
03422         || (statePtr->outputTranslation != TCL_TRANSLATE_LF);
03423 
03424     /*
03425      * Loop over all bytes in src, storing them in output buffer with proper
03426      * EOL translation.
03427      */
03428 
03429     while (srcLen + savedLF > 0) {
03430         bufPtr = statePtr->curOutPtr;
03431         if (bufPtr == NULL) {
03432             bufPtr = AllocChannelBuffer(statePtr->bufSize);
03433             statePtr->curOutPtr = bufPtr;
03434         }
03435         dst = InsertPoint(bufPtr);
03436         dstMax = SpaceLeft(bufPtr);
03437         dstLen = dstMax;
03438 
03439         toWrite = dstLen;
03440         if (toWrite > srcLen) {
03441             toWrite = srcLen;
03442         }
03443 
03444         if (translate) {
03445             if (savedLF) {
03446                 /*
03447                  * A '\n' was left over from last call to TranslateOutputEOL()
03448                  * and we need to store it in this buffer. If the channel is
03449                  * line-based, we will need to flush it.
03450                  */
03451 
03452                 *dst++ = '\n';
03453                 dstLen--;
03454                 sawLF++;
03455             }
03456             if (TranslateOutputEOL(statePtr, dst, src, &dstLen, &toWrite)) {
03457                 sawLF++;
03458             }
03459             dstLen += savedLF;
03460             savedLF = 0;
03461             if (dstLen > dstMax) {
03462                 savedLF = 1;
03463                 dstLen = dstMax;
03464             }
03465         } else {
03466             memcpy(dst, src, toWrite);
03467             dstLen = toWrite;
03468         }
03469 
03470         bufPtr->nextAdded += dstLen;
03471         if (CheckFlush(chanPtr, bufPtr, sawLF) != 0) {
03472             return -1;
03473         }
03474         total += dstLen;
03475         src += toWrite;
03476         srcLen -= toWrite;
03477         sawLF = 0;
03478     }
03479     return total;
03480 }
03481 
03482 /*
03483  *----------------------------------------------------------------------
03484  *
03485  * WriteChars --
03486  *
03487  *      Convert UTF-8 bytes to the channel's external encoding and write the
03488  *      produced bytes into an output buffer, may queue the buffer for output
03489  *      if it gets full, and also remembers whether the current buffer is
03490  *      ready e.g. if it contains a newline and we are in line buffering mode.
03491  *
03492  * Results:
03493  *      The number of bytes written or -1 in case of error. If -1,
03494  *      Tcl_GetErrno will return the error code.
03495  *
03496  * Side effects:
03497  *      May buffer up output and may cause output to be produced on the
03498  *      channel.
03499  *
03500  *----------------------------------------------------------------------
03501  */
03502 
03503 static int
03504 WriteChars(
03505     Channel *chanPtr,           /* The channel to buffer output for. */
03506     const char *src,            /* UTF-8 string to write. */
03507     int srcLen)                 /* Length of UTF-8 string in bytes. */
03508 {
03509     ChannelState *statePtr = chanPtr->state;
03510                                 /* State info for channel */
03511     ChannelBuffer *bufPtr;
03512     char *dst, *stage;
03513     int saved, savedLF, sawLF, total, dstLen, stageMax, dstWrote;
03514     int stageLen, toWrite, stageRead, endEncoding, result;
03515     int consumedSomething, translate;
03516     Tcl_Encoding encoding;
03517     char safe[BUFFER_PADDING];
03518 
03519     total = 0;
03520     sawLF = 0;
03521     savedLF = 0;
03522     saved = 0;
03523     encoding = statePtr->encoding;
03524 
03525     /*
03526      * Write the terminated escape sequence even if srcLen is 0.
03527      */
03528 
03529     endEncoding = ((statePtr->outputEncodingFlags & TCL_ENCODING_END) != 0);
03530 
03531     translate = (statePtr->flags & CHANNEL_LINEBUFFERED)
03532         || (statePtr->outputTranslation != TCL_TRANSLATE_LF);
03533 
03534     /*
03535      * Loop over all UTF-8 characters in src, storing them in staging buffer
03536      * with proper EOL translation.
03537      */
03538 
03539     consumedSomething = 1;
03540     while (consumedSomething && (srcLen + savedLF + endEncoding > 0)) {
03541         consumedSomething = 0;
03542         stage = statePtr->outputStage;
03543         stageMax = statePtr->bufSize;
03544         stageLen = stageMax;
03545 
03546         toWrite = stageLen;
03547         if (toWrite > srcLen) {
03548             toWrite = srcLen;
03549         }
03550 
03551         if (translate) {
03552             if (savedLF) {
03553                 /*
03554                  * A '\n' was left over from last call to TranslateOutputEOL()
03555                  * and we need to store it in the staging buffer. If the channel
03556                  * is line-based, we will need to flush the output buffer (after
03557                  * translating the staging buffer).
03558                  */
03559 
03560                 *stage++ = '\n';
03561                 stageLen--;
03562                 sawLF++;
03563             }
03564             if (TranslateOutputEOL(statePtr, stage, src, &stageLen, &toWrite)) {
03565                 sawLF++;
03566             }
03567 
03568             stage -= savedLF;
03569             stageLen += savedLF;
03570             savedLF = 0;
03571 
03572             if (stageLen > stageMax) {
03573                 savedLF = 1;
03574                 stageLen = stageMax;
03575             }
03576         } else {
03577             memcpy(stage, src, toWrite);
03578             stageLen = toWrite;
03579         }
03580         src += toWrite;
03581         srcLen -= toWrite;
03582 
03583         /*
03584          * Loop over all UTF-8 characters in staging buffer, converting them
03585          * to external encoding, storing them in output buffer.
03586          */
03587 
03588         while (stageLen + saved + endEncoding > 0) {
03589             bufPtr = statePtr->curOutPtr;
03590             if (bufPtr == NULL) {
03591                 bufPtr = AllocChannelBuffer(statePtr->bufSize);
03592                 statePtr->curOutPtr = bufPtr;
03593             }
03594             dst = InsertPoint(bufPtr);
03595             dstLen = SpaceLeft(bufPtr);
03596 
03597             if (saved != 0) {
03598                 /*
03599                  * Here's some translated bytes left over from the last buffer
03600                  * that we need to stick at the beginning of this buffer.
03601                  */
03602 
03603                 memcpy(dst, safe, (size_t) saved);
03604                 bufPtr->nextAdded += saved;
03605                 dst += saved;
03606                 dstLen -= saved;
03607                 saved = 0;
03608             }
03609 
03610             result = Tcl_UtfToExternal(NULL, encoding, stage, stageLen,
03611                     statePtr->outputEncodingFlags,
03612                     &statePtr->outputEncodingState, dst,
03613                     dstLen + BUFFER_PADDING, &stageRead, &dstWrote, NULL);
03614 
03615             /*
03616              * Fix for SF #506297, reported by Martin Forssen
03617              * <ruric@users.sourceforge.net>.
03618              *
03619              * The encoding chosen in the script exposing the bug writes out
03620              * three intro characters when TCL_ENCODING_START is set, but does
03621              * not consume any input as TCL_ENCODING_END is cleared. As some
03622              * output was generated the enclosing loop calls UtfToExternal
03623              * again, again with START set. Three more characters in the out
03624              * and still no use of input ... To break this infinite loop we
03625              * remove TCL_ENCODING_START from the set of flags after the first
03626              * call (no condition is required, the later calls remove an unset
03627              * flag, which is a no-op). This causes the subsequent calls to
03628              * UtfToExternal to consume and convert the actual input.
03629              */
03630 
03631             statePtr->outputEncodingFlags &= ~TCL_ENCODING_START;
03632 
03633             /*
03634              * The following code must be executed only when result is not 0.
03635              */
03636 
03637             if ((result != 0) && (stageRead + dstWrote == 0)) {
03638                 /*
03639                  * We have an incomplete UTF-8 character at the end of the
03640                  * staging buffer. It will get moved to the beginning of the
03641                  * staging buffer followed by more bytes from src.
03642                  */
03643 
03644                 src -= stageLen;
03645                 srcLen += stageLen;
03646                 stageLen = 0;
03647                 savedLF = 0;
03648                 break;
03649             }
03650             bufPtr->nextAdded += dstWrote;
03651             if (IsBufferOverflowing(bufPtr)) {
03652                 /*
03653                  * When translating from UTF-8 to external encoding, we
03654                  * allowed the translation to produce a character that crossed
03655                  * the end of the output buffer, so that we would get a
03656                  * completely full buffer before flushing it. The extra bytes
03657                  * will be moved to the beginning of the next buffer.
03658                  */
03659 
03660                 saved = -SpaceLeft(bufPtr);
03661                 memcpy(safe, dst + dstLen, (size_t) saved);
03662                 bufPtr->nextAdded = bufPtr->bufLength;
03663             }
03664             if (CheckFlush(chanPtr, bufPtr, sawLF) != 0) {
03665                 return -1;
03666             }
03667 
03668             total += dstWrote;
03669             stage += stageRead;
03670             stageLen -= stageRead;
03671             sawLF = 0;
03672 
03673             consumedSomething = 1;
03674 
03675             /*
03676              * If all translated characters are written to the buffer,
03677              * endEncoding is set to 0 because the escape sequence may be
03678              * output.
03679              */
03680 
03681             if ((stageLen + saved == 0) && (result == 0)) {
03682                 endEncoding = 0;
03683             }
03684         }
03685     }
03686 
03687     /*
03688      * If nothing was written and it happened because there was no progress in
03689      * the UTF conversion, we throw an error.
03690      */
03691 
03692     if (!consumedSomething && (total == 0)) {
03693         Tcl_SetErrno(EINVAL);
03694         return -1;
03695     }
03696     return total;
03697 }
03698 
03699 /*
03700  *---------------------------------------------------------------------------
03701  *
03702  * TranslateOutputEOL --
03703  *
03704  *      Helper function for WriteBytes() and WriteChars(). Converts the '\n'
03705  *      characters in the source buffer into the appropriate EOL form
03706  *      specified by the output translation mode.
03707  *
03708  *      EOL translation stops either when the source buffer is empty or the
03709  *      output buffer is full.
03710  *
03711  *      When converting to CRLF mode and there is only 1 byte left in the
03712  *      output buffer, this routine stores the '\r' in the last byte and then
03713  *      stores the '\n' in the byte just past the end of the buffer. The
03714  *      caller is responsible for passing in a buffer that is large enough to
03715  *      hold the extra byte.
03716  *
03717  * Results:
03718  *      The return value is 1 if a '\n' was translated from the source buffer,
03719  *      or 0 otherwise -- this can be used by the caller to decide to flush a
03720  *      line-based channel even though the channel buffer is not full.
03721  *
03722  *      *dstLenPtr is filled with how many bytes of the output buffer were
03723  *      used. As mentioned above, this can be one more that the output
03724  *      buffer's specified length if a CRLF was stored.
03725  *
03726  *      *srcLenPtr is filled with how many bytes of the source buffer were
03727  *      consumed.
03728  *
03729  * Side effects:
03730  *      It may be obvious, but bears mentioning that when converting in CRLF
03731  *      mode (which requires two bytes of storage in the output buffer), the
03732  *      number of bytes consumed from the source buffer will be less than the
03733  *      number of bytes stored in the output buffer.
03734  *
03735  *---------------------------------------------------------------------------
03736  */
03737 
03738 static int
03739 TranslateOutputEOL(
03740     ChannelState *statePtr,     /* Channel being read, for translation and
03741                                  * buffering modes. */
03742     char *dst,                  /* Output buffer filled with UTF-8 chars by
03743                                  * applying appropriate EOL translation to
03744                                  * source characters. */
03745     const char *src,            /* Source UTF-8 characters. */
03746     int *dstLenPtr,             /* On entry, the maximum length of output
03747                                  * buffer in bytes. On exit, the number of
03748                                  * bytes actually used in output buffer. */
03749     int *srcLenPtr)             /* On entry, the length of source buffer. On
03750                                  * exit, the number of bytes read from the
03751                                  * source buffer. */
03752 {
03753     char *dstEnd;
03754     int srcLen, newlineFound;
03755 
03756     newlineFound = 0;
03757     srcLen = *srcLenPtr;
03758 
03759     switch (statePtr->outputTranslation) {
03760     case TCL_TRANSLATE_LF:
03761         for (dstEnd = dst + srcLen; dst < dstEnd; ) {
03762             if (*src == '\n') {
03763                 newlineFound = 1;
03764             }
03765             *dst++ = *src++;
03766         }
03767         *dstLenPtr = srcLen;
03768         break;
03769     case TCL_TRANSLATE_CR:
03770         for (dstEnd = dst + srcLen; dst < dstEnd;) {
03771             if (*src == '\n') {
03772                 *dst++ = '\r';
03773                 newlineFound = 1;
03774                 src++;
03775             } else {
03776                 *dst++ = *src++;
03777             }
03778         }
03779         *dstLenPtr = srcLen;
03780         break;
03781     case TCL_TRANSLATE_CRLF: {
03782         /*
03783          * Since this causes the number of bytes to grow, we start off trying
03784          * to put 'srcLen' bytes into the output buffer, but allow it to store
03785          * more bytes, as long as there's still source bytes and room in the
03786          * output buffer.
03787          */
03788 
03789         char *dstStart, *dstMax;
03790         const char *srcStart;
03791 
03792         dstStart = dst;
03793         dstMax = dst + *dstLenPtr;
03794 
03795         srcStart = src;
03796 
03797         if (srcLen < *dstLenPtr) {
03798             dstEnd = dst + srcLen;
03799         } else {
03800             dstEnd = dst + *dstLenPtr;
03801         }
03802         while (dst < dstEnd) {
03803             if (*src == '\n') {
03804                 if (dstEnd < dstMax) {
03805                     dstEnd++;
03806                 }
03807                 *dst++ = '\r';
03808                 newlineFound = 1;
03809             }
03810             *dst++ = *src++;
03811         }
03812         *srcLenPtr = src - srcStart;
03813         *dstLenPtr = dst - dstStart;
03814         break;
03815     }
03816     default:
03817         break;
03818     }
03819     return newlineFound;
03820 }
03821 
03822 /*
03823  *---------------------------------------------------------------------------
03824  *
03825  * CheckFlush --
03826  *
03827  *      Helper function for WriteBytes() and WriteChars(). If the channel
03828  *      buffer is ready to be flushed, flush it.
03829  *
03830  * Results:
03831  *      The return value is -1 if there was a problem flushing the channel
03832  *      buffer, or 0 otherwise.
03833  *
03834  * Side effects:
03835  *      The buffer will be recycled if it is flushed.
03836  *
03837  *---------------------------------------------------------------------------
03838  */
03839 
03840 static int
03841 CheckFlush(
03842     Channel *chanPtr,           /* Channel being read, for buffering mode. */
03843     ChannelBuffer *bufPtr,      /* Channel buffer to possibly flush. */
03844     int newlineFlag)            /* Non-zero if a the channel buffer contains a
03845                                  * newline. */
03846 {
03847     ChannelState *statePtr = chanPtr->state;
03848                                 /* State info for channel */
03849 
03850     /*
03851      * The current buffer is ready for output:
03852      * 1. if it is full.
03853      * 2. if it contains a newline and this channel is line-buffered.
03854      * 3. if it contains any output and this channel is unbuffered.
03855      */
03856 
03857     if ((statePtr->flags & BUFFER_READY) == 0) {
03858         if (IsBufferFull(bufPtr)) {
03859             SetFlag(statePtr, BUFFER_READY);
03860         } else if (statePtr->flags & CHANNEL_LINEBUFFERED) {
03861             if (newlineFlag != 0) {
03862                 SetFlag(statePtr, BUFFER_READY);
03863             }
03864         } else if (statePtr->flags & CHANNEL_UNBUFFERED) {
03865             SetFlag(statePtr, BUFFER_READY);
03866         }
03867     }
03868     if (statePtr->flags & BUFFER_READY) {
03869         if (FlushChannel(NULL, chanPtr, 0) != 0) {
03870             return -1;
03871         }
03872     }
03873     return 0;
03874 }
03875 
03876 /*
03877  *---------------------------------------------------------------------------
03878  *
03879  * Tcl_Gets --
03880  *
03881  *      Reads a complete line of input from the channel into a Tcl_DString.
03882  *
03883  * Results:
03884  *      Length of line read (in characters) or -1 if error, EOF, or blocked.
03885  *      If -1, use Tcl_GetErrno() to retrieve the POSIX error code for the
03886  *      error or condition that occurred.
03887  *
03888  * Side effects:
03889  *      May flush output on the channel. May cause input to be consumed from
03890  *      the channel.
03891  *
03892  *---------------------------------------------------------------------------
03893  */
03894 
03895 int
03896 Tcl_Gets(
03897     Tcl_Channel chan,           /* Channel from which to read. */
03898     Tcl_DString *lineRead)      /* The line read will be appended to this
03899                                  * DString as UTF-8 characters. The caller
03900                                  * must have initialized it and is responsible
03901                                  * for managing the storage. */
03902 {
03903     Tcl_Obj *objPtr;
03904     int charsStored, length;
03905     char *string;
03906 
03907     TclNewObj(objPtr);
03908     charsStored = Tcl_GetsObj(chan, objPtr);
03909     if (charsStored > 0) {
03910         string = TclGetStringFromObj(objPtr, &length);
03911         Tcl_DStringAppend(lineRead, string, length);
03912     }
03913     TclDecrRefCount(objPtr);
03914     return charsStored;
03915 }
03916 
03917 /*
03918  *---------------------------------------------------------------------------
03919  *
03920  * Tcl_GetsObj --
03921  *
03922  *      Accumulate input from the input channel until end-of-line or
03923  *      end-of-file has been seen. Bytes read from the input channel are
03924  *      converted to UTF-8 using the encoding specified by the channel.
03925  *
03926  * Results:
03927  *      Number of characters accumulated in the object or -1 if error,
03928  *      blocked, or EOF. If -1, use Tcl_GetErrno() to retrieve the POSIX error
03929  *      code for the error or condition that occurred.
03930  *
03931  * Side effects:
03932  *      Consumes input from the channel.
03933  *
03934  *      On reading EOF, leave channel pointing at EOF char. On reading EOL,
03935  *      leave channel pointing after EOL, but don't return EOL in dst buffer.
03936  *
03937  *---------------------------------------------------------------------------
03938  */
03939 
03940 int
03941 Tcl_GetsObj(
03942     Tcl_Channel chan,           /* Channel from which to read. */
03943     Tcl_Obj *objPtr)            /* The line read will be appended to this
03944                                  * object as UTF-8 characters. */
03945 {
03946     GetsState gs;
03947     Channel *chanPtr = (Channel *) chan;
03948     ChannelState *statePtr = chanPtr->state;
03949                                 /* State info for channel */
03950     ChannelBuffer *bufPtr;
03951     int inEofChar, skip, copiedTotal, oldLength, oldFlags, oldRemoved;
03952     Tcl_Encoding encoding;
03953     char *dst, *dstEnd, *eol, *eof;
03954     Tcl_EncodingState oldState;
03955 
03956     if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
03957         copiedTotal = -1;
03958         goto done;
03959     }
03960 
03961     /*
03962      * A binary version of Tcl_GetsObj. This could also handle encodings that
03963      * are ascii-7 pure (iso8859, utf-8, ...) with a final encoding conversion
03964      * done on objPtr.
03965      */
03966 
03967     if ((statePtr->encoding == NULL)
03968             && ((statePtr->inputTranslation == TCL_TRANSLATE_LF)
03969                     || (statePtr->inputTranslation == TCL_TRANSLATE_CR))) {
03970         return TclGetsObjBinary(chan, objPtr);
03971     }
03972 
03973     /*
03974      * This operation should occur at the top of a channel stack.
03975      */
03976 
03977     chanPtr = statePtr->topChanPtr;
03978 
03979     bufPtr = statePtr->inQueueHead;
03980     encoding = statePtr->encoding;
03981 
03982     /*
03983      * Preserved so we can restore the channel's state in case we don't find a
03984      * newline in the available input.
03985      */
03986 
03987     TclGetStringFromObj(objPtr, &oldLength);
03988     oldFlags = statePtr->inputEncodingFlags;
03989     oldState = statePtr->inputEncodingState;
03990     oldRemoved = BUFFER_PADDING;
03991     if (bufPtr != NULL) {
03992         oldRemoved = bufPtr->nextRemoved;
03993     }
03994 
03995     /*
03996      * If there is no encoding, use "iso8859-1" -- Tcl_GetsObj() doesn't
03997      * produce ByteArray objects.
03998      */
03999 
04000     if (encoding == NULL) {
04001         ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
04002 
04003         if (tsdPtr->binaryEncoding == NULL) {
04004             tsdPtr->binaryEncoding = Tcl_GetEncoding(NULL, "iso8859-1");
04005             Tcl_CreateThreadExitHandler(FreeBinaryEncoding, NULL);
04006         }
04007         encoding = tsdPtr->binaryEncoding;
04008         if (encoding == NULL) {
04009             Tcl_Panic("attempted gets on binary channel where no iso8859-1 encoding available");
04010         }
04011     }
04012 
04013     /*
04014      * Object used by FilterInputBytes to keep track of how much data has been
04015      * consumed from the channel buffers.
04016      */
04017 
04018     gs.objPtr           = objPtr;
04019     gs.dstPtr           = &dst;
04020     gs.encoding         = encoding;
04021     gs.bufPtr           = bufPtr;
04022     gs.state            = oldState;
04023     gs.rawRead          = 0;
04024     gs.bytesWrote       = 0;
04025     gs.charsWrote       = 0;
04026     gs.totalChars       = 0;
04027 
04028     dst = objPtr->bytes + oldLength;
04029     dstEnd = dst;
04030 
04031     skip = 0;
04032     eof = NULL;
04033     inEofChar = statePtr->inEofChar;
04034 
04035     while (1) {
04036         if (dst >= dstEnd) {
04037             if (FilterInputBytes(chanPtr, &gs) != 0) {
04038                 goto restore;
04039             }
04040             dstEnd = dst + gs.bytesWrote;
04041         }
04042 
04043         /*
04044          * Remember if EOF char is seen, then look for EOL anyhow, because the
04045          * EOL might be before the EOF char.
04046          */
04047 
04048         if (inEofChar != '\0') {
04049             for (eol = dst; eol < dstEnd; eol++) {
04050                 if (*eol == inEofChar) {
04051                     dstEnd = eol;
04052                     eof = eol;
04053                     break;
04054                 }
04055             }
04056         }
04057 
04058         /*
04059          * On EOL, leave current file position pointing after the EOL, but
04060          * don't store the EOL in the output string.
04061          */
04062 
04063         switch (statePtr->inputTranslation) {
04064         case TCL_TRANSLATE_LF:
04065             for (eol = dst; eol < dstEnd; eol++) {
04066                 if (*eol == '\n') {
04067                     skip = 1;
04068                     goto gotEOL;
04069                 }
04070             }
04071             break;
04072         case TCL_TRANSLATE_CR:
04073             for (eol = dst; eol < dstEnd; eol++) {
04074                 if (*eol == '\r') {
04075                     skip = 1;
04076                     goto gotEOL;
04077                 }
04078             }
04079             break;
04080         case TCL_TRANSLATE_CRLF:
04081             for (eol = dst; eol < dstEnd; eol++) {
04082                 if (*eol == '\r') {
04083                     eol++;
04084 
04085                     /*
04086                      * If a CR is at the end of the buffer, then check for a
04087                      * LF at the begining of the next buffer, unless EOF char
04088                      * was found already.
04089                      */
04090 
04091                     if (eol >= dstEnd) {
04092                         int offset;
04093 
04094                         if (eol != eof) {
04095                             offset = eol - objPtr->bytes;
04096                             dst = dstEnd;
04097                             if (FilterInputBytes(chanPtr, &gs) != 0) {
04098                                 goto restore;
04099                             }
04100                             dstEnd = dst + gs.bytesWrote;
04101                             eol = objPtr->bytes + offset;
04102                         }
04103                         if (eol >= dstEnd) {
04104                             skip = 0;
04105                             goto gotEOL;
04106                         }
04107                     }
04108                     if (*eol == '\n') {
04109                         eol--;
04110                         skip = 2;
04111                         goto gotEOL;
04112                     }
04113                 }
04114             }
04115             break;
04116         case TCL_TRANSLATE_AUTO:
04117             eol = dst;
04118             skip = 1;
04119             if (statePtr->flags & INPUT_SAW_CR) {
04120                 ResetFlag(statePtr, INPUT_SAW_CR);
04121                 if ((eol < dstEnd) && (*eol == '\n')) {
04122                     /*
04123                      * Skip the raw bytes that make up the '\n'.
04124                      */
04125 
04126                     char tmp[1 + TCL_UTF_MAX];
04127                     int rawRead;
04128 
04129                     bufPtr = gs.bufPtr;
04130                     Tcl_ExternalToUtf(NULL, gs.encoding, RemovePoint(bufPtr),
04131                             gs.rawRead, statePtr->inputEncodingFlags,
04132                             &gs.state, tmp, 1 + TCL_UTF_MAX, &rawRead, NULL,
04133                             NULL);
04134                     bufPtr->nextRemoved += rawRead;
04135                     gs.rawRead -= rawRead;
04136                     gs.bytesWrote--;
04137                     gs.charsWrote--;
04138                     memmove(dst, dst + 1, (size_t) (dstEnd - dst));
04139                     dstEnd--;
04140                 }
04141             }
04142             for (eol = dst; eol < dstEnd; eol++) {
04143                 if (*eol == '\r') {
04144                     eol++;
04145                     if (eol == dstEnd) {
04146                         /*
04147                          * If buffer ended on \r, peek ahead to see if a \n is
04148                          * available, unless EOF char was found already.
04149                          */
04150 
04151                         if (eol != eof) {
04152                             int offset;
04153 
04154                             offset = eol - objPtr->bytes;
04155                             dst = dstEnd;
04156                             PeekAhead(chanPtr, &dstEnd, &gs);
04157                             eol = objPtr->bytes + offset;
04158                         }
04159 
04160                         if (eol >= dstEnd) {
04161                             eol--;
04162                             SetFlag(statePtr, INPUT_SAW_CR);
04163                             goto gotEOL;
04164                         }
04165                     }
04166                     if (*eol == '\n') {
04167                         skip++;
04168                     }
04169                     eol--;
04170                     goto gotEOL;
04171                 } else if (*eol == '\n') {
04172                     goto gotEOL;
04173                 }
04174             }
04175         }
04176         if (eof != NULL) {
04177             /*
04178              * EOF character was seen. On EOF, leave current file position
04179              * pointing at the EOF character, but don't store the EOF
04180              * character in the output string.
04181              */
04182 
04183             dstEnd = eof;
04184             SetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF);
04185             statePtr->inputEncodingFlags |= TCL_ENCODING_END;
04186         }
04187         if (statePtr->flags & CHANNEL_EOF) {
04188             skip = 0;
04189             eol = dstEnd;
04190             if (eol == objPtr->bytes + oldLength) {
04191                 /*
04192                  * If we didn't append any bytes before encountering EOF,
04193                  * caller needs to see -1.
04194                  */
04195 
04196                 Tcl_SetObjLength(objPtr, oldLength);
04197                 CommonGetsCleanup(chanPtr);
04198                 copiedTotal = -1;
04199                 goto done;
04200             }
04201             goto gotEOL;
04202         }
04203         dst = dstEnd;
04204     }
04205 
04206     /*
04207      * Found EOL or EOF, but the output buffer may now contain too many UTF-8
04208      * characters. We need to know how many raw bytes correspond to the number
04209      * of UTF-8 characters we want, plus how many raw bytes correspond to the
04210      * character(s) making up EOL (if any), so we can remove the correct
04211      * number of bytes from the channel buffer.
04212      */
04213 
04214   gotEOL:
04215     bufPtr = gs.bufPtr;
04216     if (bufPtr == NULL) {
04217         Tcl_Panic("Tcl_GetsObj: gotEOL reached with bufPtr==NULL");
04218     }
04219     statePtr->inputEncodingState = gs.state;
04220     Tcl_ExternalToUtf(NULL, gs.encoding, RemovePoint(bufPtr), gs.rawRead,
04221             statePtr->inputEncodingFlags, &statePtr->inputEncodingState, dst,
04222             eol - dst + skip + TCL_UTF_MAX, &gs.rawRead, NULL,
04223             &gs.charsWrote);
04224     bufPtr->nextRemoved += gs.rawRead;
04225 
04226     /*
04227      * Recycle all the emptied buffers.
04228      */
04229 
04230     Tcl_SetObjLength(objPtr, eol - objPtr->bytes);
04231     CommonGetsCleanup(chanPtr);
04232     ResetFlag(statePtr, CHANNEL_BLOCKED);
04233     copiedTotal = gs.totalChars + gs.charsWrote - skip;
04234     goto done;
04235 
04236     /*
04237      * Couldn't get a complete line. This only happens if we get a error
04238      * reading from the channel or we are non-blocking and there wasn't an EOL
04239      * or EOF in the data available.
04240      */
04241 
04242   restore:
04243     bufPtr = statePtr->inQueueHead;
04244     if (bufPtr == NULL) {
04245         Tcl_Panic("Tcl_GetsObj: restore reached with bufPtr==NULL");
04246     }
04247     bufPtr->nextRemoved = oldRemoved;
04248 
04249     for (bufPtr = bufPtr->nextPtr; bufPtr != NULL; bufPtr = bufPtr->nextPtr) {
04250         bufPtr->nextRemoved = BUFFER_PADDING;
04251     }
04252     CommonGetsCleanup(chanPtr);
04253 
04254     statePtr->inputEncodingState = oldState;
04255     statePtr->inputEncodingFlags = oldFlags;
04256     Tcl_SetObjLength(objPtr, oldLength);
04257 
04258     /*
04259      * We didn't get a complete line so we need to indicate to UpdateInterest
04260      * that the gets blocked. It will wait for more data instead of firing a
04261      * timer, avoiding a busy wait. This is where we are assuming that the
04262      * next operation is a gets. No more file events will be delivered on this
04263      * channel until new data arrives or some operation is performed on the
04264      * channel (e.g. gets, read, fconfigure) that changes the blocking state.
04265      * Note that this means a file event will not be delivered even though a
04266      * read would be able to consume the buffered data.
04267      */
04268 
04269     SetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
04270     copiedTotal = -1;
04271 
04272     /*
04273      * Update the notifier state so we don't block while there is still data
04274      * in the buffers.
04275      */
04276 
04277   done:
04278     UpdateInterest(chanPtr);
04279     return copiedTotal;
04280 }
04281 
04282 /*
04283  *---------------------------------------------------------------------------
04284  *
04285  * TclGetsObjBinary --
04286  *
04287  *      A variation of Tcl_GetsObj that works directly on the buffers until
04288  *      end-of-line or end-of-file has been seen. Bytes read from the input
04289  *      channel return as a ByteArray obj.
04290  *
04291  * Results:
04292  *      Number of characters accumulated in the object or -1 if error,
04293  *      blocked, or EOF. If -1, use Tcl_GetErrno() to retrieve the POSIX error
04294  *      code for the error or condition that occurred.
04295  *
04296  * Side effects:
04297  *      Consumes input from the channel.
04298  *
04299  *      On reading EOF, leave channel pointing at EOF char. On reading EOL,
04300  *      leave channel pointing after EOL, but don't return EOL in dst buffer.
04301  *
04302  *---------------------------------------------------------------------------
04303  */
04304 
04305 static int
04306 TclGetsObjBinary(
04307     Tcl_Channel chan,           /* Channel from which to read. */
04308     Tcl_Obj *objPtr)            /* The line read will be appended to this
04309                                  * object as UTF-8 characters. */
04310 {
04311     Channel *chanPtr = (Channel *) chan;
04312     ChannelState *statePtr = chanPtr->state;
04313                                 /* State info for channel */
04314     ChannelBuffer *bufPtr;
04315     int inEofChar, skip, copiedTotal, oldLength, oldFlags, oldRemoved;
04316     int rawLen, byteLen, eolChar;
04317     unsigned char *dst, *dstEnd, *eol, *eof, *byteArray;
04318 
04319     /*
04320      * This operation should occur at the top of a channel stack.
04321      */
04322 
04323     chanPtr = statePtr->topChanPtr;
04324 
04325     bufPtr = statePtr->inQueueHead;
04326 
04327     /*
04328      * Preserved so we can restore the channel's state in case we don't find a
04329      * newline in the available input.
04330      */
04331 
04332     byteArray = Tcl_GetByteArrayFromObj(objPtr, &byteLen);
04333     oldFlags = statePtr->inputEncodingFlags;
04334     oldRemoved = BUFFER_PADDING;
04335     oldLength = byteLen;
04336     if (bufPtr != NULL) {
04337         oldRemoved = bufPtr->nextRemoved;
04338     }
04339 
04340     rawLen = 0;
04341     skip = 0;
04342     eof = NULL;
04343     inEofChar = statePtr->inEofChar;
04344     /* Only handle TCL_TRANSLATE_LF and TCL_TRANSLATE_CR */
04345     eolChar = (statePtr->inputTranslation == TCL_TRANSLATE_LF) ? '\n' : '\r';
04346 
04347     while (1) {
04348         /*
04349          * Subtract the number of bytes that were removed from channel
04350          * buffer during last call.
04351          */
04352 
04353         if (bufPtr != NULL) {
04354             bufPtr->nextRemoved += rawLen;
04355             if (!IsBufferReady(bufPtr)) {
04356                 bufPtr = bufPtr->nextPtr;
04357             }
04358         }
04359 
04360         if ((bufPtr == NULL) || (bufPtr->nextAdded == BUFFER_PADDING)) {
04361             /*
04362              * All channel buffers were exhausted and the caller still
04363              * hasn't seen EOL. Need to read more bytes from the channel
04364              * device. Side effect is to allocate another channel buffer.
04365              */
04366 
04367             if (statePtr->flags & CHANNEL_BLOCKED) {
04368                 if (statePtr->flags & CHANNEL_NONBLOCKING) {
04369                     goto restore;
04370                 }
04371                 ResetFlag(statePtr, CHANNEL_BLOCKED);
04372             }
04373             if (GetInput(chanPtr) != 0) {
04374                 goto restore;
04375             }
04376             bufPtr = statePtr->inQueueTail;
04377         }
04378 
04379         dst = (unsigned char *) RemovePoint(bufPtr);
04380         dstEnd = dst + BytesLeft(bufPtr);
04381 
04382         /*
04383          * Remember if EOF char is seen, then look for EOL anyhow, because the
04384          * EOL might be before the EOF char.
04385          * XXX - in the binary case, consider coincident search for eol/eof.
04386          */
04387 
04388         if (inEofChar != '\0') {
04389             for (eol = dst; eol < dstEnd; eol++) {
04390                 if (*eol == inEofChar) {
04391                     dstEnd = eol;
04392                     eof = eol;
04393                     break;
04394                 }
04395             }
04396         }
04397 
04398         /*
04399          * On EOL, leave current file position pointing after the EOL, but
04400          * don't store the EOL in the output string.
04401          */
04402 
04403         for (eol = dst; eol < dstEnd; eol++) {
04404             if (*eol == eolChar) {
04405                 skip = 1;
04406                 goto gotEOL;
04407             }
04408         }
04409         if (eof != NULL) {
04410             /*
04411              * EOF character was seen. On EOF, leave current file position
04412              * pointing at the EOF character, but don't store the EOF
04413              * character in the output string.
04414              */
04415 
04416             SetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF);
04417             statePtr->inputEncodingFlags |= TCL_ENCODING_END;
04418         }
04419         if (statePtr->flags & CHANNEL_EOF) {
04420             skip = 0;
04421             eol = dstEnd;
04422             if ((dst == dstEnd) && (byteLen == oldLength)) {
04423                 /*
04424                  * If we didn't append any bytes before encountering EOF,
04425                  * caller needs to see -1.
04426                  */
04427 
04428                 byteArray = Tcl_SetByteArrayLength(objPtr, oldLength);
04429                 CommonGetsCleanup(chanPtr);
04430                 copiedTotal = -1;
04431                 goto done;
04432             }
04433             goto gotEOL;
04434         }
04435 
04436         /*
04437          * Copy bytes from the channel buffer to the ByteArray.
04438          * This may realloc space, so keep track of result.
04439          */
04440 
04441         rawLen = dstEnd - dst;
04442         byteArray = Tcl_SetByteArrayLength(objPtr, byteLen + rawLen);
04443         memcpy(byteArray + byteLen, dst, (size_t) rawLen);
04444         byteLen += rawLen;
04445     }
04446 
04447     /*
04448      * Found EOL or EOF, but the output buffer may now contain too many bytes.
04449      * We need to know how many bytes correspond to the number we want, so we
04450      * can remove the correct number of bytes from the channel buffer.
04451      */
04452 
04453   gotEOL:
04454     if (bufPtr == NULL) {
04455         Tcl_Panic("TclGetsObjBinary: gotEOL reached with bufPtr==NULL");
04456     }
04457 
04458     rawLen = eol - dst;
04459     byteArray = Tcl_SetByteArrayLength(objPtr, byteLen + rawLen);
04460     memcpy(byteArray + byteLen, dst, (size_t) rawLen);
04461     byteLen += rawLen;
04462     bufPtr->nextRemoved += rawLen + skip;
04463 
04464     /*
04465      * Convert the buffer if there was an encoding.
04466      * XXX - unimplemented.
04467      */
04468 
04469     if (statePtr->encoding != NULL) {
04470     }
04471 
04472     /*
04473      * Recycle all the emptied buffers.
04474      */
04475 
04476     CommonGetsCleanup(chanPtr);
04477     ResetFlag(statePtr, CHANNEL_BLOCKED);
04478     copiedTotal = byteLen;
04479     goto done;
04480 
04481     /*
04482      * Couldn't get a complete line. This only happens if we get a error
04483      * reading from the channel or we are non-blocking and there wasn't an EOL
04484      * or EOF in the data available.
04485      */
04486 
04487   restore:
04488     bufPtr = statePtr->inQueueHead;
04489     if (bufPtr == NULL) {
04490         Tcl_Panic("TclGetsObjBinary: restore reached with bufPtr==NULL");
04491     }
04492     bufPtr->nextRemoved = oldRemoved;
04493 
04494     for (bufPtr = bufPtr->nextPtr; bufPtr != NULL; bufPtr = bufPtr->nextPtr) {
04495         bufPtr->nextRemoved = BUFFER_PADDING;
04496     }
04497     CommonGetsCleanup(chanPtr);
04498 
04499     statePtr->inputEncodingFlags = oldFlags;
04500     byteArray = Tcl_SetByteArrayLength(objPtr, oldLength);
04501 
04502     /*
04503      * We didn't get a complete line so we need to indicate to UpdateInterest
04504      * that the gets blocked. It will wait for more data instead of firing a
04505      * timer, avoiding a busy wait. This is where we are assuming that the
04506      * next operation is a gets. No more file events will be delivered on this
04507      * channel until new data arrives or some operation is performed on the
04508      * channel (e.g. gets, read, fconfigure) that changes the blocking state.
04509      * Note that this means a file event will not be delivered even though a
04510      * read would be able to consume the buffered data.
04511      */
04512 
04513     SetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
04514     copiedTotal = -1;
04515 
04516     /*
04517      * Update the notifier state so we don't block while there is still data
04518      * in the buffers.
04519      */
04520 
04521   done:
04522     UpdateInterest(chanPtr);
04523     return copiedTotal;
04524 }
04525 
04526 /*
04527  *---------------------------------------------------------------------------
04528  *
04529  * FreeBinaryEncoding --
04530  *
04531  *      Frees any "iso8859-1" Tcl_Encoding created by [gets] on a binary
04532  *      channel in a thread as part of that thread's finalization.
04533  *
04534  * Results:
04535  *      None.
04536  *
04537  *---------------------------------------------------------------------------
04538  */
04539 
04540 static void
04541 FreeBinaryEncoding(
04542     ClientData dummy)   /* Not used */
04543 {
04544     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
04545 
04546     if (tsdPtr->binaryEncoding != NULL) {
04547         Tcl_FreeEncoding(tsdPtr->binaryEncoding);
04548         tsdPtr->binaryEncoding = NULL;
04549     }
04550 }
04551 
04552 /*
04553  *---------------------------------------------------------------------------
04554  *
04555  * FilterInputBytes --
04556  *
04557  *      Helper function for Tcl_GetsObj. Produces UTF-8 characters from raw
04558  *      bytes read from the channel.
04559  *
04560  *      Consumes available bytes from channel buffers. When channel buffers
04561  *      are exhausted, reads more bytes from channel device into a new channel
04562  *      buffer. It is the caller's responsibility to free the channel buffers
04563  *      that have been exhausted.
04564  *
04565  * Results:
04566  *      The return value is -1 if there was an error reading from the channel,
04567  *      0 otherwise.
04568  *
04569  * Side effects:
04570  *      Status object keeps track of how much data from channel buffers has
04571  *      been consumed and where UTF-8 bytes should be stored.
04572  *
04573  *---------------------------------------------------------------------------
04574  */
04575 
04576 static int
04577 FilterInputBytes(
04578     Channel *chanPtr,           /* Channel to read. */
04579     GetsState *gsPtr)           /* Current state of gets operation. */
04580 {
04581     ChannelState *statePtr = chanPtr->state;
04582                                 /* State info for channel */
04583     ChannelBuffer *bufPtr;
04584     char *raw, *rawStart, *dst;
04585     int offset, toRead, dstNeeded, spaceLeft, result, rawLen, length;
04586     Tcl_Obj *objPtr;
04587 #define ENCODING_LINESIZE 20    /* Lower bound on how many bytes to convert at
04588                                  * a time. Since we don't know a priori how
04589                                  * many bytes of storage this many source
04590                                  * bytes will use, we actually need at least
04591                                  * ENCODING_LINESIZE * TCL_MAX_UTF bytes of
04592                                  * room. */
04593 
04594     objPtr = gsPtr->objPtr;
04595 
04596     /*
04597      * Subtract the number of bytes that were removed from channel buffer
04598      * during last call.
04599      */
04600 
04601     bufPtr = gsPtr->bufPtr;
04602     if (bufPtr != NULL) {
04603         bufPtr->nextRemoved += gsPtr->rawRead;
04604         if (!IsBufferReady(bufPtr)) {
04605             bufPtr = bufPtr->nextPtr;
04606         }
04607     }
04608     gsPtr->totalChars += gsPtr->charsWrote;
04609 
04610     if ((bufPtr == NULL) || (bufPtr->nextAdded == BUFFER_PADDING)) {
04611         /*
04612          * All channel buffers were exhausted and the caller still hasn't seen
04613          * EOL. Need to read more bytes from the channel device. Side effect
04614          * is to allocate another channel buffer.
04615          */
04616 
04617     read:
04618         if (statePtr->flags & CHANNEL_BLOCKED) {
04619             if (statePtr->flags & CHANNEL_NONBLOCKING) {
04620                 gsPtr->charsWrote = 0;
04621                 gsPtr->rawRead = 0;
04622                 return -1;
04623             }
04624             ResetFlag(statePtr, CHANNEL_BLOCKED);
04625         }
04626         if (GetInput(chanPtr) != 0) {
04627             gsPtr->charsWrote = 0;
04628             gsPtr->rawRead = 0;
04629             return -1;
04630         }
04631         bufPtr = statePtr->inQueueTail;
04632         gsPtr->bufPtr = bufPtr;
04633     }
04634 
04635     /*
04636      * Convert some of the bytes from the channel buffer to UTF-8. Space in
04637      * objPtr's string rep is used to hold the UTF-8 characters. Grow the
04638      * string rep if we need more space.
04639      */
04640 
04641     rawStart = RemovePoint(bufPtr);
04642     raw = rawStart;
04643     rawLen = BytesLeft(bufPtr);
04644 
04645     dst = *gsPtr->dstPtr;
04646     offset = dst - objPtr->bytes;
04647     toRead = ENCODING_LINESIZE;
04648     if (toRead > rawLen) {
04649         toRead = rawLen;
04650     }
04651     dstNeeded = toRead * TCL_UTF_MAX + 1;
04652     spaceLeft = objPtr->length - offset - TCL_UTF_MAX - 1;
04653     if (dstNeeded > spaceLeft) {
04654         length = offset * 2;
04655         if (offset < dstNeeded) {
04656             length = offset + dstNeeded;
04657         }
04658         length += TCL_UTF_MAX + 1;
04659         Tcl_SetObjLength(objPtr, length);
04660         spaceLeft = length - offset;
04661         dst = objPtr->bytes + offset;
04662         *gsPtr->dstPtr = dst;
04663     }
04664     gsPtr->state = statePtr->inputEncodingState;
04665     result = Tcl_ExternalToUtf(NULL, gsPtr->encoding, raw, rawLen,
04666             statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
04667             dst, spaceLeft, &gsPtr->rawRead, &gsPtr->bytesWrote,
04668             &gsPtr->charsWrote);
04669 
04670     /*
04671      * Make sure that if we go through 'gets', that we reset the
04672      * TCL_ENCODING_START flag still. [Bug #523988]
04673      */
04674 
04675     statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
04676 
04677     if (result == TCL_CONVERT_MULTIBYTE) {
04678         /*
04679          * The last few bytes in this channel buffer were the start of a
04680          * multibyte sequence. If this buffer was full, then move them to the
04681          * next buffer so the bytes will be contiguous.
04682          */
04683 
04684         ChannelBuffer *nextPtr;
04685         int extra;
04686 
04687         nextPtr = bufPtr->nextPtr;
04688         if (!IsBufferFull(bufPtr)) {
04689             if (gsPtr->rawRead > 0) {
04690                 /*
04691                  * Some raw bytes were converted to UTF-8. Fall through,
04692                  * returning those UTF-8 characters because a EOL might be
04693                  * present in them.
04694                  */
04695             } else if (statePtr->flags & CHANNEL_EOF) {
04696                 /*
04697                  * There was a partial character followed by EOF on the
04698                  * device. Fall through, returning that nothing was found.
04699                  */
04700 
04701                 bufPtr->nextRemoved = bufPtr->nextAdded;
04702             } else {
04703                 /*
04704                  * There are no more cached raw bytes left. See if we can get
04705                  * some more.
04706                  */
04707 
04708                 goto read;
04709             }
04710         } else {
04711             if (nextPtr == NULL) {
04712                 nextPtr = AllocChannelBuffer(statePtr->bufSize);
04713                 bufPtr->nextPtr = nextPtr;
04714                 statePtr->inQueueTail = nextPtr;
04715             }
04716             extra = rawLen - gsPtr->rawRead;
04717             memcpy(nextPtr->buf + BUFFER_PADDING - extra,
04718                     raw + gsPtr->rawRead, (size_t) extra);
04719             nextPtr->nextRemoved -= extra;
04720             bufPtr->nextAdded -= extra;
04721         }
04722     }
04723 
04724     gsPtr->bufPtr = bufPtr;
04725     return 0;
04726 }
04727 
04728 /*
04729  *---------------------------------------------------------------------------
04730  *
04731  * PeekAhead --
04732  *
04733  *      Helper function used by Tcl_GetsObj(). Called when we've seen a \r at
04734  *      the end of the UTF-8 string and want to look ahead one character to
04735  *      see if it is a \n.
04736  *
04737  * Results:
04738  *      *gsPtr->dstPtr is filled with a pointer to the start of the range of
04739  *      UTF-8 characters that were found by peeking and *dstEndPtr is filled
04740  *      with a pointer to the bytes just after the end of the range.
04741  *
04742  * Side effects:
04743  *      If no more raw bytes were available in one of the channel buffers,
04744  *      tries to perform a non-blocking read to get more bytes from the
04745  *      channel device.
04746  *
04747  *---------------------------------------------------------------------------
04748  */
04749 
04750 static void
04751 PeekAhead(
04752     Channel *chanPtr,           /* The channel to read. */
04753     char **dstEndPtr,           /* Filled with pointer to end of new range of
04754                                  * UTF-8 characters. */
04755     GetsState *gsPtr)           /* Current state of gets operation. */
04756 {
04757     ChannelState *statePtr = chanPtr->state;
04758                                 /* State info for channel */
04759     ChannelBuffer *bufPtr;
04760     Tcl_DriverBlockModeProc *blockModeProc;
04761     int bytesLeft;
04762 
04763     bufPtr = gsPtr->bufPtr;
04764 
04765     /*
04766      * If there's any more raw input that's still buffered, we'll peek into
04767      * that. Otherwise, only get more data from the channel driver if it looks
04768      * like there might actually be more data. The assumption is that if the
04769      * channel buffer is filled right up to the end, then there might be more
04770      * data to read.
04771      */
04772 
04773     blockModeProc = NULL;
04774     if (bufPtr->nextPtr == NULL) {
04775         bytesLeft = BytesLeft(bufPtr) - gsPtr->rawRead;
04776         if (bytesLeft == 0) {
04777             if (!IsBufferFull(bufPtr)) {
04778                 /*
04779                  * Don't peek ahead if last read was short read.
04780                  */
04781 
04782                 goto cleanup;
04783             }
04784             if ((statePtr->flags & CHANNEL_NONBLOCKING) == 0) {
04785                 blockModeProc = Tcl_ChannelBlockModeProc(chanPtr->typePtr);
04786                 if (blockModeProc == NULL) {
04787                     /*
04788                      * Don't peek ahead if cannot set non-blocking mode.
04789                      */
04790 
04791                     goto cleanup;
04792                 }
04793                 StackSetBlockMode(chanPtr, TCL_MODE_NONBLOCKING);
04794             }
04795         }
04796     }
04797     if (FilterInputBytes(chanPtr, gsPtr) == 0) {
04798         *dstEndPtr = *gsPtr->dstPtr + gsPtr->bytesWrote;
04799     }
04800     if (blockModeProc != NULL) {
04801         StackSetBlockMode(chanPtr, TCL_MODE_BLOCKING);
04802     }
04803     return;
04804 
04805   cleanup:
04806     bufPtr->nextRemoved += gsPtr->rawRead;
04807     gsPtr->rawRead = 0;
04808     gsPtr->totalChars += gsPtr->charsWrote;
04809     gsPtr->bytesWrote = 0;
04810     gsPtr->charsWrote = 0;
04811 }
04812 
04813 /*
04814  *---------------------------------------------------------------------------
04815  *
04816  * CommonGetsCleanup --
04817  *
04818  *      Helper function for Tcl_GetsObj() to restore the channel after a
04819  *      "gets" operation.
04820  *
04821  * Results:
04822  *      None.
04823  *
04824  * Side effects:
04825  *      Encoding may be freed.
04826  *
04827  *---------------------------------------------------------------------------
04828  */
04829 
04830 static void
04831 CommonGetsCleanup(
04832     Channel *chanPtr)
04833 {
04834     ChannelState *statePtr = chanPtr->state;
04835                                 /* State info for channel */
04836     ChannelBuffer *bufPtr, *nextPtr;
04837 
04838     bufPtr = statePtr->inQueueHead;
04839     for ( ; bufPtr != NULL; bufPtr = nextPtr) {
04840         nextPtr = bufPtr->nextPtr;
04841         if (IsBufferReady(bufPtr)) {
04842             break;
04843         }
04844         RecycleBuffer(statePtr, bufPtr, 0);
04845     }
04846     statePtr->inQueueHead = bufPtr;
04847     if (bufPtr == NULL) {
04848         statePtr->inQueueTail = NULL;
04849     } else {
04850         /*
04851          * If any multi-byte characters were split across channel buffer
04852          * boundaries, the split-up bytes were moved to the next channel
04853          * buffer by FilterInputBytes(). Move the bytes back to their original
04854          * buffer because the caller could change the channel's encoding which
04855          * could change the interpretation of whether those bytes really made
04856          * up multi-byte characters after all.
04857          */
04858 
04859         nextPtr = bufPtr->nextPtr;
04860         for ( ; nextPtr != NULL; nextPtr = bufPtr->nextPtr) {
04861             int extra;
04862 
04863             extra = SpaceLeft(bufPtr);
04864             if (extra > 0) {
04865                 memcpy(InsertPoint(bufPtr),
04866                         nextPtr->buf + BUFFER_PADDING - extra,
04867                         (size_t) extra);
04868                 bufPtr->nextAdded += extra;
04869                 nextPtr->nextRemoved = BUFFER_PADDING;
04870             }
04871             bufPtr = nextPtr;
04872         }
04873     }
04874 }
04875 
04876 /*
04877  *----------------------------------------------------------------------
04878  *
04879  * Tcl_Read --
04880  *
04881  *      Reads a given number of bytes from a channel. EOL and EOF translation
04882  *      is done on the bytes being read, so the number of bytes consumed from
04883  *      the channel may not be equal to the number of bytes stored in the
04884  *      destination buffer.
04885  *
04886  *      No encoding conversions are applied to the bytes being read.
04887  *
04888  * Results:
04889  *      The number of bytes read, or -1 on error. Use Tcl_GetErrno() to
04890  *      retrieve the error code for the error that occurred.
04891  *
04892  * Side effects:
04893  *      May cause input to be buffered.
04894  *
04895  *----------------------------------------------------------------------
04896  */
04897 
04898 int
04899 Tcl_Read(
04900     Tcl_Channel chan,           /* The channel from which to read. */
04901     char *dst,                  /* Where to store input read. */
04902     int bytesToRead)            /* Maximum number of bytes to read. */
04903 {
04904     Channel *chanPtr = (Channel *) chan;
04905     ChannelState *statePtr = chanPtr->state;
04906                                 /* State info for channel */
04907 
04908     /*
04909      * This operation should occur at the top of a channel stack.
04910      */
04911 
04912     chanPtr = statePtr->topChanPtr;
04913 
04914     if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
04915         return -1;
04916     }
04917 
04918     return DoRead(chanPtr, dst, bytesToRead);
04919 }
04920 
04921 /*
04922  *----------------------------------------------------------------------
04923  *
04924  * Tcl_ReadRaw --
04925  *
04926  *      Reads a given number of bytes from a channel. EOL and EOF translation
04927  *      is done on the bytes being read, so the number of bytes consumed from
04928  *      the channel may not be equal to the number of bytes stored in the
04929  *      destination buffer.
04930  *
04931  *      No encoding conversions are applied to the bytes being read.
04932  *
04933  * Results:
04934  *      The number of bytes read, or -1 on error. Use Tcl_GetErrno() to
04935  *      retrieve the error code for the error that occurred.
04936  *
04937  * Side effects:
04938  *      May cause input to be buffered.
04939  *
04940  *----------------------------------------------------------------------
04941  */
04942 
04943 int
04944 Tcl_ReadRaw(
04945     Tcl_Channel chan,           /* The channel from which to read. */
04946     char *bufPtr,               /* Where to store input read. */
04947     int bytesToRead)            /* Maximum number of bytes to read. */
04948 {
04949     Channel *chanPtr = (Channel *) chan;
04950     ChannelState *statePtr = chanPtr->state;
04951                                 /* State info for channel */
04952     int nread, result, copied, copiedNow;
04953 
04954     /*
04955      * The check below does too much because it will reject a call to this
04956      * function with a channel which is part of an 'fcopy'. But we have to
04957      * allow this here or else the chaining in the transformation drivers will
04958      * fail with 'file busy' error instead of retrieving and transforming the
04959      * data to copy.
04960      *
04961      * We let the check procedure now believe that there is no fcopy in
04962      * progress. A better solution than this might be an additional flag
04963      * argument to switch off specific checks.
04964      */
04965 
04966     if (CheckChannelErrors(statePtr, TCL_READABLE | CHANNEL_RAW_MODE) != 0) {
04967         return -1;
04968     }
04969 
04970     /*
04971      * Check for information in the push-back buffers. If there is some, use
04972      * it. Go to the driver only if there is none (anymore) and the caller
04973      * requests more bytes.
04974      */
04975 
04976     for (copied = 0; copied < bytesToRead; copied += copiedNow) {
04977         copiedNow = CopyBuffer(chanPtr, bufPtr + copied,
04978                 bytesToRead - copied);
04979         if (copiedNow == 0) {
04980             if (statePtr->flags & CHANNEL_EOF) {
04981                 goto done;
04982             }
04983             if (statePtr->flags & CHANNEL_BLOCKED) {
04984                 if (statePtr->flags & CHANNEL_NONBLOCKING) {
04985                     goto done;
04986                 }
04987                 ResetFlag(statePtr, CHANNEL_BLOCKED);
04988             }
04989 
04990 #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
04991             /*
04992              * [Bug 943274]. Better emulation of non-blocking channels for
04993              * channels without BlockModeProc, by keeping track of true
04994              * fileevents generated by the OS == Data waiting and reading if
04995              * and only if we are sure to have data.
04996              */
04997 
04998             if ((statePtr->flags & CHANNEL_NONBLOCKING) &&
04999                     (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) &&
05000                     !(statePtr->flags & CHANNEL_HAS_MORE_DATA)) {
05001                 /*
05002                  * We bypass the driver; it would block as no data is
05003                  * available.
05004                  */
05005 
05006                 nread = -1;
05007                 result = EWOULDBLOCK;
05008             } else {
05009 #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
05010 
05011                 /*
05012                  * Now go to the driver to get as much as is possible to fill
05013                  * the remaining request. Do all the error handling by
05014                  * ourselves. The code was stolen from 'GetInput' and slightly
05015                  * adapted (different return value here).
05016                  *
05017                  * The case of 'bytesToRead == 0' at this point cannot happen.
05018                  */
05019 
05020                 nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData,
05021                         bufPtr + copied, bytesToRead - copied, &result);
05022 
05023 #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
05024             }
05025 #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
05026 
05027             if (nread > 0) {
05028                 /*
05029                  * If we get a short read, signal up that we may be BLOCKED.
05030                  * We should avoid calling the driver because on some
05031                  * platforms we will block in the low level reading code even
05032                  * though the channel is set into nonblocking mode.
05033                  */
05034 
05035                 if (nread < (bytesToRead - copied)) {
05036                     SetFlag(statePtr, CHANNEL_BLOCKED);
05037                 }
05038 
05039 #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
05040                 if (nread <= (bytesToRead - copied)) {
05041                     /*
05042                      * [Bug 943274] We have read the available data, clear
05043                      * flag.
05044                      */
05045 
05046                     ResetFlag(statePtr, CHANNEL_HAS_MORE_DATA);
05047                 }
05048 #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
05049 
05050             } else if (nread == 0) {
05051                 SetFlag(statePtr, CHANNEL_EOF);
05052                 statePtr->inputEncodingFlags |= TCL_ENCODING_END;
05053 
05054             } else if (nread < 0) {
05055                 if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
05056                     if (copied > 0) {
05057                         /*
05058                          * Information that was copied earlier has precedence
05059                          * over EAGAIN/WOULDBLOCK handling.
05060                          */
05061 
05062                         return copied;
05063                     }
05064 
05065                     SetFlag(statePtr, CHANNEL_BLOCKED);
05066                     result = EAGAIN;
05067                 }
05068 
05069                 Tcl_SetErrno(result);
05070                 return -1;
05071             }
05072 
05073             return copied + nread;
05074         }
05075     }
05076 
05077   done:
05078     return copied;
05079 }
05080 
05081 /*
05082  *---------------------------------------------------------------------------
05083  *
05084  * Tcl_ReadChars --
05085  *
05086  *      Reads from the channel until the requested number of characters have
05087  *      been seen, EOF is seen, or the channel would block. EOL and EOF
05088  *      translation is done. If reading binary data, the raw bytes are wrapped
05089  *      in a Tcl byte array object. Otherwise, the raw bytes are converted to
05090  *      UTF-8 using the channel's current encoding and stored in a Tcl string
05091  *      object.
05092  *
05093  * Results:
05094  *      The number of characters read, or -1 on error. Use Tcl_GetErrno() to
05095  *      retrieve the error code for the error that occurred.
05096  *
05097  * Side effects:
05098  *      May cause input to be buffered.
05099  *
05100  *---------------------------------------------------------------------------
05101  */
05102 
05103 int
05104 Tcl_ReadChars(
05105     Tcl_Channel chan,           /* The channel to read. */
05106     Tcl_Obj *objPtr,            /* Input data is stored in this object. */
05107     int toRead,                 /* Maximum number of characters to store, or
05108                                  * -1 to read all available data (up to EOF or
05109                                  * when channel blocks). */
05110     int appendFlag)             /* If non-zero, data read from the channel
05111                                  * will be appended to the object. Otherwise,
05112                                  * the data will replace the existing contents
05113                                  * of the object. */
05114 {
05115     Channel *chanPtr = (Channel *) chan;
05116     ChannelState *statePtr = chanPtr->state;
05117                                 /* State info for channel */
05118 
05119     /*
05120      * This operation should occur at the top of a channel stack.
05121      */
05122 
05123     chanPtr = statePtr->topChanPtr;
05124 
05125     if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
05126         /*
05127          * Update the notifier state so we don't block while there is still
05128          * data in the buffers.
05129          */
05130 
05131         UpdateInterest(chanPtr);
05132         return -1;
05133     }
05134 
05135     return DoReadChars(chanPtr, objPtr, toRead, appendFlag);
05136 }
05137 /*
05138  *---------------------------------------------------------------------------
05139  *
05140  * DoReadChars --
05141  *
05142  *      Reads from the channel until the requested number of characters have
05143  *      been seen, EOF is seen, or the channel would block. EOL and EOF
05144  *      translation is done. If reading binary data, the raw bytes are wrapped
05145  *      in a Tcl byte array object. Otherwise, the raw bytes are converted to
05146  *      UTF-8 using the channel's current encoding and stored in a Tcl string
05147  *      object.
05148  *
05149  * Results:
05150  *      The number of characters read, or -1 on error. Use Tcl_GetErrno() to
05151  *      retrieve the error code for the error that occurred.
05152  *
05153  * Side effects:
05154  *      May cause input to be buffered.
05155  *
05156  *---------------------------------------------------------------------------
05157  */
05158 
05159 static int
05160 DoReadChars(
05161     Channel *chanPtr,           /* The channel to read. */
05162     Tcl_Obj *objPtr,            /* Input data is stored in this object. */
05163     int toRead,                 /* Maximum number of characters to store, or
05164                                  * -1 to read all available data (up to EOF or
05165                                  * when channel blocks). */
05166     int appendFlag)             /* If non-zero, data read from the channel
05167                                  * will be appended to the object. Otherwise,
05168                                  * the data will replace the existing contents
05169                                  * of the object. */
05170 {
05171     ChannelState *statePtr = chanPtr->state;
05172                                 /* State info for channel */
05173     ChannelBuffer *bufPtr;
05174     int offset, factor, copied, copiedNow, result;
05175     Tcl_Encoding encoding;
05176 #define UTF_EXPANSION_FACTOR    1024
05177 
05178     /*
05179      * This operation should occur at the top of a channel stack.
05180      */
05181 
05182     chanPtr = statePtr->topChanPtr;
05183     encoding = statePtr->encoding;
05184     factor = UTF_EXPANSION_FACTOR;
05185 
05186     if (appendFlag == 0) {
05187         if (encoding == NULL) {
05188             Tcl_SetByteArrayLength(objPtr, 0);
05189         } else {
05190             Tcl_SetObjLength(objPtr, 0);
05191 
05192             /*
05193              * We're going to access objPtr->bytes directly, so we must ensure
05194              * that this is actually a string object (otherwise it might have
05195              * been pure Unicode).
05196              */
05197 
05198             TclGetString(objPtr);
05199         }
05200         offset = 0;
05201     } else {
05202         if (encoding == NULL) {
05203             Tcl_GetByteArrayFromObj(objPtr, &offset);
05204         } else {
05205             TclGetStringFromObj(objPtr, &offset);
05206         }
05207     }
05208 
05209     for (copied = 0; (unsigned) toRead > 0; ) {
05210         copiedNow = -1;
05211         if (statePtr->inQueueHead != NULL) {
05212             if (encoding == NULL) {
05213                 copiedNow = ReadBytes(statePtr, objPtr, toRead, &offset);
05214             } else {
05215                 copiedNow = ReadChars(statePtr, objPtr, toRead, &offset,
05216                         &factor);
05217             }
05218 
05219             /*
05220              * If the current buffer is empty recycle it.
05221              */
05222 
05223             bufPtr = statePtr->inQueueHead;
05224             if (IsBufferEmpty(bufPtr)) {
05225                 ChannelBuffer *nextPtr;
05226 
05227                 nextPtr = bufPtr->nextPtr;
05228                 RecycleBuffer(statePtr, bufPtr, 0);
05229                 statePtr->inQueueHead = nextPtr;
05230                 if (nextPtr == NULL) {
05231                     statePtr->inQueueTail = NULL;
05232                 }
05233             }
05234         }
05235 
05236         if (copiedNow < 0) {
05237             if (statePtr->flags & CHANNEL_EOF) {
05238                 break;
05239             }
05240             if (statePtr->flags & CHANNEL_BLOCKED) {
05241                 if (statePtr->flags & CHANNEL_NONBLOCKING) {
05242                     break;
05243                 }
05244                 ResetFlag(statePtr, CHANNEL_BLOCKED);
05245             }
05246             result = GetInput(chanPtr);
05247             if (result != 0) {
05248                 if (result == EAGAIN) {
05249                     break;
05250                 }
05251                 copied = -1;
05252                 goto done;
05253             }
05254         } else {
05255             copied += copiedNow;
05256             toRead -= copiedNow;
05257         }
05258     }
05259 
05260     ResetFlag(statePtr, CHANNEL_BLOCKED);
05261     if (encoding == NULL) {
05262         Tcl_SetByteArrayLength(objPtr, offset);
05263     } else {
05264         Tcl_SetObjLength(objPtr, offset);
05265     }
05266 
05267     /*
05268      * Update the notifier state so we don't block while there is still data
05269      * in the buffers.
05270      */
05271 
05272   done:
05273     UpdateInterest(chanPtr);
05274     return copied;
05275 }
05276 
05277 /*
05278  *---------------------------------------------------------------------------
05279  *
05280  * ReadBytes --
05281  *
05282  *      Reads from the channel until the requested number of bytes have been
05283  *      seen, EOF is seen, or the channel would block. Bytes from the channel
05284  *      are stored in objPtr as a ByteArray object. EOL and EOF translation
05285  *      are done.
05286  *
05287  *      'bytesToRead' can safely be a very large number because space is only
05288  *      allocated to hold data read from the channel as needed.
05289  *
05290  * Results:
05291  *      The return value is the number of bytes appended to the object and
05292  *      *offsetPtr is filled with the total number of bytes in the object
05293  *      (greater than the return value if there were already bytes in the
05294  *      object).
05295  *
05296  * Side effects:
05297  *      None.
05298  *
05299  *---------------------------------------------------------------------------
05300  */
05301 
05302 static int
05303 ReadBytes(
05304     ChannelState *statePtr,     /* State of the channel to read. */
05305     Tcl_Obj *objPtr,            /* Input data is appended to this ByteArray
05306                                  * object. Its length is how much space has
05307                                  * been allocated to hold data, not how many
05308                                  * bytes of data have been stored in the
05309                                  * object. */
05310     int bytesToRead,            /* Maximum number of bytes to store, or < 0 to
05311                                  * get all available bytes. Bytes are obtained
05312                                  * from the first buffer in the queue - even
05313                                  * if this number is larger than the number of
05314                                  * bytes available in the first buffer, only
05315                                  * the bytes from the first buffer are
05316                                  * returned. */
05317     int *offsetPtr)             /* On input, contains how many bytes of objPtr
05318                                  * have been used to hold data. On output,
05319                                  * filled with how many bytes are now being
05320                                  * used. */
05321 {
05322     int toRead, srcLen, offset, length, srcRead, dstWrote;
05323     ChannelBuffer *bufPtr;
05324     char *src, *dst;
05325 
05326     offset = *offsetPtr;
05327 
05328     bufPtr = statePtr->inQueueHead;
05329     src = RemovePoint(bufPtr);
05330     srcLen = BytesLeft(bufPtr);
05331 
05332     toRead = bytesToRead;
05333     if ((unsigned) toRead > (unsigned) srcLen) {
05334         toRead = srcLen;
05335     }
05336 
05337     dst = (char *) Tcl_GetByteArrayFromObj(objPtr, &length);
05338     if (toRead > length - offset - 1) {
05339         /*
05340          * Double the existing size of the object or make enough room to hold
05341          * all the characters we may get from the source buffer, whichever is
05342          * larger.
05343          */
05344 
05345         length = offset * 2;
05346         if (offset < toRead) {
05347             length = offset + toRead + 1;
05348         }
05349         dst = (char *) Tcl_SetByteArrayLength(objPtr, length);
05350     }
05351     dst += offset;
05352 
05353     if (statePtr->flags & INPUT_NEED_NL) {
05354         ResetFlag(statePtr, INPUT_NEED_NL);
05355         if ((srcLen == 0) || (*src != '\n')) {
05356             *dst = '\r';
05357             *offsetPtr += 1;
05358             return 1;
05359         }
05360         *dst++ = '\n';
05361         src++;
05362         srcLen--;
05363         toRead--;
05364     }
05365 
05366     srcRead = srcLen;
05367     dstWrote = toRead;
05368     if (TranslateInputEOL(statePtr, dst, src, &dstWrote, &srcRead) != 0) {
05369         if (dstWrote == 0) {
05370             return -1;
05371         }
05372     }
05373     bufPtr->nextRemoved += srcRead;
05374     *offsetPtr += dstWrote;
05375     return dstWrote;
05376 }
05377 
05378 /*
05379  *---------------------------------------------------------------------------
05380  *
05381  * ReadChars --
05382  *
05383  *      Reads from the channel until the requested number of UTF-8 characters
05384  *      have been seen, EOF is seen, or the channel would block. Raw bytes
05385  *      from the channel are converted to UTF-8 and stored in objPtr. EOL and
05386  *      EOF translation is done.
05387  *
05388  *      'charsToRead' can safely be a very large number because space is only
05389  *      allocated to hold data read from the channel as needed.
05390  *
05391  * Results:
05392  *      The return value is the number of characters appended to the object,
05393  *      *offsetPtr is filled with the number of bytes that were appended, and
05394  *      *factorPtr is filled with the expansion factor used to guess how many
05395  *      bytes of UTF-8 to allocate to hold N source bytes.
05396  *
05397  * Side effects:
05398  *      None.
05399  *
05400  *---------------------------------------------------------------------------
05401  */
05402 
05403 static int
05404 ReadChars(
05405     ChannelState *statePtr,     /* State of channel to read. */
05406     Tcl_Obj *objPtr,            /* Input data is appended to this object.
05407                                  * objPtr->length is how much space has been
05408                                  * allocated to hold data, not how many bytes
05409                                  * of data have been stored in the object. */
05410     int charsToRead,            /* Maximum number of characters to store, or
05411                                  * -1 to get all available characters.
05412                                  * Characters are obtained from the first
05413                                  * buffer in the queue -- even if this number
05414                                  * is larger than the number of characters
05415                                  * available in the first buffer, only the
05416                                  * characters from the first buffer are
05417                                  * returned. */
05418     int *offsetPtr,             /* On input, contains how many bytes of objPtr
05419                                  * have been used to hold data. On output,
05420                                  * filled with how many bytes are now being
05421                                  * used. */
05422     int *factorPtr)             /* On input, contains a guess of how many
05423                                  * bytes need to be allocated to hold the
05424                                  * result of converting N source bytes to
05425                                  * UTF-8. On output, contains another guess
05426                                  * based on the data seen so far. */
05427 {
05428     int toRead, factor, offset, spaceLeft, length, srcLen, dstNeeded;
05429     int srcRead, dstWrote, numChars, dstRead;
05430     ChannelBuffer *bufPtr;
05431     char *src, *dst;
05432     Tcl_EncodingState oldState;
05433     int encEndFlagSuppressed = 0;
05434 
05435     factor = *factorPtr;
05436     offset = *offsetPtr;
05437 
05438     bufPtr = statePtr->inQueueHead;
05439     src = RemovePoint(bufPtr);
05440     srcLen = BytesLeft(bufPtr);
05441 
05442     toRead = charsToRead;
05443     if ((unsigned)toRead > (unsigned)srcLen) {
05444         toRead = srcLen;
05445     }
05446 
05447     /*
05448      * 'factor' is how much we guess that the bytes in the source buffer will
05449      * expand when converted to UTF-8 chars. This guess comes from analyzing
05450      * how many characters were produced by the previous pass.
05451      */
05452 
05453     dstNeeded = toRead * factor / UTF_EXPANSION_FACTOR;
05454     spaceLeft = objPtr->length - offset - TCL_UTF_MAX - 1;
05455 
05456     if (dstNeeded > spaceLeft) {
05457         /*
05458          * Double the existing size of the object or make enough room to hold
05459          * all the characters we want from the source buffer, whichever is
05460          * larger.
05461          */
05462 
05463         length = offset * 2;
05464         if (offset < dstNeeded) {
05465             length = offset + dstNeeded;
05466         }
05467         spaceLeft = length - offset;
05468         length += TCL_UTF_MAX + 1;
05469         Tcl_SetObjLength(objPtr, length);
05470     }
05471     if (toRead == srcLen) {
05472         /*
05473          * Want to convert the whole buffer in one pass. If we have enough
05474          * space, convert it using all available space in object rather than
05475          * using the factor.
05476          */
05477 
05478         dstNeeded = spaceLeft;
05479     }
05480     dst = objPtr->bytes + offset;
05481 
05482     /*
05483      * [Bug 1462248]: The cause of the crash reported in this bug is this:
05484      *
05485      * - ReadChars, called with a single buffer, with a incomplete
05486      *   multi-byte character at the end (only the first byte of it).
05487      * - Encoding translation fails, asks for more data
05488      * - Data is read, and eof is reached, TCL_ENCODING_END (TEE) is set.
05489      * - ReadChar is called again, converts the first buffer, but due to TEE
05490      *   it does not check for incomplete multi-byte data, and the character
05491      *   just after the end of the first buffer is a valid completion of the
05492      *   multi-byte header in the actual buffer. The conversion reads more
05493      *   characters from the buffer then present. This causes nextRemoved to
05494      *   overshoot nextAdded and the next reads compute a negative srcLen,
05495      *   cause further translations to fail, causing copying of data into the
05496      *   next buffer using bad arguments, causing the mecpy for to eventually
05497      *   fail.
05498      *
05499      * In the end it is a memory access bug spiraling out of control if the
05500      * conditions are _just so_. And ultimate cause is that TEE is given to a
05501      * conversion where it should not. TEE signals that this is the last
05502      * buffer. Except in our case it is not.
05503      *
05504      * My solution is to suppress TEE if the first buffer is not the last. We
05505      * will eventually need it given that EOF has been reached, but not right
05506      * now. This is what the new flag "endEncSuppressFlag" is for.
05507      *
05508      * The bug in 'Tcl_Utf2UtfProc' where it read from memory behind the
05509      * actual buffer has been fixed as well, and fixes the problem with the
05510      * crash too, but this would still allow the generic layer to
05511      * accidentially break a multi-byte sequence if the conditions are just
05512      * right, because again the ExternalToUtf would be successful where it
05513      * should not.
05514      */
05515 
05516     if ((statePtr->inputEncodingFlags & TCL_ENCODING_END) &&
05517             (bufPtr->nextPtr != NULL)) {
05518         /*
05519          * TEE is set for a buffer which is not the last. Squash it for now,
05520          * and restore it later, before yielding control to our caller.
05521          */
05522 
05523         statePtr->inputEncodingFlags &= ~TCL_ENCODING_END;
05524         encEndFlagSuppressed = 1;
05525     }
05526 
05527     oldState = statePtr->inputEncodingState;
05528     if (statePtr->flags & INPUT_NEED_NL) {
05529         /*
05530          * We want a '\n' because the last character we saw was '\r'.
05531          */
05532 
05533         ResetFlag(statePtr, INPUT_NEED_NL);
05534         Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
05535                 statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
05536                 dst, TCL_UTF_MAX + 1, &srcRead, &dstWrote, &numChars);
05537         if ((dstWrote > 0) && (*dst == '\n')) {
05538             /*
05539              * The next char was a '\n'. Consume it and produce a '\n'.
05540              */
05541 
05542             bufPtr->nextRemoved += srcRead;
05543         } else {
05544             /*
05545              * The next char was not a '\n'. Produce a '\r'.
05546              */
05547 
05548             *dst = '\r';
05549         }
05550         statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
05551         *offsetPtr += 1;
05552 
05553         if (encEndFlagSuppressed) {
05554             statePtr->inputEncodingFlags |= TCL_ENCODING_END;
05555         }
05556         return 1;
05557     }
05558 
05559     Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
05560             statePtr->inputEncodingFlags, &statePtr->inputEncodingState, dst,
05561             dstNeeded + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars);
05562 
05563     if (encEndFlagSuppressed) {
05564         statePtr->inputEncodingFlags |= TCL_ENCODING_END;
05565     }
05566 
05567     if (srcRead == 0) {
05568         /*
05569          * Not enough bytes in src buffer to make a complete char. Copy the
05570          * bytes to the next buffer to make a new contiguous string, then tell
05571          * the caller to fill the buffer with more bytes.
05572          */
05573 
05574         ChannelBuffer *nextPtr;
05575 
05576         nextPtr = bufPtr->nextPtr;
05577         if (nextPtr == NULL) {
05578             if (srcLen > 0) {
05579                 /*
05580                  * There isn't enough data in the buffers to complete the next
05581                  * character, so we need to wait for more data before the next
05582                  * file event can be delivered. [Bug 478856]
05583                  *
05584                  * The exception to this is if the input buffer was completely
05585                  * empty before we tried to convert its contents. Nothing in,
05586                  * nothing out, and no incomplete character data. The
05587                  * conversion before the current one was complete.
05588                  */
05589 
05590                 SetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
05591             }
05592             return -1;
05593         }
05594 
05595         /*
05596          * Space is made at the beginning of the buffer to copy the previous
05597          * unused bytes there. Check first if the buffer we are using actually
05598          * has enough space at its beginning for the data we are copying.
05599          * Because if not we will write over the buffer management
05600          * information, especially the 'nextPtr'.
05601          *
05602          * Note that the BUFFER_PADDING (See AllocChannelBuffer) is used to
05603          * prevent exactly this situation. I.e. it should never happen.
05604          * Therefore it is ok to panic should it happen despite the
05605          * precautions.
05606          */
05607 
05608         if (nextPtr->nextRemoved - srcLen < 0) {
05609             Tcl_Panic("Buffer Underflow, BUFFER_PADDING not enough");
05610         }
05611 
05612         nextPtr->nextRemoved -= srcLen;
05613         memcpy(RemovePoint(nextPtr), src, (size_t) srcLen);
05614         RecycleBuffer(statePtr, bufPtr, 0);
05615         statePtr->inQueueHead = nextPtr;
05616         return ReadChars(statePtr, objPtr, charsToRead, offsetPtr, factorPtr);
05617     }
05618 
05619     dstRead = dstWrote;
05620     if (TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead) != 0) {
05621         /*
05622          * Hit EOF char. How many bytes of src correspond to where the EOF was
05623          * located in dst? Run the conversion again with an output buffer just
05624          * big enough to hold the data so we can get the correct value for
05625          * srcRead.
05626          */
05627 
05628         if (dstWrote == 0) {
05629             return -1;
05630         }
05631         statePtr->inputEncodingState = oldState;
05632         Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
05633                 statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
05634                 dst, dstRead + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars);
05635         TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead);
05636     }
05637 
05638     /*
05639      * The number of characters that we got may be less than the number that
05640      * we started with because "\r\n" sequences may have been turned into just
05641      * '\n' in dst.
05642      */
05643 
05644     numChars -= (dstRead - dstWrote);
05645 
05646     if ((unsigned) numChars > (unsigned) toRead) {
05647         /*
05648          * Got too many chars.
05649          */
05650 
05651         const char *eof;
05652 
05653         eof = Tcl_UtfAtIndex(dst, toRead);
05654         statePtr->inputEncodingState = oldState;
05655         Tcl_ExternalToUtf(NULL, statePtr->encoding, src, srcLen,
05656                 statePtr->inputEncodingFlags, &statePtr->inputEncodingState,
05657                 dst, eof - dst + TCL_UTF_MAX, &srcRead, &dstWrote, &numChars);
05658         dstRead = dstWrote;
05659         TranslateInputEOL(statePtr, dst, dst, &dstWrote, &dstRead);
05660         numChars -= (dstRead - dstWrote);
05661     }
05662     statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
05663 
05664     bufPtr->nextRemoved += srcRead;
05665     if (dstWrote > srcRead + 1) {
05666         *factorPtr = dstWrote * UTF_EXPANSION_FACTOR / srcRead;
05667     }
05668     *offsetPtr += dstWrote;
05669     return numChars;
05670 }
05671 
05672 /*
05673  *---------------------------------------------------------------------------
05674  *
05675  * TranslateInputEOL --
05676  *
05677  *      Perform input EOL and EOF translation on the source buffer, leaving
05678  *      the translated result in the destination buffer.
05679  *
05680  * Results:
05681  *      The return value is 1 if the EOF character was found when copying
05682  *      bytes to the destination buffer, 0 otherwise.
05683  *
05684  * Side effects:
05685  *      None.
05686  *
05687  *---------------------------------------------------------------------------
05688  */
05689 
05690 static int
05691 TranslateInputEOL(
05692     ChannelState *statePtr,     /* Channel being read, for EOL translation and
05693                                  * EOF character. */
05694     char *dstStart,             /* Output buffer filled with chars by applying
05695                                  * appropriate EOL translation to source
05696                                  * characters. */
05697     const char *srcStart,       /* Source characters. */
05698     int *dstLenPtr,             /* On entry, the maximum length of output
05699                                  * buffer in bytes; must be <= *srcLenPtr. On
05700                                  * exit, the number of bytes actually used in
05701                                  * output buffer. */
05702     int *srcLenPtr)             /* On entry, the length of source buffer. On
05703                                  * exit, the number of bytes read from the
05704                                  * source buffer. */
05705 {
05706     int dstLen, srcLen, inEofChar;
05707     const char *eof;
05708 
05709     dstLen = *dstLenPtr;
05710 
05711     eof = NULL;
05712     inEofChar = statePtr->inEofChar;
05713     if (inEofChar != '\0') {
05714         /*
05715          * Find EOF in translated buffer then compress out the EOL. The source
05716          * buffer may be much longer than the destination buffer - we only
05717          * want to return EOF if the EOF has been copied to the destination
05718          * buffer.
05719          */
05720 
05721         const char *src, *srcMax;
05722 
05723         srcMax = srcStart + *srcLenPtr;
05724         for (src = srcStart; src < srcMax; src++) {
05725             if (*src == inEofChar) {
05726                 eof = src;
05727                 srcLen = src - srcStart;
05728                 if (srcLen < dstLen) {
05729                     dstLen = srcLen;
05730                 }
05731                 *srcLenPtr = srcLen;
05732                 break;
05733             }
05734         }
05735     }
05736     switch (statePtr->inputTranslation) {
05737     case TCL_TRANSLATE_LF:
05738         if (dstStart != srcStart) {
05739             memcpy(dstStart, srcStart, (size_t) dstLen);
05740         }
05741         srcLen = dstLen;
05742         break;
05743     case TCL_TRANSLATE_CR: {
05744         char *dst, *dstEnd;
05745 
05746         if (dstStart != srcStart) {
05747             memcpy(dstStart, srcStart, (size_t) dstLen);
05748         }
05749         dstEnd = dstStart + dstLen;
05750         for (dst = dstStart; dst < dstEnd; dst++) {
05751             if (*dst == '\r') {
05752                 *dst = '\n';
05753             }
05754         }
05755         srcLen = dstLen;
05756         break;
05757     }
05758     case TCL_TRANSLATE_CRLF: {
05759         char *dst;
05760         const char *src, *srcEnd, *srcMax;
05761 
05762         dst = dstStart;
05763         src = srcStart;
05764         srcEnd = srcStart + dstLen;
05765         srcMax = srcStart + *srcLenPtr;
05766 
05767         for ( ; src < srcEnd; ) {
05768             if (*src == '\r') {
05769                 src++;
05770                 if (src >= srcMax) {
05771                     SetFlag(statePtr, INPUT_NEED_NL);
05772                 } else if (*src == '\n') {
05773                     *dst++ = *src++;
05774                 } else {
05775                     *dst++ = '\r';
05776                 }
05777             } else {
05778                 *dst++ = *src++;
05779             }
05780         }
05781         srcLen = src - srcStart;
05782         dstLen = dst - dstStart;
05783         break;
05784     }
05785     case TCL_TRANSLATE_AUTO: {
05786         char *dst;
05787         const char *src, *srcEnd, *srcMax;
05788 
05789         dst = dstStart;
05790         src = srcStart;
05791         srcEnd = srcStart + dstLen;
05792         srcMax = srcStart + *srcLenPtr;
05793 
05794         if ((statePtr->flags & INPUT_SAW_CR) && (src < srcMax)) {
05795             if (*src == '\n') {
05796                 src++;
05797             }
05798             ResetFlag(statePtr, INPUT_SAW_CR);
05799         }
05800         for ( ; src < srcEnd; ) {
05801             if (*src == '\r') {
05802                 src++;
05803                 if (src >= srcMax) {
05804                     SetFlag(statePtr, INPUT_SAW_CR);
05805                 } else if (*src == '\n') {
05806                     if (srcEnd < srcMax) {
05807                         srcEnd++;
05808                     }
05809                     src++;
05810                 }
05811                 *dst++ = '\n';
05812             } else {
05813                 *dst++ = *src++;
05814             }
05815         }
05816         srcLen = src - srcStart;
05817         dstLen = dst - dstStart;
05818         break;
05819     }
05820     default:
05821         return 0;
05822     }
05823     *dstLenPtr = dstLen;
05824 
05825     if ((eof != NULL) && (srcStart + srcLen >= eof)) {
05826         /*
05827          * EOF character was seen in EOL translated range. Leave current file
05828          * position pointing at the EOF character, but don't store the EOF
05829          * character in the output string.
05830          */
05831 
05832         SetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF);
05833         statePtr->inputEncodingFlags |= TCL_ENCODING_END;
05834         ResetFlag(statePtr, INPUT_SAW_CR | INPUT_NEED_NL);
05835         return 1;
05836     }
05837 
05838     *srcLenPtr = srcLen;
05839     return 0;
05840 }
05841 
05842 /*
05843  *----------------------------------------------------------------------
05844  *
05845  * Tcl_Ungets --
05846  *
05847  *      Causes the supplied string to be added to the input queue of the
05848  *      channel, at either the head or tail of the queue.
05849  *
05850  * Results:
05851  *      The number of bytes stored in the channel, or -1 on error.
05852  *
05853  * Side effects:
05854  *      Adds input to the input queue of a channel.
05855  *
05856  *----------------------------------------------------------------------
05857  */
05858 
05859 int
05860 Tcl_Ungets(
05861     Tcl_Channel chan,           /* The channel for which to add the input. */
05862     const char *str,            /* The input itself. */
05863     int len,                    /* The length of the input. */
05864     int atEnd)                  /* If non-zero, add at end of queue; otherwise
05865                                  * add at head of queue. */
05866 {
05867     Channel *chanPtr;           /* The real IO channel. */
05868     ChannelState *statePtr;     /* State of actual channel. */
05869     ChannelBuffer *bufPtr;      /* Buffer to contain the data. */
05870     int flags;
05871 
05872     chanPtr = (Channel *) chan;
05873     statePtr = chanPtr->state;
05874 
05875     /*
05876      * This operation should occur at the top of a channel stack.
05877      */
05878 
05879     chanPtr = statePtr->topChanPtr;
05880 
05881     /*
05882      * CheckChannelErrors clears too many flag bits in this one case.
05883      */
05884 
05885     flags = statePtr->flags;
05886     if (CheckChannelErrors(statePtr, TCL_READABLE) != 0) {
05887         len = -1;
05888         goto done;
05889     }
05890     statePtr->flags = flags;
05891 
05892     /*
05893      * If we have encountered a sticky EOF, just punt without storing (sticky
05894      * EOF is set if we have seen the input eofChar, to prevent reading beyond
05895      * the eofChar). Otherwise, clear the EOF flags, and clear the BLOCKED
05896      * bit. We want to discover these conditions anew in each operation.
05897      */
05898 
05899     if (statePtr->flags & CHANNEL_STICKY_EOF) {
05900         goto done;
05901     }
05902     ResetFlag(statePtr, CHANNEL_BLOCKED | CHANNEL_EOF);
05903 
05904     bufPtr = AllocChannelBuffer(len);
05905     memcpy(InsertPoint(bufPtr), str, (size_t) len);
05906     bufPtr->nextAdded += len;
05907 
05908     if (statePtr->inQueueHead == NULL) {
05909         bufPtr->nextPtr = NULL;
05910         statePtr->inQueueHead = bufPtr;
05911         statePtr->inQueueTail = bufPtr;
05912     } else if (atEnd) {
05913         bufPtr->nextPtr = NULL;
05914         statePtr->inQueueTail->nextPtr = bufPtr;
05915         statePtr->inQueueTail = bufPtr;
05916     } else {
05917         bufPtr->nextPtr = statePtr->inQueueHead;
05918         statePtr->inQueueHead = bufPtr;
05919     }
05920 
05921     /*
05922      * Update the notifier state so we don't block while there is still data
05923      * in the buffers.
05924      */
05925 
05926   done:
05927     UpdateInterest(chanPtr);
05928     return len;
05929 }
05930 
05931 /*
05932  *----------------------------------------------------------------------
05933  *
05934  * Tcl_Flush --
05935  *
05936  *      Flushes output data on a channel.
05937  *
05938  * Results:
05939  *      A standard Tcl result.
05940  *
05941  * Side effects:
05942  *      May flush output queued on this channel.
05943  *
05944  *----------------------------------------------------------------------
05945  */
05946 
05947 int
05948 Tcl_Flush(
05949     Tcl_Channel chan)           /* The Channel to flush. */
05950 {
05951     int result;                 /* Of calling FlushChannel. */
05952     Channel *chanPtr = (Channel *) chan;
05953                                 /* The actual channel. */
05954     ChannelState *statePtr = chanPtr->state;
05955                                 /* State of actual channel. */
05956 
05957     /*
05958      * This operation should occur at the top of a channel stack.
05959      */
05960 
05961     chanPtr = statePtr->topChanPtr;
05962 
05963     if (CheckChannelErrors(statePtr, TCL_WRITABLE) != 0) {
05964         return -1;
05965     }
05966 
05967     /*
05968      * Force current output buffer to be output also.
05969      */
05970 
05971     if ((statePtr->curOutPtr != NULL) && IsBufferReady(statePtr->curOutPtr)) {
05972         SetFlag(statePtr, BUFFER_READY);
05973     }
05974 
05975     result = FlushChannel(NULL, chanPtr, 0);
05976     if (result != 0) {
05977         return TCL_ERROR;
05978     }
05979 
05980     return TCL_OK;
05981 }
05982 
05983 /*
05984  *----------------------------------------------------------------------
05985  *
05986  * DiscardInputQueued --
05987  *
05988  *      Discards any input read from the channel but not yet consumed by Tcl
05989  *      reading commands.
05990  *
05991  * Results:
05992  *      None.
05993  *
05994  * Side effects:
05995  *      May discard input from the channel. If discardLastBuffer is zero,
05996  *      leaves one buffer in place for back-filling.
05997  *
05998  *----------------------------------------------------------------------
05999  */
06000 
06001 static void
06002 DiscardInputQueued(
06003     ChannelState *statePtr,     /* Channel on which to discard the queued
06004                                  * input. */
06005     int discardSavedBuffers)    /* If non-zero, discard all buffers including
06006                                  * last one. */
06007 {
06008     ChannelBuffer *bufPtr, *nxtPtr;
06009                                 /* Loop variables. */
06010 
06011     bufPtr = statePtr->inQueueHead;
06012     statePtr->inQueueHead = NULL;
06013     statePtr->inQueueTail = NULL;
06014     for (; bufPtr != NULL; bufPtr = nxtPtr) {
06015         nxtPtr = bufPtr->nextPtr;
06016         RecycleBuffer(statePtr, bufPtr, discardSavedBuffers);
06017     }
06018 
06019     /*
06020      * If discardSavedBuffers is nonzero, must also discard any previously
06021      * saved buffer in the saveInBufPtr field.
06022      */
06023 
06024     if (discardSavedBuffers && statePtr->saveInBufPtr != NULL) {
06025         ckfree((char *) statePtr->saveInBufPtr);
06026         statePtr->saveInBufPtr = NULL;
06027     }
06028 }
06029 
06030 /*
06031  *---------------------------------------------------------------------------
06032  *
06033  * GetInput --
06034  *
06035  *      Reads input data from a device into a channel buffer.
06036  *
06037  * Results:
06038  *      The return value is the Posix error code if an error occurred while
06039  *      reading from the file, or 0 otherwise.
06040  *
06041  * Side effects:
06042  *      Reads from the underlying device.
06043  *
06044  *---------------------------------------------------------------------------
06045  */
06046 
06047 static int
06048 GetInput(
06049     Channel *chanPtr)           /* Channel to read input from. */
06050 {
06051     int toRead;                 /* How much to read? */
06052     int result;                 /* Of calling driver. */
06053     int nread;                  /* How much was read from channel? */
06054     ChannelBuffer *bufPtr;      /* New buffer to add to input queue. */
06055     ChannelState *statePtr = chanPtr->state;
06056                                 /* State info for channel */
06057 
06058     /*
06059      * Prevent reading from a dead channel -- a channel that has been closed
06060      * but not yet deallocated, which can happen if the exit handler for
06061      * channel cleanup has run but the channel is still registered in some
06062      * interpreter.
06063      */
06064 
06065     if (CheckForDeadChannel(NULL, statePtr)) {
06066         return EINVAL;
06067     }
06068 
06069     /*
06070      * First check for more buffers in the pushback area of the topmost
06071      * channel in the stack and use them. They can be the result of a
06072      * transformation which went away without reading all the information
06073      * placed in the area when it was stacked.
06074      *
06075      * Two possibilities for the state: No buffers in it, or a single empty
06076      * buffer. In the latter case we can recycle it now.
06077      */
06078 
06079     if (chanPtr->inQueueHead != NULL) {
06080         if (statePtr->inQueueHead != NULL) {
06081             RecycleBuffer(statePtr, statePtr->inQueueHead, 0);
06082             statePtr->inQueueHead = NULL;
06083         }
06084 
06085         statePtr->inQueueHead = chanPtr->inQueueHead;
06086         statePtr->inQueueTail = chanPtr->inQueueTail;
06087         chanPtr->inQueueHead = NULL;
06088         chanPtr->inQueueTail = NULL;
06089         return 0;
06090     }
06091 
06092     /*
06093      * Nothing in the pushback area, fall back to the usual handling (driver,
06094      * etc.)
06095      */
06096 
06097     /*
06098      * See if we can fill an existing buffer. If we can, read only as much as
06099      * will fit in it. Otherwise allocate a new buffer, add it to the input
06100      * queue and attempt to fill it to the max.
06101      */
06102 
06103     bufPtr = statePtr->inQueueTail;
06104     if ((bufPtr != NULL) && !IsBufferFull(bufPtr)) {
06105         toRead = SpaceLeft(bufPtr);
06106     } else {
06107         bufPtr = statePtr->saveInBufPtr;
06108         statePtr->saveInBufPtr = NULL;
06109 
06110         /*
06111          * Check the actual buffersize against the requested buffersize.
06112          * Buffers which are smaller than requested are squashed. This is done
06113          * to honor dynamic changes of the buffersize made by the user.
06114          */
06115 
06116         if ((bufPtr != NULL)
06117                 && (bufPtr->bufLength - BUFFER_PADDING < statePtr->bufSize)) {
06118             ckfree((char *) bufPtr);
06119             bufPtr = NULL;
06120         }
06121 
06122         if (bufPtr == NULL) {
06123             bufPtr = AllocChannelBuffer(statePtr->bufSize);
06124         }
06125         bufPtr->nextPtr = NULL;
06126 
06127         /*
06128          * SF #427196: Use the actual size of the buffer to determine the
06129          * number of bytes to read from the channel and not the size for new
06130          * buffers. They can be different if the buffersize was changed
06131          * between reads.
06132          *
06133          * Note: This affects performance negatively if the buffersize was
06134          * extended but this small buffer is reused for all subsequent reads.
06135          * The system never uses buffers with the requested bigger size in
06136          * that case. An adjunct patch could try and delete all unused buffers
06137          * it encounters and which are smaller than the formally requested
06138          * buffersize.
06139          */
06140 
06141         toRead = SpaceLeft(bufPtr);
06142 
06143         if (statePtr->inQueueTail == NULL) {
06144             statePtr->inQueueHead = bufPtr;
06145         } else {
06146             statePtr->inQueueTail->nextPtr = bufPtr;
06147         }
06148         statePtr->inQueueTail = bufPtr;
06149     }
06150 
06151     /*
06152      * If EOF is set, we should avoid calling the driver because on some
06153      * platforms it is impossible to read from a device after EOF.
06154      */
06155 
06156     if (statePtr->flags & CHANNEL_EOF) {
06157         return 0;
06158     }
06159 
06160 #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
06161     /*
06162      * [SF Tcl Bug 943274]. Better emulation of non-blocking channels for
06163      * channels without BlockModeProc, by keeping track of true fileevents
06164      * generated by the OS == Data waiting and reading if and only if we are
06165      * sure to have data.
06166      */
06167 
06168     if ((statePtr->flags & CHANNEL_NONBLOCKING) &&
06169             (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) &&
06170             !(statePtr->flags & CHANNEL_HAS_MORE_DATA)) {
06171         /*
06172          * Bypass the driver, it would block, as no data is available
06173          */
06174 
06175         nread = -1;
06176         result = EWOULDBLOCK;
06177     } else {
06178 #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
06179 
06180         nread = (chanPtr->typePtr->inputProc)(chanPtr->instanceData,
06181                 InsertPoint(bufPtr), toRead, &result);
06182 
06183 #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
06184     }
06185 #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
06186 
06187     if (nread > 0) {
06188         bufPtr->nextAdded += nread;
06189 
06190         /*
06191          * If we get a short read, signal up that we may be BLOCKED. We should
06192          * avoid calling the driver because on some platforms we will block in
06193          * the low level reading code even though the channel is set into
06194          * nonblocking mode.
06195          */
06196 
06197         if (nread < toRead) {
06198             SetFlag(statePtr, CHANNEL_BLOCKED);
06199         }
06200 
06201 #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
06202         if (nread <= toRead) {
06203             /*
06204              * [SF Tcl Bug 943274] We have read the available data, clear
06205              * flag.
06206              */
06207 
06208             ResetFlag(statePtr, CHANNEL_HAS_MORE_DATA);
06209         }
06210 #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
06211 
06212     } else if (nread == 0) {
06213         SetFlag(statePtr, CHANNEL_EOF);
06214         statePtr->inputEncodingFlags |= TCL_ENCODING_END;
06215     } else if (nread < 0) {
06216         if ((result == EWOULDBLOCK) || (result == EAGAIN)) {
06217             SetFlag(statePtr, CHANNEL_BLOCKED);
06218             result = EAGAIN;
06219         }
06220         Tcl_SetErrno(result);
06221         return result;
06222     }
06223     return 0;
06224 }
06225 
06226 /*
06227  *----------------------------------------------------------------------
06228  *
06229  * Tcl_Seek --
06230  *
06231  *      Implements seeking on Tcl Channels. This is a public function so that
06232  *      other C facilities may be implemented on top of it.
06233  *
06234  * Results:
06235  *      The new access point or -1 on error. If error, use Tcl_GetErrno() to
06236  *      retrieve the POSIX error code for the error that occurred.
06237  *
06238  * Side effects:
06239  *      May flush output on the channel. May discard queued input.
06240  *
06241  *----------------------------------------------------------------------
06242  */
06243 
06244 Tcl_WideInt
06245 Tcl_Seek(
06246     Tcl_Channel chan,           /* The channel on which to seek. */
06247     Tcl_WideInt offset,         /* Offset to seek to. */
06248     int mode)                   /* Relative to which location to seek? */
06249 {
06250     Channel *chanPtr = (Channel *) chan;
06251                                 /* The real IO channel. */
06252     ChannelState *statePtr = chanPtr->state;
06253                                 /* State info for channel */
06254     int inputBuffered, outputBuffered;
06255                                 /* # bytes held in buffers. */
06256     int result;                 /* Of device driver operations. */
06257     Tcl_WideInt curPos;         /* Position on the device. */
06258     int wasAsync;               /* Was the channel nonblocking before the seek
06259                                  * operation? If so, must restore to
06260                                  * non-blocking mode after the seek. */
06261 
06262     if (CheckChannelErrors(statePtr, TCL_WRITABLE | TCL_READABLE) != 0) {
06263         return Tcl_LongAsWide(-1);
06264     }
06265 
06266     /*
06267      * Disallow seek on dead channels - channels that have been closed but not
06268      * yet been deallocated. Such channels can be found if the exit handler
06269      * for channel cleanup has run but the channel is still registered in an
06270      * interpreter.
06271      */
06272 
06273     if (CheckForDeadChannel(NULL, statePtr)) {
06274         return Tcl_LongAsWide(-1);
06275     }
06276 
06277     /*
06278      * This operation should occur at the top of a channel stack.
06279      */
06280 
06281     chanPtr = statePtr->topChanPtr;
06282 
06283     /*
06284      * Disallow seek on channels whose type does not have a seek procedure
06285      * defined. This means that the channel does not support seeking.
06286      */
06287 
06288     if (chanPtr->typePtr->seekProc == NULL) {
06289         Tcl_SetErrno(EINVAL);
06290         return Tcl_LongAsWide(-1);
06291     }
06292 
06293     /*
06294      * Compute how much input and output is buffered. If both input and output
06295      * is buffered, cannot compute the current position.
06296      */
06297 
06298     inputBuffered = Tcl_InputBuffered(chan);
06299     outputBuffered = Tcl_OutputBuffered(chan);
06300 
06301     if ((inputBuffered != 0) && (outputBuffered != 0)) {
06302         Tcl_SetErrno(EFAULT);
06303         return Tcl_LongAsWide(-1);
06304     }
06305 
06306     /*
06307      * If we are seeking relative to the current position, compute the
06308      * corrected offset taking into account the amount of unread input.
06309      */
06310 
06311     if (mode == SEEK_CUR) {
06312         offset -= inputBuffered;
06313     }
06314 
06315     /*
06316      * Discard any queued input - this input should not be read after the
06317      * seek.
06318      */
06319 
06320     DiscardInputQueued(statePtr, 0);
06321 
06322     /*
06323      * Reset EOF and BLOCKED flags. We invalidate them by moving the access
06324      * point. Also clear CR related flags.
06325      */
06326 
06327     statePtr->flags &=
06328         ~(CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED | INPUT_SAW_CR);
06329 
06330     /*
06331      * If the channel is in asynchronous output mode, switch it back to
06332      * synchronous mode and cancel any async flush that may be scheduled.
06333      * After the flush, the channel will be put back into asynchronous output
06334      * mode.
06335      */
06336 
06337     wasAsync = 0;
06338     if (statePtr->flags & CHANNEL_NONBLOCKING) {
06339         wasAsync = 1;
06340         result = StackSetBlockMode(chanPtr, TCL_MODE_BLOCKING);
06341         if (result != 0) {
06342             return Tcl_LongAsWide(-1);
06343         }
06344         ResetFlag(statePtr, CHANNEL_NONBLOCKING);
06345         if (statePtr->flags & BG_FLUSH_SCHEDULED) {
06346             ResetFlag(statePtr, BG_FLUSH_SCHEDULED);
06347         }
06348     }
06349 
06350     /*
06351      * If there is data buffered in statePtr->curOutPtr then mark the channel
06352      * as ready to flush before invoking FlushChannel.
06353      */
06354 
06355     if ((statePtr->curOutPtr != NULL) && IsBufferReady(statePtr->curOutPtr)) {
06356         SetFlag(statePtr, BUFFER_READY);
06357     }
06358 
06359     /*
06360      * If the flush fails we cannot recover the original position. In that
06361      * case the seek is not attempted because we do not know where the access
06362      * position is - instead we return the error. FlushChannel has already
06363      * called Tcl_SetErrno() to report the error upwards. If the flush
06364      * succeeds we do the seek also.
06365      */
06366 
06367     if (FlushChannel(NULL, chanPtr, 0) != 0) {
06368         curPos = -1;
06369     } else {
06370         /*
06371          * Now seek to the new position in the channel as requested by the
06372          * caller. Note that we prefer the wideSeekProc if that is available
06373          * and non-NULL...
06374          */
06375 
06376         if (HaveVersion(chanPtr->typePtr, TCL_CHANNEL_VERSION_3) &&
06377                 chanPtr->typePtr->wideSeekProc != NULL) {
06378             curPos = (chanPtr->typePtr->wideSeekProc) (chanPtr->instanceData,
06379                     offset, mode, &result);
06380         } else if (offset < Tcl_LongAsWide(LONG_MIN) ||
06381                 offset > Tcl_LongAsWide(LONG_MAX)) {
06382             result = EOVERFLOW;
06383             curPos = Tcl_LongAsWide(-1);
06384         } else {
06385             curPos = Tcl_LongAsWide((chanPtr->typePtr->seekProc) (
06386                     chanPtr->instanceData, Tcl_WideAsLong(offset), mode,
06387                     &result));
06388         }
06389         if (curPos == Tcl_LongAsWide(-1)) {
06390             Tcl_SetErrno(result);
06391         }
06392     }
06393 
06394     /*
06395      * Restore to nonblocking mode if that was the previous behavior.
06396      *
06397      * NOTE: Even if there was an async flush active we do not restore it now
06398      * because we already flushed all the queued output, above.
06399      */
06400 
06401     if (wasAsync) {
06402         SetFlag(statePtr, CHANNEL_NONBLOCKING);
06403         result = StackSetBlockMode(chanPtr, TCL_MODE_NONBLOCKING);
06404         if (result != 0) {
06405             return Tcl_LongAsWide(-1);
06406         }
06407     }
06408 
06409     return curPos;
06410 }
06411 
06412 /*
06413  *----------------------------------------------------------------------
06414  *
06415  * Tcl_Tell --
06416  *
06417  *      Returns the position of the next character to be read/written on this
06418  *      channel.
06419  *
06420  * Results:
06421  *      A nonnegative integer on success, -1 on failure. If failed, use
06422  *      Tcl_GetErrno() to retrieve the POSIX error code for the error that
06423  *      occurred.
06424  *
06425  * Side effects:
06426  *      None.
06427  *
06428  *----------------------------------------------------------------------
06429  */
06430 
06431 Tcl_WideInt
06432 Tcl_Tell(
06433     Tcl_Channel chan)           /* The channel to return pos for. */
06434 {
06435     Channel *chanPtr = (Channel *) chan;
06436                                 /* The real IO channel. */
06437     ChannelState *statePtr = chanPtr->state;
06438                                 /* State info for channel */
06439     int inputBuffered, outputBuffered;
06440                                 /* # bytes held in buffers. */
06441     int result;                 /* Of calling device driver. */
06442     Tcl_WideInt curPos;         /* Position on device. */
06443 
06444     if (CheckChannelErrors(statePtr, TCL_WRITABLE | TCL_READABLE) != 0) {
06445         return Tcl_LongAsWide(-1);
06446     }
06447 
06448     /*
06449      * Disallow tell on dead channels -- channels that have been closed but
06450      * not yet been deallocated. Such channels can be found if the exit
06451      * handler for channel cleanup has run but the channel is still registered
06452      * in an interpreter.
06453      */
06454 
06455     if (CheckForDeadChannel(NULL, statePtr)) {
06456         return Tcl_LongAsWide(-1);
06457     }
06458 
06459     /*
06460      * This operation should occur at the top of a channel stack.
06461      */
06462 
06463     chanPtr = statePtr->topChanPtr;
06464 
06465     /*
06466      * Disallow tell on channels whose type does not have a seek procedure
06467      * defined. This means that the channel does not support seeking.
06468      */
06469 
06470     if (chanPtr->typePtr->seekProc == NULL) {
06471         Tcl_SetErrno(EINVAL);
06472         return Tcl_LongAsWide(-1);
06473     }
06474 
06475     /*
06476      * Compute how much input and output is buffered. If both input and output
06477      * is buffered, cannot compute the current position.
06478      */
06479 
06480     inputBuffered = Tcl_InputBuffered(chan);
06481     outputBuffered = Tcl_OutputBuffered(chan);
06482 
06483     if ((inputBuffered != 0) && (outputBuffered != 0)) {
06484         Tcl_SetErrno(EFAULT);
06485         return Tcl_LongAsWide(-1);
06486     }
06487 
06488     /*
06489      * Get the current position in the device and compute the position where
06490      * the next character will be read or written. Note that we prefer the
06491      * wideSeekProc if that is available and non-NULL...
06492      */
06493 
06494     if (HaveVersion(chanPtr->typePtr, TCL_CHANNEL_VERSION_3) &&
06495             chanPtr->typePtr->wideSeekProc != NULL) {
06496         curPos = (chanPtr->typePtr->wideSeekProc) (chanPtr->instanceData,
06497                 Tcl_LongAsWide(0), SEEK_CUR, &result);
06498     } else {
06499         curPos = Tcl_LongAsWide((chanPtr->typePtr->seekProc) (
06500                 chanPtr->instanceData, 0, SEEK_CUR, &result));
06501     }
06502     if (curPos == Tcl_LongAsWide(-1)) {
06503         Tcl_SetErrno(result);
06504         return Tcl_LongAsWide(-1);
06505     }
06506     if (inputBuffered != 0) {
06507         return curPos - inputBuffered;
06508     }
06509     return curPos + outputBuffered;
06510 }
06511 
06512 /*
06513  *---------------------------------------------------------------------------
06514  *
06515  * Tcl_SeekOld, Tcl_TellOld --
06516  *
06517  *      Backward-compatability versions of the seek/tell interface that do not
06518  *      support 64-bit offsets. This interface is not documented or expected
06519  *      to be supported indefinitely.
06520  *
06521  * Results:
06522  *      As for Tcl_Seek and Tcl_Tell respectively, except truncated to
06523  *      whatever value will fit in an 'int'.
06524  *
06525  * Side effects:
06526  *      As for Tcl_Seek and Tcl_Tell respectively.
06527  *
06528  *---------------------------------------------------------------------------
06529  */
06530 
06531 int
06532 Tcl_SeekOld(
06533     Tcl_Channel chan,           /* The channel on which to seek. */
06534     int offset,                 /* Offset to seek to. */
06535     int mode)                   /* Relative to which location to seek? */
06536 {
06537     Tcl_WideInt wOffset, wResult;
06538 
06539     wOffset = Tcl_LongAsWide((long)offset);
06540     wResult = Tcl_Seek(chan, wOffset, mode);
06541     return (int)Tcl_WideAsLong(wResult);
06542 }
06543 
06544 int
06545 Tcl_TellOld(
06546     Tcl_Channel chan)           /* The channel to return pos for. */
06547 {
06548     Tcl_WideInt wResult;
06549 
06550     wResult = Tcl_Tell(chan);
06551     return (int)Tcl_WideAsLong(wResult);
06552 }
06553 
06554 /*
06555  *---------------------------------------------------------------------------
06556  *
06557  * Tcl_TruncateChannel --
06558  *
06559  *      Truncate a channel to the given length.
06560  *
06561  * Results:
06562  *      TCL_OK on success, TCL_ERROR if the operation failed (e.g. is not
06563  *      supported by the type of channel, or the underlying OS operation
06564  *      failed in some way).
06565  *
06566  * Side effects:
06567  *      Seeks the channel to the current location. Sets errno on OS error.
06568  *
06569  *---------------------------------------------------------------------------
06570  */
06571 
06572 int
06573 Tcl_TruncateChannel(
06574     Tcl_Channel chan,           /* Channel to truncate. */
06575     Tcl_WideInt length)         /* Length to truncate it to. */
06576 {
06577     Channel *chanPtr = (Channel *) chan;
06578     Tcl_DriverTruncateProc *truncateProc =
06579             Tcl_ChannelTruncateProc(chanPtr->typePtr);
06580     int result;
06581 
06582     if (truncateProc == NULL) {
06583         /*
06584          * Feature not supported and it's not emulatable. Pretend it's
06585          * returned an EINVAL, a very generic error!
06586          */
06587 
06588         Tcl_SetErrno(EINVAL);
06589         return TCL_ERROR;
06590     }
06591 
06592     if (!(chanPtr->state->flags & TCL_WRITABLE)) {
06593         /*
06594          * We require that the file was opened of writing. Do that check now
06595          * so that we only flush if we think we're going to succeed.
06596          */
06597 
06598         Tcl_SetErrno(EINVAL);
06599         return TCL_ERROR;
06600     }
06601 
06602     /*
06603      * Seek first to force a total flush of all pending buffers and ditch any
06604      * pre-read input data.
06605      */
06606 
06607     if (Tcl_Seek(chan, (Tcl_WideInt)0, SEEK_CUR) == Tcl_LongAsWide(-1)) {
06608         return TCL_ERROR;
06609     }
06610 
06611     /*
06612      * We're all flushed to disk now and we also don't have any unfortunate
06613      * input baggage around either; can truncate with impunity.
06614      */
06615 
06616     result = truncateProc(chanPtr->instanceData, length);
06617     if (result != 0) {
06618         Tcl_SetErrno(result);
06619         return TCL_ERROR;
06620     }
06621     return TCL_OK;
06622 }
06623 
06624 /*
06625  *---------------------------------------------------------------------------
06626  *
06627  * CheckChannelErrors --
06628  *
06629  *      See if the channel is in an ready state and can perform the desired
06630  *      operation.
06631  *
06632  * Results:
06633  *      The return value is 0 if the channel is OK, otherwise the return value
06634  *      is -1 and errno is set to indicate the error.
06635  *
06636  * Side effects:
06637  *      May clear the EOF and/or BLOCKED bits if reading from channel.
06638  *
06639  *---------------------------------------------------------------------------
06640  */
06641 
06642 static int
06643 CheckChannelErrors(
06644     ChannelState *statePtr,     /* Channel to check. */
06645     int flags)                  /* Test if channel supports desired operation:
06646                                  * TCL_READABLE, TCL_WRITABLE. Also indicates
06647                                  * Raw read or write for special close
06648                                  * processing */
06649 {
06650     int direction = flags & (TCL_READABLE|TCL_WRITABLE);
06651 
06652     /*
06653      * Check for unreported error.
06654      */
06655 
06656     if (statePtr->unreportedError != 0) {
06657         Tcl_SetErrno(statePtr->unreportedError);
06658         statePtr->unreportedError = 0;
06659 
06660         /*
06661          * TIP #219, Tcl Channel Reflection API.
06662          * Move a defered error message back into the channel bypass.
06663          */
06664 
06665         if (statePtr->chanMsg != NULL) {
06666             TclDecrRefCount(statePtr->chanMsg);
06667         }
06668         statePtr->chanMsg = statePtr->unreportedMsg;
06669         statePtr->unreportedMsg = NULL;
06670         return -1;
06671     }
06672 
06673     /*
06674      * Only the raw read and write operations are allowed during close in
06675      * order to drain data from stacked channels.
06676      */
06677 
06678     if ((statePtr->flags & CHANNEL_CLOSED) &&
06679             ((flags & CHANNEL_RAW_MODE) == 0)) {
06680         Tcl_SetErrno(EACCES);
06681         return -1;
06682     }
06683 
06684     /*
06685      * Fail if the channel is not opened for desired operation.
06686      */
06687 
06688     if ((statePtr->flags & direction) == 0) {
06689         Tcl_SetErrno(EACCES);
06690         return -1;
06691     }
06692 
06693     /*
06694      * Fail if the channel is in the middle of a background copy.
06695      *
06696      * Don't do this tests for raw channels here or else the chaining in the
06697      * transformation drivers will fail with 'file busy' error instead of
06698      * retrieving and transforming the data to copy.
06699      */
06700 
06701     if ((statePtr->csPtr != NULL) && ((flags & CHANNEL_RAW_MODE) == 0)) {
06702         Tcl_SetErrno(EBUSY);
06703         return -1;
06704     }
06705 
06706     if (direction == TCL_READABLE) {
06707         /*
06708          * If we have not encountered a sticky EOF, clear the EOF bit (sticky
06709          * EOF is set if we have seen the input eofChar, to prevent reading
06710          * beyond the eofChar). Also, always clear the BLOCKED bit. We want to
06711          * discover these conditions anew in each operation.
06712          */
06713 
06714         if ((statePtr->flags & CHANNEL_STICKY_EOF) == 0) {
06715             ResetFlag(statePtr, CHANNEL_EOF);
06716         }
06717         ResetFlag(statePtr, CHANNEL_BLOCKED | CHANNEL_NEED_MORE_DATA);
06718     }
06719 
06720     return 0;
06721 }
06722 
06723 /*
06724  *----------------------------------------------------------------------
06725  *
06726  * Tcl_Eof --
06727  *
06728  *      Returns 1 if the channel is at EOF, 0 otherwise.
06729  *
06730  * Results:
06731  *      1 or 0, always.
06732  *
06733  * Side effects:
06734  *      None.
06735  *
06736  *----------------------------------------------------------------------
06737  */
06738 
06739 int
06740 Tcl_Eof(
06741     Tcl_Channel chan)           /* Does this channel have EOF? */
06742 {
06743     ChannelState *statePtr = ((Channel *) chan)->state;
06744                                 /* State of real channel structure. */
06745 
06746     return ((statePtr->flags & CHANNEL_STICKY_EOF) ||
06747             ((statePtr->flags & CHANNEL_EOF) &&
06748             (Tcl_InputBuffered(chan) == 0))) ? 1 : 0;
06749 }
06750 
06751 /*
06752  *----------------------------------------------------------------------
06753  *
06754  * Tcl_InputBlocked --
06755  *
06756  *      Returns 1 if input is blocked on this channel, 0 otherwise.
06757  *
06758  * Results:
06759  *      0 or 1, always.
06760  *
06761  * Side effects:
06762  *      None.
06763  *
06764  *----------------------------------------------------------------------
06765  */
06766 
06767 int
06768 Tcl_InputBlocked(
06769     Tcl_Channel chan)           /* Is this channel blocked? */
06770 {
06771     ChannelState *statePtr = ((Channel *) chan)->state;
06772                                 /* State of real channel structure. */
06773 
06774     return (statePtr->flags & CHANNEL_BLOCKED) ? 1 : 0;
06775 }
06776 
06777 /*
06778  *----------------------------------------------------------------------
06779  *
06780  * Tcl_InputBuffered --
06781  *
06782  *      Returns the number of bytes of input currently buffered in the common
06783  *      internal buffer of a channel.
06784  *
06785  * Results:
06786  *      The number of input bytes buffered, or zero if the channel is not open
06787  *      for reading.
06788  *
06789  * Side effects:
06790  *      None.
06791  *
06792  *----------------------------------------------------------------------
06793  */
06794 
06795 int
06796 Tcl_InputBuffered(
06797     Tcl_Channel chan)           /* The channel to query. */
06798 {
06799     ChannelState *statePtr = ((Channel *) chan)->state;
06800                                 /* State of real channel structure. */
06801     ChannelBuffer *bufPtr;
06802     int bytesBuffered;
06803 
06804     for (bytesBuffered = 0, bufPtr = statePtr->inQueueHead; bufPtr != NULL;
06805             bufPtr = bufPtr->nextPtr) {
06806         bytesBuffered += BytesLeft(bufPtr);
06807     }
06808 
06809     /*
06810      * Don't forget the bytes in the topmost pushback area.
06811      */
06812 
06813     for (bufPtr = statePtr->topChanPtr->inQueueHead; bufPtr != NULL;
06814             bufPtr = bufPtr->nextPtr) {
06815         bytesBuffered += BytesLeft(bufPtr);
06816     }
06817 
06818     return bytesBuffered;
06819 }
06820 
06821 /*
06822  *----------------------------------------------------------------------
06823  *
06824  * Tcl_OutputBuffered --
06825  *
06826  *    Returns the number of bytes of output currently buffered in the common
06827  *    internal buffer of a channel.
06828  *
06829  * Results:
06830  *    The number of output bytes buffered, or zero if the channel is not open
06831  *    for writing.
06832  *
06833  * Side effects:
06834  *    None.
06835  *
06836  *----------------------------------------------------------------------
06837  */
06838 
06839 int
06840 Tcl_OutputBuffered(
06841     Tcl_Channel chan)           /* The channel to query. */
06842 {
06843     ChannelState *statePtr = ((Channel *) chan)->state;
06844                                 /* State of real channel structure. */
06845     ChannelBuffer *bufPtr;
06846     int bytesBuffered;
06847 
06848     for (bytesBuffered = 0, bufPtr = statePtr->outQueueHead; bufPtr != NULL;
06849             bufPtr = bufPtr->nextPtr) {
06850         bytesBuffered += BytesLeft(bufPtr);
06851     }
06852     if (statePtr->curOutPtr != NULL) {
06853         register ChannelBuffer *curOutPtr = statePtr->curOutPtr;
06854 
06855         if (IsBufferReady(curOutPtr)) {
06856             bytesBuffered += BytesLeft(curOutPtr);
06857         }
06858     }
06859 
06860     return bytesBuffered;
06861 }
06862 
06863 /*
06864  *----------------------------------------------------------------------
06865  *
06866  * Tcl_ChannelBuffered --
06867  *
06868  *      Returns the number of bytes of input currently buffered in the
06869  *      internal buffer (push back area) of a channel.
06870  *
06871  * Results:
06872  *      The number of input bytes buffered, or zero if the channel is not open
06873  *      for reading.
06874  *
06875  * Side effects:
06876  *      None.
06877  *
06878  *----------------------------------------------------------------------
06879  */
06880 
06881 int
06882 Tcl_ChannelBuffered(
06883     Tcl_Channel chan)           /* The channel to query. */
06884 {
06885     Channel *chanPtr = (Channel *) chan;
06886                                 /* Real channel structure. */
06887     ChannelBuffer *bufPtr;
06888     int bytesBuffered = 0;
06889 
06890     for (bufPtr = chanPtr->inQueueHead; bufPtr != NULL;
06891             bufPtr = bufPtr->nextPtr) {
06892         bytesBuffered += BytesLeft(bufPtr);
06893     }
06894 
06895     return bytesBuffered;
06896 }
06897 
06898 /*
06899  *----------------------------------------------------------------------
06900  *
06901  * Tcl_SetChannelBufferSize --
06902  *
06903  *      Sets the size of buffers to allocate to store input or output in the
06904  *      channel. The size must be between 1 byte and 1 MByte.
06905  *
06906  * Results:
06907  *      None.
06908  *
06909  * Side effects:
06910  *      Sets the size of buffers subsequently allocated for this channel.
06911  *
06912  *----------------------------------------------------------------------
06913  */
06914 
06915 void
06916 Tcl_SetChannelBufferSize(
06917     Tcl_Channel chan,           /* The channel whose buffer size to set. */
06918     int sz)                     /* The size to set. */
06919 {
06920     ChannelState *statePtr;     /* State of real channel structure. */
06921 
06922     /*
06923      * If the buffer size is smaller than 1 byte or larger than one MByte, do
06924      * not accept the requested size and leave the current buffer size.
06925      */
06926 
06927     if (sz < 1 || sz > 1024*1024) {
06928         return;
06929     }
06930 
06931     statePtr = ((Channel *) chan)->state;
06932     statePtr->bufSize = sz;
06933 
06934     if (statePtr->outputStage != NULL) {
06935         ckfree((char *) statePtr->outputStage);
06936         statePtr->outputStage = NULL;
06937     }
06938     if ((statePtr->encoding != NULL) && (statePtr->flags & TCL_WRITABLE)) {
06939         statePtr->outputStage = (char *)
06940                 ckalloc((unsigned) (statePtr->bufSize + 2));
06941     }
06942 }
06943 
06944 /*
06945  *----------------------------------------------------------------------
06946  *
06947  * Tcl_GetChannelBufferSize --
06948  *
06949  *      Retrieves the size of buffers to allocate for this channel.
06950  *
06951  * Results:
06952  *      The size.
06953  *
06954  * Side effects:
06955  *      None.
06956  *
06957  *----------------------------------------------------------------------
06958  */
06959 
06960 int
06961 Tcl_GetChannelBufferSize(
06962     Tcl_Channel chan)           /* The channel for which to find the buffer
06963                                  * size. */
06964 {
06965     ChannelState *statePtr = ((Channel *) chan)->state;
06966                                 /* State of real channel structure. */
06967 
06968     return statePtr->bufSize;
06969 }
06970 
06971 /*
06972  *----------------------------------------------------------------------
06973  *
06974  * Tcl_BadChannelOption --
06975  *
06976  *      This procedure generates a "bad option" error message in an (optional)
06977  *      interpreter. It is used by channel drivers when a invalid Set/Get
06978  *      option is requested. Its purpose is to concatenate the generic options
06979  *      list to the specific ones and factorize the generic options error
06980  *      message string.
06981  *
06982  * Results:
06983  *      TCL_ERROR.
06984  *
06985  * Side effects:
06986 
06987  *      An error message is generated in interp's result object to indicate
06988  *      that a command was invoked with the a bad option. The message has the
06989  *      form:
06990  *              bad option "blah": should be one of
06991  *              <...generic options...>+<...specific options...>
06992  *      "blah" is the optionName argument and "<specific options>" is a space
06993  *      separated list of specific option words. The function takes good care
06994  *      of inserting minus signs before each option, commas after, and an "or"
06995  *      before the last option.
06996  *
06997  *----------------------------------------------------------------------
06998  */
06999 
07000 int
07001 Tcl_BadChannelOption(
07002     Tcl_Interp *interp,         /* Current interpreter (can be NULL).*/
07003     const char *optionName,     /* 'bad option' name */
07004     const char *optionList)     /* Specific options list to append to the
07005                                  * standard generic options. Can be NULL for
07006                                  * generic options only. */
07007 {
07008     if (interp != NULL) {
07009         const char *genericopt =
07010                 "blocking buffering buffersize encoding eofchar translation";
07011         const char **argv;
07012         int argc, i;
07013         Tcl_DString ds;
07014 
07015         Tcl_DStringInit(&ds);
07016         Tcl_DStringAppend(&ds, genericopt, -1);
07017         if (optionList && (*optionList)) {
07018             Tcl_DStringAppend(&ds, " ", 1);
07019             Tcl_DStringAppend(&ds, optionList, -1);
07020         }
07021         if (Tcl_SplitList(interp, Tcl_DStringValue(&ds),
07022                 &argc, &argv) != TCL_OK) {
07023             Tcl_Panic("malformed option list in channel driver");
07024         }
07025         Tcl_ResetResult(interp);
07026         Tcl_AppendResult(interp, "bad option \"", optionName,
07027                 "\": should be one of ", NULL);
07028         argc--;
07029         for (i = 0; i < argc; i++) {
07030             Tcl_AppendResult(interp, "-", argv[i], ", ", NULL);
07031         }
07032         Tcl_AppendResult(interp, "or -", argv[i], NULL);
07033         Tcl_DStringFree(&ds);
07034         ckfree((char *) argv);
07035     }
07036     Tcl_SetErrno(EINVAL);
07037     return TCL_ERROR;
07038 }
07039 
07040 /*
07041  *----------------------------------------------------------------------
07042  *
07043  * Tcl_GetChannelOption --
07044  *
07045  *      Gets a mode associated with an IO channel. If the optionName arg is
07046  *      non NULL, retrieves the value of that option. If the optionName arg is
07047  *      NULL, retrieves a list of alternating option names and values for the
07048  *      given channel.
07049  *
07050  * Results:
07051  *      A standard Tcl result. Also sets the supplied DString to the string
07052  *      value of the option(s) returned.
07053  *
07054  * Side effects:
07055  *      None.
07056  *
07057  *----------------------------------------------------------------------
07058  */
07059 
07060 int
07061 Tcl_GetChannelOption(
07062     Tcl_Interp *interp,         /* For error reporting - can be NULL. */
07063     Tcl_Channel chan,           /* Channel on which to get option. */
07064     const char *optionName,     /* Option to get. */
07065     Tcl_DString *dsPtr)         /* Where to store value(s). */
07066 {
07067     size_t len;                 /* Length of optionName string. */
07068     char optionVal[128];        /* Buffer for sprintf. */
07069     Channel *chanPtr = (Channel *) chan;
07070     ChannelState *statePtr = chanPtr->state;
07071                                 /* State info for channel */
07072     int flags;
07073 
07074     /*
07075      * Disallow options on dead channels -- channels that have been closed but
07076      * not yet been deallocated. Such channels can be found if the exit
07077      * handler for channel cleanup has run but the channel is still registered
07078      * in an interpreter.
07079      */
07080 
07081     if (CheckForDeadChannel(interp, statePtr)) {
07082         return TCL_ERROR;
07083     }
07084 
07085     /*
07086      * This operation should occur at the top of a channel stack.
07087      */
07088 
07089     chanPtr = statePtr->topChanPtr;
07090 
07091     /*
07092      * If we are in the middle of a background copy, use the saved flags.
07093      */
07094 
07095     if (statePtr->csPtr) {
07096         if (chanPtr == statePtr->csPtr->readPtr) {
07097             flags = statePtr->csPtr->readFlags;
07098         } else {
07099             flags = statePtr->csPtr->writeFlags;
07100         }
07101     } else {
07102         flags = statePtr->flags;
07103     }
07104 
07105     /*
07106      * If the optionName is NULL it means that we want a list of all options
07107      * and values.
07108      */
07109 
07110     if (optionName == NULL) {
07111         len = 0;
07112     } else {
07113         len = strlen(optionName);
07114     }
07115 
07116     if (len == 0 || HaveOpt(2, "-blocking")) {
07117         if (len == 0) {
07118             Tcl_DStringAppendElement(dsPtr, "-blocking");
07119         }
07120         Tcl_DStringAppendElement(dsPtr,
07121                 (flags & CHANNEL_NONBLOCKING) ? "0" : "1");
07122         if (len > 0) {
07123             return TCL_OK;
07124         }
07125     }
07126     if (len == 0 || HaveOpt(7, "-buffering")) {
07127         if (len == 0) {
07128             Tcl_DStringAppendElement(dsPtr, "-buffering");
07129         }
07130         if (flags & CHANNEL_LINEBUFFERED) {
07131             Tcl_DStringAppendElement(dsPtr, "line");
07132         } else if (flags & CHANNEL_UNBUFFERED) {
07133             Tcl_DStringAppendElement(dsPtr, "none");
07134         } else {
07135             Tcl_DStringAppendElement(dsPtr, "full");
07136         }
07137         if (len > 0) {
07138             return TCL_OK;
07139         }
07140     }
07141     if (len == 0 || HaveOpt(7, "-buffersize")) {
07142         if (len == 0) {
07143             Tcl_DStringAppendElement(dsPtr, "-buffersize");
07144         }
07145         TclFormatInt(optionVal, statePtr->bufSize);
07146         Tcl_DStringAppendElement(dsPtr, optionVal);
07147         if (len > 0) {
07148             return TCL_OK;
07149         }
07150     }
07151     if (len == 0 || HaveOpt(2, "-encoding")) {
07152         if (len == 0) {
07153             Tcl_DStringAppendElement(dsPtr, "-encoding");
07154         }
07155         if (statePtr->encoding == NULL) {
07156             Tcl_DStringAppendElement(dsPtr, "binary");
07157         } else {
07158             Tcl_DStringAppendElement(dsPtr,
07159                     Tcl_GetEncodingName(statePtr->encoding));
07160         }
07161         if (len > 0) {
07162             return TCL_OK;
07163         }
07164     }
07165     if (len == 0 || HaveOpt(2, "-eofchar")) {
07166         if (len == 0) {
07167             Tcl_DStringAppendElement(dsPtr, "-eofchar");
07168         }
07169         if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
07170                 (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
07171             Tcl_DStringStartSublist(dsPtr);
07172         }
07173         if (flags & TCL_READABLE) {
07174             if (statePtr->inEofChar == 0) {
07175                 Tcl_DStringAppendElement(dsPtr, "");
07176             } else {
07177                 char buf[4];
07178 
07179                 sprintf(buf, "%c", statePtr->inEofChar);
07180                 Tcl_DStringAppendElement(dsPtr, buf);
07181             }
07182         }
07183         if (flags & TCL_WRITABLE) {
07184             if (statePtr->outEofChar == 0) {
07185                 Tcl_DStringAppendElement(dsPtr, "");
07186             } else {
07187                 char buf[4];
07188 
07189                 sprintf(buf, "%c", statePtr->outEofChar);
07190                 Tcl_DStringAppendElement(dsPtr, buf);
07191             }
07192         }
07193         if (!(flags & (TCL_READABLE|TCL_WRITABLE))) {
07194             /*
07195              * Not readable or writable (e.g. server socket)
07196              */
07197 
07198             Tcl_DStringAppendElement(dsPtr, "");
07199         }
07200         if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
07201                 (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
07202             Tcl_DStringEndSublist(dsPtr);
07203         }
07204         if (len > 0) {
07205             return TCL_OK;
07206         }
07207     }
07208     if (len == 0 || HaveOpt(1, "-translation")) {
07209         if (len == 0) {
07210             Tcl_DStringAppendElement(dsPtr, "-translation");
07211         }
07212         if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
07213                 (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
07214             Tcl_DStringStartSublist(dsPtr);
07215         }
07216         if (flags & TCL_READABLE) {
07217             if (statePtr->inputTranslation == TCL_TRANSLATE_AUTO) {
07218                 Tcl_DStringAppendElement(dsPtr, "auto");
07219             } else if (statePtr->inputTranslation == TCL_TRANSLATE_CR) {
07220                 Tcl_DStringAppendElement(dsPtr, "cr");
07221             } else if (statePtr->inputTranslation == TCL_TRANSLATE_CRLF) {
07222                 Tcl_DStringAppendElement(dsPtr, "crlf");
07223             } else {
07224                 Tcl_DStringAppendElement(dsPtr, "lf");
07225             }
07226         }
07227         if (flags & TCL_WRITABLE) {
07228             if (statePtr->outputTranslation == TCL_TRANSLATE_AUTO) {
07229                 Tcl_DStringAppendElement(dsPtr, "auto");
07230             } else if (statePtr->outputTranslation == TCL_TRANSLATE_CR) {
07231                 Tcl_DStringAppendElement(dsPtr, "cr");
07232             } else if (statePtr->outputTranslation == TCL_TRANSLATE_CRLF) {
07233                 Tcl_DStringAppendElement(dsPtr, "crlf");
07234             } else {
07235                 Tcl_DStringAppendElement(dsPtr, "lf");
07236             }
07237         }
07238         if (!(flags & (TCL_READABLE|TCL_WRITABLE))) {
07239             /*
07240              * Not readable or writable (e.g. server socket)
07241              */
07242 
07243             Tcl_DStringAppendElement(dsPtr, "auto");
07244         }
07245         if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
07246                 (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
07247             Tcl_DStringEndSublist(dsPtr);
07248         }
07249         if (len > 0) {
07250             return TCL_OK;
07251         }
07252     }
07253 
07254     if (chanPtr->typePtr->getOptionProc != NULL) {
07255         /*
07256          * Let the driver specific handle additional options and result code
07257          * and message.
07258          */
07259 
07260         return (chanPtr->typePtr->getOptionProc) (chanPtr->instanceData,
07261                 interp, optionName, dsPtr);
07262     } else {
07263         /*
07264          * No driver specific options case.
07265          */
07266 
07267         if (len == 0) {
07268             return TCL_OK;
07269         }
07270         return Tcl_BadChannelOption(interp, optionName, NULL);
07271     }
07272 }
07273 
07274 /*
07275  *---------------------------------------------------------------------------
07276  *
07277  * Tcl_SetChannelOption --
07278  *
07279  *      Sets an option on a channel.
07280  *
07281  * Results:
07282  *      A standard Tcl result. On error, sets interp's result object if
07283  *      interp is not NULL.
07284  *
07285  * Side effects:
07286  *      May modify an option on a device.
07287  *
07288  *---------------------------------------------------------------------------
07289  */
07290 
07291 int
07292 Tcl_SetChannelOption(
07293     Tcl_Interp *interp,         /* For error reporting - can be NULL. */
07294     Tcl_Channel chan,           /* Channel on which to set mode. */
07295     const char *optionName,     /* Which option to set? */
07296     const char *newValue)       /* New value for option. */
07297 {
07298     Channel *chanPtr = (Channel *) chan;
07299                                 /* The real IO channel. */
07300     ChannelState *statePtr = chanPtr->state;
07301                                 /* State info for channel */
07302     size_t len;                 /* Length of optionName string. */
07303     int argc;
07304     const char **argv;
07305 
07306     /*
07307      * If the channel is in the middle of a background copy, fail.
07308      */
07309 
07310     if (statePtr->csPtr) {
07311         if (interp) {
07312             Tcl_AppendResult(interp, "unable to set channel options: "
07313                     "background copy in progress", NULL);
07314         }
07315         return TCL_ERROR;
07316     }
07317 
07318     /*
07319      * Disallow options on dead channels -- channels that have been closed but
07320      * not yet been deallocated. Such channels can be found if the exit
07321      * handler for channel cleanup has run but the channel is still registered
07322      * in an interpreter.
07323      */
07324 
07325     if (CheckForDeadChannel(NULL, statePtr)) {
07326         return TCL_ERROR;
07327     }
07328 
07329     /*
07330      * This operation should occur at the top of a channel stack.
07331      */
07332 
07333     chanPtr = statePtr->topChanPtr;
07334 
07335     len = strlen(optionName);
07336 
07337     if (HaveOpt(2, "-blocking")) {
07338         int newMode;
07339 
07340         if (Tcl_GetBoolean(interp, newValue, &newMode) == TCL_ERROR) {
07341             return TCL_ERROR;
07342         }
07343         if (newMode) {
07344             newMode = TCL_MODE_BLOCKING;
07345         } else {
07346             newMode = TCL_MODE_NONBLOCKING;
07347         }
07348         return SetBlockMode(interp, chanPtr, newMode);
07349     } else if (HaveOpt(7, "-buffering")) {
07350         len = strlen(newValue);
07351         if ((newValue[0] == 'f') && (strncmp(newValue, "full", len) == 0)) {
07352             statePtr->flags &=
07353                     ~(CHANNEL_UNBUFFERED|CHANNEL_LINEBUFFERED);
07354         } else if ((newValue[0] == 'l') &&
07355                 (strncmp(newValue, "line", len) == 0)) {
07356             ResetFlag(statePtr, CHANNEL_UNBUFFERED);
07357             SetFlag(statePtr, CHANNEL_LINEBUFFERED);
07358         } else if ((newValue[0] == 'n') &&
07359                 (strncmp(newValue, "none", len) == 0)) {
07360             ResetFlag(statePtr, CHANNEL_LINEBUFFERED);
07361             SetFlag(statePtr, CHANNEL_UNBUFFERED);
07362         } else {
07363             if (interp) {
07364                 Tcl_AppendResult(interp, "bad value for -buffering: "
07365                         "must be one of full, line, or none", NULL);
07366                 return TCL_ERROR;
07367             }
07368         }
07369         return TCL_OK;
07370     } else if (HaveOpt(7, "-buffersize")) {
07371         int newBufferSize;
07372 
07373         if (Tcl_GetInt(interp, newValue, &newBufferSize) == TCL_ERROR) {
07374             return TCL_ERROR;
07375         }
07376         Tcl_SetChannelBufferSize(chan, newBufferSize);
07377     } else if (HaveOpt(2, "-encoding")) {
07378         Tcl_Encoding encoding;
07379 
07380         if ((newValue[0] == '\0') || (strcmp(newValue, "binary") == 0)) {
07381             encoding = NULL;
07382         } else {
07383             encoding = Tcl_GetEncoding(interp, newValue);
07384             if (encoding == NULL) {
07385                 return TCL_ERROR;
07386             }
07387         }
07388 
07389         /*
07390          * When the channel has an escape sequence driven encoding such as
07391          * iso2022, the terminated escape sequence must write to the buffer.
07392          */
07393 
07394         if ((statePtr->encoding != NULL) && (statePtr->curOutPtr != NULL)
07395                 && (CheckChannelErrors(statePtr, TCL_WRITABLE) == 0)) {
07396             statePtr->outputEncodingFlags |= TCL_ENCODING_END;
07397             WriteChars(chanPtr, "", 0);
07398         }
07399         Tcl_FreeEncoding(statePtr->encoding);
07400         statePtr->encoding = encoding;
07401         statePtr->inputEncodingState = NULL;
07402         statePtr->inputEncodingFlags = TCL_ENCODING_START;
07403         statePtr->outputEncodingState = NULL;
07404         statePtr->outputEncodingFlags = TCL_ENCODING_START;
07405         ResetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
07406         UpdateInterest(chanPtr);
07407     } else if (HaveOpt(2, "-eofchar")) {
07408         if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
07409             return TCL_ERROR;
07410         }
07411         if (argc == 0) {
07412             statePtr->inEofChar = 0;
07413             statePtr->outEofChar = 0;
07414         } else if (argc == 1 || argc == 2) {
07415             int outIndex = (argc - 1);
07416             int inValue = (int) argv[0][0];
07417             int outValue = (int) argv[outIndex][0];
07418             if (inValue & 0x80 || outValue & 0x80) {
07419                 if (interp) {
07420                     Tcl_AppendResult(interp, "bad value for -eofchar: ",
07421                             "must be non-NUL ASCII character", NULL);
07422                 }
07423                 ckfree((char *) argv);
07424                 return TCL_ERROR;
07425             }
07426             if (statePtr->flags & TCL_READABLE) {
07427                 statePtr->inEofChar = inValue;
07428             }
07429             if (statePtr->flags & TCL_WRITABLE) {
07430                 statePtr->outEofChar = outValue;
07431             }
07432         } else {
07433             if (interp) {
07434                 Tcl_AppendResult(interp,
07435                         "bad value for -eofchar: should be a list of zero,"
07436                         " one, or two elements", NULL);
07437             }
07438             ckfree((char *) argv);
07439             return TCL_ERROR;
07440         }
07441         if (argv != NULL) {
07442             ckfree((char *) argv);
07443         }
07444 
07445         /*
07446          * [Bug 930851] Reset EOF and BLOCKED flags. Changing the character
07447          * which signals eof can transform a current eof condition into a 'go
07448          * ahead'. Ditto for blocked.
07449          */
07450 
07451         statePtr->flags &=
07452                 ~(CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED);
07453 
07454         return TCL_OK;
07455     } else if (HaveOpt(1, "-translation")) {
07456         const char *readMode, *writeMode;
07457 
07458         if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
07459             return TCL_ERROR;
07460         }
07461 
07462         if (argc == 1) {
07463             readMode = (statePtr->flags & TCL_READABLE) ? argv[0] : NULL;
07464             writeMode = (statePtr->flags & TCL_WRITABLE) ? argv[0] : NULL;
07465         } else if (argc == 2) {
07466             readMode = (statePtr->flags & TCL_READABLE) ? argv[0] : NULL;
07467             writeMode = (statePtr->flags & TCL_WRITABLE) ? argv[1] : NULL;
07468         } else {
07469             if (interp) {
07470                 Tcl_AppendResult(interp,
07471                         "bad value for -translation: must be a one or two"
07472                         " element list", NULL);
07473             }
07474             ckfree((char *) argv);
07475             return TCL_ERROR;
07476         }
07477 
07478         if (readMode) {
07479             TclEolTranslation translation;
07480             if (*readMode == '\0') {
07481                 translation = statePtr->inputTranslation;
07482             } else if (strcmp(readMode, "auto") == 0) {
07483                 translation = TCL_TRANSLATE_AUTO;
07484             } else if (strcmp(readMode, "binary") == 0) {
07485                 translation = TCL_TRANSLATE_LF;
07486                 statePtr->inEofChar = 0;
07487                 Tcl_FreeEncoding(statePtr->encoding);
07488                 statePtr->encoding = NULL;
07489             } else if (strcmp(readMode, "lf") == 0) {
07490                 translation = TCL_TRANSLATE_LF;
07491             } else if (strcmp(readMode, "cr") == 0) {
07492                 translation = TCL_TRANSLATE_CR;
07493             } else if (strcmp(readMode, "crlf") == 0) {
07494                 translation = TCL_TRANSLATE_CRLF;
07495             } else if (strcmp(readMode, "platform") == 0) {
07496                 translation = TCL_PLATFORM_TRANSLATION;
07497             } else {
07498                 if (interp) {
07499                     Tcl_AppendResult(interp,
07500                             "bad value for -translation: "
07501                             "must be one of auto, binary, cr, lf, crlf,"
07502                             " or platform", NULL);
07503                 }
07504                 ckfree((char *) argv);
07505                 return TCL_ERROR;
07506             }
07507 
07508             /*
07509              * Reset the EOL flags since we need to look at any buffered data
07510              * to see if the new translation mode allows us to complete the
07511              * line.
07512              */
07513 
07514             if (translation != statePtr->inputTranslation) {
07515                 statePtr->inputTranslation = translation;
07516                 ResetFlag(statePtr, INPUT_SAW_CR | CHANNEL_NEED_MORE_DATA);
07517                 UpdateInterest(chanPtr);
07518             }
07519         }
07520         if (writeMode) {
07521             if (*writeMode == '\0') {
07522                 /* Do nothing. */
07523             } else if (strcmp(writeMode, "auto") == 0) {
07524                 /*
07525                  * This is a hack to get TCP sockets to produce output in CRLF
07526                  * mode if they are being set into AUTO mode. A better
07527                  * solution for achieving this effect will be coded later.
07528                  */
07529 
07530                 if (strcmp(Tcl_ChannelName(chanPtr->typePtr), "tcp") == 0) {
07531                     statePtr->outputTranslation = TCL_TRANSLATE_CRLF;
07532                 } else {
07533                     statePtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
07534                 }
07535             } else if (strcmp(writeMode, "binary") == 0) {
07536                 statePtr->outEofChar = 0;
07537                 statePtr->outputTranslation = TCL_TRANSLATE_LF;
07538                 Tcl_FreeEncoding(statePtr->encoding);
07539                 statePtr->encoding = NULL;
07540             } else if (strcmp(writeMode, "lf") == 0) {
07541                 statePtr->outputTranslation = TCL_TRANSLATE_LF;
07542             } else if (strcmp(writeMode, "cr") == 0) {
07543                 statePtr->outputTranslation = TCL_TRANSLATE_CR;
07544             } else if (strcmp(writeMode, "crlf") == 0) {
07545                 statePtr->outputTranslation = TCL_TRANSLATE_CRLF;
07546             } else if (strcmp(writeMode, "platform") == 0) {
07547                 statePtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
07548             } else {
07549                 if (interp) {
07550                     Tcl_AppendResult(interp,
07551                             "bad value for -translation: "
07552                             "must be one of auto, binary, cr, lf, crlf,"
07553                             " or platform", NULL);
07554                 }
07555                 ckfree((char *) argv);
07556                 return TCL_ERROR;
07557             }
07558         }
07559         ckfree((char *) argv);
07560         return TCL_OK;
07561     } else if (chanPtr->typePtr->setOptionProc != NULL) {
07562         return (*chanPtr->typePtr->setOptionProc)(chanPtr->instanceData,
07563                 interp, optionName, newValue);
07564     } else {
07565         return Tcl_BadChannelOption(interp, optionName, NULL);
07566     }
07567 
07568     /*
07569      * If bufsize changes, need to get rid of old utility buffer.
07570      */
07571 
07572     if (statePtr->saveInBufPtr != NULL) {
07573         RecycleBuffer(statePtr, statePtr->saveInBufPtr, 1);
07574         statePtr->saveInBufPtr = NULL;
07575     }
07576     if ((statePtr->inQueueHead != NULL)
07577             && (statePtr->inQueueHead->nextPtr == NULL)
07578             && IsBufferEmpty(statePtr->inQueueHead)) {
07579         RecycleBuffer(statePtr, statePtr->inQueueHead, 1);
07580         statePtr->inQueueHead = NULL;
07581         statePtr->inQueueTail = NULL;
07582     }
07583 
07584     /*
07585      * If encoding or bufsize changes, need to update output staging buffer.
07586      */
07587 
07588     if (statePtr->outputStage != NULL) {
07589         ckfree(statePtr->outputStage);
07590         statePtr->outputStage = NULL;
07591     }
07592     if ((statePtr->encoding != NULL) && (statePtr->flags & TCL_WRITABLE)) {
07593         statePtr->outputStage = ckalloc((unsigned) (statePtr->bufSize + 2));
07594     }
07595     return TCL_OK;
07596 }
07597 
07598 /*
07599  *----------------------------------------------------------------------
07600  *
07601  * CleanupChannelHandlers --
07602  *
07603  *      Removes channel handlers that refer to the supplied interpreter, so
07604  *      that if the actual channel is not closed now, these handlers will not
07605  *      run on subsequent events on the channel. This would be erroneous,
07606  *      because the interpreter no longer has a reference to this channel.
07607  *
07608  * Results:
07609  *      None.
07610  *
07611  * Side effects:
07612  *      Removes channel handlers.
07613  *
07614  *----------------------------------------------------------------------
07615  */
07616 
07617 static void
07618 CleanupChannelHandlers(
07619     Tcl_Interp *interp,
07620     Channel *chanPtr)
07621 {
07622     ChannelState *statePtr = chanPtr->state;
07623                                 /* State info for channel */
07624     EventScriptRecord *sPtr, *prevPtr, *nextPtr;
07625 
07626     /*
07627      * Remove fileevent records on this channel that refer to the given
07628      * interpreter.
07629      */
07630 
07631     for (sPtr = statePtr->scriptRecordPtr, prevPtr = NULL;
07632             sPtr != NULL; sPtr = nextPtr) {
07633         nextPtr = sPtr->nextPtr;
07634         if (sPtr->interp == interp) {
07635             if (prevPtr == NULL) {
07636                 statePtr->scriptRecordPtr = nextPtr;
07637             } else {
07638                 prevPtr->nextPtr = nextPtr;
07639             }
07640 
07641             Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
07642                     TclChannelEventScriptInvoker, sPtr);
07643 
07644             TclDecrRefCount(sPtr->scriptPtr);
07645             ckfree((char *) sPtr);
07646         } else {
07647             prevPtr = sPtr;
07648         }
07649     }
07650 }
07651 
07652 /*
07653  *----------------------------------------------------------------------
07654  *
07655  * Tcl_NotifyChannel --
07656  *
07657  *      This procedure is called by a channel driver when a driver detects an
07658  *      event on a channel. This procedure is responsible for actually
07659  *      handling the event by invoking any channel handler callbacks.
07660  *
07661  * Results:
07662  *      None.
07663  *
07664  * Side effects:
07665  *      Whatever the channel handler callback procedure does.
07666  *
07667  *----------------------------------------------------------------------
07668  */
07669 
07670 void
07671 Tcl_NotifyChannel(
07672     Tcl_Channel channel,        /* Channel that detected an event. */
07673     int mask)                   /* OR'ed combination of TCL_READABLE,
07674                                  * TCL_WRITABLE, or TCL_EXCEPTION: indicates
07675                                  * which events were detected. */
07676 {
07677     Channel *chanPtr = (Channel *) channel;
07678     ChannelState *statePtr = chanPtr->state;
07679                                 /* State info for channel */
07680     ChannelHandler *chPtr;
07681     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
07682     NextChannelHandler nh;
07683     Channel *upChanPtr;
07684     const Tcl_ChannelType *upTypePtr;
07685 
07686 #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
07687     /*
07688      * [SF Tcl Bug 943274] For a non-blocking channel without blockmodeproc we
07689      * keep track of actual input coming from the OS so that we can do a
07690      * credible imitation of non-blocking behaviour.
07691      */
07692 
07693     if ((mask & TCL_READABLE) &&
07694             (statePtr->flags & CHANNEL_NONBLOCKING) &&
07695             (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) &&
07696             !(statePtr->flags & CHANNEL_TIMER_FEV)) {
07697         SetFlag(statePtr, CHANNEL_HAS_MORE_DATA);
07698     }
07699 #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
07700 
07701     /*
07702      * In contrast to the other API functions this procedure walks towards the
07703      * top of a stack and not down from it.
07704      *
07705      * The channel calling this procedure is the one who generated the event,
07706      * and thus does not take part in handling it. IOW, its HandlerProc is not
07707      * called, instead we begin with the channel above it.
07708      *
07709      * This behaviour also allows the transformation channels to generate
07710      * their own events and pass them upward.
07711      */
07712 
07713     while (mask && (chanPtr->upChanPtr != (NULL))) {
07714         Tcl_DriverHandlerProc *upHandlerProc;
07715 
07716         upChanPtr = chanPtr->upChanPtr;
07717         upTypePtr = upChanPtr->typePtr;
07718         upHandlerProc = Tcl_ChannelHandlerProc(upTypePtr);
07719         if (upHandlerProc != NULL) {
07720             mask = (*upHandlerProc) (upChanPtr->instanceData, mask);
07721         }
07722 
07723         /*
07724          * ELSE: Ignore transformations which are unable to handle the event
07725          * coming from below. Assume that they don't change the mask and pass
07726          * it on.
07727          */
07728 
07729         chanPtr = upChanPtr;
07730     }
07731 
07732     channel = (Tcl_Channel) chanPtr;
07733 
07734     /*
07735      * Here we have either reached the top of the stack or the mask is empty.
07736      * We break out of the procedure if it is the latter.
07737      */
07738 
07739     if (!mask) {
07740         return;
07741     }
07742 
07743     /*
07744      * We are now above the topmost channel in a stack and have events left.
07745      * Now call the channel handlers as usual.
07746      *
07747      * Preserve the channel struct in case the script closes it.
07748      */
07749 
07750     Tcl_Preserve(channel);
07751     Tcl_Preserve(statePtr);
07752 
07753     /*
07754      * If we are flushing in the background, be sure to call FlushChannel for
07755      * writable events. Note that we have to discard the writable event so we
07756      * don't call any write handlers before the flush is complete.
07757      */
07758 
07759     if ((statePtr->flags & BG_FLUSH_SCHEDULED) && (mask & TCL_WRITABLE)) {
07760         FlushChannel(NULL, chanPtr, 1);
07761         mask &= ~TCL_WRITABLE;
07762     }
07763 
07764     /*
07765      * Add this invocation to the list of recursive invocations of
07766      * ChannelHandlerEventProc.
07767      */
07768 
07769     nh.nextHandlerPtr = NULL;
07770     nh.nestedHandlerPtr = tsdPtr->nestedHandlerPtr;
07771     tsdPtr->nestedHandlerPtr = &nh;
07772 
07773     for (chPtr = statePtr->chPtr; chPtr != NULL; ) {
07774         /*
07775          * If this channel handler is interested in any of the events that
07776          * have occurred on the channel, invoke its procedure.
07777          */
07778 
07779         if ((chPtr->mask & mask) != 0) {
07780             nh.nextHandlerPtr = chPtr->nextPtr;
07781             (*(chPtr->proc))(chPtr->clientData, mask);
07782             chPtr = nh.nextHandlerPtr;
07783         } else {
07784             chPtr = chPtr->nextPtr;
07785         }
07786     }
07787 
07788     /*
07789      * Update the notifier interest, since it may have changed after invoking
07790      * event handlers. Skip that if the channel was deleted in the call to the
07791      * channel handler.
07792      */
07793 
07794     if (chanPtr->typePtr != NULL) {
07795         UpdateInterest(chanPtr);
07796     }
07797 
07798     Tcl_Release(statePtr);
07799     Tcl_Release(channel);
07800 
07801     tsdPtr->nestedHandlerPtr = nh.nestedHandlerPtr;
07802 }
07803 
07804 /*
07805  *----------------------------------------------------------------------
07806  *
07807  * UpdateInterest --
07808  *
07809  *      Arrange for the notifier to call us back at appropriate times based on
07810  *      the current state of the channel.
07811  *
07812  * Results:
07813  *      None.
07814  *
07815  * Side effects:
07816  *      May schedule a timer or driver handler.
07817  *
07818  *----------------------------------------------------------------------
07819  */
07820 
07821 static void
07822 UpdateInterest(
07823     Channel *chanPtr)           /* Channel to update. */
07824 {
07825     ChannelState *statePtr = chanPtr->state;
07826                                 /* State info for channel */
07827     int mask = statePtr->interestMask;
07828 
07829     /*
07830      * If there are flushed buffers waiting to be written, then we need to
07831      * watch for the channel to become writable.
07832      */
07833 
07834     if (statePtr->flags & BG_FLUSH_SCHEDULED) {
07835         mask |= TCL_WRITABLE;
07836     }
07837 
07838     /*
07839      * If there is data in the input queue, and we aren't waiting for more
07840      * data, then we need to schedule a timer so we don't block in the
07841      * notifier. Also, cancel the read interest so we don't get duplicate
07842      * events.
07843      */
07844 
07845     if (mask & TCL_READABLE) {
07846         if (!(statePtr->flags & CHANNEL_NEED_MORE_DATA)
07847                 && (statePtr->inQueueHead != NULL)
07848                 && IsBufferReady(statePtr->inQueueHead)) {
07849             mask &= ~TCL_READABLE;
07850 
07851             /*
07852              * Andreas Kupries, April 11, 2003
07853              *
07854              * Some operating systems (Solaris 2.6 and higher (but not Solaris
07855              * 2.5, go figure)) generate READABLE and EXCEPTION events when
07856              * select()'ing [*] on a plain file, even if EOF was not yet
07857              * reached. This is a problem in the following situation:
07858              *
07859              * - An extension asks to get both READABLE and EXCEPTION events.
07860              * - It reads data into a buffer smaller than the buffer used by
07861              *   Tcl itself.
07862              * - It does not process all events in the event queue, but only
07863              *   one, at least in some situations.
07864              *
07865              * In that case we can get into a situation where
07866              *
07867              * - Tcl drops READABLE here, because it has data in its own
07868              *   buffers waiting to be read by the extension.
07869              * - A READABLE event is syntesized via timer.
07870              * - The OS still reports the EXCEPTION condition on the file.
07871              * - And the extension gets the EXCPTION event first, and handles
07872              *   this as EOF.
07873              *
07874              * End result ==> Premature end of reading from a file.
07875              *
07876              * The concrete example is 'Expect', and its [expect] command
07877              * (and at the C-level, deep in the bowels of Expect,
07878              * 'exp_get_next_event'. See marker 'SunOS' for commentary in
07879              * that function too).
07880              *
07881              * [*] As the Tcl notifier does. See also for marker 'SunOS' in
07882              * file 'exp_event.c' of Expect.
07883              *
07884              * Our solution here is to drop the interest in the EXCEPTION
07885              * events too. This compiles on all platforms, and also passes the
07886              * testsuite on all of them.
07887              */
07888 
07889             mask &= ~TCL_EXCEPTION;
07890 
07891             if (!statePtr->timer) {
07892                 statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,
07893                         chanPtr);
07894             }
07895         }
07896     }
07897     (chanPtr->typePtr->watchProc)(chanPtr->instanceData, mask);
07898 }
07899 
07900 /*
07901  *----------------------------------------------------------------------
07902  *
07903  * ChannelTimerProc --
07904  *
07905  *      Timer handler scheduled by UpdateInterest to monitor the channel
07906  *      buffers until they are empty.
07907  *
07908  * Results:
07909  *      None.
07910  *
07911  * Side effects:
07912  *      May invoke channel handlers.
07913  *
07914  *----------------------------------------------------------------------
07915  */
07916 
07917 static void
07918 ChannelTimerProc(
07919     ClientData clientData)
07920 {
07921     Channel *chanPtr = clientData;
07922     ChannelState *statePtr = chanPtr->state;
07923                                 /* State info for channel */
07924 
07925     if (!(statePtr->flags & CHANNEL_NEED_MORE_DATA)
07926             && (statePtr->interestMask & TCL_READABLE)
07927             && (statePtr->inQueueHead != NULL)
07928             && IsBufferReady(statePtr->inQueueHead)) {
07929         /*
07930          * Restart the timer in case a channel handler reenters the event loop
07931          * before UpdateInterest gets called by Tcl_NotifyChannel.
07932          */
07933 
07934         statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,chanPtr);
07935 
07936 #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
07937         /*
07938          * Set the TIMER flag to notify the higher levels that the driver
07939          * might have no data for us. We do this only if we are in
07940          * non-blocking mode and the driver has no BlockModeProc because only
07941          * then we really don't know if the driver will block or not. A
07942          * similar test is done in "PeekAhead".
07943          */
07944 
07945         if ((statePtr->flags & CHANNEL_NONBLOCKING) &&
07946             (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL)) {
07947             SetFlag(statePtr, CHANNEL_TIMER_FEV);
07948         }
07949 #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
07950 
07951         Tcl_Preserve(statePtr);
07952         Tcl_NotifyChannel((Tcl_Channel)chanPtr, TCL_READABLE);
07953 
07954 #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
07955         ResetFlag(statePtr, CHANNEL_TIMER_FEV);
07956 #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
07957 
07958         Tcl_Release(statePtr);
07959     } else {
07960         statePtr->timer = NULL;
07961         UpdateInterest(chanPtr);
07962     }
07963 }
07964 
07965 /*
07966  *----------------------------------------------------------------------
07967  *
07968  * Tcl_CreateChannelHandler --
07969  *
07970  *      Arrange for a given procedure to be invoked whenever the channel
07971  *      indicated by the chanPtr arg becomes readable or writable.
07972  *
07973  * Results:
07974  *      None.
07975  *
07976  * Side effects:
07977  *      From now on, whenever the I/O channel given by chanPtr becomes ready
07978  *      in the way indicated by mask, proc will be invoked. See the manual
07979  *      entry for details on the calling sequence to proc. If there is already
07980  *      an event handler for chan, proc and clientData, then the mask will be
07981  *      updated.
07982  *
07983  *----------------------------------------------------------------------
07984  */
07985 
07986 void
07987 Tcl_CreateChannelHandler(
07988     Tcl_Channel chan,           /* The channel to create the handler for. */
07989     int mask,                   /* OR'ed combination of TCL_READABLE,
07990                                  * TCL_WRITABLE, and TCL_EXCEPTION: indicates
07991                                  * conditions under which proc should be
07992                                  * called. Use 0 to disable a registered
07993                                  * handler. */
07994     Tcl_ChannelProc *proc,      /* Procedure to call for each selected
07995                                  * event. */
07996     ClientData clientData)      /* Arbitrary data to pass to proc. */
07997 {
07998     ChannelHandler *chPtr;
07999     Channel *chanPtr = (Channel *) chan;
08000     ChannelState *statePtr = chanPtr->state;
08001                                 /* State info for channel */
08002 
08003     /*
08004      * Check whether this channel handler is not already registered. If it is
08005      * not, create a new record, else reuse existing record (smash current
08006      * values).
08007      */
08008 
08009     for (chPtr = statePtr->chPtr; chPtr != NULL; chPtr = chPtr->nextPtr) {
08010         if ((chPtr->chanPtr == chanPtr) && (chPtr->proc == proc) &&
08011                 (chPtr->clientData == clientData)) {
08012             break;
08013         }
08014     }
08015     if (chPtr == NULL) {
08016         chPtr = (ChannelHandler *) ckalloc(sizeof(ChannelHandler));
08017         chPtr->mask = 0;
08018         chPtr->proc = proc;
08019         chPtr->clientData = clientData;
08020         chPtr->chanPtr = chanPtr;
08021         chPtr->nextPtr = statePtr->chPtr;
08022         statePtr->chPtr = chPtr;
08023     }
08024 
08025     /*
08026      * The remainder of the initialization below is done regardless of whether
08027      * or not this is a new record or a modification of an old one.
08028      */
08029 
08030     chPtr->mask = mask;
08031 
08032     /*
08033      * Recompute the interest mask for the channel - this call may actually be
08034      * disabling an existing handler.
08035      */
08036 
08037     statePtr->interestMask = 0;
08038     for (chPtr = statePtr->chPtr; chPtr != NULL; chPtr = chPtr->nextPtr) {
08039         statePtr->interestMask |= chPtr->mask;
08040     }
08041 
08042     UpdateInterest(statePtr->topChanPtr);
08043 }
08044 
08045 /*
08046  *----------------------------------------------------------------------
08047  *
08048  * Tcl_DeleteChannelHandler --
08049  *
08050  *      Cancel a previously arranged callback arrangement for an IO channel.
08051  *
08052  * Results:
08053  *      None.
08054  *
08055  * Side effects:
08056  *      If a callback was previously registered for this chan, proc and
08057  *      clientData, it is removed and the callback will no longer be called
08058  *      when the channel becomes ready for IO.
08059  *
08060  *----------------------------------------------------------------------
08061  */
08062 
08063 void
08064 Tcl_DeleteChannelHandler(
08065     Tcl_Channel chan,           /* The channel for which to remove the
08066                                  * callback. */
08067     Tcl_ChannelProc *proc,      /* The procedure in the callback to delete. */
08068     ClientData clientData)      /* The client data in the callback to
08069                                  * delete. */
08070 {
08071     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
08072     ChannelHandler *chPtr, *prevChPtr;
08073     Channel *chanPtr = (Channel *) chan;
08074     ChannelState *statePtr = chanPtr->state;
08075                                 /* State info for channel */
08076     NextChannelHandler *nhPtr;
08077 
08078     /*
08079      * Find the entry and the previous one in the list.
08080      */
08081 
08082     for (prevChPtr = NULL, chPtr = statePtr->chPtr; chPtr != NULL;
08083             chPtr = chPtr->nextPtr) {
08084         if ((chPtr->chanPtr == chanPtr) && (chPtr->clientData == clientData)
08085                 && (chPtr->proc == proc)) {
08086             break;
08087         }
08088         prevChPtr = chPtr;
08089     }
08090 
08091     /*
08092      * If not found, return without doing anything.
08093      */
08094 
08095     if (chPtr == NULL) {
08096         return;
08097     }
08098 
08099     /*
08100      * If ChannelHandlerEventProc is about to process this handler, tell it to
08101      * process the next one instead - we are going to delete *this* one.
08102      */
08103 
08104     for (nhPtr = tsdPtr->nestedHandlerPtr; nhPtr != NULL;
08105             nhPtr = nhPtr->nestedHandlerPtr) {
08106         if (nhPtr->nextHandlerPtr == chPtr) {
08107             nhPtr->nextHandlerPtr = chPtr->nextPtr;
08108         }
08109     }
08110 
08111     /*
08112      * Splice it out of the list of channel handlers.
08113      */
08114 
08115     if (prevChPtr == NULL) {
08116         statePtr->chPtr = chPtr->nextPtr;
08117     } else {
08118         prevChPtr->nextPtr = chPtr->nextPtr;
08119     }
08120     ckfree((char *) chPtr);
08121 
08122     /*
08123      * Recompute the interest list for the channel, so that infinite loops
08124      * will not result if Tcl_DeleteChannelHandler is called inside an event.
08125      */
08126 
08127     statePtr->interestMask = 0;
08128     for (chPtr = statePtr->chPtr; chPtr != NULL; chPtr = chPtr->nextPtr) {
08129         statePtr->interestMask |= chPtr->mask;
08130     }
08131 
08132     UpdateInterest(statePtr->topChanPtr);
08133 }
08134 
08135 /*
08136  *----------------------------------------------------------------------
08137  *
08138  * DeleteScriptRecord --
08139  *
08140  *      Delete a script record for this combination of channel, interp and
08141  *      mask.
08142  *
08143  * Results:
08144  *      None.
08145  *
08146  * Side effects:
08147  *      Deletes a script record and cancels a channel event handler.
08148  *
08149  *----------------------------------------------------------------------
08150  */
08151 
08152 static void
08153 DeleteScriptRecord(
08154     Tcl_Interp *interp,         /* Interpreter in which script was to be
08155                                  * executed. */
08156     Channel *chanPtr,           /* The channel for which to delete the script
08157                                  * record (if any). */
08158     int mask)                   /* Events in mask must exactly match mask of
08159                                  * script to delete. */
08160 {
08161     ChannelState *statePtr = chanPtr->state;
08162                                 /* State info for channel */
08163     EventScriptRecord *esPtr, *prevEsPtr;
08164 
08165     for (esPtr = statePtr->scriptRecordPtr, prevEsPtr = NULL; esPtr != NULL;
08166             prevEsPtr = esPtr, esPtr = esPtr->nextPtr) {
08167         if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
08168             if (esPtr == statePtr->scriptRecordPtr) {
08169                 statePtr->scriptRecordPtr = esPtr->nextPtr;
08170             } else {
08171                 prevEsPtr->nextPtr = esPtr->nextPtr;
08172             }
08173 
08174             Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
08175                     TclChannelEventScriptInvoker, esPtr);
08176 
08177             TclDecrRefCount(esPtr->scriptPtr);
08178             ckfree((char *) esPtr);
08179 
08180             break;
08181         }
08182     }
08183 }
08184 
08185 /*
08186  *----------------------------------------------------------------------
08187  *
08188  * CreateScriptRecord --
08189  *
08190  *      Creates a record to store a script to be executed when a specific
08191  *      event fires on a specific channel.
08192  *
08193  * Results:
08194  *      None.
08195  *
08196  * Side effects:
08197  *      Causes the script to be stored for later execution.
08198  *
08199  *----------------------------------------------------------------------
08200  */
08201 
08202 static void
08203 CreateScriptRecord(
08204     Tcl_Interp *interp,         /* Interpreter in which to execute the stored
08205                                  * script. */
08206     Channel *chanPtr,           /* Channel for which script is to be stored */
08207     int mask,                   /* Set of events for which script will be
08208                                  * invoked. */
08209     Tcl_Obj *scriptPtr)         /* Pointer to script object. */
08210 {
08211     ChannelState *statePtr = chanPtr->state;
08212                                 /* State info for channel */
08213     EventScriptRecord *esPtr;
08214 
08215     for (esPtr=statePtr->scriptRecordPtr; esPtr!=NULL; esPtr=esPtr->nextPtr) {
08216         if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
08217             TclDecrRefCount(esPtr->scriptPtr);
08218             esPtr->scriptPtr = NULL;
08219             break;
08220         }
08221     }
08222     if (esPtr == NULL) {
08223         esPtr = (EventScriptRecord *) ckalloc(sizeof(EventScriptRecord));
08224         Tcl_CreateChannelHandler((Tcl_Channel) chanPtr, mask,
08225                 TclChannelEventScriptInvoker, esPtr);
08226         esPtr->nextPtr = statePtr->scriptRecordPtr;
08227         statePtr->scriptRecordPtr = esPtr;
08228     }
08229     esPtr->chanPtr = chanPtr;
08230     esPtr->interp = interp;
08231     esPtr->mask = mask;
08232     Tcl_IncrRefCount(scriptPtr);
08233     esPtr->scriptPtr = scriptPtr;
08234 }
08235 
08236 /*
08237  *----------------------------------------------------------------------
08238  *
08239  * TclChannelEventScriptInvoker --
08240  *
08241  *      Invokes a script scheduled by "fileevent" for when the channel becomes
08242  *      ready for IO. This function is invoked by the channel handler which
08243  *      was created by the Tcl "fileevent" command.
08244  *
08245  * Results:
08246  *      None.
08247  *
08248  * Side effects:
08249  *      Whatever the script does.
08250  *
08251  *----------------------------------------------------------------------
08252  */
08253 
08254 void
08255 TclChannelEventScriptInvoker(
08256     ClientData clientData,      /* The script+interp record. */
08257     int mask)                   /* Not used. */
08258 {
08259     Tcl_Interp *interp;         /* Interpreter in which to eval the script. */
08260     Channel *chanPtr;           /* The channel for which this handler is
08261                                  * registered. */
08262     EventScriptRecord *esPtr;   /* The event script + interpreter to eval it
08263                                  * in. */
08264     int result;                 /* Result of call to eval script. */
08265 
08266     esPtr = clientData;
08267     chanPtr = esPtr->chanPtr;
08268     mask = esPtr->mask;
08269     interp = esPtr->interp;
08270 
08271     /*
08272      * We must preserve the interpreter so we can report errors on it later.
08273      * Note that we do not need to preserve the channel because that is done
08274      * by Tcl_NotifyChannel before calling channel handlers.
08275      */
08276 
08277     Tcl_Preserve(interp);
08278     result = Tcl_EvalObjEx(interp, esPtr->scriptPtr, TCL_EVAL_GLOBAL);
08279 
08280     /*
08281      * On error, cause a background error and remove the channel handler and
08282      * the script record.
08283      *
08284      * NOTE: Must delete channel handler before causing the background error
08285      * because the background error may want to reinstall the handler.
08286      */
08287 
08288     if (result != TCL_OK) {
08289         if (chanPtr->typePtr != NULL) {
08290             DeleteScriptRecord(interp, chanPtr, mask);
08291         }
08292         TclBackgroundException(interp, result);
08293     }
08294     Tcl_Release(interp);
08295 }
08296 
08297 /*
08298  *----------------------------------------------------------------------
08299  *
08300  * Tcl_FileEventObjCmd --
08301  *
08302  *      This procedure implements the "fileevent" Tcl command. See the user
08303  *      documentation for details on what it does. This command is based on
08304  *      the Tk command "fileevent" which in turn is based on work contributed
08305  *      by Mark Diekhans.
08306  *
08307  * Results:
08308  *      A standard Tcl result.
08309  *
08310  * Side effects:
08311  *      May create a channel handler for the specified channel.
08312  *
08313  *----------------------------------------------------------------------
08314  */
08315 
08316         /* ARGSUSED */
08317 int
08318 Tcl_FileEventObjCmd(
08319     ClientData clientData,      /* Not used. */
08320     Tcl_Interp *interp,         /* Interpreter in which the channel for which
08321                                  * to create the handler is found. */
08322     int objc,                   /* Number of arguments. */
08323     Tcl_Obj *const objv[])      /* Argument objects. */
08324 {
08325     Channel *chanPtr;           /* The channel to create the handler for. */
08326     ChannelState *statePtr;     /* State info for channel */
08327     Tcl_Channel chan;           /* The opaque type for the channel. */
08328     char *chanName;
08329     int modeIndex;              /* Index of mode argument. */
08330     int mask;
08331     static const char *modeOptions[] = {"readable", "writable", NULL};
08332     static int maskArray[] = {TCL_READABLE, TCL_WRITABLE};
08333 
08334     if ((objc != 3) && (objc != 4)) {
08335         Tcl_WrongNumArgs(interp, 1, objv, "channelId event ?script?");
08336         return TCL_ERROR;
08337     }
08338     if (Tcl_GetIndexFromObj(interp, objv[2], modeOptions, "event name", 0,
08339             &modeIndex) != TCL_OK) {
08340         return TCL_ERROR;
08341     }
08342     mask = maskArray[modeIndex];
08343 
08344     chanName = TclGetString(objv[1]);
08345     chan = Tcl_GetChannel(interp, chanName, NULL);
08346     if (chan == NULL) {
08347         return TCL_ERROR;
08348     }
08349     chanPtr = (Channel *) chan;
08350     statePtr = chanPtr->state;
08351     if ((statePtr->flags & mask) == 0) {
08352         Tcl_AppendResult(interp, "channel is not ",
08353                 (mask == TCL_READABLE) ? "readable" : "writable", NULL);
08354         return TCL_ERROR;
08355     }
08356 
08357     /*
08358      * If we are supposed to return the script, do so.
08359      */
08360 
08361     if (objc == 3) {
08362         EventScriptRecord *esPtr;
08363         for (esPtr = statePtr->scriptRecordPtr; esPtr != NULL;
08364                 esPtr = esPtr->nextPtr) {
08365             if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
08366                 Tcl_SetObjResult(interp, esPtr->scriptPtr);
08367                 break;
08368             }
08369         }
08370         return TCL_OK;
08371     }
08372 
08373     /*
08374      * If we are supposed to delete a stored script, do so.
08375      */
08376 
08377     if (*(TclGetString(objv[3])) == '\0') {
08378         DeleteScriptRecord(interp, chanPtr, mask);
08379         return TCL_OK;
08380     }
08381 
08382     /*
08383      * Make the script record that will link between the event and the script
08384      * to invoke. This also creates a channel event handler which will
08385      * evaluate the script in the supplied interpreter.
08386      */
08387 
08388     CreateScriptRecord(interp, chanPtr, mask, objv[3]);
08389 
08390     return TCL_OK;
08391 }
08392 
08393 /*
08394  *----------------------------------------------------------------------
08395  *
08396  * TclCopyChannel --
08397  *
08398  *      This routine copies data from one channel to another, either
08399  *      synchronously or asynchronously. If a command script is supplied, the
08400  *      operation runs in the background. The script is invoked when the copy
08401  *      completes. Otherwise the function waits until the copy is completed
08402  *      before returning.
08403  *
08404  * Results:
08405  *      A standard Tcl result.
08406  *
08407  * Side effects:
08408  *      May schedule a background copy operation that causes both channels to
08409  *      be marked busy.
08410  *
08411  *----------------------------------------------------------------------
08412  */
08413 
08414 int
08415 TclCopyChannel(
08416     Tcl_Interp *interp,         /* Current interpreter. */
08417     Tcl_Channel inChan,         /* Channel to read from. */
08418     Tcl_Channel outChan,        /* Channel to write to. */
08419     int toRead,                 /* Amount of data to copy, or -1 for all. */
08420     Tcl_Obj *cmdPtr)            /* Pointer to script to execute or NULL. */
08421 {
08422     Channel *inPtr = (Channel *) inChan;
08423     Channel *outPtr = (Channel *) outChan;
08424     ChannelState *inStatePtr, *outStatePtr;
08425     int readFlags, writeFlags;
08426     CopyState *csPtr;
08427     int nonBlocking = (cmdPtr) ? CHANNEL_NONBLOCKING : 0;
08428 
08429     inStatePtr = inPtr->state;
08430     outStatePtr = outPtr->state;
08431 
08432     if (inStatePtr->csPtr) {
08433         if (interp) {
08434             Tcl_AppendResult(interp, "channel \"",
08435                     Tcl_GetChannelName(inChan), "\" is busy", NULL);
08436         }
08437         return TCL_ERROR;
08438     }
08439     if (outStatePtr->csPtr) {
08440         if (interp) {
08441             Tcl_AppendResult(interp, "channel \"",
08442                     Tcl_GetChannelName(outChan), "\" is busy", NULL);
08443         }
08444         return TCL_ERROR;
08445     }
08446 
08447     readFlags = inStatePtr->flags;
08448     writeFlags = outStatePtr->flags;
08449 
08450     /*
08451      * Set up the blocking mode appropriately. Background copies need
08452      * non-blocking channels. Foreground copies need blocking channels. If
08453      * there is an error, restore the old blocking mode.
08454      */
08455 
08456     if (nonBlocking != (readFlags & CHANNEL_NONBLOCKING)) {
08457         if (SetBlockMode(interp, inPtr, nonBlocking ?
08458                 TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING) != TCL_OK) {
08459             return TCL_ERROR;
08460         }
08461     }
08462     if ((inPtr!=outPtr) && (nonBlocking!=(writeFlags&CHANNEL_NONBLOCKING)) &&
08463             (SetBlockMode(NULL, outPtr, nonBlocking ?
08464                     TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING) != TCL_OK) &&
08465             (nonBlocking != (readFlags & CHANNEL_NONBLOCKING))) {
08466         SetBlockMode(NULL, inPtr, (readFlags & CHANNEL_NONBLOCKING)
08467                 ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
08468         return TCL_ERROR;
08469     }
08470 
08471     /*
08472      * Make sure the output side is unbuffered.
08473      */
08474 
08475     outStatePtr->flags = (outStatePtr->flags & ~(CHANNEL_LINEBUFFERED))
08476         | CHANNEL_UNBUFFERED;
08477 
08478     /*
08479      * Allocate a new CopyState to maintain info about the current copy in
08480      * progress. This structure will be deallocated when the copy is
08481      * completed.
08482      */
08483 
08484     csPtr = (CopyState *) ckalloc(sizeof(CopyState) + inStatePtr->bufSize);
08485     csPtr->bufSize = inStatePtr->bufSize;
08486     csPtr->readPtr = inPtr;
08487     csPtr->writePtr = outPtr;
08488     csPtr->readFlags = readFlags;
08489     csPtr->writeFlags = writeFlags;
08490     csPtr->toRead = toRead;
08491     csPtr->total = 0;
08492     csPtr->interp = interp;
08493     if (cmdPtr) {
08494         Tcl_IncrRefCount(cmdPtr);
08495     }
08496     csPtr->cmdPtr = cmdPtr;
08497     inStatePtr->csPtr = csPtr;
08498     outStatePtr->csPtr = csPtr;
08499 
08500     /*
08501      * Start copying data between the channels.
08502      */
08503 
08504     return CopyData(csPtr, 0);
08505 }
08506 
08507 /*
08508  *----------------------------------------------------------------------
08509  *
08510  * CopyData --
08511  *
08512  *      This function implements the lowest level of the copying mechanism for
08513  *      TclCopyChannel.
08514  *
08515  * Results:
08516  *      Returns TCL_OK on success, else TCL_ERROR.
08517  *
08518  * Side effects:
08519  *      Moves data between channels, may create channel handlers.
08520  *
08521  *----------------------------------------------------------------------
08522  */
08523 
08524 static int
08525 CopyData(
08526     CopyState *csPtr,           /* State of copy operation. */
08527     int mask)                   /* Current channel event flags. */
08528 {
08529     Tcl_Interp *interp;
08530     Tcl_Obj *cmdPtr, *errObj = NULL, *bufObj = NULL, *msg = NULL;
08531     Tcl_Channel inChan, outChan;
08532     ChannelState *inStatePtr, *outStatePtr;
08533     int result = TCL_OK, size, total, sizeb;
08534     char *buffer;
08535     int inBinary, outBinary, sameEncoding;
08536                                 /* Encoding control */
08537     int underflow;              /* Input underflow */
08538 
08539     inChan      = (Tcl_Channel) csPtr->readPtr;
08540     outChan     = (Tcl_Channel) csPtr->writePtr;
08541     inStatePtr  = csPtr->readPtr->state;
08542     outStatePtr = csPtr->writePtr->state;
08543     interp      = csPtr->interp;
08544     cmdPtr      = csPtr->cmdPtr;
08545 
08546     /*
08547      * Copy the data the slow way, using the translation mechanism.
08548      *
08549      * Note: We have make sure that we use the topmost channel in a stack for
08550      * the copying. The caller uses Tcl_GetChannel to access it, and thus gets
08551      * the bottom of the stack.
08552      */
08553 
08554     inBinary = (inStatePtr->encoding == NULL);
08555     outBinary = (outStatePtr->encoding == NULL);
08556     sameEncoding = (inStatePtr->encoding == outStatePtr->encoding);
08557 
08558     if (!(inBinary || sameEncoding)) {
08559         TclNewObj(bufObj);
08560         Tcl_IncrRefCount(bufObj);
08561     }
08562 
08563     while (csPtr->toRead != 0) {
08564         /*
08565          * Check for unreported background errors.
08566          */
08567 
08568         Tcl_GetChannelError(inChan, &msg);
08569         if ((inStatePtr->unreportedError != 0) || (msg != NULL)) {
08570             Tcl_SetErrno(inStatePtr->unreportedError);
08571             inStatePtr->unreportedError = 0;
08572             goto readError;
08573         }
08574         Tcl_GetChannelError(outChan, &msg);
08575         if ((outStatePtr->unreportedError != 0) || (msg != NULL)) {
08576             Tcl_SetErrno(outStatePtr->unreportedError);
08577             outStatePtr->unreportedError = 0;
08578             goto writeError;
08579         }
08580 
08581         /*
08582          * Read up to bufSize bytes.
08583          */
08584 
08585         if ((csPtr->toRead == -1) || (csPtr->toRead > csPtr->bufSize)) {
08586             sizeb = csPtr->bufSize;
08587         } else {
08588             sizeb = csPtr->toRead;
08589         }
08590 
08591         if (inBinary || sameEncoding) {
08592             size = DoRead(inStatePtr->topChanPtr, csPtr->buffer, sizeb);
08593         } else {
08594             size = DoReadChars(inStatePtr->topChanPtr, bufObj, sizeb,
08595                     0 /* No append */);
08596         }
08597         underflow = (size >= 0) && (size < sizeb);      /* Input underflow */
08598 
08599         if (size < 0) {
08600         readError:
08601             if (interp) {
08602                 TclNewObj(errObj);
08603                 Tcl_AppendStringsToObj(errObj, "error reading \"",
08604                         Tcl_GetChannelName(inChan), "\": ", NULL);
08605                 if (msg != NULL) {
08606                     Tcl_AppendObjToObj(errObj, msg);
08607                 } else {
08608                     Tcl_AppendStringsToObj(errObj, Tcl_PosixError(interp),
08609                             NULL);
08610                 }
08611             }
08612             if (msg != NULL) {
08613                 Tcl_DecrRefCount(msg);
08614             }
08615             break;
08616         } else if (underflow) {
08617             /*
08618              * We had an underflow on the read side. If we are at EOF, then
08619              * the copying is done, otherwise set up a channel handler to
08620              * detect when the channel becomes readable again.
08621              */
08622 
08623             if ((size == 0) && Tcl_Eof(inChan)) {
08624                 break;
08625             }
08626             if (! Tcl_Eof(inChan) && !(mask & TCL_READABLE)) {
08627                 if (mask & TCL_WRITABLE) {
08628                     Tcl_DeleteChannelHandler(outChan, CopyEventProc, csPtr);
08629                 }
08630                 Tcl_CreateChannelHandler(inChan, TCL_READABLE, CopyEventProc,
08631                         csPtr);
08632             }
08633             if (size == 0) {
08634                 if (bufObj != NULL) {
08635                     TclDecrRefCount(bufObj);
08636                     bufObj = NULL;
08637                 }
08638                 return TCL_OK;
08639             }
08640         }
08641 
08642         /*
08643          * Now write the buffer out.
08644          */
08645 
08646         if (inBinary || sameEncoding) {
08647             buffer = csPtr->buffer;
08648             sizeb = size;
08649         } else {
08650             buffer = TclGetStringFromObj(bufObj, &sizeb);
08651         }
08652 
08653         if (outBinary || sameEncoding) {
08654             sizeb = DoWrite(outStatePtr->topChanPtr, buffer, sizeb);
08655         } else {
08656             sizeb = DoWriteChars(outStatePtr->topChanPtr, buffer, sizeb);
08657         }
08658 
08659         if (inBinary || sameEncoding) {
08660             /*
08661              * Both read and write counted bytes.
08662              */
08663 
08664             size = sizeb;
08665         } /* else: Read counted characters, write counted bytes, i.e.
08666            * size != sizeb */
08667 
08668         if (sizeb < 0) {
08669         writeError:
08670             if (interp) {
08671                 TclNewObj(errObj);
08672                 Tcl_AppendStringsToObj(errObj, "error writing \"",
08673                         Tcl_GetChannelName(outChan), "\": ", NULL);
08674                 if (msg != NULL) {
08675                     Tcl_AppendObjToObj(errObj, msg);
08676                 } else {
08677                     Tcl_AppendStringsToObj(errObj, Tcl_PosixError(interp),
08678                             NULL);
08679                 }
08680             }
08681             if (msg != NULL) {
08682                 Tcl_DecrRefCount(msg);
08683             }
08684             break;
08685         }
08686 
08687         /*
08688          * Update the current byte count. Do it now so the count is valid
08689          * before a return or break takes us out of the loop. The invariant at
08690          * the top of the loop should be that csPtr->toRead holds the number
08691          * of bytes left to copy.
08692          */
08693 
08694         if (csPtr->toRead != -1) {
08695             csPtr->toRead -= size;
08696         }
08697         csPtr->total += size;
08698 
08699         /*
08700          * Break loop if EOF && (size>0)
08701          */
08702 
08703         if (Tcl_Eof(inChan)) {
08704             break;
08705         }
08706 
08707         /*
08708          * Check to see if the write is happening in the background. If so,
08709          * stop copying and wait for the channel to become writable again.
08710          * After input underflow we already installed a readable handler
08711          * therefore we don't need a writable handler.
08712          */
08713 
08714         if (!underflow && (outStatePtr->flags & BG_FLUSH_SCHEDULED)) {
08715             if (!(mask & TCL_WRITABLE)) {
08716                 if (mask & TCL_READABLE) {
08717                     Tcl_DeleteChannelHandler(inChan, CopyEventProc, csPtr);
08718                 }
08719                 Tcl_CreateChannelHandler(outChan, TCL_WRITABLE,
08720                         CopyEventProc, csPtr);
08721             }
08722             if (bufObj != NULL) {
08723                 TclDecrRefCount(bufObj);
08724                 bufObj = NULL;
08725             }
08726             return TCL_OK;
08727         }
08728 
08729         /*
08730          * For background copies, we only do one buffer per invocation so we
08731          * don't starve the rest of the system.
08732          */
08733 
08734         if (cmdPtr) {
08735             /*
08736              * The first time we enter this code, there won't be a channel
08737              * handler established yet, so do it here.
08738              */
08739 
08740             if (mask == 0) {
08741                 Tcl_CreateChannelHandler(outChan, TCL_WRITABLE, CopyEventProc,
08742                         csPtr);
08743             }
08744             if (bufObj != NULL) {
08745                 TclDecrRefCount(bufObj);
08746                 bufObj = NULL;
08747             }
08748             return TCL_OK;
08749         }
08750     } /* while */
08751 
08752     if (bufObj != NULL) {
08753         TclDecrRefCount(bufObj);
08754         bufObj = NULL;
08755     }
08756 
08757     /*
08758      * Make the callback or return the number of bytes transferred. The local
08759      * total is used because StopCopy frees csPtr.
08760      */
08761 
08762     total = csPtr->total;
08763     if (cmdPtr && interp) {
08764         int code;
08765         /*
08766          * Get a private copy of the command so we can mutate it by adding
08767          * arguments. Note that StopCopy frees our saved reference to the
08768          * original command obj.
08769          */
08770 
08771         cmdPtr = Tcl_DuplicateObj(cmdPtr);
08772         Tcl_IncrRefCount(cmdPtr);
08773         StopCopy(csPtr);
08774         Tcl_Preserve(interp);
08775 
08776         Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewIntObj(total));
08777         if (errObj) {
08778             Tcl_ListObjAppendElement(interp, cmdPtr, errObj);
08779         }
08780         code = Tcl_EvalObjEx(interp, cmdPtr, TCL_EVAL_GLOBAL);
08781         if (code != TCL_OK) {
08782             TclBackgroundException(interp, code);
08783             result = TCL_ERROR;
08784         }
08785         TclDecrRefCount(cmdPtr);
08786         Tcl_Release(interp);
08787     } else {
08788         StopCopy(csPtr);
08789         if (interp) {
08790             if (errObj) {
08791                 Tcl_SetObjResult(interp, errObj);
08792                 result = TCL_ERROR;
08793             } else {
08794                 Tcl_ResetResult(interp);
08795                 Tcl_SetObjResult(interp, Tcl_NewIntObj(total));
08796             }
08797         }
08798     }
08799     return result;
08800 }
08801 
08802 /*
08803  *----------------------------------------------------------------------
08804  *
08805  * DoRead --
08806  *
08807  *      Reads a given number of bytes from a channel.
08808  *
08809  *      No encoding conversions are applied to the bytes being read.
08810  *
08811  * Results:
08812  *      The number of characters read, or -1 on error. Use Tcl_GetErrno() to
08813  *      retrieve the error code for the error that occurred.
08814  *
08815  * Side effects:
08816  *      May cause input to be buffered.
08817  *
08818  *----------------------------------------------------------------------
08819  */
08820 
08821 static int
08822 DoRead(
08823     Channel *chanPtr,           /* The channel from which to read. */
08824     char *bufPtr,               /* Where to store input read. */
08825     int toRead)                 /* Maximum number of bytes to read. */
08826 {
08827     ChannelState *statePtr = chanPtr->state;
08828                                 /* State info for channel */
08829     int copied;                 /* How many characters were copied into the
08830                                  * result string? */
08831     int copiedNow;              /* How many characters were copied from the
08832                                  * current input buffer? */
08833     int result;                 /* Of calling GetInput. */
08834 
08835     /*
08836      * If we have not encountered a sticky EOF, clear the EOF bit. Either way
08837      * clear the BLOCKED bit. We want to discover these anew during each
08838      * operation.
08839      */
08840 
08841     if (!(statePtr->flags & CHANNEL_STICKY_EOF)) {
08842         ResetFlag(statePtr, CHANNEL_EOF);
08843     }
08844     ResetFlag(statePtr, CHANNEL_BLOCKED | CHANNEL_NEED_MORE_DATA);
08845 
08846     for (copied = 0; copied < toRead; copied += copiedNow) {
08847         copiedNow = CopyAndTranslateBuffer(statePtr, bufPtr + copied,
08848                 toRead - copied);
08849         if (copiedNow == 0) {
08850             if (statePtr->flags & CHANNEL_EOF) {
08851                 goto done;
08852             }
08853             if (statePtr->flags & CHANNEL_BLOCKED) {
08854                 if (statePtr->flags & CHANNEL_NONBLOCKING) {
08855                     goto done;
08856                 }
08857                 ResetFlag(statePtr, CHANNEL_BLOCKED);
08858             }
08859             result = GetInput(chanPtr);
08860             if (result != 0) {
08861                 if (result != EAGAIN) {
08862                     copied = -1;
08863                 }
08864                 goto done;
08865             }
08866         }
08867     }
08868 
08869     ResetFlag(statePtr, CHANNEL_BLOCKED);
08870 
08871     /*
08872      * Update the notifier state so we don't block while there is still data
08873      * in the buffers.
08874      */
08875 
08876   done:
08877     UpdateInterest(chanPtr);
08878     return copied;
08879 }
08880 
08881 /*
08882  *----------------------------------------------------------------------
08883  *
08884  * CopyAndTranslateBuffer --
08885  *
08886  *      Copy at most one buffer of input to the result space, doing eol
08887  *      translations according to mode in effect currently.
08888  *
08889  * Results:
08890  *      Number of bytes stored in the result buffer (as opposed to the number
08891  *      of bytes read from the channel). May return zero if no input is
08892  *      available to be translated.
08893  *
08894  * Side effects:
08895  *      Consumes buffered input. May deallocate one buffer.
08896  *
08897  *----------------------------------------------------------------------
08898  */
08899 
08900 static int
08901 CopyAndTranslateBuffer(
08902     ChannelState *statePtr,     /* Channel state from which to read input. */
08903     char *result,               /* Where to store the copied input. */
08904     int space)                  /* How many bytes are available in result to
08905                                  * store the copied input? */
08906 {
08907     ChannelBuffer *bufPtr;      /* The buffer from which to copy bytes. */
08908     int bytesInBuffer;          /* How many bytes are available to be copied
08909                                  * in the current input buffer? */
08910     int copied;                 /* How many characters were already copied
08911                                  * into the destination space? */
08912     int i;                      /* Iterates over the copied input looking for
08913                                  * the input eofChar. */
08914 
08915     /*
08916      * If there is no input at all, return zero. The invariant is that either
08917      * there is no buffer in the queue, or if the first buffer is empty, it is
08918      * also the last buffer (and thus there is no input in the queue). Note
08919      * also that if the buffer is empty, we leave it in the queue.
08920      */
08921 
08922     if (statePtr->inQueueHead == NULL) {
08923         return 0;
08924     }
08925     bufPtr = statePtr->inQueueHead;
08926     bytesInBuffer = BytesLeft(bufPtr);
08927 
08928     copied = 0;
08929     switch (statePtr->inputTranslation) {
08930     case TCL_TRANSLATE_LF:
08931         if (bytesInBuffer == 0) {
08932             return 0;
08933         }
08934 
08935         /*
08936          * Copy the current chunk into the result buffer.
08937          */
08938 
08939         if (bytesInBuffer < space) {
08940             space = bytesInBuffer;
08941         }
08942         memcpy(result, RemovePoint(bufPtr), (size_t) space);
08943         bufPtr->nextRemoved += space;
08944         copied = space;
08945         break;
08946     case TCL_TRANSLATE_CR: {
08947         char *end;
08948 
08949         if (bytesInBuffer == 0) {
08950             return 0;
08951         }
08952 
08953         /*
08954          * Copy the current chunk into the result buffer, then replace all \r
08955          * with \n.
08956          */
08957 
08958         if (bytesInBuffer < space) {
08959             space = bytesInBuffer;
08960         }
08961         memcpy(result, RemovePoint(bufPtr), (size_t) space);
08962         bufPtr->nextRemoved += space;
08963         copied = space;
08964 
08965         for (end = result + copied; result < end; result++) {
08966             if (*result == '\r') {
08967                 *result = '\n';
08968             }
08969         }
08970         break;
08971     }
08972     case TCL_TRANSLATE_CRLF: {
08973         char *src, *end, *dst;
08974         int curByte;
08975 
08976         /*
08977          * If there is a held-back "\r" at EOF, produce it now.
08978          */
08979 
08980         if (bytesInBuffer == 0) {
08981             if ((statePtr->flags & (INPUT_SAW_CR | CHANNEL_EOF)) ==
08982                     (INPUT_SAW_CR | CHANNEL_EOF)) {
08983                 result[0] = '\r';
08984                 ResetFlag(statePtr, INPUT_SAW_CR);
08985                 return 1;
08986             }
08987             return 0;
08988         }
08989 
08990         /*
08991          * Copy the current chunk and replace "\r\n" with "\n" (but not
08992          * standalone "\r"!).
08993          */
08994 
08995         if (bytesInBuffer < space) {
08996             space = bytesInBuffer;
08997         }
08998         memcpy(result, RemovePoint(bufPtr), (size_t) space);
08999         bufPtr->nextRemoved += space;
09000         copied = space;
09001 
09002         end = result + copied;
09003         dst = result;
09004         for (src = result; src < end; src++) {
09005             curByte = *src;
09006             if (curByte == '\n') {
09007                 ResetFlag(statePtr, INPUT_SAW_CR);
09008             } else if (statePtr->flags & INPUT_SAW_CR) {
09009                 ResetFlag(statePtr, INPUT_SAW_CR);
09010                 *dst = '\r';
09011                 dst++;
09012             }
09013             if (curByte == '\r') {
09014                 SetFlag(statePtr, INPUT_SAW_CR);
09015             } else {
09016                 *dst = (char) curByte;
09017                 dst++;
09018             }
09019         }
09020         copied = dst - result;
09021         break;
09022     }
09023     case TCL_TRANSLATE_AUTO: {
09024         char *src, *end, *dst;
09025         int curByte;
09026 
09027         if (bytesInBuffer == 0) {
09028             return 0;
09029         }
09030 
09031         /*
09032          * Loop over the current buffer, converting "\r" and "\r\n" to "\n".
09033          */
09034 
09035         if (bytesInBuffer < space) {
09036             space = bytesInBuffer;
09037         }
09038         memcpy(result, RemovePoint(bufPtr), (size_t) space);
09039         bufPtr->nextRemoved += space;
09040         copied = space;
09041 
09042         end = result + copied;
09043         dst = result;
09044         for (src = result; src < end; src++) {
09045             curByte = *src;
09046             if (curByte == '\r') {
09047                 SetFlag(statePtr, INPUT_SAW_CR);
09048                 *dst = '\n';
09049                 dst++;
09050             } else {
09051                 if ((curByte != '\n') || !(statePtr->flags & INPUT_SAW_CR)) {
09052                     *dst = (char) curByte;
09053                     dst++;
09054                 }
09055                 ResetFlag(statePtr, INPUT_SAW_CR);
09056             }
09057         }
09058         copied = dst - result;
09059         break;
09060     }
09061     default:
09062         Tcl_Panic("unknown eol translation mode");
09063     }
09064 
09065     /*
09066      * If an in-stream EOF character is set for this channel, check that the
09067      * input we copied so far does not contain the EOF char. If it does, copy
09068      * only up to and excluding that character.
09069      */
09070 
09071     if (statePtr->inEofChar != 0) {
09072         for (i = 0; i < copied; i++) {
09073             if (result[i] == (char) statePtr->inEofChar) {
09074                 /*
09075                  * Set sticky EOF so that no further input is presented to the
09076                  * caller.
09077                  */
09078 
09079                 SetFlag(statePtr, CHANNEL_EOF | CHANNEL_STICKY_EOF);
09080                 statePtr->inputEncodingFlags |= TCL_ENCODING_END;
09081                 copied = i;
09082                 break;
09083             }
09084         }
09085     }
09086 
09087     /*
09088      * If the current buffer is empty recycle it.
09089      */
09090 
09091     if (IsBufferEmpty(bufPtr)) {
09092         statePtr->inQueueHead = bufPtr->nextPtr;
09093         if (statePtr->inQueueHead == NULL) {
09094             statePtr->inQueueTail = NULL;
09095         }
09096         RecycleBuffer(statePtr, bufPtr, 0);
09097     }
09098 
09099     /*
09100      * Return the number of characters copied into the result buffer. This may
09101      * be different from the number of bytes consumed, because of EOL
09102      * translations.
09103      */
09104 
09105     return copied;
09106 }
09107 
09108 /*
09109  *----------------------------------------------------------------------
09110  *
09111  * CopyBuffer --
09112  *
09113  *      Copy at most one buffer of input to the result space.
09114  *
09115  * Results:
09116  *      Number of bytes stored in the result buffer. May return zero if no
09117  *      input is available.
09118  *
09119  * Side effects:
09120  *      Consumes buffered input. May deallocate one buffer.
09121  *
09122  *----------------------------------------------------------------------
09123  */
09124 
09125 static int
09126 CopyBuffer(
09127     Channel *chanPtr,           /* Channel from which to read input. */
09128     char *result,               /* Where to store the copied input. */
09129     int space)                  /* How many bytes are available in result to
09130                                  * store the copied input? */
09131 {
09132     ChannelBuffer *bufPtr;      /* The buffer from which to copy bytes. */
09133     int bytesInBuffer;          /* How many bytes are available to be copied
09134                                  * in the current input buffer? */
09135     int copied;                 /* How many characters were already copied
09136                                  * into the destination space? */
09137 
09138     /*
09139      * If there is no input at all, return zero. The invariant is that either
09140      * there is no buffer in the queue, or if the first buffer is empty, it is
09141      * also the last buffer (and thus there is no input in the queue). Note
09142      * also that if the buffer is empty, we don't leave it in the queue, but
09143      * recycle it.
09144      */
09145 
09146     if (chanPtr->inQueueHead == NULL) {
09147         return 0;
09148     }
09149     bufPtr = chanPtr->inQueueHead;
09150     bytesInBuffer = BytesLeft(bufPtr);
09151 
09152     copied = 0;
09153 
09154     if (bytesInBuffer == 0) {
09155         RecycleBuffer(chanPtr->state, bufPtr, 0);
09156         chanPtr->inQueueHead = NULL;
09157         chanPtr->inQueueTail = NULL;
09158         return 0;
09159     }
09160 
09161     /*
09162      * Copy the current chunk into the result buffer.
09163      */
09164 
09165     if (bytesInBuffer < space) {
09166         space = bytesInBuffer;
09167     }
09168 
09169     memcpy(result, RemovePoint(bufPtr), (size_t) space);
09170     bufPtr->nextRemoved += space;
09171     copied = space;
09172 
09173     /*
09174      * We don't care about in-stream EOF characters here as the data read here
09175      * may still flow through one or more transformations, i.e. is not in its
09176      * final state yet.
09177      */
09178 
09179     /*
09180      * If the current buffer is empty recycle it.
09181      */
09182 
09183     if (IsBufferEmpty(bufPtr)) {
09184         chanPtr->inQueueHead = bufPtr->nextPtr;
09185         if (chanPtr->inQueueHead == NULL) {
09186             chanPtr->inQueueTail = NULL;
09187         }
09188         RecycleBuffer(chanPtr->state, bufPtr, 0);
09189     }
09190 
09191     /*
09192      * Return the number of characters copied into the result buffer.
09193      */
09194 
09195     return copied;
09196 }
09197 
09198 /*
09199  *----------------------------------------------------------------------
09200  *
09201  * DoWrite --
09202  *
09203  *      Puts a sequence of characters into an output buffer, may queue the
09204  *      buffer for output if it gets full, and also remembers whether the
09205  *      current buffer is ready e.g. if it contains a newline and we are in
09206  *      line buffering mode.
09207  *
09208  * Results:
09209  *      The number of bytes written or -1 in case of error. If -1,
09210  *      Tcl_GetErrno will return the error code.
09211  *
09212  * Side effects:
09213  *      May buffer up output and may cause output to be produced on the
09214  *      channel.
09215  *
09216  *----------------------------------------------------------------------
09217  */
09218 
09219 static int
09220 DoWrite(
09221     Channel *chanPtr,           /* The channel to buffer output for. */
09222     const char *src,            /* Data to write. */
09223     int srcLen)                 /* Number of bytes to write. */
09224 {
09225     ChannelState *statePtr = chanPtr->state;
09226                                 /* State info for channel */
09227     ChannelBuffer *outBufPtr;   /* Current output buffer. */
09228     int foundNewline;           /* Did we find a newline in output? */
09229     char *dPtr;
09230     const char *sPtr;           /* Search variables for newline. */
09231     int crsent;                 /* In CRLF eol translation mode, remember the
09232                                  * fact that a CR was output to the channel
09233                                  * without its following NL. */
09234     int i;                      /* Loop index for newline search. */
09235     int destCopied;             /* How many bytes were used in this
09236                                  * destination buffer to hold the output? */
09237     int totalDestCopied;        /* How many bytes total were copied to the
09238                                  * channel buffer? */
09239     int srcCopied;              /* How many bytes were copied from the source
09240                                  * string? */
09241     char *destPtr;              /* Where in line to copy to? */
09242 
09243     /*
09244      * If we are in network (or windows) translation mode, record the fact
09245      * that we have not yet sent a CR to the channel.
09246      */
09247 
09248     crsent = 0;
09249 
09250     /*
09251      * Loop filling buffers and flushing them until all output has been
09252      * consumed.
09253      */
09254 
09255     srcCopied = 0;
09256     totalDestCopied = 0;
09257 
09258     while (srcLen > 0) {
09259         /*
09260          * Make sure there is a current output buffer to accept output.
09261          */
09262 
09263         if (statePtr->curOutPtr == NULL) {
09264             statePtr->curOutPtr = AllocChannelBuffer(statePtr->bufSize);
09265         }
09266 
09267         outBufPtr = statePtr->curOutPtr;
09268 
09269         destCopied = SpaceLeft(outBufPtr);
09270         if (destCopied > srcLen) {
09271             destCopied = srcLen;
09272         }
09273 
09274         destPtr = InsertPoint(outBufPtr);
09275         switch (statePtr->outputTranslation) {
09276         case TCL_TRANSLATE_LF:
09277             srcCopied = destCopied;
09278             memcpy(destPtr, src, (size_t) destCopied);
09279             break;
09280         case TCL_TRANSLATE_CR:
09281             srcCopied = destCopied;
09282             memcpy(destPtr, src, (size_t) destCopied);
09283             for (dPtr = destPtr; dPtr < destPtr + destCopied; dPtr++) {
09284                 if (*dPtr == '\n') {
09285                     *dPtr = '\r';
09286                 }
09287             }
09288             break;
09289         case TCL_TRANSLATE_CRLF:
09290             for (srcCopied = 0, dPtr = destPtr, sPtr = src;
09291                     dPtr < destPtr + destCopied;
09292                     dPtr++, sPtr++, srcCopied++) {
09293                 if (*sPtr == '\n') {
09294                     if (crsent) {
09295                         *dPtr = '\n';
09296                         crsent = 0;
09297                     } else {
09298                         *dPtr = '\r';
09299                         crsent = 1;
09300                         sPtr--, srcCopied--;
09301                     }
09302                 } else {
09303                     *dPtr = *sPtr;
09304                 }
09305             }
09306             break;
09307         case TCL_TRANSLATE_AUTO:
09308             Tcl_Panic("Tcl_Write: AUTO output translation mode not supported");
09309         default:
09310             Tcl_Panic("Tcl_Write: unknown output translation mode");
09311         }
09312 
09313         /*
09314          * The current buffer is ready for output if it is full, or if it
09315          * contains a newline and this channel is line-buffered, or if it
09316          * contains any output and this channel is unbuffered.
09317          */
09318 
09319         outBufPtr->nextAdded += destCopied;
09320         if (!(statePtr->flags & BUFFER_READY)) {
09321             if (IsBufferFull(outBufPtr)) {
09322                 SetFlag(statePtr, BUFFER_READY);
09323             } else if (statePtr->flags & CHANNEL_LINEBUFFERED) {
09324                 for (sPtr = src, i = 0, foundNewline = 0;
09325                         (i < srcCopied) && (!foundNewline);
09326                         i++, sPtr++) {
09327                     if (*sPtr == '\n') {
09328                         foundNewline = 1;
09329                         break;
09330                     }
09331                 }
09332                 if (foundNewline) {
09333                     SetFlag(statePtr, BUFFER_READY);
09334                 }
09335             } else if (statePtr->flags & CHANNEL_UNBUFFERED) {
09336                 SetFlag(statePtr, BUFFER_READY);
09337             }
09338         }
09339 
09340         totalDestCopied += srcCopied;
09341         src += srcCopied;
09342         srcLen -= srcCopied;
09343 
09344         if (statePtr->flags & BUFFER_READY) {
09345             if (FlushChannel(NULL, chanPtr, 0) != 0) {
09346                 return -1;
09347             }
09348         }
09349     } /* Closes "while" */
09350 
09351     return totalDestCopied;
09352 }
09353 
09354 /*
09355  *----------------------------------------------------------------------
09356  *
09357  * CopyEventProc --
09358  *
09359  *      This routine is invoked as a channel event handler for the background
09360  *      copy operation. It is just a trivial wrapper around the CopyData
09361  *      routine.
09362  *
09363  * Results:
09364  *      None.
09365  *
09366  * Side effects:
09367  *      None.
09368  *
09369  *----------------------------------------------------------------------
09370  */
09371 
09372 static void
09373 CopyEventProc(
09374     ClientData clientData,
09375     int mask)
09376 {
09377     (void) CopyData((CopyState *) clientData, mask);
09378 }
09379 
09380 /*
09381  *----------------------------------------------------------------------
09382  *
09383  * StopCopy --
09384  *
09385  *      This routine halts a copy that is in progress.
09386  *
09387  * Results:
09388  *      None.
09389  *
09390  * Side effects:
09391  *      Removes any pending channel handlers and restores the blocking and
09392  *      buffering modes of the channels. The CopyState is freed.
09393  *
09394  *----------------------------------------------------------------------
09395  */
09396 
09397 static void
09398 StopCopy(
09399     CopyState *csPtr)           /* State for bg copy to stop . */
09400 {
09401     ChannelState *inStatePtr, *outStatePtr;
09402     int nonBlocking;
09403 
09404     if (!csPtr) {
09405         return;
09406     }
09407 
09408     inStatePtr = csPtr->readPtr->state;
09409     outStatePtr = csPtr->writePtr->state;
09410 
09411     /*
09412      * Restore the old blocking mode and output buffering mode.
09413      */
09414 
09415     nonBlocking = (csPtr->readFlags & CHANNEL_NONBLOCKING);
09416     if (nonBlocking != (inStatePtr->flags & CHANNEL_NONBLOCKING)) {
09417         SetBlockMode(NULL, csPtr->readPtr,
09418                 nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
09419     }
09420     if (csPtr->readPtr != csPtr->writePtr) {
09421         nonBlocking = (csPtr->writeFlags & CHANNEL_NONBLOCKING);
09422         if (nonBlocking != (outStatePtr->flags & CHANNEL_NONBLOCKING)) {
09423             SetBlockMode(NULL, csPtr->writePtr,
09424                     nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
09425         }
09426     }
09427     outStatePtr->flags &= ~(CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED);
09428     outStatePtr->flags |=
09429             csPtr->writeFlags & (CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED);
09430 
09431     if (csPtr->cmdPtr) {
09432         Tcl_DeleteChannelHandler((Tcl_Channel) csPtr->readPtr, CopyEventProc,
09433                 csPtr);
09434         if (csPtr->readPtr != csPtr->writePtr) {
09435             Tcl_DeleteChannelHandler((Tcl_Channel) csPtr->writePtr,
09436                     CopyEventProc, csPtr);
09437         }
09438         TclDecrRefCount(csPtr->cmdPtr);
09439     }
09440     inStatePtr->csPtr = NULL;
09441     outStatePtr->csPtr = NULL;
09442     ckfree((char *) csPtr);
09443 }
09444 
09445 /*
09446  *----------------------------------------------------------------------
09447  *
09448  * StackSetBlockMode --
09449  *
09450  *      This function sets the blocking mode for a channel, iterating through
09451  *      each channel in a stack and updates the state flags.
09452  *
09453  * Results:
09454  *      0 if OK, result code from failed blockModeProc otherwise.
09455  *
09456  * Side effects:
09457  *      Modifies the blocking mode of the channel and possibly generates an
09458  *      error.
09459  *
09460  *----------------------------------------------------------------------
09461  */
09462 
09463 static int
09464 StackSetBlockMode(
09465     Channel *chanPtr,           /* Channel to modify. */
09466     int mode)                   /* One of TCL_MODE_BLOCKING or
09467                                  * TCL_MODE_NONBLOCKING. */
09468 {
09469     int result = 0;
09470     Tcl_DriverBlockModeProc *blockModeProc;
09471 
09472     /*
09473      * Start at the top of the channel stack
09474      */
09475 
09476     chanPtr = chanPtr->state->topChanPtr;
09477     while (chanPtr != NULL) {
09478         blockModeProc = Tcl_ChannelBlockModeProc(chanPtr->typePtr);
09479         if (blockModeProc != NULL) {
09480             result = (*blockModeProc) (chanPtr->instanceData, mode);
09481             if (result != 0) {
09482                 Tcl_SetErrno(result);
09483                 return result;
09484             }
09485         }
09486         chanPtr = chanPtr->downChanPtr;
09487     }
09488     return 0;
09489 }
09490 
09491 /*
09492  *----------------------------------------------------------------------
09493  *
09494  * SetBlockMode --
09495  *
09496  *      This function sets the blocking mode for a channel and updates the
09497  *      state flags.
09498  *
09499  * Results:
09500  *      A standard Tcl result.
09501  *
09502  * Side effects:
09503  *      Modifies the blocking mode of the channel and possibly generates an
09504  *      error.
09505  *
09506  *----------------------------------------------------------------------
09507  */
09508 
09509 static int
09510 SetBlockMode(
09511     Tcl_Interp *interp,         /* Interp for error reporting. */
09512     Channel *chanPtr,           /* Channel to modify. */
09513     int mode)                   /* One of TCL_MODE_BLOCKING or
09514                                  * TCL_MODE_NONBLOCKING. */
09515 {
09516     int result = 0;
09517     ChannelState *statePtr = chanPtr->state;
09518                                 /* State info for channel */
09519 
09520     result = StackSetBlockMode(chanPtr, mode);
09521     if (result != 0) {
09522         if (interp != NULL) {
09523             /*
09524              * TIP #219.
09525              * Move error messages put by the driver into the bypass area and
09526              * put them into the regular interpreter result. Fall back to the
09527              * regular message if nothing was found in the bypass.
09528              *
09529              * Note that we cannot have a message in the interpreter bypass
09530              * area, StackSetBlockMode is restricted to the channel bypass.
09531              * We still need the interp as the destination of the move.
09532              */
09533 
09534             if (!TclChanCaughtErrorBypass(interp, (Tcl_Channel) chanPtr)) {
09535                 Tcl_AppendResult(interp, "error setting blocking mode: ",
09536                         Tcl_PosixError(interp), NULL);
09537             }
09538         } else {
09539             /*
09540              * TIP #219.
09541              * If we have no interpreter to put a bypass message into we have
09542              * to clear it, to prevent its propagation and use in other places
09543              * unrelated to the actual occurence of the problem.
09544              */
09545 
09546             Tcl_SetChannelError((Tcl_Channel) chanPtr, NULL);
09547         }
09548         return TCL_ERROR;
09549     }
09550     if (mode == TCL_MODE_BLOCKING) {
09551         ResetFlag(statePtr, CHANNEL_NONBLOCKING | BG_FLUSH_SCHEDULED);
09552     } else {
09553         SetFlag(statePtr, CHANNEL_NONBLOCKING);
09554     }
09555     return TCL_OK;
09556 }
09557 
09558 /*
09559  *----------------------------------------------------------------------
09560  *
09561  * Tcl_GetChannelNames --
09562  *
09563  *      Return the names of all open channels in the interp.
09564  *
09565  * Results:
09566  *      TCL_OK or TCL_ERROR.
09567  *
09568  * Side effects:
09569  *      Interp result modified with list of channel names.
09570  *
09571  *----------------------------------------------------------------------
09572  */
09573 
09574 int
09575 Tcl_GetChannelNames(
09576     Tcl_Interp *interp)         /* Interp for error reporting. */
09577 {
09578     return Tcl_GetChannelNamesEx(interp, NULL);
09579 }
09580 
09581 /*
09582  *----------------------------------------------------------------------
09583  *
09584  * Tcl_GetChannelNamesEx --
09585  *
09586  *      Return the names of open channels in the interp filtered filtered
09587  *      through a pattern. If pattern is NULL, it returns all the open
09588  *      channels.
09589  *
09590  * Results:
09591  *      TCL_OK or TCL_ERROR.
09592  *
09593  * Side effects:
09594  *      Interp result modified with list of channel names.
09595  *
09596  *----------------------------------------------------------------------
09597  */
09598 
09599 int
09600 Tcl_GetChannelNamesEx(
09601     Tcl_Interp *interp,         /* Interp for error reporting. */
09602     const char *pattern)        /* Pattern to filter on. */
09603 {
09604     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
09605     ChannelState *statePtr;
09606     const char *name;           /* Name for channel */
09607     Tcl_Obj *resultPtr;         /* Pointer to result object */
09608     Tcl_HashTable *hTblPtr;     /* Hash table of channels. */
09609     Tcl_HashEntry *hPtr;        /* Search variable. */
09610     Tcl_HashSearch hSearch;     /* Search variable. */
09611 
09612     if (interp == NULL) {
09613         return TCL_OK;
09614     }
09615 
09616     /*
09617      * Get the channel table that stores the channels registered for this
09618      * interpreter.
09619      */
09620 
09621     hTblPtr = GetChannelTable(interp);
09622     TclNewObj(resultPtr);
09623     if ((pattern != NULL) && TclMatchIsTrivial(pattern)
09624             && !((pattern[0] == 's') && (pattern[1] == 't')
09625             && (pattern[2] == 'd'))) {
09626         if ((Tcl_FindHashEntry(hTblPtr, pattern) != NULL)
09627                 && (Tcl_ListObjAppendElement(interp, resultPtr,
09628                 Tcl_NewStringObj(pattern, -1)) != TCL_OK)) {
09629             goto error;
09630         }
09631         goto done;
09632     }
09633     for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch); hPtr != NULL;
09634             hPtr = Tcl_NextHashEntry(&hSearch)) {
09635 
09636         statePtr = ((Channel *) Tcl_GetHashValue(hPtr))->state;
09637         if (statePtr->topChanPtr == (Channel *) tsdPtr->stdinChannel) {
09638             name = "stdin";
09639         } else if (statePtr->topChanPtr == (Channel *) tsdPtr->stdoutChannel) {
09640             name = "stdout";
09641         } else if (statePtr->topChanPtr == (Channel *) tsdPtr->stderrChannel) {
09642             name = "stderr";
09643         } else {
09644             /*
09645              * This is also stored in Tcl_GetHashKey(hTblPtr, hPtr), but it's
09646              * simpler to just grab the name from the statePtr.
09647              */
09648 
09649             name = statePtr->channelName;
09650         }
09651 
09652         if (((pattern == NULL) || Tcl_StringMatch(name, pattern)) &&
09653                 (Tcl_ListObjAppendElement(interp, resultPtr,
09654                         Tcl_NewStringObj(name, -1)) != TCL_OK)) {
09655         error:
09656             TclDecrRefCount(resultPtr);
09657             return TCL_ERROR;
09658         }
09659     }
09660 
09661   done:
09662     Tcl_SetObjResult(interp, resultPtr);
09663     return TCL_OK;
09664 }
09665 
09666 /*
09667  *----------------------------------------------------------------------
09668  *
09669  * Tcl_IsChannelRegistered --
09670  *
09671  *      Checks whether the channel is associated with the interp. See also
09672  *      Tcl_RegisterChannel and Tcl_UnregisterChannel.
09673  *
09674  * Results:
09675  *      0 if the channel is not registered in the interpreter, 1 else.
09676  *
09677  * Side effects:
09678  *      None.
09679  *
09680  *----------------------------------------------------------------------
09681  */
09682 
09683 int
09684 Tcl_IsChannelRegistered(
09685     Tcl_Interp *interp,         /* The interp to query of the channel */
09686     Tcl_Channel chan)           /* The channel to check */
09687 {
09688     Tcl_HashTable *hTblPtr;     /* Hash table of channels. */
09689     Tcl_HashEntry *hPtr;        /* Search variable. */
09690     Channel *chanPtr;           /* The real IO channel. */
09691     ChannelState *statePtr;     /* State of the real channel. */
09692 
09693     /*
09694      * Always check bottom-most channel in the stack. This is the one that
09695      * gets registered.
09696      */
09697 
09698     chanPtr = ((Channel *) chan)->state->bottomChanPtr;
09699     statePtr = chanPtr->state;
09700 
09701     hTblPtr = Tcl_GetAssocData(interp, "tclIO", NULL);
09702     if (hTblPtr == NULL) {
09703         return 0;
09704     }
09705     hPtr = Tcl_FindHashEntry(hTblPtr, statePtr->channelName);
09706     if (hPtr == NULL) {
09707         return 0;
09708     }
09709     if ((Channel *) Tcl_GetHashValue(hPtr) != chanPtr) {
09710         return 0;
09711     }
09712 
09713     return 1;
09714 }
09715 
09716 /*
09717  *----------------------------------------------------------------------
09718  *
09719  * Tcl_IsChannelShared --
09720  *
09721  *      Checks whether the channel is shared by multiple interpreters.
09722  *
09723  * Results:
09724  *      A boolean value (0 = Not shared, 1 = Shared).
09725  *
09726  * Side effects:
09727  *      None.
09728  *
09729  *----------------------------------------------------------------------
09730  */
09731 
09732 int
09733 Tcl_IsChannelShared(
09734     Tcl_Channel chan)           /* The channel to query */
09735 {
09736     ChannelState *statePtr = ((Channel *) chan)->state;
09737                                 /* State of real channel structure. */
09738 
09739     return ((statePtr->refCount > 1) ? 1 : 0);
09740 }
09741 
09742 /*
09743  *----------------------------------------------------------------------
09744  *
09745  * Tcl_IsChannelExisting --
09746  *
09747  *      Checks whether a channel of the given name exists in the
09748  *      (thread)-global list of all channels. See Tcl_GetChannelNamesEx for
09749  *      function exposed at the Tcl level.
09750  *
09751  * Results:
09752  *      A boolean value (0 = Does not exist, 1 = Does exist).
09753  *
09754  * Side effects:
09755  *      None.
09756  *
09757  *----------------------------------------------------------------------
09758  */
09759 
09760 int
09761 Tcl_IsChannelExisting(
09762     const char *chanName)       /* The name of the channel to look for. */
09763 {
09764     ChannelState *statePtr;
09765     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
09766     const char *name;
09767     int chanNameLen;
09768 
09769     chanNameLen = strlen(chanName);
09770     for (statePtr = tsdPtr->firstCSPtr; statePtr != NULL;
09771             statePtr = statePtr->nextCSPtr) {
09772         if (statePtr->topChanPtr == (Channel *) tsdPtr->stdinChannel) {
09773             name = "stdin";
09774         } else if (statePtr->topChanPtr == (Channel *) tsdPtr->stdoutChannel) {
09775             name = "stdout";
09776         } else if (statePtr->topChanPtr == (Channel *) tsdPtr->stderrChannel) {
09777             name = "stderr";
09778         } else {
09779             name = statePtr->channelName;
09780         }
09781 
09782         if ((*chanName == *name) &&
09783                 (memcmp(name, chanName, (size_t) chanNameLen) == 0)) {
09784             return 1;
09785         }
09786     }
09787 
09788     return 0;
09789 }
09790 
09791 /*
09792  *----------------------------------------------------------------------
09793  *
09794  * Tcl_ChannelName --
09795  *
09796  *      Return the name of the channel type.
09797  *
09798  * Results:
09799  *      A pointer the name of the channel type.
09800  *
09801  * Side effects:
09802  *      None.
09803  *
09804  *----------------------------------------------------------------------
09805  */
09806 
09807 const char *
09808 Tcl_ChannelName(
09809     const Tcl_ChannelType *chanTypePtr) /* Pointer to channel type. */
09810 {
09811     return chanTypePtr->typeName;
09812 }
09813 
09814 /*
09815  *----------------------------------------------------------------------
09816  *
09817  * Tcl_ChannelVersion --
09818  *
09819  *      Return the of version of the channel type.
09820  *
09821  * Results:
09822  *      One of the TCL_CHANNEL_VERSION_* constants from tcl.h
09823  *
09824  * Side effects:
09825  *      None.
09826  *
09827  *----------------------------------------------------------------------
09828  */
09829 
09830 Tcl_ChannelTypeVersion
09831 Tcl_ChannelVersion(
09832     const Tcl_ChannelType *chanTypePtr)
09833                                 /* Pointer to channel type. */
09834 {
09835     if (chanTypePtr->version == TCL_CHANNEL_VERSION_2) {
09836         return TCL_CHANNEL_VERSION_2;
09837     } else if (chanTypePtr->version == TCL_CHANNEL_VERSION_3) {
09838         return TCL_CHANNEL_VERSION_3;
09839     } else if (chanTypePtr->version == TCL_CHANNEL_VERSION_4) {
09840         return TCL_CHANNEL_VERSION_4;
09841     } else if (chanTypePtr->version == TCL_CHANNEL_VERSION_5) {
09842         return TCL_CHANNEL_VERSION_5;
09843     } else {
09844         /*
09845          * In <v2 channel versions, the version field is occupied by the
09846          * Tcl_DriverBlockModeProc
09847          */
09848 
09849         return TCL_CHANNEL_VERSION_1;
09850     }
09851 }
09852 
09853 /*
09854  *----------------------------------------------------------------------
09855  *
09856  * HaveVersion --
09857  *
09858  *      Return whether a channel type is (at least) of a given version.
09859  *
09860  * Results:
09861  *      True if the minimum version is exceeded by the version actually
09862  *      present.
09863  *
09864  * Side effects:
09865  *      None.
09866  *
09867  *----------------------------------------------------------------------
09868  */
09869 
09870 static int
09871 HaveVersion(
09872     const Tcl_ChannelType *chanTypePtr,
09873     Tcl_ChannelTypeVersion minimumVersion)
09874 {
09875     Tcl_ChannelTypeVersion actualVersion = Tcl_ChannelVersion(chanTypePtr);
09876 
09877     return (PTR2INT(actualVersion)) >= (PTR2INT(minimumVersion));
09878 }
09879 
09880 /*
09881  *----------------------------------------------------------------------
09882  *
09883  * Tcl_ChannelBlockModeProc --
09884  *
09885  *      Return the Tcl_DriverBlockModeProc of the channel type.
09886  *
09887  * Results:
09888  *      A pointer to the proc.
09889  *
09890  * Side effects:
09891  *      None.
09892  *
09893  *---------------------------------------------------------------------- */
09894 
09895 Tcl_DriverBlockModeProc *
09896 Tcl_ChannelBlockModeProc(
09897     const Tcl_ChannelType *chanTypePtr)
09898                                 /* Pointer to channel type. */
09899 {
09900     if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) {
09901         return chanTypePtr->blockModeProc;
09902     } else {
09903         /*
09904          * The v1 structure had the blockModeProc in a different place.
09905          */
09906 
09907         return (Tcl_DriverBlockModeProc *) (chanTypePtr->version);
09908     }
09909 }
09910 
09911 /*
09912  *----------------------------------------------------------------------
09913  *
09914  * Tcl_ChannelCloseProc --
09915  *
09916  *      Return the Tcl_DriverCloseProc of the channel type.
09917  *
09918  * Results:
09919  *      A pointer to the proc.
09920  *
09921  * Side effects:
09922  *      None.
09923  *
09924  *----------------------------------------------------------------------
09925  */
09926 
09927 Tcl_DriverCloseProc *
09928 Tcl_ChannelCloseProc(
09929     const Tcl_ChannelType *chanTypePtr)
09930                                 /* Pointer to channel type. */
09931 {
09932     return chanTypePtr->closeProc;
09933 }
09934 
09935 /*
09936  *----------------------------------------------------------------------
09937  *
09938  * Tcl_ChannelClose2Proc --
09939  *
09940  *      Return the Tcl_DriverClose2Proc of the channel type.
09941  *
09942  * Results:
09943  *      A pointer to the proc.
09944  *
09945  * Side effects:
09946  *      None.
09947  *
09948  *----------------------------------------------------------------------
09949  */
09950 
09951 Tcl_DriverClose2Proc *
09952 Tcl_ChannelClose2Proc(
09953     const Tcl_ChannelType *chanTypePtr)
09954                                 /* Pointer to channel type. */
09955 {
09956     return chanTypePtr->close2Proc;
09957 }
09958 
09959 /*
09960  *----------------------------------------------------------------------
09961  *
09962  * Tcl_ChannelInputProc --
09963  *
09964  *      Return the Tcl_DriverInputProc of the channel type.
09965  *
09966  * Results:
09967  *      A pointer to the proc.
09968  *
09969  * Side effects:
09970  *      None.
09971  *
09972  *----------------------------------------------------------------------
09973  */
09974 
09975 Tcl_DriverInputProc *
09976 Tcl_ChannelInputProc(
09977     const Tcl_ChannelType *chanTypePtr)
09978                                 /* Pointer to channel type. */
09979 {
09980     return chanTypePtr->inputProc;
09981 }
09982 
09983 /*
09984  *----------------------------------------------------------------------
09985  *
09986  * Tcl_ChannelOutputProc --
09987  *
09988  *      Return the Tcl_DriverOutputProc of the channel type.
09989  *
09990  * Results:
09991  *      A pointer to the proc.
09992  *
09993  * Side effects:
09994  *      None.
09995  *
09996  *----------------------------------------------------------------------
09997  */
09998 
09999 Tcl_DriverOutputProc *
10000 Tcl_ChannelOutputProc(
10001     const Tcl_ChannelType *chanTypePtr)
10002                                 /* Pointer to channel type. */
10003 {
10004     return chanTypePtr->outputProc;
10005 }
10006 
10007 /*
10008  *----------------------------------------------------------------------
10009  *
10010  * Tcl_ChannelSeekProc --
10011  *
10012  *      Return the Tcl_DriverSeekProc of the channel type.
10013  *
10014  * Results:
10015  *      A pointer to the proc.
10016  *
10017  * Side effects:
10018  *      None.
10019  *
10020  *----------------------------------------------------------------------
10021  */
10022 
10023 Tcl_DriverSeekProc *
10024 Tcl_ChannelSeekProc(
10025     const Tcl_ChannelType *chanTypePtr)
10026                                 /* Pointer to channel type. */
10027 {
10028     return chanTypePtr->seekProc;
10029 }
10030 
10031 /*
10032  *----------------------------------------------------------------------
10033  *
10034  * Tcl_ChannelSetOptionProc --
10035  *
10036  *      Return the Tcl_DriverSetOptionProc of the channel type.
10037  *
10038  * Results:
10039  *      A pointer to the proc.
10040  *
10041  * Side effects:
10042  *      None.
10043  *
10044  *----------------------------------------------------------------------
10045  */
10046 
10047 Tcl_DriverSetOptionProc *
10048 Tcl_ChannelSetOptionProc(
10049     const Tcl_ChannelType *chanTypePtr)
10050                                 /* Pointer to channel type. */
10051 {
10052     return chanTypePtr->setOptionProc;
10053 }
10054 
10055 /*
10056  *----------------------------------------------------------------------
10057  *
10058  * Tcl_ChannelGetOptionProc --
10059  *
10060  *      Return the Tcl_DriverGetOptionProc of the channel type.
10061  *
10062  * Results:
10063  *      A pointer to the proc.
10064  *
10065  * Side effects:
10066  *      None.
10067  *
10068  *----------------------------------------------------------------------
10069  */
10070 
10071 Tcl_DriverGetOptionProc *
10072 Tcl_ChannelGetOptionProc(
10073     const Tcl_ChannelType *chanTypePtr)
10074                                 /* Pointer to channel type. */
10075 {
10076     return chanTypePtr->getOptionProc;
10077 }
10078 
10079 /*
10080  *----------------------------------------------------------------------
10081  *
10082  * Tcl_ChannelWatchProc --
10083  *
10084  *      Return the Tcl_DriverWatchProc of the channel type.
10085  *
10086  * Results:
10087  *      A pointer to the proc.
10088  *
10089  * Side effects:
10090  *      None.
10091  *
10092  *----------------------------------------------------------------------
10093  */
10094 
10095 Tcl_DriverWatchProc *
10096 Tcl_ChannelWatchProc(
10097     const Tcl_ChannelType *chanTypePtr)
10098                                 /* Pointer to channel type. */
10099 {
10100     return chanTypePtr->watchProc;
10101 }
10102 
10103 /*
10104  *----------------------------------------------------------------------
10105  *
10106  * Tcl_ChannelGetHandleProc --
10107  *
10108  *      Return the Tcl_DriverGetHandleProc of the channel type.
10109  *
10110  * Results:
10111  *      A pointer to the proc.
10112  *
10113  * Side effects:
10114  *      None.
10115  *
10116  *----------------------------------------------------------------------
10117  */
10118 
10119 Tcl_DriverGetHandleProc *
10120 Tcl_ChannelGetHandleProc(
10121     const Tcl_ChannelType *chanTypePtr)
10122                                 /* Pointer to channel type. */
10123 {
10124     return chanTypePtr->getHandleProc;
10125 }
10126 
10127 /*
10128  *----------------------------------------------------------------------
10129  *
10130  * Tcl_ChannelFlushProc --
10131  *
10132  *      Return the Tcl_DriverFlushProc of the channel type.
10133  *
10134  * Results:
10135  *      A pointer to the proc.
10136  *
10137  * Side effects:
10138  *      None.
10139  *
10140  *----------------------------------------------------------------------
10141  */
10142 
10143 Tcl_DriverFlushProc *
10144 Tcl_ChannelFlushProc(
10145     const Tcl_ChannelType *chanTypePtr)
10146                                 /* Pointer to channel type. */
10147 {
10148     if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) {
10149         return chanTypePtr->flushProc;
10150     } else {
10151         return NULL;
10152     }
10153 }
10154 
10155 /*
10156  *----------------------------------------------------------------------
10157  *
10158  * Tcl_ChannelHandlerProc --
10159  *
10160  *      Return the Tcl_DriverHandlerProc of the channel type.
10161  *
10162  * Results:
10163  *      A pointer to the proc.
10164  *
10165  * Side effects:
10166  *      None.
10167  *
10168  *----------------------------------------------------------------------
10169  */
10170 
10171 Tcl_DriverHandlerProc *
10172 Tcl_ChannelHandlerProc(
10173     const Tcl_ChannelType *chanTypePtr)
10174                                 /* Pointer to channel type. */
10175 {
10176     if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) {
10177         return chanTypePtr->handlerProc;
10178     } else {
10179         return NULL;
10180     }
10181 }
10182 
10183 /*
10184  *----------------------------------------------------------------------
10185  *
10186  * Tcl_ChannelWideSeekProc --
10187  *
10188  *      Return the Tcl_DriverWideSeekProc of the channel type.
10189  *
10190  * Results:
10191  *      A pointer to the proc.
10192  *
10193  * Side effects:
10194  *      None.
10195  *
10196  *----------------------------------------------------------------------
10197  */
10198 
10199 Tcl_DriverWideSeekProc *
10200 Tcl_ChannelWideSeekProc(
10201     const Tcl_ChannelType *chanTypePtr)
10202                                 /* Pointer to channel type. */
10203 {
10204     if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_3)) {
10205         return chanTypePtr->wideSeekProc;
10206     } else {
10207         return NULL;
10208     }
10209 }
10210 
10211 /*
10212  *----------------------------------------------------------------------
10213  *
10214  * Tcl_ChannelThreadActionProc --
10215  *
10216  *      TIP #218, Channel Thread Actions. Return the
10217  *      Tcl_DriverThreadActionProc of the channel type.
10218  *
10219  * Results:
10220  *      A pointer to the proc.
10221  *
10222  * Side effects:
10223  *      None.
10224  *
10225  *----------------------------------------------------------------------
10226  */
10227 
10228 Tcl_DriverThreadActionProc *
10229 Tcl_ChannelThreadActionProc(
10230     const Tcl_ChannelType *chanTypePtr)
10231                                 /* Pointer to channel type. */
10232 {
10233     if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_4)) {
10234         return chanTypePtr->threadActionProc;
10235     } else {
10236         return NULL;
10237     }
10238 }
10239 
10240 /*
10241  *----------------------------------------------------------------------
10242  *
10243  * Tcl_SetChannelErrorInterp --
10244  *
10245  *      TIP #219, Tcl Channel Reflection API.
10246  *      Store an error message for the I/O system.
10247  *
10248  * Results:
10249  *      None.
10250  *
10251  * Side effects:
10252  *      Discards a previously stored message.
10253  *
10254  *----------------------------------------------------------------------
10255  */
10256 
10257 void
10258 Tcl_SetChannelErrorInterp(
10259     Tcl_Interp *interp,         /* Interp to store the data into. */
10260     Tcl_Obj *msg)               /* Error message to store. */
10261 {
10262     Interp *iPtr = (Interp *) interp;
10263 
10264     if (iPtr->chanMsg != NULL) {
10265         TclDecrRefCount(iPtr->chanMsg);
10266         iPtr->chanMsg = NULL;
10267     }
10268 
10269     if (msg != NULL) {
10270         iPtr->chanMsg = FixLevelCode(msg);
10271         Tcl_IncrRefCount(iPtr->chanMsg);
10272     }
10273     return;
10274 }
10275 
10276 /*
10277  *----------------------------------------------------------------------
10278  *
10279  * Tcl_SetChannelError --
10280  *
10281  *      TIP #219, Tcl Channel Reflection API.
10282  *      Store an error message for the I/O system.
10283  *
10284  * Results:
10285  *      None.
10286  *
10287  * Side effects:
10288  *      Discards a previously stored message.
10289  *
10290  *----------------------------------------------------------------------
10291  */
10292 
10293 void
10294 Tcl_SetChannelError(
10295     Tcl_Channel chan,           /* Channel to store the data into. */
10296     Tcl_Obj *msg)               /* Error message to store. */
10297 {
10298     ChannelState *statePtr = ((Channel *) chan)->state;
10299 
10300     if (statePtr->chanMsg != NULL) {
10301         TclDecrRefCount(statePtr->chanMsg);
10302         statePtr->chanMsg = NULL;
10303     }
10304 
10305     if (msg != NULL) {
10306         statePtr->chanMsg = FixLevelCode(msg);
10307         Tcl_IncrRefCount(statePtr->chanMsg);
10308     }
10309     return;
10310 }
10311 
10312 /*
10313  *----------------------------------------------------------------------
10314  *
10315  * FixLevelCode --
10316  *
10317  *      TIP #219, Tcl Channel Reflection API.
10318  *      Scans an error message for bad -code / -level directives. Returns a
10319  *      modified copy with such directives corrected, and the input if it had
10320  *      no problems.
10321  *
10322  * Results:
10323  *      A Tcl_Obj*
10324  *
10325  * Side effects:
10326  *      None.
10327  *
10328  *----------------------------------------------------------------------
10329  */
10330 
10331 static Tcl_Obj *
10332 FixLevelCode(
10333     Tcl_Obj *msg)
10334 {
10335     int explicitResult, numOptions, lc, lcn;
10336     Tcl_Obj **lv, **lvn;
10337     int res, i, j, val, lignore, cignore;
10338     int newlevel = -1, newcode = -1;
10339 
10340     /* ASSERT msg != NULL */
10341 
10342     /*
10343      * Process the caught message.
10344      *
10345      * Syntax = (option value)... ?message?
10346      *
10347      * Bad message syntax causes a panic, because the other side uses
10348      * Tcl_GetReturnOptions and list construction functions to marshall the
10349      * information. Hence an error means that we've got serious breakage.
10350      */
10351 
10352     res = Tcl_ListObjGetElements(NULL, msg, &lc, &lv);
10353     if (res != TCL_OK) {
10354         Tcl_Panic("Tcl_SetChannelError(Interp): Bad syntax of message");
10355     }
10356 
10357     explicitResult = (1 == (lc % 2));
10358     numOptions = lc - explicitResult;
10359 
10360     /*
10361      * No options, nothing to do.
10362      */
10363 
10364     if (numOptions == 0) {
10365         return msg;
10366     }
10367 
10368     /*
10369      * Check for -code x, x != 1|error, and -level x, x != 0
10370      */
10371 
10372     for (i = 0; i < numOptions; i += 2) {
10373         if (0 == strcmp(TclGetString(lv[i]), "-code")) {
10374             /*
10375              * !"error", !integer, integer != 1 (numeric code for error)
10376              */
10377 
10378             res = TclGetIntFromObj(NULL, lv[i+1], &val);
10379             if (((res == TCL_OK) && (val != 1)) || ((res != TCL_OK) &&
10380                     (0 != strcmp(TclGetString(lv[i+1]), "error")))) {
10381                 newcode = 1;
10382             }
10383         } else if (0 == strcmp(TclGetString(lv[i]), "-level")) {
10384             /*
10385              * !integer, integer != 0
10386              */
10387 
10388             res = TclGetIntFromObj(NULL, lv [i+1], &val);
10389             if ((res != TCL_OK) || (val != 0)) {
10390                 newlevel = 0;
10391             }
10392         }
10393     }
10394 
10395     /*
10396      * -code, -level are either not present or ok. Nothing to do.
10397      */
10398 
10399     if ((newlevel < 0) && (newcode < 0)) {
10400         return msg;
10401     }
10402 
10403     lcn = numOptions;
10404     if (explicitResult) {
10405         lcn ++;
10406     }
10407     if (newlevel >= 0) {
10408         lcn += 2;
10409     }
10410     if (newcode >= 0) {
10411         lcn += 2;
10412     }
10413 
10414     lvn = (Tcl_Obj **) ckalloc(lcn * sizeof(Tcl_Obj *));
10415 
10416     /*
10417      * New level/code information is spliced into the first occurence of
10418      * -level, -code, further occurences are ignored. The options cannot be
10419      * not present, we would not come here. Options which are ok are simply
10420      * copied over.
10421      */
10422 
10423     lignore = cignore = 0;
10424     for (i=0, j=0; i<numOptions; i+=2) {
10425         if (0 == strcmp(TclGetString(lv[i]), "-level")) {
10426             if (newlevel >= 0) {
10427                 lvn[j++] = lv[i];
10428                 lvn[j++] = Tcl_NewIntObj(newlevel);
10429                 newlevel = -1;
10430                 lignore = 1;
10431                 continue;
10432             } else if (lignore) {
10433                 continue;
10434             }
10435         } else if (0 == strcmp(TclGetString(lv[i]), "-code")) {
10436             if (newcode >= 0) {
10437                 lvn[j++] = lv[i];
10438                 lvn[j++] = Tcl_NewIntObj(newcode);
10439                 newcode = -1;
10440                 cignore = 1;
10441                 continue;
10442             } else if (cignore) {
10443                 continue;
10444             }
10445         }
10446 
10447         /*
10448          * Keep everything else, possibly copied down.
10449          */
10450 
10451         lvn[j++] = lv[i];
10452         lvn[j++] = lv[i+1];
10453     }
10454     if (newlevel >= 0) {
10455         Tcl_Panic("Defined newlevel not used in rewrite");
10456     }
10457     if (newcode >= 0) {
10458         Tcl_Panic("Defined newcode not used in rewrite");
10459     }
10460 
10461     if (explicitResult) {
10462         lvn[j++] = lv[i];
10463     }
10464 
10465     msg = Tcl_NewListObj(j, lvn);
10466 
10467     ckfree((char *) lvn);
10468     return msg;
10469 }
10470 
10471 /*
10472  *----------------------------------------------------------------------
10473  *
10474  * Tcl_GetChannelErrorInterp --
10475  *
10476  *      TIP #219, Tcl Channel Reflection API.
10477  *      Return the message stored by the channel driver.
10478  *
10479  * Results:
10480  *      Tcl error message object.
10481  *
10482  * Side effects:
10483  *      Resets the stored data to NULL.
10484  *
10485  *----------------------------------------------------------------------
10486  */
10487 
10488 void
10489 Tcl_GetChannelErrorInterp(
10490     Tcl_Interp *interp,         /* Interp to query. */
10491     Tcl_Obj **msg)              /* Place for error message. */
10492 {
10493     Interp *iPtr = (Interp *) interp;
10494 
10495     *msg = iPtr->chanMsg;
10496     iPtr->chanMsg = NULL;
10497 }
10498 
10499 /*
10500  *----------------------------------------------------------------------
10501  *
10502  * Tcl_GetChannelError --
10503  *
10504  *      TIP #219, Tcl Channel Reflection API.
10505  *      Return the message stored by the channel driver.
10506  *
10507  * Results:
10508  *      Tcl error message object.
10509  *
10510  * Side effects:
10511  *      Resets the stored data to NULL.
10512  *
10513  *----------------------------------------------------------------------
10514  */
10515 
10516 void
10517 Tcl_GetChannelError(
10518     Tcl_Channel chan,           /* Channel to query. */
10519     Tcl_Obj **msg)              /* Place for error message. */
10520 {
10521     ChannelState *statePtr = ((Channel *) chan)->state;
10522 
10523     *msg = statePtr->chanMsg;
10524     statePtr->chanMsg = NULL;
10525 }
10526 
10527 /*
10528  *----------------------------------------------------------------------
10529  *
10530  * Tcl_ChannelTruncateProc --
10531  *
10532  *      TIP #208 (subsection relating to truncation, based on TIP #206).
10533  *      Return the Tcl_DriverTruncateProc of the channel type.
10534  *
10535  * Results:
10536  *      A pointer to the proc.
10537  *
10538  * Side effects:
10539  *      None.
10540  *
10541  *----------------------------------------------------------------------
10542  */
10543 
10544 Tcl_DriverTruncateProc *
10545 Tcl_ChannelTruncateProc(
10546     const Tcl_ChannelType *chanTypePtr)
10547                                 /* Pointer to channel type. */
10548 {
10549     if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_5)) {
10550         return chanTypePtr->truncateProc;
10551     } else {
10552         return NULL;
10553     }
10554 }
10555 
10556 /*
10557  *----------------------------------------------------------------------
10558  *
10559  * DupChannelIntRep --
10560  *
10561  *      Initialize the internal representation of a new Tcl_Obj to a copy of
10562  *      the internal representation of an existing string object.
10563  *
10564  * Results:
10565  *      None.
10566  *
10567  * Side effects:
10568  *      copyPtr's internal rep is set to a copy of srcPtr's internal
10569  *      representation.
10570  *
10571  *----------------------------------------------------------------------
10572  */
10573 
10574 static void
10575 DupChannelIntRep(
10576     register Tcl_Obj *srcPtr,   /* Object with internal rep to copy. Must have
10577                                  * an internal rep of type "Channel". */
10578     register Tcl_Obj *copyPtr)  /* Object with internal rep to set. Must not
10579                                  * currently have an internal rep.*/
10580 {
10581     ChannelState *statePtr = GET_CHANNELSTATE(srcPtr);
10582     SET_CHANNELSTATE(copyPtr, statePtr);
10583     Tcl_Preserve((ClientData) statePtr);
10584     copyPtr->typePtr = &tclChannelType;
10585 }
10586 
10587 /*
10588  *----------------------------------------------------------------------
10589  *
10590  * SetChannelFromAny --
10591  *
10592  *      Create an internal representation of type "Channel" for an object.
10593  *
10594  * Results:
10595  *      This operation always succeeds and returns TCL_OK.
10596  *
10597  * Side effects:
10598  *      Any old internal reputation for objPtr is freed and the internal
10599  *      representation is set to "Channel".
10600  *
10601  *----------------------------------------------------------------------
10602  */
10603 
10604 static int
10605 SetChannelFromAny(
10606     Tcl_Interp *interp,         /* Used for error reporting if not NULL. */
10607     register Tcl_Obj *objPtr)   /* The object to convert. */
10608 {
10609     ChannelState *statePtr;
10610 
10611     if (objPtr->typePtr == &tclChannelType) {
10612         /*
10613          * The channel is valid until any call to DetachChannel occurs.
10614          * Ensure consistency checks are done.
10615          */
10616         statePtr = GET_CHANNELSTATE(objPtr);
10617         if (statePtr->flags & (CHANNEL_TAINTED|CHANNEL_CLOSED)) {
10618             ResetFlag(statePtr, CHANNEL_TAINTED);
10619             Tcl_Release((ClientData) statePtr);
10620             UpdateStringOfChannel(objPtr);
10621             objPtr->typePtr = NULL;
10622         }
10623     }
10624     if (objPtr->typePtr != &tclChannelType) {
10625         Tcl_Channel chan;
10626 
10627         /*
10628          * We need a valid string with which to check for a valid channel, but
10629          * make sure not to free internal rep until validated. [Bug 1847044]
10630          */
10631         if ((objPtr->typePtr != NULL) && (objPtr->bytes == NULL)) {
10632             objPtr->typePtr->updateStringProc(objPtr);
10633         }
10634 
10635         chan = Tcl_GetChannel(interp, objPtr->bytes, NULL);
10636         if (chan == NULL) {
10637             return TCL_ERROR;
10638         }
10639 
10640         TclFreeIntRep(objPtr);
10641         statePtr = ((Channel *)chan)->state;
10642         Tcl_Preserve((ClientData) statePtr);
10643         SET_CHANNELSTATE(objPtr, statePtr);
10644         objPtr->typePtr = &tclChannelType;
10645     }
10646     return TCL_OK;
10647 }
10648 
10649 /*
10650  *----------------------------------------------------------------------
10651  *
10652  * UpdateStringOfChannel --
10653  *
10654  *      Update the string representation for an object whose internal
10655  *      representation is "Channel".
10656  *
10657  * Results:
10658  *      None.
10659  *
10660  * Side effects:
10661  *      The object's string may be set by converting its Unicode represention
10662  *      to UTF format.
10663  *
10664  *----------------------------------------------------------------------
10665  */
10666 
10667 static void
10668 UpdateStringOfChannel(
10669     Tcl_Obj *objPtr)            /* Object with string rep to update. */
10670 {
10671     if (objPtr->bytes == NULL) {
10672         ChannelState *statePtr = GET_CHANNELSTATE(objPtr);
10673         const char *name = statePtr->channelName;
10674         if (name) {
10675             size_t len = strlen(name);
10676             objPtr->bytes = (char *) ckalloc(len + 1);
10677             objPtr->length = len;
10678             memcpy(objPtr->bytes, name, len);
10679         } else {
10680             objPtr->bytes = tclEmptyStringRep;
10681             objPtr->length = 0;
10682         }
10683     }
10684 }
10685 
10686 /*
10687  *----------------------------------------------------------------------
10688  *
10689  * FreeChannelIntRep --
10690  *
10691  *      Release statePtr storage.
10692  *
10693  * Results:
10694  *      None.
10695  *
10696  * Side effects:
10697  *      May cause state to be freed.
10698  *
10699  *----------------------------------------------------------------------
10700  */
10701 
10702 static void
10703 FreeChannelIntRep(
10704     Tcl_Obj *objPtr)            /* Object with internal rep to free. */
10705 {
10706     Tcl_Release((ClientData) GET_CHANNELSTATE(objPtr));
10707 }
10708 
10709 #if 0
10710 /*
10711  * For future debugging work, a simple function to print the flags of a
10712  * channel in semi-readable form.
10713  */
10714 
10715 static int
10716 DumpFlags(
10717     char *str,
10718     int flags)
10719 {
10720     char buf[20];
10721     int i = 0;
10722 
10723 #define ChanFlag(chr,bit) (buf[i++] = ((flags & (bit)) ? (chr) : '_'))
10724 
10725     ChanFlag('r', TCL_READABLE);
10726     ChanFlag('w', TCL_WRITABLE);
10727     ChanFlag('n', CHANNEL_NONBLOCKING);
10728     ChanFlag('l', CHANNEL_LINEBUFFERED);
10729     ChanFlag('u', CHANNEL_UNBUFFERED);
10730     ChanFlag('R', BUFFER_READY);
10731     ChanFlag('F', BG_FLUSH_SCHEDULED);
10732     ChanFlag('c', CHANNEL_CLOSED);
10733     ChanFlag('E', CHANNEL_EOF);
10734     ChanFlag('S', CHANNEL_STICKY_EOF);
10735     ChanFlag('B', CHANNEL_BLOCKED);
10736     ChanFlag('/', INPUT_SAW_CR);
10737     ChanFlag('*', INPUT_NEED_NL);
10738     ChanFlag('D', CHANNEL_DEAD);
10739     ChanFlag('R', CHANNEL_RAW_MODE);
10740 #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
10741     ChanFlag('T', CHANNEL_TIMER_FEV);
10742     ChanFlag('H', CHANNEL_HAS_MORE_DATA);
10743 #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
10744     ChanFlag('x', CHANNEL_INCLOSE);
10745 
10746     buf[i] ='\0';
10747 
10748     fprintf(stderr, "%s: %s\n", str, buf);
10749     return 0;
10750 }
10751 #endif
10752 
10753 /*
10754  * Local Variables:
10755  * mode: c
10756  * c-basic-offset: 4
10757  * fill-column: 78
10758  * End:
10759  */



Generated on Wed Mar 12 12:18:18 2008 by  doxygen 1.5.1