tclIO.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:273k
源码类别:

通讯编程

开发平台:

Visual C++

  1.     /*
  2.      * If we are in the middle of a background copy, use the saved flags.
  3.      */
  4.     if (statePtr->csPtr) {
  5. if (chanPtr == statePtr->csPtr->readPtr) {
  6.     flags = statePtr->csPtr->readFlags;
  7. } else {
  8.     flags = statePtr->csPtr->writeFlags;
  9. }
  10.     } else {
  11. flags = statePtr->flags;
  12.     }
  13.     /*
  14.      * If the optionName is NULL it means that we want a list of all
  15.      * options and values.
  16.      */
  17.     
  18.     if (optionName == (char *) NULL) {
  19.         len = 0;
  20.     } else {
  21.         len = strlen(optionName);
  22.     }
  23.     
  24.     if ((len == 0) || ((len > 2) && (optionName[1] == 'b') &&
  25.             (strncmp(optionName, "-blocking", len) == 0))) {
  26.         if (len == 0) {
  27.             Tcl_DStringAppendElement(dsPtr, "-blocking");
  28.         }
  29.         Tcl_DStringAppendElement(dsPtr,
  30. (flags & CHANNEL_NONBLOCKING) ? "0" : "1");
  31.         if (len > 0) {
  32.             return TCL_OK;
  33.         }
  34.     }
  35.     if ((len == 0) || ((len > 7) && (optionName[1] == 'b') &&
  36.             (strncmp(optionName, "-buffering", len) == 0))) {
  37.         if (len == 0) {
  38.             Tcl_DStringAppendElement(dsPtr, "-buffering");
  39.         }
  40.         if (flags & CHANNEL_LINEBUFFERED) {
  41.             Tcl_DStringAppendElement(dsPtr, "line");
  42.         } else if (flags & CHANNEL_UNBUFFERED) {
  43.             Tcl_DStringAppendElement(dsPtr, "none");
  44.         } else {
  45.             Tcl_DStringAppendElement(dsPtr, "full");
  46.         }
  47.         if (len > 0) {
  48.             return TCL_OK;
  49.         }
  50.     }
  51.     if ((len == 0) || ((len > 7) && (optionName[1] == 'b') &&
  52.             (strncmp(optionName, "-buffersize", len) == 0))) {
  53.         if (len == 0) {
  54.             Tcl_DStringAppendElement(dsPtr, "-buffersize");
  55.         }
  56.         TclFormatInt(optionVal, statePtr->bufSize);
  57.         Tcl_DStringAppendElement(dsPtr, optionVal);
  58.         if (len > 0) {
  59.             return TCL_OK;
  60.         }
  61.     }
  62.     if ((len == 0) ||
  63.     ((len > 2) && (optionName[1] == 'e') &&
  64.     (strncmp(optionName, "-encoding", len) == 0))) {
  65. if (len == 0) {
  66.     Tcl_DStringAppendElement(dsPtr, "-encoding");
  67. }
  68. if (statePtr->encoding == NULL) {
  69.     Tcl_DStringAppendElement(dsPtr, "binary");
  70. } else {
  71.     Tcl_DStringAppendElement(dsPtr,
  72.     Tcl_GetEncodingName(statePtr->encoding));
  73. }
  74. if (len > 0) {
  75.     return TCL_OK;
  76. }
  77.     }
  78.     if ((len == 0) ||
  79.             ((len > 2) && (optionName[1] == 'e') &&
  80.                     (strncmp(optionName, "-eofchar", len) == 0))) {
  81.         if (len == 0) {
  82.             Tcl_DStringAppendElement(dsPtr, "-eofchar");
  83.         }
  84.         if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
  85.                 (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
  86.             Tcl_DStringStartSublist(dsPtr);
  87.         }
  88.         if (flags & TCL_READABLE) {
  89.             if (statePtr->inEofChar == 0) {
  90.                 Tcl_DStringAppendElement(dsPtr, "");
  91.             } else {
  92.                 char buf[4];
  93.                 sprintf(buf, "%c", statePtr->inEofChar);
  94.                 Tcl_DStringAppendElement(dsPtr, buf);
  95.             }
  96.         }
  97.         if (flags & TCL_WRITABLE) {
  98.             if (statePtr->outEofChar == 0) {
  99.                 Tcl_DStringAppendElement(dsPtr, "");
  100.             } else {
  101.                 char buf[4];
  102.                 sprintf(buf, "%c", statePtr->outEofChar);
  103.                 Tcl_DStringAppendElement(dsPtr, buf);
  104.             }
  105.         }
  106.         if ( !(flags & (TCL_READABLE|TCL_WRITABLE))) {
  107.             /* Not readable or writable (server socket) */
  108.             Tcl_DStringAppendElement(dsPtr, "");
  109.         }
  110.         if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
  111.                 (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
  112.             Tcl_DStringEndSublist(dsPtr);
  113.         }
  114.         if (len > 0) {
  115.             return TCL_OK;
  116.         }
  117.     }
  118.     if ((len == 0) ||
  119.             ((len > 1) && (optionName[1] == 't') &&
  120.                     (strncmp(optionName, "-translation", len) == 0))) {
  121.         if (len == 0) {
  122.             Tcl_DStringAppendElement(dsPtr, "-translation");
  123.         }
  124.         if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
  125.                 (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
  126.             Tcl_DStringStartSublist(dsPtr);
  127.         }
  128.         if (flags & TCL_READABLE) {
  129.             if (statePtr->inputTranslation == TCL_TRANSLATE_AUTO) {
  130.                 Tcl_DStringAppendElement(dsPtr, "auto");
  131.             } else if (statePtr->inputTranslation == TCL_TRANSLATE_CR) {
  132.                 Tcl_DStringAppendElement(dsPtr, "cr");
  133.             } else if (statePtr->inputTranslation == TCL_TRANSLATE_CRLF) {
  134.                 Tcl_DStringAppendElement(dsPtr, "crlf");
  135.             } else {
  136.                 Tcl_DStringAppendElement(dsPtr, "lf");
  137.             }
  138.         }
  139.         if (flags & TCL_WRITABLE) {
  140.             if (statePtr->outputTranslation == TCL_TRANSLATE_AUTO) {
  141.                 Tcl_DStringAppendElement(dsPtr, "auto");
  142.             } else if (statePtr->outputTranslation == TCL_TRANSLATE_CR) {
  143.                 Tcl_DStringAppendElement(dsPtr, "cr");
  144.             } else if (statePtr->outputTranslation == TCL_TRANSLATE_CRLF) {
  145.                 Tcl_DStringAppendElement(dsPtr, "crlf");
  146.             } else {
  147.                 Tcl_DStringAppendElement(dsPtr, "lf");
  148.             }
  149.         }
  150.         if ( !(flags & (TCL_READABLE|TCL_WRITABLE))) {
  151.             /* Not readable or writable (server socket) */
  152.             Tcl_DStringAppendElement(dsPtr, "auto");
  153.         }
  154.         if (((flags & (TCL_READABLE|TCL_WRITABLE)) ==
  155.                 (TCL_READABLE|TCL_WRITABLE)) && (len == 0)) {
  156.             Tcl_DStringEndSublist(dsPtr);
  157.         }
  158.         if (len > 0) {
  159.             return TCL_OK;
  160.         }
  161.     }
  162.     if (chanPtr->typePtr->getOptionProc != (Tcl_DriverGetOptionProc *) NULL) {
  163. /*
  164.  * let the driver specific handle additional options
  165.  * and result code and message.
  166.  */
  167.         return (chanPtr->typePtr->getOptionProc) (chanPtr->instanceData,
  168. interp, optionName, dsPtr);
  169.     } else {
  170. /*
  171.  * no driver specific options case.
  172.  */
  173.         if (len == 0) {
  174.             return TCL_OK;
  175.         }
  176. return Tcl_BadChannelOption(interp, optionName, NULL);
  177.     }
  178. }
  179. /*
  180.  *---------------------------------------------------------------------------
  181.  *
  182.  * Tcl_SetChannelOption --
  183.  *
  184.  * Sets an option on a channel.
  185.  *
  186.  * Results:
  187.  * A standard Tcl result.  On error, sets interp's result object
  188.  * if interp is not NULL.
  189.  *
  190.  * Side effects:
  191.  * May modify an option on a device.
  192.  *
  193.  *---------------------------------------------------------------------------
  194.  */
  195. int
  196. Tcl_SetChannelOption(interp, chan, optionName, newValue)
  197.     Tcl_Interp *interp; /* For error reporting - can be NULL. */
  198.     Tcl_Channel chan; /* Channel on which to set mode. */
  199.     CONST char *optionName; /* Which option to set? */
  200.     CONST char *newValue; /* New value for option. */
  201. {
  202.     Channel *chanPtr = (Channel *) chan; /* The real IO channel. */
  203.     ChannelState *statePtr = chanPtr->state; /* state info for channel */
  204.     size_t len; /* Length of optionName string. */
  205.     int argc;
  206.     CONST char **argv;
  207.     /*
  208.      * If the channel is in the middle of a background copy, fail.
  209.      */
  210.     if (statePtr->csPtr) {
  211. if (interp) {
  212.     Tcl_AppendResult(interp,
  213.     "unable to set channel options: background copy in progress",
  214.     (char *) NULL);
  215. }
  216.         return TCL_ERROR;
  217.     }
  218.     /*
  219.      * Disallow options on dead channels -- channels that have been closed but
  220.      * not yet been deallocated. Such channels can be found if the exit
  221.      * handler for channel cleanup has run but the channel is still
  222.      * registered in an interpreter.
  223.      */
  224.     if (CheckForDeadChannel(NULL, statePtr)) {
  225. return TCL_ERROR;
  226.     }
  227.     /*
  228.      * This operation should occur at the top of a channel stack.
  229.      */
  230.     chanPtr = statePtr->topChanPtr;
  231.     len = strlen(optionName);
  232.     if ((len > 2) && (optionName[1] == 'b') &&
  233.             (strncmp(optionName, "-blocking", len) == 0)) {
  234. int newMode;
  235.         if (Tcl_GetBoolean(interp, newValue, &newMode) == TCL_ERROR) {
  236.             return TCL_ERROR;
  237.         }
  238.         if (newMode) {
  239.             newMode = TCL_MODE_BLOCKING;
  240.         } else {
  241.             newMode = TCL_MODE_NONBLOCKING;
  242.         }
  243. return SetBlockMode(interp, chanPtr, newMode);
  244.     } else if ((len > 7) && (optionName[1] == 'b') &&
  245.             (strncmp(optionName, "-buffering", len) == 0)) {
  246.         len = strlen(newValue);
  247.         if ((newValue[0] == 'f') && (strncmp(newValue, "full", len) == 0)) {
  248.             statePtr->flags &=
  249.                 (~(CHANNEL_UNBUFFERED|CHANNEL_LINEBUFFERED));
  250.         } else if ((newValue[0] == 'l') &&
  251.                 (strncmp(newValue, "line", len) == 0)) {
  252.             statePtr->flags &= (~(CHANNEL_UNBUFFERED));
  253.             statePtr->flags |= CHANNEL_LINEBUFFERED;
  254.         } else if ((newValue[0] == 'n') &&
  255.                 (strncmp(newValue, "none", len) == 0)) {
  256.             statePtr->flags &= (~(CHANNEL_LINEBUFFERED));
  257.             statePtr->flags |= CHANNEL_UNBUFFERED;
  258.         } else {
  259.             if (interp) {
  260.                 Tcl_AppendResult(interp, "bad value for -buffering: ",
  261.                         "must be one of full, line, or none",
  262.                         (char *) NULL);
  263.                 return TCL_ERROR;
  264.             }
  265.         }
  266. return TCL_OK;
  267.     } else if ((len > 7) && (optionName[1] == 'b') &&
  268.             (strncmp(optionName, "-buffersize", len) == 0)) {
  269. int newBufferSize;
  270. if (Tcl_GetInt(interp, newValue, &newBufferSize) == TCL_ERROR) {
  271.     return TCL_ERROR;
  272. }
  273. Tcl_SetChannelBufferSize(chan, newBufferSize);
  274.     } else if ((len > 2) && (optionName[1] == 'e') &&
  275.     (strncmp(optionName, "-encoding", len) == 0)) {
  276. Tcl_Encoding encoding;
  277. if ((newValue[0] == '') || (strcmp(newValue, "binary") == 0)) {
  278.     encoding = NULL;
  279. } else {
  280.     encoding = Tcl_GetEncoding(interp, newValue);
  281.     if (encoding == NULL) {
  282. return TCL_ERROR;
  283.     }
  284. }
  285. /*
  286.  * When the channel has an escape sequence driven encoding such as
  287.  * iso2022, the terminated escape sequence must write to the buffer.
  288.  */
  289. if ((statePtr->encoding != NULL) && (statePtr->curOutPtr != NULL)
  290. && (CheckChannelErrors(statePtr, TCL_WRITABLE) == 0)) {
  291.     statePtr->outputEncodingFlags |= TCL_ENCODING_END;
  292.     WriteChars(chanPtr, "", 0);
  293. }
  294. Tcl_FreeEncoding(statePtr->encoding);
  295. statePtr->encoding = encoding;
  296. statePtr->inputEncodingState = NULL;
  297. statePtr->inputEncodingFlags = TCL_ENCODING_START;
  298. statePtr->outputEncodingState = NULL;
  299. statePtr->outputEncodingFlags = TCL_ENCODING_START;
  300. statePtr->flags &= ~CHANNEL_NEED_MORE_DATA;
  301. UpdateInterest(chanPtr);
  302.     } else if ((len > 2) && (optionName[1] == 'e') &&
  303.             (strncmp(optionName, "-eofchar", len) == 0)) {
  304.         if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
  305.             return TCL_ERROR;
  306.         }
  307.         if (argc == 0) {
  308.             statePtr->inEofChar = 0;
  309.             statePtr->outEofChar = 0;
  310.         } else if (argc == 1) {
  311.             if (statePtr->flags & TCL_WRITABLE) {
  312.                 statePtr->outEofChar = (int) argv[0][0];
  313.             }
  314.             if (statePtr->flags & TCL_READABLE) {
  315.                 statePtr->inEofChar = (int) argv[0][0];
  316.             }
  317.         } else if (argc != 2) {
  318.             if (interp) {
  319.                 Tcl_AppendResult(interp,
  320.                         "bad value for -eofchar: should be a list of zero,",
  321.                         " one, or two elements", (char *) NULL);
  322.             }
  323.             ckfree((char *) argv);
  324.             return TCL_ERROR;
  325.         } else {
  326.             if (statePtr->flags & TCL_READABLE) {
  327.                 statePtr->inEofChar = (int) argv[0][0];
  328.             }
  329.             if (statePtr->flags & TCL_WRITABLE) {
  330.                 statePtr->outEofChar = (int) argv[1][0];
  331.             }
  332.         }
  333.         if (argv != NULL) {
  334.             ckfree((char *) argv);
  335.         }
  336. /*
  337.  * [SF Tcl Bug 930851] Reset EOF and BLOCKED flags. Changing
  338.  * the character which signals eof can transform a current eof
  339.  * condition into a 'go ahead'. Ditto for blocked.
  340.  */
  341. statePtr->flags &= (~(CHANNEL_EOF | CHANNEL_STICKY_EOF | CHANNEL_BLOCKED));
  342. return TCL_OK;
  343.     } else if ((len > 1) && (optionName[1] == 't') &&
  344.             (strncmp(optionName, "-translation", len) == 0)) {
  345. CONST char *readMode, *writeMode;
  346.         if (Tcl_SplitList(interp, newValue, &argc, &argv) == TCL_ERROR) {
  347.             return TCL_ERROR;
  348.         }
  349.         if (argc == 1) {
  350.     readMode = (statePtr->flags & TCL_READABLE) ? argv[0] : NULL;
  351.     writeMode = (statePtr->flags & TCL_WRITABLE) ? argv[0] : NULL;
  352. } else if (argc == 2) {
  353.     readMode = (statePtr->flags & TCL_READABLE) ? argv[0] : NULL;
  354.     writeMode = (statePtr->flags & TCL_WRITABLE) ? argv[1] : NULL;
  355. } else {
  356.             if (interp) {
  357.                 Tcl_AppendResult(interp,
  358.                         "bad value for -translation: must be a one or two",
  359.                         " element list", (char *) NULL);
  360.             }
  361.             ckfree((char *) argv);
  362.             return TCL_ERROR;
  363. }
  364. if (readMode) {
  365.     TclEolTranslation translation;
  366.     if (*readMode == '') {
  367. translation = statePtr->inputTranslation;
  368.     } else if (strcmp(readMode, "auto") == 0) {
  369. translation = TCL_TRANSLATE_AUTO;
  370.     } else if (strcmp(readMode, "binary") == 0) {
  371. translation = TCL_TRANSLATE_LF;
  372. statePtr->inEofChar = 0;
  373. Tcl_FreeEncoding(statePtr->encoding);     
  374. statePtr->encoding = NULL;
  375.     } else if (strcmp(readMode, "lf") == 0) {
  376. translation = TCL_TRANSLATE_LF;
  377.     } else if (strcmp(readMode, "cr") == 0) {
  378. translation = TCL_TRANSLATE_CR;
  379.     } else if (strcmp(readMode, "crlf") == 0) {
  380. translation = TCL_TRANSLATE_CRLF;
  381.     } else if (strcmp(readMode, "platform") == 0) {
  382. translation = TCL_PLATFORM_TRANSLATION;
  383.     } else {
  384. if (interp) {
  385.     Tcl_AppendResult(interp,
  386.     "bad value for -translation: ",
  387.     "must be one of auto, binary, cr, lf, crlf,",
  388.     " or platform", (char *) NULL);
  389. }
  390. ckfree((char *) argv);
  391. return TCL_ERROR;
  392.     }
  393.     /*
  394.      * Reset the EOL flags since we need to look at any buffered
  395.      * data to see if the new translation mode allows us to
  396.      * complete the line.
  397.      */
  398.     if (translation != statePtr->inputTranslation) {
  399. statePtr->inputTranslation = translation;
  400. statePtr->flags &= ~(INPUT_SAW_CR);
  401. statePtr->flags &= ~(CHANNEL_NEED_MORE_DATA);
  402. UpdateInterest(chanPtr);
  403.     }
  404. }
  405. if (writeMode) {
  406.     if (*writeMode == '') {
  407. /* Do nothing. */
  408.     } else if (strcmp(writeMode, "auto") == 0) {
  409. /*
  410.  * This is a hack to get TCP sockets to produce output
  411.  * in CRLF mode if they are being set into AUTO mode.
  412.  * A better solution for achieving this effect will be
  413.  * coded later.
  414.  */
  415. if (strcmp(Tcl_ChannelName(chanPtr->typePtr), "tcp") == 0) {
  416.     statePtr->outputTranslation = TCL_TRANSLATE_CRLF;
  417. } else {
  418.     statePtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
  419. }
  420.     } else if (strcmp(writeMode, "binary") == 0) {
  421. statePtr->outEofChar = 0;
  422. statePtr->outputTranslation = TCL_TRANSLATE_LF;
  423. Tcl_FreeEncoding(statePtr->encoding);     
  424. statePtr->encoding = NULL;
  425.     } else if (strcmp(writeMode, "lf") == 0) {
  426. statePtr->outputTranslation = TCL_TRANSLATE_LF;
  427.     } else if (strcmp(writeMode, "cr") == 0) {
  428. statePtr->outputTranslation = TCL_TRANSLATE_CR;
  429.     } else if (strcmp(writeMode, "crlf") == 0) {
  430. statePtr->outputTranslation = TCL_TRANSLATE_CRLF;
  431.     } else if (strcmp(writeMode, "platform") == 0) {
  432. statePtr->outputTranslation = TCL_PLATFORM_TRANSLATION;
  433.     } else {
  434. if (interp) {
  435.     Tcl_AppendResult(interp,
  436.     "bad value for -translation: ",
  437.     "must be one of auto, binary, cr, lf, crlf,",
  438.     " or platform", (char *) NULL);
  439. }
  440. ckfree((char *) argv);
  441. return TCL_ERROR;
  442.     }
  443. }
  444.         ckfree((char *) argv);            
  445.         return TCL_OK;
  446.     } else if (chanPtr->typePtr->setOptionProc != NULL) {
  447.         return (*chanPtr->typePtr->setOptionProc)(chanPtr->instanceData,
  448.                 interp, optionName, newValue);
  449.     } else {
  450. return Tcl_BadChannelOption(interp, optionName, (char *) NULL);
  451.     }
  452.     /*
  453.      * If bufsize changes, need to get rid of old utility buffer.
  454.      */
  455.     if (statePtr->saveInBufPtr != NULL) {
  456. RecycleBuffer(statePtr, statePtr->saveInBufPtr, 1);
  457. statePtr->saveInBufPtr = NULL;
  458.     }
  459.     if (statePtr->inQueueHead != NULL) {
  460. if ((statePtr->inQueueHead->nextPtr == NULL)
  461. && (statePtr->inQueueHead->nextAdded ==
  462. statePtr->inQueueHead->nextRemoved)) {
  463.     RecycleBuffer(statePtr, statePtr->inQueueHead, 1);
  464.     statePtr->inQueueHead = NULL;
  465.     statePtr->inQueueTail = NULL;
  466. }
  467.     }
  468.     /*
  469.      * If encoding or bufsize changes, need to update output staging buffer.
  470.      */
  471.     if (statePtr->outputStage != NULL) {
  472. ckfree((char *) statePtr->outputStage);
  473. statePtr->outputStage = NULL;
  474.     }
  475.     if ((statePtr->encoding != NULL) && (statePtr->flags & TCL_WRITABLE)) {
  476. statePtr->outputStage = (char *) 
  477.     ckalloc((unsigned) (statePtr->bufSize + 2));
  478.     }
  479.     return TCL_OK;
  480. }
  481. /*
  482.  *----------------------------------------------------------------------
  483.  *
  484.  * CleanupChannelHandlers --
  485.  *
  486.  * Removes channel handlers that refer to the supplied interpreter,
  487.  * so that if the actual channel is not closed now, these handlers
  488.  * will not run on subsequent events on the channel. This would be
  489.  * erroneous, because the interpreter no longer has a reference to
  490.  * this channel.
  491.  *
  492.  * Results:
  493.  * None.
  494.  *
  495.  * Side effects:
  496.  * Removes channel handlers.
  497.  *
  498.  *----------------------------------------------------------------------
  499.  */
  500. static void
  501. CleanupChannelHandlers(interp, chanPtr)
  502.     Tcl_Interp *interp;
  503.     Channel *chanPtr;
  504. {
  505.     ChannelState *statePtr = chanPtr->state; /* state info for channel */
  506.     EventScriptRecord *sPtr, *prevPtr, *nextPtr;
  507.     /*
  508.      * Remove fileevent records on this channel that refer to the
  509.      * given interpreter.
  510.      */
  511.     
  512.     for (sPtr = statePtr->scriptRecordPtr,
  513.              prevPtr = (EventScriptRecord *) NULL;
  514.  sPtr != (EventScriptRecord *) NULL;
  515.  sPtr = nextPtr) {
  516.         nextPtr = sPtr->nextPtr;
  517.         if (sPtr->interp == interp) {
  518.             if (prevPtr == (EventScriptRecord *) NULL) {
  519.                 statePtr->scriptRecordPtr = nextPtr;
  520.             } else {
  521.                 prevPtr->nextPtr = nextPtr;
  522.             }
  523.             Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
  524.                     TclChannelEventScriptInvoker, (ClientData) sPtr);
  525.     Tcl_DecrRefCount(sPtr->scriptPtr);
  526.             ckfree((char *) sPtr);
  527.         } else {
  528.             prevPtr = sPtr;
  529.         }
  530.     }
  531. }
  532. /*
  533.  *----------------------------------------------------------------------
  534.  *
  535.  * Tcl_NotifyChannel --
  536.  *
  537.  * This procedure is called by a channel driver when a driver
  538.  * detects an event on a channel.  This procedure is responsible
  539.  * for actually handling the event by invoking any channel
  540.  * handler callbacks.
  541.  *
  542.  * Results:
  543.  * None.
  544.  *
  545.  * Side effects:
  546.  * Whatever the channel handler callback procedure does.
  547.  *
  548.  *----------------------------------------------------------------------
  549.  */
  550. void
  551. Tcl_NotifyChannel(channel, mask)
  552.     Tcl_Channel channel; /* Channel that detected an event. */
  553.     int mask; /* OR'ed combination of TCL_READABLE,
  554.  * TCL_WRITABLE, or TCL_EXCEPTION: indicates
  555.  * which events were detected. */
  556. {
  557.     Channel *chanPtr = (Channel *) channel;
  558.     ChannelState *statePtr = chanPtr->state; /* state info for channel */
  559.     ChannelHandler *chPtr;
  560.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  561.     NextChannelHandler nh;
  562.     Channel* upChanPtr;
  563.     Tcl_ChannelType* upTypePtr;
  564. #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
  565.     /* [SF Tcl Bug 943274]
  566.      * For a non-blocking channel without blockmodeproc we keep track
  567.      * of actual input coming from the OS so that we can do a credible
  568.      * imitation of non-blocking behaviour.
  569.      */
  570.     if ((mask & TCL_READABLE) &&
  571. (statePtr->flags & CHANNEL_NONBLOCKING) &&
  572. (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL) &&
  573. !(statePtr->flags & CHANNEL_TIMER_FEV)) {
  574.         statePtr->flags |= CHANNEL_HAS_MORE_DATA;
  575.     }
  576. #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
  577.     /*
  578.      * In contrast to the other API functions this procedure walks towards
  579.      * the top of a stack and not down from it.
  580.      *
  581.      * The channel calling this procedure is the one who generated the event,
  582.      * and thus does not take part in handling it. IOW, its HandlerProc is
  583.      * not called, instead we begin with the channel above it.
  584.      *
  585.      * This behaviour also allows the transformation channels to
  586.      * generate their own events and pass them upward.
  587.      */
  588.     while (mask && (chanPtr->upChanPtr != ((Channel*) NULL))) {
  589. Tcl_DriverHandlerProc* upHandlerProc;
  590.         upChanPtr = chanPtr->upChanPtr;
  591. upTypePtr = upChanPtr->typePtr;
  592. upHandlerProc = Tcl_ChannelHandlerProc(upTypePtr);
  593. if (upHandlerProc != NULL) {
  594.     mask = (*upHandlerProc) (upChanPtr->instanceData, mask);
  595. }
  596. /* ELSE:
  597.  * Ignore transformations which are unable to handle the event
  598.  * coming from below. Assume that they don't change the mask and
  599.  * pass it on.
  600.  */
  601. chanPtr = upChanPtr;
  602.     }
  603.     channel = (Tcl_Channel) chanPtr;
  604.     /*
  605.      * Here we have either reached the top of the stack or the mask is
  606.      * empty.  We break out of the procedure if it is the latter.
  607.      */
  608.     if (!mask) {
  609.         return;
  610.     }
  611.     /*
  612.      * We are now above the topmost channel in a stack and have events
  613.      * left. Now call the channel handlers as usual.
  614.      *
  615.      * Preserve the channel struct in case the script closes it.
  616.      */
  617.      
  618.     Tcl_Preserve((ClientData) channel);
  619.     Tcl_Preserve((ClientData) statePtr);
  620.     /*
  621.      * If we are flushing in the background, be sure to call FlushChannel
  622.      * for writable events.  Note that we have to discard the writable
  623.      * event so we don't call any write handlers before the flush is
  624.      * complete.
  625.      */
  626.     if ((statePtr->flags & BG_FLUSH_SCHEDULED) && (mask & TCL_WRITABLE)) {
  627. FlushChannel(NULL, chanPtr, 1);
  628. mask &= ~TCL_WRITABLE;
  629.     }
  630.     /*
  631.      * Add this invocation to the list of recursive invocations of
  632.      * ChannelHandlerEventProc.
  633.      */
  634.     
  635.     nh.nextHandlerPtr = (ChannelHandler *) NULL;
  636.     nh.nestedHandlerPtr = tsdPtr->nestedHandlerPtr;
  637.     tsdPtr->nestedHandlerPtr = &nh;
  638.     for (chPtr = statePtr->chPtr; chPtr != (ChannelHandler *) NULL; ) {
  639. /*
  640.  * If this channel handler is interested in any of the events that
  641.  * have occurred on the channel, invoke its procedure.
  642.  */
  643. if ((chPtr->mask & mask) != 0) {
  644.     nh.nextHandlerPtr = chPtr->nextPtr;
  645.     (*(chPtr->proc))(chPtr->clientData, mask);
  646.     chPtr = nh.nextHandlerPtr;
  647. } else {
  648.     chPtr = chPtr->nextPtr;
  649. }
  650.     }
  651.     /*
  652.      * Update the notifier interest, since it may have changed after
  653.      * invoking event handlers. Skip that if the channel was deleted
  654.      * in the call to the channel handler.
  655.      */
  656.     if (chanPtr->typePtr != NULL) {
  657.         UpdateInterest(chanPtr);
  658.     }
  659.     Tcl_Release((ClientData) statePtr);
  660.     Tcl_Release((ClientData) channel);
  661.     tsdPtr->nestedHandlerPtr = nh.nestedHandlerPtr;
  662. }
  663. /*
  664.  *----------------------------------------------------------------------
  665.  *
  666.  * UpdateInterest --
  667.  *
  668.  * Arrange for the notifier to call us back at appropriate times
  669.  * based on the current state of the channel.
  670.  *
  671.  * Results:
  672.  * None.
  673.  *
  674.  * Side effects:
  675.  * May schedule a timer or driver handler.
  676.  *
  677.  *----------------------------------------------------------------------
  678.  */
  679. static void
  680. UpdateInterest(chanPtr)
  681.     Channel *chanPtr; /* Channel to update. */
  682. {
  683.     ChannelState *statePtr = chanPtr->state; /* state info for channel */
  684.     int mask = statePtr->interestMask;
  685.     /*
  686.      * If there are flushed buffers waiting to be written, then
  687.      * we need to watch for the channel to become writable.
  688.      */
  689.     if (statePtr->flags & BG_FLUSH_SCHEDULED) {
  690. mask |= TCL_WRITABLE;
  691.     }
  692.     /*
  693.      * If there is data in the input queue, and we aren't waiting for more
  694.      * data, then we need to schedule a timer so we don't block in the
  695.      * notifier.  Also, cancel the read interest so we don't get duplicate
  696.      * events.
  697.      */
  698.     if (mask & TCL_READABLE) {
  699. if (!(statePtr->flags & CHANNEL_NEED_MORE_DATA)
  700. && (statePtr->inQueueHead != (ChannelBuffer *) NULL)
  701. && (statePtr->inQueueHead->nextRemoved <
  702. statePtr->inQueueHead->nextAdded)) {
  703.     mask &= ~TCL_READABLE;
  704.     /*
  705.      * Andreas Kupries, April 11, 2003
  706.      *
  707.      * Some operating systems (Solaris 2.6 and higher (but not
  708.      * Solaris 2.5, go figure)) generate READABLE and
  709.      * EXCEPTION events when select()'ing [*] on a plain file,
  710.      * even if EOF was not yet reached. This is a problem in
  711.      * the following situation:
  712.      *
  713.      * - An extension asks to get both READABLE and EXCEPTION
  714.      *   events.
  715.      * - It reads data into a buffer smaller than the buffer
  716.      *   used by Tcl itself.
  717.      * - It does not process all events in the event queue, but
  718.      *   only only one, at least in some situations.
  719.      *
  720.      * In that case we can get into a situation where
  721.      *
  722.      * - Tcl drops READABLE here, because it has data in its own
  723.      *   buffers waiting to be read by the extension.
  724.      * - A READABLE event is syntesized via timer.
  725.      * - The OS still reports the EXCEPTION condition on the file.
  726.      * - And the extension gets the EXCPTION event first, and
  727.      *   handles this as EOF.
  728.      *
  729.      * End result ==> Premature end of reading from a file.
  730.      *
  731.      * The concrete example is 'Expect', and its [expect]
  732.      * command (and at the C-level, deep in the bowels of
  733.      * Expect, 'exp_get_next_event'. See marker 'SunOS' for
  734.      * commentary in that function too).
  735.      *
  736.      * [*] As the Tcl notifier does. See also for marker
  737.      * 'SunOS' in file 'exp_event.c' of Expect.
  738.      *
  739.      * Our solution here is to drop the interest in the
  740.      * EXCEPTION events too. This compiles on all platforms,
  741.      * and also passes the testsuite on all of them.
  742.      */
  743.     mask &= ~TCL_EXCEPTION;
  744.     if (!statePtr->timer) {
  745. statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,
  746. (ClientData) chanPtr);
  747.     }
  748. }
  749.     }
  750.     (chanPtr->typePtr->watchProc)(chanPtr->instanceData, mask);
  751. }
  752. /*
  753.  *----------------------------------------------------------------------
  754.  *
  755.  * ChannelTimerProc --
  756.  *
  757.  * Timer handler scheduled by UpdateInterest to monitor the
  758.  * channel buffers until they are empty.
  759.  *
  760.  * Results:
  761.  * None.
  762.  *
  763.  * Side effects:
  764.  * May invoke channel handlers.
  765.  *
  766.  *----------------------------------------------------------------------
  767.  */
  768. static void
  769. ChannelTimerProc(clientData)
  770.     ClientData clientData;
  771. {
  772.     Channel *chanPtr = (Channel *) clientData;
  773.     ChannelState *statePtr = chanPtr->state; /* state info for channel */
  774.     if (!(statePtr->flags & CHANNEL_NEED_MORE_DATA)
  775.     && (statePtr->interestMask & TCL_READABLE)
  776.     && (statePtr->inQueueHead != (ChannelBuffer *) NULL)
  777.     && (statePtr->inQueueHead->nextRemoved <
  778.     statePtr->inQueueHead->nextAdded)) {
  779. /*
  780.  * Restart the timer in case a channel handler reenters the
  781.  * event loop before UpdateInterest gets called by Tcl_NotifyChannel.
  782.  */
  783. statePtr->timer = Tcl_CreateTimerHandler(0, ChannelTimerProc,
  784. (ClientData) chanPtr);
  785. #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
  786. /* Set the TIMER flag to notify the higher levels that the
  787.  * driver might have no data for us. We do this only if we are
  788.  * in non-blocking mode and the driver has no BlockModeProc
  789.  * because only then we really don't know if the driver will
  790.  * block or not. A similar test is done in "PeekAhead".
  791.  */
  792. if ((statePtr->flags & CHANNEL_NONBLOCKING) &&
  793.     (Tcl_ChannelBlockModeProc(chanPtr->typePtr) == NULL)) {
  794.     statePtr->flags |= CHANNEL_TIMER_FEV;
  795. }
  796. #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
  797. Tcl_Preserve((ClientData) statePtr);
  798. Tcl_NotifyChannel((Tcl_Channel)chanPtr, TCL_READABLE);
  799. #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
  800. statePtr->flags &= ~CHANNEL_TIMER_FEV; 
  801. #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
  802. Tcl_Release((ClientData) statePtr);
  803.     } else {
  804. statePtr->timer = NULL;
  805. UpdateInterest(chanPtr);
  806.     }
  807. }
  808. /*
  809.  *----------------------------------------------------------------------
  810.  *
  811.  * Tcl_CreateChannelHandler --
  812.  *
  813.  * Arrange for a given procedure to be invoked whenever the
  814.  * channel indicated by the chanPtr arg becomes readable or
  815.  * writable.
  816.  *
  817.  * Results:
  818.  * None.
  819.  *
  820.  * Side effects:
  821.  * From now on, whenever the I/O channel given by chanPtr becomes
  822.  * ready in the way indicated by mask, proc will be invoked.
  823.  * See the manual entry for details on the calling sequence
  824.  * to proc.  If there is already an event handler for chan, proc
  825.  * and clientData, then the mask will be updated.
  826.  *
  827.  *----------------------------------------------------------------------
  828.  */
  829. void
  830. Tcl_CreateChannelHandler(chan, mask, proc, clientData)
  831.     Tcl_Channel chan; /* The channel to create the handler for. */
  832.     int mask; /* OR'ed combination of TCL_READABLE,
  833.  * TCL_WRITABLE, and TCL_EXCEPTION:
  834.  * indicates conditions under which
  835.  * proc should be called. Use 0 to
  836.                                  * disable a registered handler. */
  837.     Tcl_ChannelProc *proc; /* Procedure to call for each
  838.  * selected event. */
  839.     ClientData clientData; /* Arbitrary data to pass to proc. */
  840. {
  841.     ChannelHandler *chPtr;
  842.     Channel *chanPtr = (Channel *) chan;
  843.     ChannelState *statePtr = chanPtr->state; /* state info for channel */
  844.     /*
  845.      * Check whether this channel handler is not already registered. If
  846.      * it is not, create a new record, else reuse existing record (smash
  847.      * current values).
  848.      */
  849.     for (chPtr = statePtr->chPtr;
  850.  chPtr != (ChannelHandler *) NULL;
  851.  chPtr = chPtr->nextPtr) {
  852.         if ((chPtr->chanPtr == chanPtr) && (chPtr->proc == proc) &&
  853.                 (chPtr->clientData == clientData)) {
  854.             break;
  855.         }
  856.     }
  857.     if (chPtr == (ChannelHandler *) NULL) {
  858.         chPtr = (ChannelHandler *) ckalloc((unsigned) sizeof(ChannelHandler));
  859.         chPtr->mask = 0;
  860.         chPtr->proc = proc;
  861.         chPtr->clientData = clientData;
  862.         chPtr->chanPtr = chanPtr;
  863.         chPtr->nextPtr = statePtr->chPtr;
  864.         statePtr->chPtr = chPtr;
  865.     }
  866.     /*
  867.      * The remainder of the initialization below is done regardless of
  868.      * whether or not this is a new record or a modification of an old
  869.      * one.
  870.      */
  871.     chPtr->mask = mask;
  872.     /*
  873.      * Recompute the interest mask for the channel - this call may actually
  874.      * be disabling an existing handler.
  875.      */
  876.     
  877.     statePtr->interestMask = 0;
  878.     for (chPtr = statePtr->chPtr;
  879.  chPtr != (ChannelHandler *) NULL;
  880.  chPtr = chPtr->nextPtr) {
  881. statePtr->interestMask |= chPtr->mask;
  882.     }
  883.     UpdateInterest(statePtr->topChanPtr);
  884. }
  885. /*
  886.  *----------------------------------------------------------------------
  887.  *
  888.  * Tcl_DeleteChannelHandler --
  889.  *
  890.  * Cancel a previously arranged callback arrangement for an IO
  891.  * channel.
  892.  *
  893.  * Results:
  894.  * None.
  895.  *
  896.  * Side effects:
  897.  * If a callback was previously registered for this chan, proc and
  898.  *  clientData , it is removed and the callback will no longer be called
  899.  * when the channel becomes ready for IO.
  900.  *
  901.  *----------------------------------------------------------------------
  902.  */
  903. void
  904. Tcl_DeleteChannelHandler(chan, proc, clientData)
  905.     Tcl_Channel chan; /* The channel for which to remove the
  906.                                  * callback. */
  907.     Tcl_ChannelProc *proc; /* The procedure in the callback to delete. */
  908.     ClientData clientData; /* The client data in the callback
  909.                                  * to delete. */
  910.     
  911. {
  912.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  913.     ChannelHandler *chPtr, *prevChPtr;
  914.     Channel *chanPtr = (Channel *) chan;
  915.     ChannelState *statePtr = chanPtr->state; /* state info for channel */
  916.     NextChannelHandler *nhPtr;
  917.     /*
  918.      * Find the entry and the previous one in the list.
  919.      */
  920.     for (prevChPtr = (ChannelHandler *) NULL, chPtr = statePtr->chPtr;
  921.  chPtr != (ChannelHandler *) NULL;
  922.  chPtr = chPtr->nextPtr) {
  923.         if ((chPtr->chanPtr == chanPtr) && (chPtr->clientData == clientData)
  924.                 && (chPtr->proc == proc)) {
  925.             break;
  926.         }
  927.         prevChPtr = chPtr;
  928.     }
  929.     /*
  930.      * If not found, return without doing anything.
  931.      */
  932.     if (chPtr == (ChannelHandler *) NULL) {
  933.         return;
  934.     }
  935.     /*
  936.      * If ChannelHandlerEventProc is about to process this handler, tell it to
  937.      * process the next one instead - we are going to delete *this* one.
  938.      */
  939.     for (nhPtr = tsdPtr->nestedHandlerPtr;
  940.  nhPtr != (NextChannelHandler *) NULL;
  941.  nhPtr = nhPtr->nestedHandlerPtr) {
  942.         if (nhPtr->nextHandlerPtr == chPtr) {
  943.             nhPtr->nextHandlerPtr = chPtr->nextPtr;
  944.         }
  945.     }
  946.     /*
  947.      * Splice it out of the list of channel handlers.
  948.      */
  949.     
  950.     if (prevChPtr == (ChannelHandler *) NULL) {
  951.         statePtr->chPtr = chPtr->nextPtr;
  952.     } else {
  953.         prevChPtr->nextPtr = chPtr->nextPtr;
  954.     }
  955.     ckfree((char *) chPtr);
  956.     /*
  957.      * Recompute the interest list for the channel, so that infinite loops
  958.      * will not result if Tcl_DeleteChannelHandler is called inside an
  959.      * event.
  960.      */
  961.     statePtr->interestMask = 0;
  962.     for (chPtr = statePtr->chPtr;
  963.  chPtr != (ChannelHandler *) NULL;
  964.  chPtr = chPtr->nextPtr) {
  965.         statePtr->interestMask |= chPtr->mask;
  966.     }
  967.     UpdateInterest(statePtr->topChanPtr);
  968. }
  969. /*
  970.  *----------------------------------------------------------------------
  971.  *
  972.  * DeleteScriptRecord --
  973.  *
  974.  * Delete a script record for this combination of channel, interp
  975.  * and mask.
  976.  *
  977.  * Results:
  978.  * None.
  979.  *
  980.  * Side effects:
  981.  * Deletes a script record and cancels a channel event handler.
  982.  *
  983.  *----------------------------------------------------------------------
  984.  */
  985. static void
  986. DeleteScriptRecord(interp, chanPtr, mask)
  987.     Tcl_Interp *interp; /* Interpreter in which script was to be
  988.                                  * executed. */
  989.     Channel *chanPtr; /* The channel for which to delete the
  990.                                  * script record (if any). */
  991.     int mask; /* Events in mask must exactly match mask
  992.                                  * of script to delete. */
  993. {
  994.     ChannelState *statePtr = chanPtr->state; /* state info for channel */
  995.     EventScriptRecord *esPtr, *prevEsPtr;
  996.     for (esPtr = statePtr->scriptRecordPtr,
  997.              prevEsPtr = (EventScriptRecord *) NULL;
  998.  esPtr != (EventScriptRecord *) NULL;
  999.  prevEsPtr = esPtr, esPtr = esPtr->nextPtr) {
  1000.         if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
  1001.             if (esPtr == statePtr->scriptRecordPtr) {
  1002.                 statePtr->scriptRecordPtr = esPtr->nextPtr;
  1003.             } else {
  1004.                 prevEsPtr->nextPtr = esPtr->nextPtr;
  1005.             }
  1006.             Tcl_DeleteChannelHandler((Tcl_Channel) chanPtr,
  1007.                     TclChannelEventScriptInvoker, (ClientData) esPtr);
  1008.             
  1009.     Tcl_DecrRefCount(esPtr->scriptPtr);
  1010.             ckfree((char *) esPtr);
  1011.             break;
  1012.         }
  1013.     }
  1014. }
  1015. /*
  1016.  *----------------------------------------------------------------------
  1017.  *
  1018.  * CreateScriptRecord --
  1019.  *
  1020.  * Creates a record to store a script to be executed when a specific
  1021.  * event fires on a specific channel.
  1022.  *
  1023.  * Results:
  1024.  * None.
  1025.  *
  1026.  * Side effects:
  1027.  * Causes the script to be stored for later execution.
  1028.  *
  1029.  *----------------------------------------------------------------------
  1030.  */
  1031. static void
  1032. CreateScriptRecord(interp, chanPtr, mask, scriptPtr)
  1033.     Tcl_Interp *interp; /* Interpreter in which to execute
  1034.                                          * the stored script. */
  1035.     Channel *chanPtr; /* Channel for which script is to
  1036.                                          * be stored. */
  1037.     int mask; /* Set of events for which script
  1038.                                          * will be invoked. */
  1039.     Tcl_Obj *scriptPtr; /* Pointer to script object. */
  1040. {
  1041.     ChannelState *statePtr = chanPtr->state; /* state info for channel */
  1042.     EventScriptRecord *esPtr;
  1043.     for (esPtr = statePtr->scriptRecordPtr;
  1044.  esPtr != (EventScriptRecord *) NULL;
  1045.  esPtr = esPtr->nextPtr) {
  1046.         if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
  1047.     Tcl_DecrRefCount(esPtr->scriptPtr);
  1048.     esPtr->scriptPtr = (Tcl_Obj *) NULL;
  1049.             break;
  1050.         }
  1051.     }
  1052.     if (esPtr == (EventScriptRecord *) NULL) {
  1053.         esPtr = (EventScriptRecord *) ckalloc((unsigned)
  1054.                 sizeof(EventScriptRecord));
  1055.         Tcl_CreateChannelHandler((Tcl_Channel) chanPtr, mask,
  1056.                 TclChannelEventScriptInvoker, (ClientData) esPtr);
  1057.         esPtr->nextPtr = statePtr->scriptRecordPtr;
  1058.         statePtr->scriptRecordPtr = esPtr;
  1059.     }
  1060.     esPtr->chanPtr = chanPtr;
  1061.     esPtr->interp = interp;
  1062.     esPtr->mask = mask;
  1063.     Tcl_IncrRefCount(scriptPtr);
  1064.     esPtr->scriptPtr = scriptPtr;
  1065. }
  1066. /*
  1067.  *----------------------------------------------------------------------
  1068.  *
  1069.  * TclChannelEventScriptInvoker --
  1070.  *
  1071.  * Invokes a script scheduled by "fileevent" for when the channel
  1072.  * becomes ready for IO. This function is invoked by the channel
  1073.  * handler which was created by the Tcl "fileevent" command.
  1074.  *
  1075.  * Results:
  1076.  * None.
  1077.  *
  1078.  * Side effects:
  1079.  * Whatever the script does.
  1080.  *
  1081.  *----------------------------------------------------------------------
  1082.  */
  1083. void
  1084. TclChannelEventScriptInvoker(clientData, mask)
  1085.     ClientData clientData; /* The script+interp record. */
  1086.     int mask; /* Not used. */
  1087. {
  1088.     Tcl_Interp *interp; /* Interpreter in which to eval the script. */
  1089.     Channel *chanPtr; /* The channel for which this handler is
  1090.                                  * registered. */
  1091.     EventScriptRecord *esPtr; /* The event script + interpreter to eval it
  1092.                                  * in. */
  1093.     int result; /* Result of call to eval script. */
  1094.     esPtr = (EventScriptRecord *) clientData;
  1095.     chanPtr = esPtr->chanPtr;
  1096.     mask = esPtr->mask;
  1097.     interp = esPtr->interp;
  1098.     /*
  1099.      * We must preserve the interpreter so we can report errors on it
  1100.      * later.  Note that we do not need to preserve the channel because
  1101.      * that is done by Tcl_NotifyChannel before calling channel handlers.
  1102.      */
  1103.     
  1104.     Tcl_Preserve((ClientData) interp);
  1105.     result = Tcl_EvalObjEx(interp, esPtr->scriptPtr, TCL_EVAL_GLOBAL);
  1106.     /*
  1107.      * On error, cause a background error and remove the channel handler
  1108.      * and the script record.
  1109.      *
  1110.      * NOTE: Must delete channel handler before causing the background error
  1111.      * because the background error may want to reinstall the handler.
  1112.      */
  1113.     
  1114.     if (result != TCL_OK) {
  1115. if (chanPtr->typePtr != NULL) {
  1116.     DeleteScriptRecord(interp, chanPtr, mask);
  1117. }
  1118.         Tcl_BackgroundError(interp);
  1119.     }
  1120.     Tcl_Release((ClientData) interp);
  1121. }
  1122. /*
  1123.  *----------------------------------------------------------------------
  1124.  *
  1125.  * Tcl_FileEventObjCmd --
  1126.  *
  1127.  * This procedure implements the "fileevent" Tcl command. See the
  1128.  * user documentation for details on what it does. This command is
  1129.  * based on the Tk command "fileevent" which in turn is based on work
  1130.  * contributed by Mark Diekhans.
  1131.  *
  1132.  * Results:
  1133.  * A standard Tcl result.
  1134.  *
  1135.  * Side effects:
  1136.  * May create a channel handler for the specified channel.
  1137.  *
  1138.  *----------------------------------------------------------------------
  1139.  */
  1140. /* ARGSUSED */
  1141. int
  1142. Tcl_FileEventObjCmd(clientData, interp, objc, objv)
  1143.     ClientData clientData; /* Not used. */
  1144.     Tcl_Interp *interp; /* Interpreter in which the channel
  1145.                                          * for which to create the handler
  1146.                                          * is found. */
  1147.     int objc; /* Number of arguments. */
  1148.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  1149. {
  1150.     Channel *chanPtr; /* The channel to create
  1151.                                          * the handler for. */
  1152.     ChannelState *statePtr; /* state info for channel */
  1153.     Tcl_Channel chan; /* The opaque type for the channel. */
  1154.     char *chanName;
  1155.     int modeIndex; /* Index of mode argument. */
  1156.     int mask;
  1157.     static CONST char *modeOptions[] = {"readable", "writable", NULL};
  1158.     static int maskArray[] = {TCL_READABLE, TCL_WRITABLE};
  1159.     if ((objc != 3) && (objc != 4)) {
  1160. Tcl_WrongNumArgs(interp, 1, objv, "channelId event ?script?");
  1161. return TCL_ERROR;
  1162.     }
  1163.     if (Tcl_GetIndexFromObj(interp, objv[2], modeOptions, "event name", 0,
  1164.     &modeIndex) != TCL_OK) {
  1165. return TCL_ERROR;
  1166.     }
  1167.     mask = maskArray[modeIndex];
  1168.     chanName = Tcl_GetString(objv[1]);
  1169.     chan = Tcl_GetChannel(interp, chanName, NULL);
  1170.     if (chan == (Tcl_Channel) NULL) {
  1171. return TCL_ERROR;
  1172.     }
  1173.     chanPtr  = (Channel *) chan;
  1174.     statePtr = chanPtr->state;
  1175.     if ((statePtr->flags & mask) == 0) {
  1176.         Tcl_AppendResult(interp, "channel is not ",
  1177.                 (mask == TCL_READABLE) ? "readable" : "writable",
  1178.                 (char *) NULL);
  1179.         return TCL_ERROR;
  1180.     }
  1181.     
  1182.     /*
  1183.      * If we are supposed to return the script, do so.
  1184.      */
  1185.     if (objc == 3) {
  1186. EventScriptRecord *esPtr;
  1187. for (esPtr = statePtr->scriptRecordPtr;
  1188.              esPtr != (EventScriptRecord *) NULL;
  1189.              esPtr = esPtr->nextPtr) {
  1190.     if ((esPtr->interp == interp) && (esPtr->mask == mask)) {
  1191. Tcl_SetObjResult(interp, esPtr->scriptPtr);
  1192. break;
  1193.     }
  1194. }
  1195.         return TCL_OK;
  1196.     }
  1197.     /*
  1198.      * If we are supposed to delete a stored script, do so.
  1199.      */
  1200.     if (*(Tcl_GetString(objv[3])) == '') {
  1201.         DeleteScriptRecord(interp, chanPtr, mask);
  1202.         return TCL_OK;
  1203.     }
  1204.     /*
  1205.      * Make the script record that will link between the event and the
  1206.      * script to invoke. This also creates a channel event handler which
  1207.      * will evaluate the script in the supplied interpreter.
  1208.      */
  1209.     CreateScriptRecord(interp, chanPtr, mask, objv[3]);
  1210.     
  1211.     return TCL_OK;
  1212. }
  1213. /*
  1214.  *----------------------------------------------------------------------
  1215.  *
  1216.  * TclCopyChannel --
  1217.  *
  1218.  * This routine copies data from one channel to another, either
  1219.  * synchronously or asynchronously.  If a command script is
  1220.  * supplied, the operation runs in the background.  The script
  1221.  * is invoked when the copy completes.  Otherwise the function
  1222.  * waits until the copy is completed before returning.
  1223.  *
  1224.  * Results:
  1225.  * A standard Tcl result.
  1226.  *
  1227.  * Side effects:
  1228.  * May schedule a background copy operation that causes both
  1229.  * channels to be marked busy.
  1230.  *
  1231.  *----------------------------------------------------------------------
  1232.  */
  1233. int
  1234. TclCopyChannel(interp, inChan, outChan, toRead, cmdPtr)
  1235.     Tcl_Interp *interp; /* Current interpreter. */
  1236.     Tcl_Channel inChan; /* Channel to read from. */
  1237.     Tcl_Channel outChan; /* Channel to write to. */
  1238.     int toRead; /* Amount of data to copy, or -1 for all. */
  1239.     Tcl_Obj *cmdPtr; /* Pointer to script to execute or NULL. */
  1240. {
  1241.     Channel *inPtr = (Channel *) inChan;
  1242.     Channel *outPtr = (Channel *) outChan;
  1243.     ChannelState *inStatePtr, *outStatePtr;
  1244.     int readFlags, writeFlags;
  1245.     CopyState *csPtr;
  1246.     int nonBlocking = (cmdPtr) ? CHANNEL_NONBLOCKING : 0;
  1247.     inStatePtr = inPtr->state;
  1248.     outStatePtr = outPtr->state;
  1249.     if (inStatePtr->csPtr) {
  1250. if (interp) {
  1251.     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel "",
  1252.     Tcl_GetChannelName(inChan), "" is busy", NULL);
  1253. }
  1254. return TCL_ERROR;
  1255.     }
  1256.     if (outStatePtr->csPtr) {
  1257. if (interp) {
  1258.     Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel "",
  1259.     Tcl_GetChannelName(outChan), "" is busy", NULL);
  1260. }
  1261. return TCL_ERROR;
  1262.     }
  1263.     readFlags = inStatePtr->flags;
  1264.     writeFlags = outStatePtr->flags;
  1265.     /*
  1266.      * Set up the blocking mode appropriately.  Background copies need
  1267.      * non-blocking channels.  Foreground copies need blocking channels.
  1268.      * If there is an error, restore the old blocking mode.
  1269.      */
  1270.     if (nonBlocking != (readFlags & CHANNEL_NONBLOCKING)) {
  1271. if (SetBlockMode(interp, inPtr,
  1272. nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING)
  1273. != TCL_OK) {
  1274.     return TCL_ERROR;
  1275. }
  1276.     }     
  1277.     if (inPtr != outPtr) {
  1278. if (nonBlocking != (writeFlags & CHANNEL_NONBLOCKING)) {
  1279.     if (SetBlockMode(NULL, outPtr,
  1280.     nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING)
  1281.     != TCL_OK) {
  1282. if (nonBlocking != (readFlags & CHANNEL_NONBLOCKING)) {
  1283.     SetBlockMode(NULL, inPtr,
  1284.     (readFlags & CHANNEL_NONBLOCKING)
  1285.     ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
  1286.     return TCL_ERROR;
  1287. }
  1288.     }
  1289. }
  1290.     }
  1291.     /*
  1292.      * Make sure the output side is unbuffered.
  1293.      */
  1294.     outStatePtr->flags = (outStatePtr->flags & ~(CHANNEL_LINEBUFFERED))
  1295. | CHANNEL_UNBUFFERED;
  1296.     /*
  1297.      * Allocate a new CopyState to maintain info about the current copy in
  1298.      * progress.  This structure will be deallocated when the copy is
  1299.      * completed.
  1300.      */
  1301.     csPtr = (CopyState*) ckalloc(sizeof(CopyState) + inStatePtr->bufSize);
  1302.     csPtr->bufSize    = inStatePtr->bufSize;
  1303.     csPtr->readPtr    = inPtr;
  1304.     csPtr->writePtr   = outPtr;
  1305.     csPtr->readFlags  = readFlags;
  1306.     csPtr->writeFlags = writeFlags;
  1307.     csPtr->toRead     = toRead;
  1308.     csPtr->total      = 0;
  1309.     csPtr->interp     = interp;
  1310.     if (cmdPtr) {
  1311. Tcl_IncrRefCount(cmdPtr);
  1312.     }
  1313.     csPtr->cmdPtr = cmdPtr;
  1314.     inStatePtr->csPtr = csPtr;
  1315.     outStatePtr->csPtr = csPtr;
  1316.     /*
  1317.      * Start copying data between the channels.
  1318.      */
  1319.     return CopyData(csPtr, 0);
  1320. }
  1321. /*
  1322.  *----------------------------------------------------------------------
  1323.  *
  1324.  * CopyData --
  1325.  *
  1326.  * This function implements the lowest level of the copying
  1327.  * mechanism for TclCopyChannel.
  1328.  *
  1329.  * Results:
  1330.  * Returns TCL_OK on success, else TCL_ERROR.
  1331.  *
  1332.  * Side effects:
  1333.  * Moves data between channels, may create channel handlers.
  1334.  *
  1335.  *----------------------------------------------------------------------
  1336.  */
  1337. static int
  1338. CopyData(csPtr, mask)
  1339.     CopyState *csPtr; /* State of copy operation. */
  1340.     int mask; /* Current channel event flags. */
  1341. {
  1342.     Tcl_Interp *interp;
  1343.     Tcl_Obj *cmdPtr, *errObj = NULL, *bufObj = NULL;
  1344.     Tcl_Channel inChan, outChan;
  1345.     ChannelState *inStatePtr, *outStatePtr;
  1346.     int result = TCL_OK, size, total, sizeb;
  1347.     char* buffer;
  1348.     int inBinary, outBinary, sameEncoding; /* Encoding control */
  1349.     int underflow; /* input underflow */
  1350.     inChan = (Tcl_Channel) csPtr->readPtr;
  1351.     outChan = (Tcl_Channel) csPtr->writePtr;
  1352.     inStatePtr = csPtr->readPtr->state;
  1353.     outStatePtr = csPtr->writePtr->state;
  1354.     interp = csPtr->interp;
  1355.     cmdPtr = csPtr->cmdPtr;
  1356.     /*
  1357.      * Copy the data the slow way, using the translation mechanism.
  1358.      *
  1359.      * Note: We have make sure that we use the topmost channel in a stack
  1360.      * for the copying. The caller uses Tcl_GetChannel to access it, and
  1361.      * thus gets the bottom of the stack.
  1362.      */
  1363.     inBinary     = (inStatePtr->encoding  == NULL);
  1364.     outBinary    = (outStatePtr->encoding == NULL);
  1365.     sameEncoding = (inStatePtr->encoding  == outStatePtr->encoding);
  1366.     if (!(inBinary || sameEncoding)) {
  1367.         bufObj = Tcl_NewObj ();
  1368. Tcl_IncrRefCount (bufObj);
  1369.     }
  1370.     while (csPtr->toRead != 0) {
  1371. /*
  1372.  * Check for unreported background errors.
  1373.  */
  1374. if (inStatePtr->unreportedError != 0) {
  1375.     Tcl_SetErrno(inStatePtr->unreportedError);
  1376.     inStatePtr->unreportedError = 0;
  1377.     goto readError;
  1378. }
  1379. if (outStatePtr->unreportedError != 0) {
  1380.     Tcl_SetErrno(outStatePtr->unreportedError);
  1381.     outStatePtr->unreportedError = 0;
  1382.     goto writeError;
  1383. }
  1384. /*
  1385.  * Read up to bufSize bytes.
  1386.  */
  1387. if ((csPtr->toRead == -1) || (csPtr->toRead > csPtr->bufSize)) {
  1388.     sizeb = csPtr->bufSize;
  1389. } else {
  1390.     sizeb = csPtr->toRead;
  1391. }
  1392. if (inBinary || sameEncoding) {
  1393.     size = DoRead(inStatePtr->topChanPtr, csPtr->buffer, sizeb);
  1394. } else {
  1395.     size = DoReadChars(inStatePtr->topChanPtr, bufObj, sizeb, 0 /* No append */);
  1396. }
  1397. underflow = (size >= 0) && (size < sizeb); /* input underflow */
  1398. if (size < 0) {
  1399.     readError:
  1400.     errObj = Tcl_NewObj();
  1401.     Tcl_AppendStringsToObj(errObj, "error reading "",
  1402.     Tcl_GetChannelName(inChan), "": ",
  1403.     Tcl_PosixError(interp), (char *) NULL);
  1404.     break;
  1405. } else if (underflow) {
  1406.     /*
  1407.      * We had an underflow on the read side.  If we are at EOF,
  1408.      * then the copying is done, otherwise set up a channel
  1409.      * handler to detect when the channel becomes readable again.
  1410.      */
  1411.     
  1412.     if ((size == 0) && Tcl_Eof(inChan)) {
  1413. break;
  1414.     }
  1415.     if (! Tcl_Eof(inChan) && !(mask & TCL_READABLE)) {
  1416. if (mask & TCL_WRITABLE) {
  1417.     Tcl_DeleteChannelHandler(outChan, CopyEventProc,
  1418.     (ClientData) csPtr);
  1419. }
  1420. Tcl_CreateChannelHandler(inChan, TCL_READABLE,
  1421. CopyEventProc, (ClientData) csPtr);
  1422.     }
  1423.     if (size == 0) {
  1424.         if (bufObj != (Tcl_Obj*) NULL) {
  1425.     Tcl_DecrRefCount (bufObj);
  1426.     bufObj = (Tcl_Obj*) NULL;
  1427. }
  1428. return TCL_OK;
  1429.     }
  1430. }
  1431. /*
  1432.  * Now write the buffer out.
  1433.  */
  1434. if (inBinary || sameEncoding) {
  1435.     buffer = csPtr->buffer;
  1436.     sizeb = size;
  1437. } else {
  1438.     buffer = Tcl_GetStringFromObj (bufObj, &sizeb);
  1439. }
  1440. if (outBinary || sameEncoding) {
  1441.     sizeb = DoWrite(outStatePtr->topChanPtr, buffer, sizeb);
  1442. } else {
  1443.     sizeb = DoWriteChars(outStatePtr->topChanPtr, buffer, sizeb);
  1444. }
  1445. if (inBinary || sameEncoding) {
  1446.     /* Both read and write counted bytes */
  1447.     size = sizeb;
  1448. } /* else : Read counted characters, write counted bytes, i.e. size != sizeb */
  1449. if (sizeb < 0) {
  1450.     writeError:
  1451.     errObj = Tcl_NewObj();
  1452.     Tcl_AppendStringsToObj(errObj, "error writing "",
  1453.     Tcl_GetChannelName(outChan), "": ",
  1454.     Tcl_PosixError(interp), (char *) NULL);
  1455.     break;
  1456. }
  1457. /*
  1458.  * Update the current byte count.  Do it now so the count is
  1459.  * valid before a return or break takes us out of the loop.
  1460.  * The invariant at the top of the loop should be that 
  1461.  * csPtr->toRead holds the number of bytes left to copy.
  1462.  */
  1463. if (csPtr->toRead != -1) {
  1464.     csPtr->toRead -= size;
  1465. }
  1466. csPtr->total += size;
  1467. /*
  1468.  * Break loop if EOF && (size>0)
  1469.  */
  1470.         if (Tcl_Eof(inChan)) {
  1471.             break;
  1472.         }
  1473. /*
  1474.  * Check to see if the write is happening in the background.  If so,
  1475.  * stop copying and wait for the channel to become writable again.
  1476.  * After input underflow we already installed a readable handler
  1477.  * therefore we don't need a writable handler.
  1478.  */
  1479. if ( ! underflow && (outStatePtr->flags & BG_FLUSH_SCHEDULED) ) {
  1480.     if (!(mask & TCL_WRITABLE)) {
  1481. if (mask & TCL_READABLE) {
  1482.     Tcl_DeleteChannelHandler(inChan, CopyEventProc,
  1483.     (ClientData) csPtr);
  1484. }
  1485. Tcl_CreateChannelHandler(outChan, TCL_WRITABLE,
  1486. CopyEventProc, (ClientData) csPtr);
  1487.     }
  1488.     if (bufObj != (Tcl_Obj*) NULL) {
  1489.         Tcl_DecrRefCount (bufObj);
  1490. bufObj = (Tcl_Obj*) NULL;
  1491.     }
  1492.     return TCL_OK;
  1493. }
  1494. /*
  1495.  * For background copies, we only do one buffer per invocation so
  1496.  * we don't starve the rest of the system.
  1497.  */
  1498. if (cmdPtr) {
  1499.     /*
  1500.      * The first time we enter this code, there won't be a
  1501.      * channel handler established yet, so do it here.
  1502.      */
  1503.     if (mask == 0) {
  1504. Tcl_CreateChannelHandler(outChan, TCL_WRITABLE,
  1505. CopyEventProc, (ClientData) csPtr);
  1506.     }
  1507.     if (bufObj != (Tcl_Obj*) NULL) {
  1508.         Tcl_DecrRefCount (bufObj);
  1509. bufObj = (Tcl_Obj*) NULL;
  1510.     }
  1511.     return TCL_OK;
  1512. }
  1513.     } /* while */
  1514.     if (bufObj != (Tcl_Obj*) NULL) {
  1515.         Tcl_DecrRefCount (bufObj);
  1516. bufObj = (Tcl_Obj*) NULL;
  1517.     }
  1518.     /*
  1519.      * Make the callback or return the number of bytes transferred.
  1520.      * The local total is used because StopCopy frees csPtr.
  1521.      */
  1522.     total = csPtr->total;
  1523.     if (cmdPtr && interp) {
  1524. /*
  1525.  * Get a private copy of the command so we can mutate it
  1526.  * by adding arguments.  Note that StopCopy frees our saved
  1527.  * reference to the original command obj.
  1528.  */
  1529. cmdPtr = Tcl_DuplicateObj(cmdPtr);
  1530. Tcl_IncrRefCount(cmdPtr);
  1531. StopCopy(csPtr);
  1532. Tcl_Preserve((ClientData) interp);
  1533. Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewIntObj(total));
  1534. if (errObj) {
  1535.     Tcl_ListObjAppendElement(interp, cmdPtr, errObj);
  1536. }
  1537. if (Tcl_EvalObjEx(interp, cmdPtr, TCL_EVAL_GLOBAL) != TCL_OK) {
  1538.     Tcl_BackgroundError(interp);
  1539.     result = TCL_ERROR;
  1540. }
  1541. Tcl_DecrRefCount(cmdPtr);
  1542. Tcl_Release((ClientData) interp);
  1543.     } else {
  1544. StopCopy(csPtr);
  1545. if (interp) {
  1546.     if (errObj) {
  1547. Tcl_SetObjResult(interp, errObj);
  1548. result = TCL_ERROR;
  1549.     } else {
  1550. Tcl_ResetResult(interp);
  1551. Tcl_SetIntObj(Tcl_GetObjResult(interp), total);
  1552.     }
  1553. }
  1554.     }
  1555.     return result;
  1556. }
  1557. /*
  1558.  *----------------------------------------------------------------------
  1559.  *
  1560.  * DoRead --
  1561.  *
  1562.  * Reads a given number of bytes from a channel.
  1563.  *
  1564.  * No encoding conversions are applied to the bytes being read.
  1565.  *
  1566.  * Results:
  1567.  * The number of characters read, or -1 on error. Use Tcl_GetErrno()
  1568.  * to retrieve the error code for the error that occurred.
  1569.  *
  1570.  * Side effects:
  1571.  * May cause input to be buffered.
  1572.  *
  1573.  *----------------------------------------------------------------------
  1574.  */
  1575. static int
  1576. DoRead(chanPtr, bufPtr, toRead)
  1577.     Channel *chanPtr; /* The channel from which to read. */
  1578.     char *bufPtr; /* Where to store input read. */
  1579.     int toRead; /* Maximum number of bytes to read. */
  1580. {
  1581.     ChannelState *statePtr = chanPtr->state; /* state info for channel */
  1582.     int copied; /* How many characters were copied into
  1583.                                  * the result string? */
  1584.     int copiedNow; /* How many characters were copied from
  1585.                                  * the current input buffer? */
  1586.     int result; /* Of calling GetInput. */
  1587.     /*
  1588.      * If we have not encountered a sticky EOF, clear the EOF bit. Either
  1589.      * way clear the BLOCKED bit. We want to discover these anew during
  1590.      * each operation.
  1591.      */
  1592.     if (!(statePtr->flags & CHANNEL_STICKY_EOF)) {
  1593.         statePtr->flags &= ~CHANNEL_EOF;
  1594.     }
  1595.     statePtr->flags &= ~(CHANNEL_BLOCKED | CHANNEL_NEED_MORE_DATA);
  1596.     
  1597.     for (copied = 0; copied < toRead; copied += copiedNow) {
  1598.         copiedNow = CopyAndTranslateBuffer(statePtr, bufPtr + copied,
  1599.                 toRead - copied);
  1600.         if (copiedNow == 0) {
  1601.             if (statePtr->flags & CHANNEL_EOF) {
  1602. goto done;
  1603.             }
  1604.             if (statePtr->flags & CHANNEL_BLOCKED) {
  1605.                 if (statePtr->flags & CHANNEL_NONBLOCKING) {
  1606.     goto done;
  1607.                 }
  1608.                 statePtr->flags &= (~(CHANNEL_BLOCKED));
  1609.             }
  1610.             result = GetInput(chanPtr);
  1611.             if (result != 0) {
  1612.                 if (result != EAGAIN) {
  1613.                     copied = -1;
  1614.                 }
  1615. goto done;
  1616.             }
  1617.         }
  1618.     }
  1619.     statePtr->flags &= (~(CHANNEL_BLOCKED));
  1620.     done:
  1621.     /*
  1622.      * Update the notifier state so we don't block while there is still
  1623.      * data in the buffers.
  1624.      */
  1625.     UpdateInterest(chanPtr);
  1626.     return copied;
  1627. }
  1628. /*
  1629.  *----------------------------------------------------------------------
  1630.  *
  1631.  * CopyAndTranslateBuffer --
  1632.  *
  1633.  * Copy at most one buffer of input to the result space, doing
  1634.  * eol translations according to mode in effect currently.
  1635.  *
  1636.  * Results:
  1637.  * Number of bytes stored in the result buffer (as opposed to the
  1638.  * number of bytes read from the channel).  May return
  1639.  * zero if no input is available to be translated.
  1640.  *
  1641.  * Side effects:
  1642.  * Consumes buffered input. May deallocate one buffer.
  1643.  *
  1644.  *----------------------------------------------------------------------
  1645.  */
  1646. static int
  1647. CopyAndTranslateBuffer(statePtr, result, space)
  1648.     ChannelState *statePtr; /* Channel state from which to read input. */
  1649.     char *result; /* Where to store the copied input. */
  1650.     int space; /* How many bytes are available in result
  1651.                                  * to store the copied input? */
  1652. {
  1653.     ChannelBuffer *bufPtr; /* The buffer from which to copy bytes. */
  1654.     int bytesInBuffer; /* How many bytes are available to be
  1655.                                  * copied in the current input buffer? */
  1656.     int copied; /* How many characters were already copied
  1657.                                  * into the destination space? */
  1658.     int i; /* Iterates over the copied input looking
  1659.                                  * for the input eofChar. */
  1660.     
  1661.     /*
  1662.      * If there is no input at all, return zero. The invariant is that either
  1663.      * there is no buffer in the queue, or if the first buffer is empty, it
  1664.      * is also the last buffer (and thus there is no input in the queue).
  1665.      * Note also that if the buffer is empty, we leave it in the queue.
  1666.      */
  1667.     
  1668.     if (statePtr->inQueueHead == (ChannelBuffer *) NULL) {
  1669.         return 0;
  1670.     }
  1671.     bufPtr = statePtr->inQueueHead;
  1672.     bytesInBuffer = bufPtr->nextAdded - bufPtr->nextRemoved;
  1673.     copied = 0;
  1674.     switch (statePtr->inputTranslation) {
  1675.         case TCL_TRANSLATE_LF: {
  1676.             if (bytesInBuffer == 0) {
  1677.                 return 0;
  1678.             }
  1679.     /*
  1680.              * Copy the current chunk into the result buffer.
  1681.              */
  1682.     if (bytesInBuffer < space) {
  1683. space = bytesInBuffer;
  1684.     }
  1685.     memcpy((VOID *) result,
  1686.     (VOID *) (bufPtr->buf + bufPtr->nextRemoved),
  1687.     (size_t) space);
  1688.     bufPtr->nextRemoved += space;
  1689.     copied = space;
  1690.             break;
  1691. }
  1692.         case TCL_TRANSLATE_CR: {
  1693.     char *end;
  1694.     
  1695.             if (bytesInBuffer == 0) {
  1696.                 return 0;
  1697.             }
  1698.     /*
  1699.              * Copy the current chunk into the result buffer, then
  1700.              * replace all r with n.
  1701.              */
  1702.     if (bytesInBuffer < space) {
  1703. space = bytesInBuffer;
  1704.     }
  1705.     memcpy((VOID *) result,
  1706.     (VOID *) (bufPtr->buf + bufPtr->nextRemoved),
  1707.     (size_t) space);
  1708.     bufPtr->nextRemoved += space;
  1709.     copied = space;
  1710.     for (end = result + copied; result < end; result++) {
  1711. if (*result == 'r') {
  1712.     *result = 'n';
  1713. }
  1714.             }
  1715.             break;
  1716. }
  1717.         case TCL_TRANSLATE_CRLF: {
  1718.     char *src, *end, *dst;
  1719.     int curByte;
  1720.     
  1721.             /*
  1722.              * If there is a held-back "r" at EOF, produce it now.
  1723.              */
  1724.             
  1725.     if (bytesInBuffer == 0) {
  1726.                 if ((statePtr->flags & (INPUT_SAW_CR | CHANNEL_EOF)) ==
  1727.                         (INPUT_SAW_CR | CHANNEL_EOF)) {
  1728.                     result[0] = 'r';
  1729.                     statePtr->flags &= ~INPUT_SAW_CR;
  1730.                     return 1;
  1731.                 }
  1732.                 return 0;
  1733.             }
  1734.             /*
  1735.              * Copy the current chunk and replace "rn" with "n"
  1736.              * (but not standalone "r"!).
  1737.              */
  1738.     if (bytesInBuffer < space) {
  1739. space = bytesInBuffer;
  1740.     }
  1741.     memcpy((VOID *) result,
  1742.     (VOID *) (bufPtr->buf + bufPtr->nextRemoved),
  1743.     (size_t) space);
  1744.     bufPtr->nextRemoved += space;
  1745.     copied = space;
  1746.     end = result + copied;
  1747.     dst = result;
  1748.     for (src = result; src < end; src++) {
  1749. curByte = *src;
  1750. if (curByte == 'n') {
  1751.                     statePtr->flags &= ~INPUT_SAW_CR;
  1752. } else if (statePtr->flags & INPUT_SAW_CR) {
  1753.     statePtr->flags &= ~INPUT_SAW_CR;
  1754.     *dst = 'r';
  1755.     dst++;
  1756. }
  1757. if (curByte == 'r') {
  1758.     statePtr->flags |= INPUT_SAW_CR;
  1759. } else {
  1760.     *dst = (char) curByte;
  1761.     dst++;
  1762. }
  1763.     }
  1764.     copied = dst - result;
  1765.     break;
  1766. }
  1767.         case TCL_TRANSLATE_AUTO: {
  1768.     char *src, *end, *dst;
  1769.     int curByte;
  1770.             if (bytesInBuffer == 0) {
  1771.                 return 0;
  1772.             }
  1773.             /*
  1774.              * Loop over the current buffer, converting "r" and "rn"
  1775.              * to "n".
  1776.              */
  1777.     if (bytesInBuffer < space) {
  1778. space = bytesInBuffer;
  1779.     }
  1780.     memcpy((VOID *) result,
  1781.     (VOID *) (bufPtr->buf + bufPtr->nextRemoved),
  1782.     (size_t) space);
  1783.     bufPtr->nextRemoved += space;
  1784.     copied = space;
  1785.     end = result + copied;
  1786.     dst = result;
  1787.     for (src = result; src < end; src++) {
  1788. curByte = *src;
  1789. if (curByte == 'r') {
  1790.     statePtr->flags |= INPUT_SAW_CR;
  1791.     *dst = 'n';
  1792.     dst++;
  1793. } else {
  1794.     if ((curByte != 'n') || 
  1795.     !(statePtr->flags & INPUT_SAW_CR)) {
  1796. *dst = (char) curByte;
  1797. dst++;
  1798.     }
  1799.     statePtr->flags &= ~INPUT_SAW_CR;
  1800. }
  1801.     }
  1802.     copied = dst - result;
  1803.             break;
  1804. }
  1805.         default: {
  1806.             panic("unknown eol translation mode");
  1807. }
  1808.     }
  1809.     /*
  1810.      * If an in-stream EOF character is set for this channel, check that
  1811.      * the input we copied so far does not contain the EOF char.  If it does,
  1812.      * copy only up to and excluding that character.
  1813.      */
  1814.     
  1815.     if (statePtr->inEofChar != 0) {
  1816.         for (i = 0; i < copied; i++) {
  1817.             if (result[i] == (char) statePtr->inEofChar) {
  1818. /*
  1819.  * Set sticky EOF so that no further input is presented
  1820.  * to the caller.
  1821.  */
  1822. statePtr->flags |= (CHANNEL_EOF | CHANNEL_STICKY_EOF);
  1823. statePtr->inputEncodingFlags |= TCL_ENCODING_END;
  1824. copied = i;
  1825.                 break;
  1826.             }
  1827.         }
  1828.     }
  1829.     /*
  1830.      * If the current buffer is empty recycle it.
  1831.      */
  1832.     if (bufPtr->nextRemoved == bufPtr->nextAdded) {
  1833.         statePtr->inQueueHead = bufPtr->nextPtr;
  1834.         if (statePtr->inQueueHead == (ChannelBuffer *) NULL) {
  1835.             statePtr->inQueueTail = (ChannelBuffer *) NULL;
  1836.         }
  1837.         RecycleBuffer(statePtr, bufPtr, 0);
  1838.     }
  1839.     /*
  1840.      * Return the number of characters copied into the result buffer.
  1841.      * This may be different from the number of bytes consumed, because
  1842.      * of EOL translations.
  1843.      */
  1844.     return copied;
  1845. }
  1846. /*
  1847.  *----------------------------------------------------------------------
  1848.  *
  1849.  * CopyBuffer --
  1850.  *
  1851.  * Copy at most one buffer of input to the result space.
  1852.  *
  1853.  * Results:
  1854.  * Number of bytes stored in the result buffer.  May return
  1855.  * zero if no input is available.
  1856.  *
  1857.  * Side effects:
  1858.  * Consumes buffered input. May deallocate one buffer.
  1859.  *
  1860.  *----------------------------------------------------------------------
  1861.  */
  1862. static int
  1863. CopyBuffer(chanPtr, result, space)
  1864.     Channel *chanPtr; /* Channel from which to read input. */
  1865.     char *result; /* Where to store the copied input. */
  1866.     int space; /* How many bytes are available in result
  1867.                                  * to store the copied input? */
  1868. {
  1869.     ChannelBuffer *bufPtr; /* The buffer from which to copy bytes. */
  1870.     int bytesInBuffer; /* How many bytes are available to be
  1871.                                  * copied in the current input buffer? */
  1872.     int copied; /* How many characters were already copied
  1873.                                  * into the destination space? */
  1874.     
  1875.     /*
  1876.      * If there is no input at all, return zero. The invariant is that
  1877.      * either there is no buffer in the queue, or if the first buffer
  1878.      * is empty, it is also the last buffer (and thus there is no
  1879.      * input in the queue).  Note also that if the buffer is empty, we
  1880.      * don't leave it in the queue, but recycle it.
  1881.      */
  1882.     
  1883.     if (chanPtr->inQueueHead == (ChannelBuffer *) NULL) {
  1884.         return 0;
  1885.     }
  1886.     bufPtr = chanPtr->inQueueHead;
  1887.     bytesInBuffer = bufPtr->nextAdded - bufPtr->nextRemoved;
  1888.     copied = 0;
  1889.     if (bytesInBuffer == 0) {
  1890.         RecycleBuffer(chanPtr->state, bufPtr, 0);
  1891. chanPtr->inQueueHead = (ChannelBuffer*) NULL;
  1892. chanPtr->inQueueTail = (ChannelBuffer*) NULL;
  1893.         return 0;
  1894.     }
  1895.     /*
  1896.      * Copy the current chunk into the result buffer.
  1897.      */
  1898.     if (bytesInBuffer < space) {
  1899.         space = bytesInBuffer;
  1900.     }
  1901.     memcpy((VOID *) result,
  1902.    (VOID *) (bufPtr->buf + bufPtr->nextRemoved),
  1903.    (size_t) space);
  1904.     bufPtr->nextRemoved += space;
  1905.     copied = space;
  1906.     /*
  1907.      * We don't care about in-stream EOF characters here as the data
  1908.      * read here may still flow through one or more transformations,
  1909.      * i.e. is not in its final state yet.
  1910.      */
  1911.     /*
  1912.      * If the current buffer is empty recycle it.
  1913.      */
  1914.     if (bufPtr->nextRemoved == bufPtr->nextAdded) {
  1915.         chanPtr->inQueueHead = bufPtr->nextPtr;
  1916.         if (chanPtr->inQueueHead == (ChannelBuffer *) NULL) {
  1917.             chanPtr->inQueueTail = (ChannelBuffer *) NULL;
  1918.         }
  1919.         RecycleBuffer(chanPtr->state, bufPtr, 0);
  1920.     }
  1921.     /*
  1922.      * Return the number of characters copied into the result buffer.
  1923.      */
  1924.     return copied;
  1925. }
  1926. /*
  1927.  *----------------------------------------------------------------------
  1928.  *
  1929.  * DoWrite --
  1930.  *
  1931.  * Puts a sequence of characters into an output buffer, may queue the
  1932.  * buffer for output if it gets full, and also remembers whether the
  1933.  * current buffer is ready e.g. if it contains a newline and we are in
  1934.  * line buffering mode.
  1935.  *
  1936.  * Results:
  1937.  * The number of bytes written or -1 in case of error. If -1,
  1938.  * Tcl_GetErrno will return the error code.
  1939.  *
  1940.  * Side effects:
  1941.  * May buffer up output and may cause output to be produced on the
  1942.  * channel.
  1943.  *
  1944.  *----------------------------------------------------------------------
  1945.  */
  1946. static int
  1947. DoWrite(chanPtr, src, srcLen)
  1948.     Channel *chanPtr; /* The channel to buffer output for. */
  1949.     CONST char *src; /* Data to write. */
  1950.     int srcLen; /* Number of bytes to write. */
  1951. {
  1952.     ChannelState *statePtr = chanPtr->state; /* state info for channel */
  1953.     ChannelBuffer *outBufPtr; /* Current output buffer. */
  1954.     int foundNewline; /* Did we find a newline in output? */
  1955.     char *dPtr;
  1956.     CONST char *sPtr; /* Search variables for newline. */
  1957.     int crsent; /* In CRLF eol translation mode,
  1958.                                          * remember the fact that a CR was
  1959.                                          * output to the channel without
  1960.                                          * its following NL. */
  1961.     int i; /* Loop index for newline search. */
  1962.     int destCopied; /* How many bytes were used in this
  1963.                                          * destination buffer to hold the
  1964.                                          * output? */
  1965.     int totalDestCopied; /* How many bytes total were
  1966.                                          * copied to the channel buffer? */
  1967.     int srcCopied; /* How many bytes were copied from
  1968.                                          * the source string? */
  1969.     char *destPtr; /* Where in line to copy to? */
  1970.     /*
  1971.      * If we are in network (or windows) translation mode, record the fact
  1972.      * that we have not yet sent a CR to the channel.
  1973.      */
  1974.     crsent = 0;
  1975.     
  1976.     /*
  1977.      * Loop filling buffers and flushing them until all output has been
  1978.      * consumed.
  1979.      */
  1980.     srcCopied = 0;
  1981.     totalDestCopied = 0;
  1982.     while (srcLen > 0) {
  1983.         
  1984.         /*
  1985.          * Make sure there is a current output buffer to accept output.
  1986.          */
  1987.         if (statePtr->curOutPtr == (ChannelBuffer *) NULL) {
  1988.             statePtr->curOutPtr = AllocChannelBuffer(statePtr->bufSize);
  1989.         }
  1990.         outBufPtr = statePtr->curOutPtr;
  1991.         destCopied = outBufPtr->bufLength - outBufPtr->nextAdded;
  1992.         if (destCopied > srcLen) {
  1993.             destCopied = srcLen;
  1994.         }
  1995.         
  1996.         destPtr = outBufPtr->buf + outBufPtr->nextAdded;
  1997.         switch (statePtr->outputTranslation) {
  1998.             case TCL_TRANSLATE_LF:
  1999.                 srcCopied = destCopied;
  2000.                 memcpy((VOID *) destPtr, (VOID *) src, (size_t) destCopied);
  2001.                 break;
  2002.             case TCL_TRANSLATE_CR:
  2003.                 srcCopied = destCopied;
  2004.                 memcpy((VOID *) destPtr, (VOID *) src, (size_t) destCopied);
  2005.                 for (dPtr = destPtr; dPtr < destPtr + destCopied; dPtr++) {
  2006.                     if (*dPtr == 'n') {
  2007.                         *dPtr = 'r';
  2008.                     }
  2009.                 }
  2010.                 break;
  2011.             case TCL_TRANSLATE_CRLF:
  2012.                 for (srcCopied = 0, dPtr = destPtr, sPtr = src;
  2013.                      dPtr < destPtr + destCopied;
  2014.                      dPtr++, sPtr++, srcCopied++) {
  2015.                     if (*sPtr == 'n') {
  2016.                         if (crsent) {
  2017.                             *dPtr = 'n';
  2018.                             crsent = 0;
  2019.                         } else {
  2020.                             *dPtr = 'r';
  2021.                             crsent = 1;
  2022.                             sPtr--, srcCopied--;
  2023.                         }
  2024.                     } else {
  2025.                         *dPtr = *sPtr;
  2026.                     }
  2027.                 }
  2028.                 break;
  2029.             case TCL_TRANSLATE_AUTO:
  2030.                 panic("Tcl_Write: AUTO output translation mode not supported");
  2031.             default:
  2032.                 panic("Tcl_Write: unknown output translation mode");
  2033.         }
  2034.         /*
  2035.          * The current buffer is ready for output if it is full, or if it
  2036.          * contains a newline and this channel is line-buffered, or if it
  2037.          * contains any output and this channel is unbuffered.
  2038.          */
  2039.         outBufPtr->nextAdded += destCopied;
  2040.         if (!(statePtr->flags & BUFFER_READY)) {
  2041.             if (outBufPtr->nextAdded == outBufPtr->bufLength) {
  2042.                 statePtr->flags |= BUFFER_READY;
  2043.             } else if (statePtr->flags & CHANNEL_LINEBUFFERED) {
  2044.                 for (sPtr = src, i = 0, foundNewline = 0;
  2045.      (i < srcCopied) && (!foundNewline);
  2046.      i++, sPtr++) {
  2047.                     if (*sPtr == 'n') {
  2048.                         foundNewline = 1;
  2049.                         break;
  2050.                     }
  2051.                 }
  2052.                 if (foundNewline) {
  2053.                     statePtr->flags |= BUFFER_READY;
  2054.                 }
  2055.             } else if (statePtr->flags & CHANNEL_UNBUFFERED) {
  2056.                 statePtr->flags |= BUFFER_READY;
  2057.             }
  2058.         }
  2059.         
  2060.         totalDestCopied += srcCopied;
  2061.         src += srcCopied;
  2062.         srcLen -= srcCopied;
  2063.         if (statePtr->flags & BUFFER_READY) {
  2064.             if (FlushChannel(NULL, chanPtr, 0) != 0) {
  2065.                 return -1;
  2066.             }
  2067.         }
  2068.     } /* Closes "while" */
  2069.     return totalDestCopied;
  2070. }
  2071. /*
  2072.  *----------------------------------------------------------------------
  2073.  *
  2074.  * CopyEventProc --
  2075.  *
  2076.  * This routine is invoked as a channel event handler for
  2077.  * the background copy operation.  It is just a trivial wrapper
  2078.  * around the CopyData routine.
  2079.  *
  2080.  * Results:
  2081.  * None.
  2082.  *
  2083.  * Side effects:
  2084.  * None.
  2085.  *
  2086.  *----------------------------------------------------------------------
  2087.  */
  2088. static void
  2089. CopyEventProc(clientData, mask)
  2090.     ClientData clientData;
  2091.     int mask;
  2092. {
  2093.     (void) CopyData((CopyState *)clientData, mask);
  2094. }
  2095. /*
  2096.  *----------------------------------------------------------------------
  2097.  *
  2098.  * StopCopy --
  2099.  *
  2100.  * This routine halts a copy that is in progress.
  2101.  *
  2102.  * Results:
  2103.  * None.
  2104.  *
  2105.  * Side effects:
  2106.  * Removes any pending channel handlers and restores the blocking
  2107.  * and buffering modes of the channels.  The CopyState is freed.
  2108.  *
  2109.  *----------------------------------------------------------------------
  2110.  */
  2111. static void
  2112. StopCopy(csPtr)
  2113.     CopyState *csPtr; /* State for bg copy to stop . */
  2114. {
  2115.     ChannelState *inStatePtr, *outStatePtr;
  2116.     int nonBlocking;
  2117.     if (!csPtr) {
  2118. return;
  2119.     }
  2120.     inStatePtr = csPtr->readPtr->state;
  2121.     outStatePtr = csPtr->writePtr->state;
  2122.     /*
  2123.      * Restore the old blocking mode and output buffering mode.
  2124.      */
  2125.     nonBlocking = (csPtr->readFlags & CHANNEL_NONBLOCKING);
  2126.     if (nonBlocking != (inStatePtr->flags & CHANNEL_NONBLOCKING)) {
  2127. SetBlockMode(NULL, csPtr->readPtr,
  2128. nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
  2129.     }
  2130.     if (csPtr->readPtr != csPtr->writePtr) {
  2131. nonBlocking = (csPtr->writeFlags & CHANNEL_NONBLOCKING);
  2132. if (nonBlocking != (outStatePtr->flags & CHANNEL_NONBLOCKING)) {
  2133.     SetBlockMode(NULL, csPtr->writePtr,
  2134.     nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING);
  2135. }
  2136.     }
  2137.     outStatePtr->flags &= ~(CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED);
  2138.     outStatePtr->flags |=
  2139. csPtr->writeFlags & (CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED);
  2140.     if (csPtr->cmdPtr) {
  2141. Tcl_DeleteChannelHandler((Tcl_Channel)csPtr->readPtr, CopyEventProc,
  2142. (ClientData)csPtr);
  2143. if (csPtr->readPtr != csPtr->writePtr) {
  2144.     Tcl_DeleteChannelHandler((Tcl_Channel)csPtr->writePtr,
  2145.     CopyEventProc, (ClientData)csPtr);
  2146. }
  2147.         Tcl_DecrRefCount(csPtr->cmdPtr);
  2148.     }
  2149.     inStatePtr->csPtr  = NULL;
  2150.     outStatePtr->csPtr = NULL;
  2151.     ckfree((char*) csPtr);
  2152. }
  2153. /*
  2154.  *----------------------------------------------------------------------
  2155.  *
  2156.  * StackSetBlockMode --
  2157.  *
  2158.  * This function sets the blocking mode for a channel, iterating
  2159.  * through each channel in a stack and updates the state flags.
  2160.  *
  2161.  * Results:
  2162.  * 0 if OK, result code from failed blockModeProc otherwise.
  2163.  *
  2164.  * Side effects:
  2165.  * Modifies the blocking mode of the channel and possibly generates
  2166.  * an error.
  2167.  *
  2168.  *----------------------------------------------------------------------
  2169.  */
  2170. static int
  2171. StackSetBlockMode(chanPtr, mode)
  2172.     Channel *chanPtr; /* Channel to modify. */
  2173.     int mode; /* One of TCL_MODE_BLOCKING or
  2174.  * TCL_MODE_NONBLOCKING. */
  2175. {
  2176.     int result = 0;
  2177.     Tcl_DriverBlockModeProc *blockModeProc;
  2178.     /*
  2179.      * Start at the top of the channel stack
  2180.      */
  2181.     chanPtr = chanPtr->state->topChanPtr;
  2182.     while (chanPtr != (Channel *) NULL) {
  2183. blockModeProc = Tcl_ChannelBlockModeProc(chanPtr->typePtr);
  2184. if (blockModeProc != NULL) {
  2185.     result = (*blockModeProc) (chanPtr->instanceData, mode);
  2186.     if (result != 0) {
  2187. Tcl_SetErrno(result);
  2188. return result;
  2189.     }
  2190. }
  2191. chanPtr = chanPtr->downChanPtr;
  2192.     }
  2193.     return 0;
  2194. }
  2195. /*
  2196.  *----------------------------------------------------------------------
  2197.  *
  2198.  * SetBlockMode --
  2199.  *
  2200.  * This function sets the blocking mode for a channel and updates
  2201.  * the state flags.
  2202.  *
  2203.  * Results:
  2204.  * A standard Tcl result.
  2205.  *
  2206.  * Side effects:
  2207.  * Modifies the blocking mode of the channel and possibly generates
  2208.  * an error.
  2209.  *
  2210.  *----------------------------------------------------------------------
  2211.  */
  2212. static int
  2213. SetBlockMode(interp, chanPtr, mode)
  2214.     Tcl_Interp *interp; /* Interp for error reporting. */
  2215.     Channel *chanPtr; /* Channel to modify. */
  2216.     int mode; /* One of TCL_MODE_BLOCKING or
  2217.  * TCL_MODE_NONBLOCKING. */
  2218. {
  2219.     ChannelState *statePtr = chanPtr->state; /* state info for channel */
  2220.     int result = 0;
  2221.     result = StackSetBlockMode(chanPtr, mode);
  2222.     if (result != 0) {
  2223. if (interp != (Tcl_Interp *) NULL) {
  2224.     Tcl_AppendResult(interp, "error setting blocking mode: ",
  2225.     Tcl_PosixError(interp), (char *) NULL);
  2226. }
  2227. return TCL_ERROR;
  2228.     }
  2229.     if (mode == TCL_MODE_BLOCKING) {
  2230. statePtr->flags &= (~(CHANNEL_NONBLOCKING | BG_FLUSH_SCHEDULED));
  2231.     } else {
  2232. statePtr->flags |= CHANNEL_NONBLOCKING;
  2233.     }
  2234.     return TCL_OK;
  2235. }
  2236. /*
  2237.  *----------------------------------------------------------------------
  2238.  *
  2239.  * Tcl_GetChannelNames --
  2240.  *
  2241.  * Return the names of all open channels in the interp.
  2242.  *
  2243.  * Results:
  2244.  * TCL_OK or TCL_ERROR.
  2245.  *
  2246.  * Side effects:
  2247.  * Interp result modified with list of channel names.
  2248.  *
  2249.  *----------------------------------------------------------------------
  2250.  */
  2251. int
  2252. Tcl_GetChannelNames(interp)
  2253.     Tcl_Interp *interp; /* Interp for error reporting. */
  2254. {
  2255.     return Tcl_GetChannelNamesEx(interp, (char *) NULL);
  2256. }
  2257. /*
  2258.  *----------------------------------------------------------------------
  2259.  *
  2260.  * Tcl_GetChannelNamesEx --
  2261.  *
  2262.  * Return the names of open channels in the interp filtered
  2263.  * filtered through a pattern.  If pattern is NULL, it returns
  2264.  * all the open channels.
  2265.  *
  2266.  * Results:
  2267.  * TCL_OK or TCL_ERROR.
  2268.  *
  2269.  * Side effects:
  2270.  * Interp result modified with list of channel names.
  2271.  *
  2272.  *----------------------------------------------------------------------
  2273.  */
  2274. int
  2275. Tcl_GetChannelNamesEx(interp, pattern)
  2276.     Tcl_Interp *interp; /* Interp for error reporting. */
  2277.     CONST char *pattern; /* pattern to filter on. */
  2278. {
  2279.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  2280.     ChannelState *statePtr;
  2281.     CONST char *name; /* name for channel */
  2282.     Tcl_Obj *resultPtr; /* pointer to result object */
  2283.     Tcl_HashTable *hTblPtr; /* Hash table of channels. */
  2284.     Tcl_HashEntry *hPtr; /* Search variable. */
  2285.     Tcl_HashSearch hSearch; /* Search variable. */
  2286.     if (interp == (Tcl_Interp *) NULL) {
  2287. return TCL_OK;
  2288.     }
  2289.     /*
  2290.      * Get the channel table that stores the channels registered
  2291.      * for this interpreter.
  2292.      */
  2293.     hTblPtr = GetChannelTable(interp);
  2294.     resultPtr = Tcl_GetObjResult(interp);
  2295.     for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
  2296.  hPtr != (Tcl_HashEntry *) NULL;
  2297.  hPtr = Tcl_NextHashEntry(&hSearch)) {
  2298. statePtr = ((Channel *) Tcl_GetHashValue(hPtr))->state;
  2299.         if (statePtr->topChanPtr == (Channel *) tsdPtr->stdinChannel) {
  2300.     name = "stdin";
  2301. } else if (statePtr->topChanPtr == (Channel *) tsdPtr->stdoutChannel) {
  2302.     name = "stdout";
  2303. } else if (statePtr->topChanPtr == (Channel *) tsdPtr->stderrChannel) {
  2304.     name = "stderr";
  2305. } else {
  2306.     /*
  2307.      * This is also stored in Tcl_GetHashKey(hTblPtr, hPtr),
  2308.      * but it's simpler to just grab the name from the statePtr.
  2309.      */
  2310.     name = statePtr->channelName;
  2311. }
  2312. if (((pattern == NULL) || Tcl_StringMatch(name, pattern)) &&
  2313. (Tcl_ListObjAppendElement(interp, resultPtr,
  2314. Tcl_NewStringObj(name, -1)) != TCL_OK)) {
  2315.     return TCL_ERROR;
  2316. }
  2317.     }
  2318.     return TCL_OK;
  2319. }
  2320. /*
  2321.  *----------------------------------------------------------------------
  2322.  *
  2323.  * Tcl_IsChannelRegistered --
  2324.  *
  2325.  * Checks whether the channel is associated with the interp.
  2326.  * See also Tcl_RegisterChannel and Tcl_UnregisterChannel.
  2327.  *
  2328.  * Results:
  2329.  * 0 if the channel is not registered in the interpreter, 1 else.
  2330.  *
  2331.  * Side effects:
  2332.  * None.
  2333.  *
  2334.  *----------------------------------------------------------------------
  2335.  */
  2336. int
  2337. Tcl_IsChannelRegistered (interp, chan)
  2338.      Tcl_Interp* interp; /* The interp to query of the channel */
  2339.      Tcl_Channel chan; /* The channel to check */
  2340. {
  2341.     Tcl_HashTable *hTblPtr; /* Hash table of channels. */
  2342.     Tcl_HashEntry *hPtr; /* Search variable. */
  2343.     Channel *chanPtr; /* The real IO channel. */
  2344.     ChannelState *statePtr; /* State of the real channel. */
  2345.     /*
  2346.      * Always check bottom-most channel in the stack.  This is the one
  2347.      * that gets registered.
  2348.      */
  2349.     chanPtr = ((Channel *) chan)->state->bottomChanPtr;
  2350.     statePtr = chanPtr->state;
  2351.     hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp, "tclIO", NULL);
  2352.     if (hTblPtr == (Tcl_HashTable *) NULL) {
  2353.         return 0;
  2354.     }
  2355.     hPtr = Tcl_FindHashEntry(hTblPtr, statePtr->channelName);
  2356.     if (hPtr == (Tcl_HashEntry *) NULL) {
  2357.         return 0;
  2358.     }
  2359.     if ((Channel *) Tcl_GetHashValue(hPtr) != chanPtr) {
  2360.         return 0;
  2361.     }
  2362.     return 1;
  2363. }
  2364. /*
  2365.  *----------------------------------------------------------------------
  2366.  *
  2367.  * Tcl_IsChannelShared --
  2368.  *
  2369.  * Checks whether the channel is shared by multiple interpreters.
  2370.  *
  2371.  * Results:
  2372.  * A boolean value (0 = Not shared, 1 = Shared).
  2373.  *
  2374.  * Side effects:
  2375.  * None.
  2376.  *
  2377.  *----------------------------------------------------------------------
  2378.  */
  2379. int
  2380. Tcl_IsChannelShared (chan)
  2381.     Tcl_Channel chan; /* The channel to query */
  2382. {
  2383.     ChannelState *statePtr = ((Channel *) chan)->state;
  2384. /* State of real channel structure. */
  2385.     return ((statePtr->refCount > 1) ? 1 : 0);
  2386. }
  2387. /*
  2388.  *----------------------------------------------------------------------
  2389.  *
  2390.  * Tcl_IsChannelExisting --
  2391.  *
  2392.  * Checks whether a channel of the given name exists in the
  2393.  * (thread)-global list of all channels.
  2394.  * See Tcl_GetChannelNamesEx for function exposed at the Tcl level.
  2395.  *
  2396.  * Results:
  2397.  * A boolean value (0 = Does not exist, 1 = Does exist).
  2398.  *
  2399.  * Side effects:
  2400.  * None.
  2401.  *
  2402.  *----------------------------------------------------------------------
  2403.  */
  2404. int
  2405. Tcl_IsChannelExisting(chanName)
  2406.     CONST char* chanName; /* The name of the channel to look for. */
  2407. {
  2408.     ChannelState *statePtr;
  2409.     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
  2410.     CONST char *name;
  2411.     int chanNameLen;
  2412.     chanNameLen = strlen(chanName);
  2413.     for (statePtr = tsdPtr->firstCSPtr;
  2414.  statePtr != NULL;
  2415.  statePtr = statePtr->nextCSPtr) {
  2416.         if (statePtr->topChanPtr == (Channel *) tsdPtr->stdinChannel) {
  2417.     name = "stdin";
  2418. } else if (statePtr->topChanPtr == (Channel *) tsdPtr->stdoutChannel) {
  2419.     name = "stdout";
  2420. } else if (statePtr->topChanPtr == (Channel *) tsdPtr->stderrChannel) {
  2421.     name = "stderr";
  2422. } else {
  2423.     name = statePtr->channelName;
  2424. }
  2425. if ((*chanName == *name) &&
  2426. (memcmp(name, chanName, (size_t) chanNameLen) == 0)) {
  2427.     return 1;
  2428. }
  2429.     }
  2430.     return 0;
  2431. }
  2432. /*
  2433.  *----------------------------------------------------------------------
  2434.  *
  2435.  * Tcl_ChannelName --
  2436.  *
  2437.  * Return the name of the channel type.
  2438.  *
  2439.  * Results:
  2440.  * A pointer the name of the channel type.
  2441.  *
  2442.  * Side effects:
  2443.  * None.
  2444.  *
  2445.  *----------------------------------------------------------------------
  2446.  */
  2447. CONST char *
  2448. Tcl_ChannelName(chanTypePtr)
  2449.     Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
  2450. {
  2451.     return chanTypePtr->typeName;
  2452. }
  2453. /*
  2454.  *----------------------------------------------------------------------
  2455.  *
  2456.  * Tcl_ChannelVersion --
  2457.  *
  2458.  * Return the of version of the channel type.
  2459.  *
  2460.  * Results:
  2461.  * One of the TCL_CHANNEL_VERSION_* constants from tcl.h
  2462.  *
  2463.  * Side effects:
  2464.  * None.
  2465.  *
  2466.  *----------------------------------------------------------------------
  2467.  */
  2468. Tcl_ChannelTypeVersion
  2469. Tcl_ChannelVersion(chanTypePtr)
  2470.     Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
  2471. {
  2472.     if (chanTypePtr->version == TCL_CHANNEL_VERSION_2) {
  2473. return TCL_CHANNEL_VERSION_2;
  2474.     } else if (chanTypePtr->version == TCL_CHANNEL_VERSION_3) {
  2475. return TCL_CHANNEL_VERSION_3;
  2476.     } else if (chanTypePtr->version == TCL_CHANNEL_VERSION_4) {
  2477. return TCL_CHANNEL_VERSION_4;
  2478.     } else {
  2479. /*
  2480.  * In <v2 channel versions, the version field is occupied
  2481.  * by the Tcl_DriverBlockModeProc
  2482.  */
  2483. return TCL_CHANNEL_VERSION_1;
  2484.     }
  2485. }
  2486. /*
  2487.  *----------------------------------------------------------------------
  2488.  *
  2489.  * HaveVersion --
  2490.  *
  2491.  * Return whether a channel type is (at least) of a given version.
  2492.  *
  2493.  * Results:
  2494.  * True if the minimum version is exceeded by the version actually
  2495.  * present.
  2496.  *
  2497.  * Side effects:
  2498.  * None.
  2499.  *
  2500.  *----------------------------------------------------------------------
  2501.  */
  2502. static int
  2503. HaveVersion(chanTypePtr, minimumVersion)
  2504.     Tcl_ChannelType *chanTypePtr;
  2505.     Tcl_ChannelTypeVersion minimumVersion;
  2506. {
  2507.     Tcl_ChannelTypeVersion actualVersion = Tcl_ChannelVersion(chanTypePtr);
  2508.     return ((int)actualVersion) >= ((int)minimumVersion);
  2509. }
  2510. /*
  2511.  *----------------------------------------------------------------------
  2512.  *
  2513.  * Tcl_ChannelBlockModeProc --
  2514.  *
  2515.  * Return the Tcl_DriverBlockModeProc of the channel type.
  2516.  *
  2517.  * Results:
  2518.  * A pointer to the proc.
  2519.  *
  2520.  * Side effects:
  2521.  * None.
  2522.  *
  2523.  *---------------------------------------------------------------------- */
  2524. Tcl_DriverBlockModeProc *
  2525. Tcl_ChannelBlockModeProc(chanTypePtr)
  2526.     Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
  2527. {
  2528.     if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) {
  2529. return chanTypePtr->blockModeProc;
  2530.     } else {
  2531. /*
  2532.  * The v1 structure had the blockModeProc in a different place.
  2533.  */
  2534. return (Tcl_DriverBlockModeProc *) (chanTypePtr->version);
  2535.     }
  2536. }
  2537. /*
  2538.  *----------------------------------------------------------------------
  2539.  *
  2540.  * Tcl_ChannelCloseProc --
  2541.  *
  2542.  * Return the Tcl_DriverCloseProc of the channel type.
  2543.  *
  2544.  * Results:
  2545.  * A pointer to the proc.
  2546.  *
  2547.  * Side effects:
  2548.  * None.
  2549.  *
  2550.  *----------------------------------------------------------------------
  2551.  */
  2552. Tcl_DriverCloseProc *
  2553. Tcl_ChannelCloseProc(chanTypePtr)
  2554.     Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
  2555. {
  2556.     return chanTypePtr->closeProc;
  2557. }
  2558. /*
  2559.  *----------------------------------------------------------------------
  2560.  *
  2561.  * Tcl_ChannelClose2Proc --
  2562.  *
  2563.  * Return the Tcl_DriverClose2Proc of the channel type.
  2564.  *
  2565.  * Results:
  2566.  * A pointer to the proc.
  2567.  *
  2568.  * Side effects:
  2569.  * None.
  2570.  *
  2571.  *----------------------------------------------------------------------
  2572.  */
  2573. Tcl_DriverClose2Proc *
  2574. Tcl_ChannelClose2Proc(chanTypePtr)
  2575.     Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
  2576. {
  2577.     return chanTypePtr->close2Proc;
  2578. }
  2579. /*
  2580.  *----------------------------------------------------------------------
  2581.  *
  2582.  * Tcl_ChannelInputProc --
  2583.  *
  2584.  * Return the Tcl_DriverInputProc of the channel type.
  2585.  *
  2586.  * Results:
  2587.  * A pointer to the proc.
  2588.  *
  2589.  * Side effects:
  2590.  * None.
  2591.  *
  2592.  *----------------------------------------------------------------------
  2593.  */
  2594. Tcl_DriverInputProc *
  2595. Tcl_ChannelInputProc(chanTypePtr)
  2596.     Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
  2597. {
  2598.     return chanTypePtr->inputProc;
  2599. }
  2600. /*
  2601.  *----------------------------------------------------------------------
  2602.  *
  2603.  * Tcl_ChannelOutputProc --
  2604.  *
  2605.  * Return the Tcl_DriverOutputProc of the channel type.
  2606.  *
  2607.  * Results:
  2608.  * A pointer to the proc.
  2609.  *
  2610.  * Side effects:
  2611.  * None.
  2612.  *
  2613.  *----------------------------------------------------------------------
  2614.  */
  2615. Tcl_DriverOutputProc *
  2616. Tcl_ChannelOutputProc(chanTypePtr)
  2617.     Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
  2618. {
  2619.     return chanTypePtr->outputProc;
  2620. }
  2621. /*
  2622.  *----------------------------------------------------------------------
  2623.  *
  2624.  * Tcl_ChannelSeekProc --
  2625.  *
  2626.  * Return the Tcl_DriverSeekProc of the channel type.
  2627.  *
  2628.  * Results:
  2629.  * A pointer to the proc.
  2630.  *
  2631.  * Side effects:
  2632.  * None.
  2633.  *
  2634.  *----------------------------------------------------------------------
  2635.  */
  2636. Tcl_DriverSeekProc *
  2637. Tcl_ChannelSeekProc(chanTypePtr)
  2638.     Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
  2639. {
  2640.     return chanTypePtr->seekProc;
  2641. }
  2642. /*
  2643.  *----------------------------------------------------------------------
  2644.  *
  2645.  * Tcl_ChannelSetOptionProc --
  2646.  *
  2647.  * Return the Tcl_DriverSetOptionProc of the channel type.
  2648.  *
  2649.  * Results:
  2650.  * A pointer to the proc.
  2651.  *
  2652.  * Side effects:
  2653.  * None.
  2654.  *
  2655.  *----------------------------------------------------------------------
  2656.  */
  2657. Tcl_DriverSetOptionProc *
  2658. Tcl_ChannelSetOptionProc(chanTypePtr)
  2659.     Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
  2660. {
  2661.     return chanTypePtr->setOptionProc;
  2662. }
  2663. /*
  2664.  *----------------------------------------------------------------------
  2665.  *
  2666.  * Tcl_ChannelGetOptionProc --
  2667.  *
  2668.  * Return the Tcl_DriverGetOptionProc of the channel type.
  2669.  *
  2670.  * Results:
  2671.  * A pointer to the proc.
  2672.  *
  2673.  * Side effects:
  2674.  * None.
  2675.  *
  2676.  *----------------------------------------------------------------------
  2677.  */
  2678. Tcl_DriverGetOptionProc *
  2679. Tcl_ChannelGetOptionProc(chanTypePtr)
  2680.     Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
  2681. {
  2682.     return chanTypePtr->getOptionProc;
  2683. }
  2684. /*
  2685.  *----------------------------------------------------------------------
  2686.  *
  2687.  * Tcl_ChannelWatchProc --
  2688.  *
  2689.  * Return the Tcl_DriverWatchProc of the channel type.
  2690.  *
  2691.  * Results:
  2692.  * A pointer to the proc.
  2693.  *
  2694.  * Side effects:
  2695.  * None.
  2696.  *
  2697.  *----------------------------------------------------------------------
  2698.  */
  2699. Tcl_DriverWatchProc *
  2700. Tcl_ChannelWatchProc(chanTypePtr)
  2701.     Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
  2702. {
  2703.     return chanTypePtr->watchProc;
  2704. }
  2705. /*
  2706.  *----------------------------------------------------------------------
  2707.  *
  2708.  * Tcl_ChannelGetHandleProc --
  2709.  *
  2710.  * Return the Tcl_DriverGetHandleProc of the channel type.
  2711.  *
  2712.  * Results:
  2713.  * A pointer to the proc.
  2714.  *
  2715.  * Side effects:
  2716.  * None.
  2717.  *
  2718.  *----------------------------------------------------------------------
  2719.  */
  2720. Tcl_DriverGetHandleProc *
  2721. Tcl_ChannelGetHandleProc(chanTypePtr)
  2722.     Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
  2723. {
  2724.     return chanTypePtr->getHandleProc;
  2725. }
  2726. /*
  2727.  *----------------------------------------------------------------------
  2728.  *
  2729.  * Tcl_ChannelFlushProc --
  2730.  *
  2731.  * Return the Tcl_DriverFlushProc of the channel type.
  2732.  *
  2733.  * Results:
  2734.  * A pointer to the proc.
  2735.  *
  2736.  * Side effects:
  2737.  * None.
  2738.  *
  2739.  *----------------------------------------------------------------------
  2740.  */
  2741. Tcl_DriverFlushProc *
  2742. Tcl_ChannelFlushProc(chanTypePtr)
  2743.     Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
  2744. {
  2745.     if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) {
  2746. return chanTypePtr->flushProc;
  2747.     } else {
  2748. return NULL;
  2749.     }
  2750. }
  2751. /*
  2752.  *----------------------------------------------------------------------
  2753.  *
  2754.  * Tcl_ChannelHandlerProc --
  2755.  *
  2756.  * Return the Tcl_DriverHandlerProc of the channel type.
  2757.  *
  2758.  * Results:
  2759.  * A pointer to the proc.
  2760.  *
  2761.  * Side effects:
  2762.  * None.
  2763.  *
  2764.  *----------------------------------------------------------------------
  2765.  */
  2766. Tcl_DriverHandlerProc *
  2767. Tcl_ChannelHandlerProc(chanTypePtr)
  2768.     Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
  2769. {
  2770.     if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_2)) {
  2771. return chanTypePtr->handlerProc;
  2772.     } else {
  2773. return NULL;
  2774.     }
  2775. }
  2776. /*
  2777.  *----------------------------------------------------------------------
  2778.  *
  2779.  * Tcl_ChannelWideSeekProc --
  2780.  *
  2781.  * Return the Tcl_DriverWideSeekProc of the channel type.
  2782.  *
  2783.  * Results:
  2784.  * A pointer to the proc.
  2785.  *
  2786.  * Side effects:
  2787.  * None.
  2788.  *
  2789.  *----------------------------------------------------------------------
  2790.  */
  2791. Tcl_DriverWideSeekProc *
  2792. Tcl_ChannelWideSeekProc(chanTypePtr)
  2793.     Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
  2794. {
  2795.     if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_3)) {
  2796. return chanTypePtr->wideSeekProc;
  2797.     } else {
  2798. return NULL;
  2799.     }
  2800. }
  2801. /*
  2802.  *----------------------------------------------------------------------
  2803.  *
  2804.  * Tcl_ChannelThreadActionProc --
  2805.  *
  2806.  * Return the Tcl_DriverThreadActionProc of the channel type.
  2807.  *
  2808.  * Results:
  2809.  * A pointer to the proc.
  2810.  *
  2811.  * Side effects:
  2812.  * None.
  2813.  *
  2814.  *----------------------------------------------------------------------
  2815.  */
  2816. Tcl_DriverThreadActionProc *
  2817. Tcl_ChannelThreadActionProc(chanTypePtr)
  2818.     Tcl_ChannelType *chanTypePtr; /* Pointer to channel type. */
  2819. {
  2820.     if (HaveVersion(chanTypePtr, TCL_CHANNEL_VERSION_4)) {
  2821. return chanTypePtr->threadActionProc;
  2822.     } else {
  2823. return NULL;
  2824.     }
  2825. }
  2826. #if 0
  2827. /* For future debugging work, a simple function to print the flags of
  2828.  * a channel in semi-readable form.
  2829.  */
  2830. static int
  2831. DumpFlags (str, flags)
  2832.      char* str;
  2833.      int flags;
  2834. {
  2835.   char buf [20];
  2836.   int i = 0;
  2837.   if (flags & TCL_READABLE)           {buf[i] = 'r';} else {buf [i]='_';}; i++;
  2838.   if (flags & TCL_WRITABLE)           {buf[i] = 'w';} else {buf [i]='_';}; i++;
  2839.   if (flags & CHANNEL_NONBLOCKING)    {buf[i] = 'n';} else {buf [i]='_';}; i++;
  2840.   if (flags & CHANNEL_LINEBUFFERED)   {buf[i] = 'l';} else {buf [i]='_';}; i++;
  2841.   if (flags & CHANNEL_UNBUFFERED)     {buf[i] = 'u';} else {buf [i]='_';}; i++;
  2842.   if (flags & BUFFER_READY)           {buf[i] = 'R';} else {buf [i]='_';}; i++;
  2843.   if (flags & BG_FLUSH_SCHEDULED)     {buf[i] = 'F';} else {buf [i]='_';}; i++;
  2844.   if (flags & CHANNEL_CLOSED)         {buf[i] = 'c';} else {buf [i]='_';}; i++;
  2845.   if (flags & CHANNEL_EOF)            {buf[i] = 'E';} else {buf [i]='_';}; i++;
  2846.   if (flags & CHANNEL_STICKY_EOF)     {buf[i] = 'S';} else {buf [i]='_';}; i++;
  2847.   if (flags & CHANNEL_BLOCKED)        {buf[i] = 'B';} else {buf [i]='_';}; i++;
  2848.   if (flags & INPUT_SAW_CR)           {buf[i] = '/';} else {buf [i]='_';}; i++;
  2849.   if (flags & INPUT_NEED_NL)          {buf[i] = '*';} else {buf [i]='_';}; i++;
  2850.   if (flags & CHANNEL_DEAD)           {buf[i] = 'D';} else {buf [i]='_';}; i++;
  2851.   if (flags & CHANNEL_RAW_MODE)       {buf[i] = 'R';} else {buf [i]='_';}; i++;
  2852. #ifdef TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING
  2853.   if (flags & CHANNEL_TIMER_FEV)      {buf[i] = 'T';} else {buf [i]='_';}; i++;
  2854.   if (flags & CHANNEL_HAS_MORE_DATA)  {buf[i] = 'H';} else {buf [i]='_';}; i++;
  2855. #endif /* TCL_IO_TRACK_OS_FOR_DRIVER_WITH_BAD_BLOCKING */
  2856.   if (flags & CHANNEL_INCLOSE)        {buf[i] = 'x';} else {buf [i]='_';}; i++;
  2857.   buf [i] ='';
  2858.   fprintf (stderr,"%s: %sn", str, buf); fflush(stderr);
  2859.   return 0;
  2860. }
  2861. #endif