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

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tclIOCmd.c --
  3.  *
  4.  * Contains the definitions of most of the Tcl commands relating to IO.
  5.  *
  6.  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
  7.  *
  8.  * See the file "license.terms" for information on usage and redistribution
  9.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  10.  *
  11.  * RCS: @(#) $Id: tclIOCmd.c,v 1.15.2.2 2004/07/16 22:38:37 andreas_kupries Exp $
  12.  */
  13. #include "tclInt.h"
  14. #include "tclPort.h"
  15. /*
  16.  * Callback structure for accept callback in a TCP server.
  17.  */
  18. typedef struct AcceptCallback {
  19.     char *script; /* Script to invoke. */
  20.     Tcl_Interp *interp; /* Interpreter in which to run it. */
  21. } AcceptCallback;
  22. /*
  23.  * Static functions for this file:
  24.  */
  25. static void AcceptCallbackProc _ANSI_ARGS_((ClientData callbackData,
  26.             Tcl_Channel chan, char *address, int port));
  27. static void RegisterTcpServerInterpCleanup _ANSI_ARGS_((Tcl_Interp *interp,
  28.             AcceptCallback *acceptCallbackPtr));
  29. static void TcpAcceptCallbacksDeleteProc _ANSI_ARGS_((
  30.     ClientData clientData, Tcl_Interp *interp));
  31. static void TcpServerCloseProc _ANSI_ARGS_((ClientData callbackData));
  32. static void UnregisterTcpServerInterpCleanupProc _ANSI_ARGS_((
  33.     Tcl_Interp *interp, AcceptCallback *acceptCallbackPtr));
  34. /*
  35.  *----------------------------------------------------------------------
  36.  *
  37.  * Tcl_PutsObjCmd --
  38.  *
  39.  * This procedure is invoked to process the "puts" Tcl command.
  40.  * See the user documentation for details on what it does.
  41.  *
  42.  * Results:
  43.  * A standard Tcl result.
  44.  *
  45.  * Side effects:
  46.  * Produces output on a channel.
  47.  *
  48.  *----------------------------------------------------------------------
  49.  */
  50. /* ARGSUSED */
  51. int
  52. Tcl_PutsObjCmd(dummy, interp, objc, objv)
  53.     ClientData dummy; /* Not used. */
  54.     Tcl_Interp *interp; /* Current interpreter. */
  55.     int objc; /* Number of arguments. */
  56.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  57. {
  58.     Tcl_Channel chan; /* The channel to puts on. */
  59.     Tcl_Obj *string; /* String to write. */
  60.     int newline; /* Add a newline at end? */
  61.     char *channelId; /* Name of channel for puts. */
  62.     int result; /* Result of puts operation. */
  63.     int mode; /* Mode in which channel is opened. */
  64.     switch (objc) {
  65.     case 2: /* puts $x */
  66. string = objv[1];
  67. newline = 1;
  68. channelId = "stdout";
  69. break;
  70.     case 3: /* puts -nonewline $x  or  puts $chan $x */ 
  71. if (strcmp(Tcl_GetString(objv[1]), "-nonewline") == 0) {
  72.     newline = 0;
  73.     channelId = "stdout";
  74. } else {
  75.     newline = 1;
  76.     channelId = Tcl_GetString(objv[1]);
  77. }
  78. string = objv[2];
  79. break;
  80.     case 4: /* puts -nonewline $chan $x  or  puts $chan $x nonewline */
  81. if (strcmp(Tcl_GetString(objv[1]), "-nonewline") == 0) {
  82.     channelId = Tcl_GetString(objv[2]);
  83.     string = objv[3];
  84. } else {
  85.     /*
  86.      * The code below provides backwards compatibility with an
  87.      * old form of the command that is no longer recommended
  88.      * or documented.
  89.      */
  90.     char *arg;
  91.     int length;
  92.     arg = Tcl_GetStringFromObj(objv[3], &length);
  93.     if ((length != 9) || (strncmp(arg, "nonewline", (size_t) length) != 0)) {
  94. Tcl_AppendResult(interp, "bad argument "", arg,
  95.  "": should be "nonewline"",
  96.  (char *) NULL);
  97. return TCL_ERROR;
  98.     }
  99.     channelId = Tcl_GetString(objv[1]);
  100.     string = objv[2];
  101. }
  102. newline = 0;
  103. break;
  104.     default: /* puts  or  puts some bad number of arguments... */
  105. Tcl_WrongNumArgs(interp, 1, objv, "?-nonewline? ?channelId? string");
  106. return TCL_ERROR;
  107.     }
  108.     chan = Tcl_GetChannel(interp, channelId, &mode);
  109.     if (chan == (Tcl_Channel) NULL) {
  110.         return TCL_ERROR;
  111.     }
  112.     if ((mode & TCL_WRITABLE) == 0) {
  113. Tcl_AppendResult(interp, "channel "", channelId,
  114.                 "" wasn't opened for writing", (char *) NULL);
  115.         return TCL_ERROR;
  116.     }
  117.     result = Tcl_WriteObj(chan, string);
  118.     if (result < 0) {
  119.         goto error;
  120.     }
  121.     if (newline != 0) {
  122.         result = Tcl_WriteChars(chan, "n", 1);
  123.         if (result < 0) {
  124.             goto error;
  125.         }
  126.     }
  127.     return TCL_OK;
  128.     error:
  129.     Tcl_AppendResult(interp, "error writing "", channelId, "": ",
  130.     Tcl_PosixError(interp), (char *) NULL);
  131.     return TCL_ERROR;
  132. }
  133. /*
  134.  *----------------------------------------------------------------------
  135.  *
  136.  * Tcl_FlushObjCmd --
  137.  *
  138.  * This procedure is called to process the Tcl "flush" command.
  139.  * See the user documentation for details on what it does.
  140.  *
  141.  * Results:
  142.  * A standard Tcl result.
  143.  *
  144.  * Side effects:
  145.  * May cause output to appear on the specified channel.
  146.  *
  147.  *----------------------------------------------------------------------
  148.  */
  149. /* ARGSUSED */
  150. int
  151. Tcl_FlushObjCmd(dummy, interp, objc, objv)
  152.     ClientData dummy; /* Not used. */
  153.     Tcl_Interp *interp; /* Current interpreter. */
  154.     int objc; /* Number of arguments. */
  155.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  156. {
  157.     Tcl_Channel chan; /* The channel to flush on. */
  158.     char *channelId;
  159.     int mode;
  160.     if (objc != 2) {
  161. Tcl_WrongNumArgs(interp, 1, objv, "channelId");
  162. return TCL_ERROR;
  163.     }
  164.     channelId = Tcl_GetString(objv[1]);
  165.     chan = Tcl_GetChannel(interp, channelId, &mode);
  166.     if (chan == (Tcl_Channel) NULL) {
  167. return TCL_ERROR;
  168.     }
  169.     if ((mode & TCL_WRITABLE) == 0) {
  170. Tcl_AppendResult(interp, "channel "", channelId,
  171. "" wasn't opened for writing", (char *) NULL);
  172.         return TCL_ERROR;
  173.     }
  174.     
  175.     if (Tcl_Flush(chan) != TCL_OK) {
  176. Tcl_AppendResult(interp, "error flushing "", channelId, "": ",
  177. Tcl_PosixError(interp), (char *) NULL);
  178. return TCL_ERROR;
  179.     }
  180.     return TCL_OK;
  181. }
  182. /*
  183.  *----------------------------------------------------------------------
  184.  *
  185.  * Tcl_GetsObjCmd --
  186.  *
  187.  * This procedure is called to process the Tcl "gets" command.
  188.  * See the user documentation for details on what it does.
  189.  *
  190.  * Results:
  191.  * A standard Tcl result.
  192.  *
  193.  * Side effects:
  194.  * May consume input from channel.
  195.  *
  196.  *----------------------------------------------------------------------
  197.  */
  198. /* ARGSUSED */
  199. int
  200. Tcl_GetsObjCmd(dummy, interp, objc, objv)
  201.     ClientData dummy; /* Not used. */
  202.     Tcl_Interp *interp; /* Current interpreter. */
  203.     int objc; /* Number of arguments. */
  204.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  205. {
  206.     Tcl_Channel chan; /* The channel to read from. */
  207.     int lineLen; /* Length of line just read. */
  208.     int mode; /* Mode in which channel is opened. */
  209.     char *name;
  210.     Tcl_Obj *resultPtr, *linePtr;
  211.     if ((objc != 2) && (objc != 3)) {
  212. Tcl_WrongNumArgs(interp, 1, objv, "channelId ?varName?");
  213. return TCL_ERROR;
  214.     }
  215.     name = Tcl_GetString(objv[1]);
  216.     chan = Tcl_GetChannel(interp, name, &mode);
  217.     if (chan == (Tcl_Channel) NULL) {
  218. return TCL_ERROR;
  219.     }
  220.     if ((mode & TCL_READABLE) == 0) {
  221. Tcl_AppendResult(interp, "channel "", name,
  222. "" wasn't opened for reading", (char *) NULL);
  223.         return TCL_ERROR;
  224.     }
  225.     linePtr = Tcl_NewObj();
  226.     lineLen = Tcl_GetsObj(chan, linePtr);
  227.     if (lineLen < 0) {
  228.         if (!Tcl_Eof(chan) && !Tcl_InputBlocked(chan)) {
  229.     Tcl_DecrRefCount(linePtr);
  230.     Tcl_ResetResult(interp);
  231.     Tcl_AppendResult(interp, "error reading "", name, "": ",
  232.     Tcl_PosixError(interp), (char *) NULL);
  233.             return TCL_ERROR;
  234.         }
  235.         lineLen = -1;
  236.     }
  237.     if (objc == 3) {
  238. if (Tcl_ObjSetVar2(interp, objv[2], NULL, linePtr,
  239. TCL_LEAVE_ERR_MSG) == NULL) {
  240.     Tcl_DecrRefCount(linePtr);
  241.             return TCL_ERROR;
  242.         }
  243. resultPtr = Tcl_GetObjResult(interp);
  244. Tcl_SetIntObj(resultPtr, lineLen);
  245.         return TCL_OK;
  246.     } else {
  247. Tcl_SetObjResult(interp, linePtr);
  248.     }
  249.     return TCL_OK;
  250. }
  251. /*
  252.  *----------------------------------------------------------------------
  253.  *
  254.  * Tcl_ReadObjCmd --
  255.  *
  256.  * This procedure is invoked to process the Tcl "read" command.
  257.  * See the user documentation for details on what it does.
  258.  *
  259.  * Results:
  260.  * A standard Tcl result.
  261.  *
  262.  * Side effects:
  263.  * May consume input from channel.
  264.  *
  265.  *----------------------------------------------------------------------
  266.  */
  267. /* ARGSUSED */
  268. int
  269. Tcl_ReadObjCmd(dummy, interp, objc, objv)
  270.     ClientData dummy; /* Not used. */
  271.     Tcl_Interp *interp; /* Current interpreter. */
  272.     int objc; /* Number of arguments. */
  273.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  274. {
  275.     Tcl_Channel chan; /* The channel to read from. */
  276.     int newline, i; /* Discard newline at end? */
  277.     int toRead; /* How many bytes to read? */
  278.     int charactersRead; /* How many characters were read? */
  279.     int mode; /* Mode in which channel is opened. */
  280.     char *name;
  281.     Tcl_Obj *resultPtr;
  282.     if ((objc != 2) && (objc != 3)) {
  283. argerror:
  284. Tcl_WrongNumArgs(interp, 1, objv, "channelId ?numChars?");
  285. Tcl_AppendResult(interp, " or "", Tcl_GetString(objv[0]),
  286. " ?-nonewline? channelId"", (char *) NULL);
  287. return TCL_ERROR;
  288.     }
  289.     i = 1;
  290.     newline = 0;
  291.     if (strcmp(Tcl_GetString(objv[1]), "-nonewline") == 0) {
  292. newline = 1;
  293. i++;
  294.     }
  295.     if (i == objc) {
  296.         goto argerror;
  297.     }
  298.     name = Tcl_GetString(objv[i]);
  299.     chan = Tcl_GetChannel(interp, name, &mode);
  300.     if (chan == (Tcl_Channel) NULL) {
  301. return TCL_ERROR;
  302.     }
  303.     if ((mode & TCL_READABLE) == 0) {
  304. Tcl_AppendResult(interp, "channel "", name, 
  305.                 "" wasn't opened for reading", (char *) NULL);
  306.         return TCL_ERROR;
  307.     }
  308.     i++; /* Consumed channel name. */
  309.     /*
  310.      * Compute how many bytes to read, and see whether the final
  311.      * newline should be dropped.
  312.      */
  313.     toRead = -1;
  314.     if (i < objc) {
  315. char *arg;
  316. arg = Tcl_GetString(objv[i]);
  317. if (isdigit(UCHAR(arg[0]))) { /* INTL: digit */
  318.     if (Tcl_GetIntFromObj(interp, objv[i], &toRead) != TCL_OK) {
  319.                 return TCL_ERROR;
  320.     }
  321. } else if (strcmp(arg, "nonewline") == 0) {
  322.     newline = 1;
  323. } else {
  324.     Tcl_AppendResult(interp, "bad argument "", arg,
  325.     "": should be "nonewline"", (char *) NULL);
  326.     return TCL_ERROR;
  327.         }
  328.     }
  329.     resultPtr = Tcl_NewObj();
  330.     Tcl_IncrRefCount(resultPtr);
  331.     charactersRead = Tcl_ReadChars(chan, resultPtr, toRead, 0);
  332.     if (charactersRead < 0) {
  333. Tcl_ResetResult(interp);
  334. Tcl_AppendResult(interp, "error reading "", name, "": ",
  335. Tcl_PosixError(interp), (char *) NULL);
  336. Tcl_DecrRefCount(resultPtr);
  337. return TCL_ERROR;
  338.     }
  339.     
  340.     /*
  341.      * If requested, remove the last newline in the channel if at EOF.
  342.      */
  343.     
  344.     if ((charactersRead > 0) && (newline != 0)) {
  345. char *result;
  346. int length;
  347. result = Tcl_GetStringFromObj(resultPtr, &length);
  348. if (result[length - 1] == 'n') {
  349.     Tcl_SetObjLength(resultPtr, length - 1);
  350. }
  351.     }
  352.     Tcl_SetObjResult(interp, resultPtr);
  353.     Tcl_DecrRefCount(resultPtr);
  354.     return TCL_OK;
  355. }
  356. /*
  357.  *----------------------------------------------------------------------
  358.  *
  359.  * Tcl_SeekObjCmd --
  360.  *
  361.  * This procedure is invoked to process the Tcl "seek" command. See
  362.  * the user documentation for details on what it does.
  363.  *
  364.  * Results:
  365.  * A standard Tcl result.
  366.  *
  367.  * Side effects:
  368.  * Moves the position of the access point on the specified channel.
  369.  * May flush queued output.
  370.  *
  371.  *----------------------------------------------------------------------
  372.  */
  373. /* ARGSUSED */
  374. int
  375. Tcl_SeekObjCmd(clientData, interp, objc, objv)
  376.     ClientData clientData; /* Not used. */
  377.     Tcl_Interp *interp; /* Current interpreter. */
  378.     int objc; /* Number of arguments. */
  379.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  380. {
  381.     Tcl_Channel chan; /* The channel to tell on. */
  382.     Tcl_WideInt offset; /* Where to seek? */
  383.     int mode; /* How to seek? */
  384.     Tcl_WideInt result; /* Of calling Tcl_Seek. */
  385.     char *chanName;
  386.     int optionIndex;
  387.     static CONST char *originOptions[] = {
  388. "start", "current", "end", (char *) NULL
  389.     };
  390.     static int modeArray[] = {SEEK_SET, SEEK_CUR, SEEK_END};
  391.     if ((objc != 3) && (objc != 4)) {
  392. Tcl_WrongNumArgs(interp, 1, objv, "channelId offset ?origin?");
  393. return TCL_ERROR;
  394.     }
  395.     chanName = Tcl_GetString(objv[1]);
  396.     chan = Tcl_GetChannel(interp, chanName, NULL);
  397.     if (chan == (Tcl_Channel) NULL) {
  398. return TCL_ERROR;
  399.     }
  400.     if (Tcl_GetWideIntFromObj(interp, objv[2], &offset) != TCL_OK) {
  401. return TCL_ERROR;
  402.     }
  403.     mode = SEEK_SET;
  404.     if (objc == 4) {
  405. if (Tcl_GetIndexFromObj(interp, objv[3], originOptions, "origin", 0,
  406. &optionIndex) != TCL_OK) {
  407.     return TCL_ERROR;
  408. }
  409. mode = modeArray[optionIndex];
  410.     }
  411.     result = Tcl_Seek(chan, offset, mode);
  412.     if (result == Tcl_LongAsWide(-1)) {
  413.         Tcl_AppendResult(interp, "error during seek on "", 
  414. chanName, "": ", Tcl_PosixError(interp), (char *) NULL);
  415.         return TCL_ERROR;
  416.     }
  417.     return TCL_OK;
  418. }
  419. /*
  420.  *----------------------------------------------------------------------
  421.  *
  422.  * Tcl_TellObjCmd --
  423.  *
  424.  * This procedure is invoked to process the Tcl "tell" command.
  425.  * See the user documentation for details on what it does.
  426.  *
  427.  * Results:
  428.  * A standard Tcl result.
  429.  *
  430.  * Side effects:
  431.  * None.
  432.  *
  433.  *----------------------------------------------------------------------
  434.  */
  435. /* ARGSUSED */
  436. int
  437. Tcl_TellObjCmd(clientData, interp, objc, objv)
  438.     ClientData clientData; /* Not used. */
  439.     Tcl_Interp *interp; /* Current interpreter. */
  440.     int objc; /* Number of arguments. */
  441.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  442. {
  443.     Tcl_Channel chan; /* The channel to tell on. */
  444.     char *chanName;
  445.     if (objc != 2) {
  446. Tcl_WrongNumArgs(interp, 1, objv, "channelId");
  447. return TCL_ERROR;
  448.     }
  449.     /*
  450.      * Try to find a channel with the right name and permissions in
  451.      * the IO channel table of this interpreter.
  452.      */
  453.     
  454.     chanName = Tcl_GetString(objv[1]);
  455.     chan = Tcl_GetChannel(interp, chanName, NULL);
  456.     if (chan == (Tcl_Channel) NULL) {
  457. return TCL_ERROR;
  458.     }
  459.     Tcl_SetWideIntObj(Tcl_GetObjResult(interp), Tcl_Tell(chan));
  460.     return TCL_OK;
  461. }
  462. /*
  463.  *----------------------------------------------------------------------
  464.  *
  465.  * Tcl_CloseObjCmd --
  466.  *
  467.  * This procedure is invoked to process the Tcl "close" command.
  468.  * See the user documentation for details on what it does.
  469.  *
  470.  * Results:
  471.  * A standard Tcl result.
  472.  *
  473.  * Side effects:
  474.  * May discard queued input; may flush queued output.
  475.  *
  476.  *----------------------------------------------------------------------
  477.  */
  478. /* ARGSUSED */
  479. int
  480. Tcl_CloseObjCmd(clientData, interp, objc, objv)
  481.     ClientData clientData; /* Not used. */
  482.     Tcl_Interp *interp; /* Current interpreter. */
  483.     int objc; /* Number of arguments. */
  484.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  485. {
  486.     Tcl_Channel chan; /* The channel to close. */
  487.     char *arg;
  488.     if (objc != 2) {
  489. Tcl_WrongNumArgs(interp, 1, objv, "channelId");
  490. return TCL_ERROR;
  491.     }
  492.     arg = Tcl_GetString(objv[1]);
  493.     chan = Tcl_GetChannel(interp, arg, NULL);
  494.     if (chan == (Tcl_Channel) NULL) {
  495. return TCL_ERROR;
  496.     }
  497.     if (Tcl_UnregisterChannel(interp, chan) != TCL_OK) {
  498.         /*
  499.          * If there is an error message and it ends with a newline, remove
  500.          * the newline. This is done for command pipeline channels where the
  501.          * error output from the subprocesses is stored in interp's result.
  502.          *
  503.          * NOTE: This is likely to not have any effect on regular error
  504.          * messages produced by drivers during the closing of a channel,
  505.          * because the Tcl convention is that such error messages do not
  506.          * have a terminating newline.
  507.          */
  508. Tcl_Obj *resultPtr;
  509. char *string;
  510. int len;
  511. resultPtr = Tcl_GetObjResult(interp);
  512. string = Tcl_GetStringFromObj(resultPtr, &len);
  513.         if ((len > 0) && (string[len - 1] == 'n')) {
  514.     Tcl_SetObjLength(resultPtr, len - 1);
  515.         }
  516.         return TCL_ERROR;
  517.     }
  518.     return TCL_OK;
  519. }
  520. /*
  521.  *----------------------------------------------------------------------
  522.  *
  523.  * Tcl_FconfigureObjCmd --
  524.  *
  525.  * This procedure is invoked to process the Tcl "fconfigure" command.
  526.  * See the user documentation for details on what it does.
  527.  *
  528.  * Results:
  529.  * A standard Tcl result.
  530.  *
  531.  * Side effects:
  532.  * May modify the behavior of an IO channel.
  533.  *
  534.  *----------------------------------------------------------------------
  535.  */
  536. /* ARGSUSED */
  537. int
  538. Tcl_FconfigureObjCmd(clientData, interp, objc, objv)
  539.     ClientData clientData; /* Not used. */
  540.     Tcl_Interp *interp; /* Current interpreter. */
  541.     int objc; /* Number of arguments. */
  542.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  543. {
  544.     char *chanName, *optionName, *valueName;
  545.     Tcl_Channel chan; /* The channel to set a mode on. */
  546.     int i; /* Iterate over arg-value pairs. */
  547.     Tcl_DString ds; /* DString to hold result of
  548.                                          * calling Tcl_GetChannelOption. */
  549.     if ((objc < 2) || (((objc % 2) == 1) && (objc != 3))) {
  550. Tcl_WrongNumArgs(interp, 1, objv,
  551. "channelId ?optionName? ?value? ?optionName value?...");
  552.         return TCL_ERROR;
  553.     }
  554.     chanName = Tcl_GetString(objv[1]);
  555.     chan = Tcl_GetChannel(interp, chanName, NULL);
  556.     if (chan == (Tcl_Channel) NULL) {
  557.         return TCL_ERROR;
  558.     }
  559.     if (objc == 2) {
  560.         Tcl_DStringInit(&ds);
  561.         if (Tcl_GetChannelOption(interp, chan, (char *) NULL, &ds) != TCL_OK) {
  562.     Tcl_DStringFree(&ds);
  563.     return TCL_ERROR;
  564.         }
  565.         Tcl_DStringResult(interp, &ds);
  566.         return TCL_OK;
  567.     }
  568.     if (objc == 3) {
  569.         Tcl_DStringInit(&ds);
  570. optionName = Tcl_GetString(objv[2]);
  571.         if (Tcl_GetChannelOption(interp, chan, optionName, &ds) != TCL_OK) {
  572.             Tcl_DStringFree(&ds);
  573.             return TCL_ERROR;
  574.         }
  575.         Tcl_DStringResult(interp, &ds);
  576.         return TCL_OK;
  577.     }
  578.     for (i = 3; i < objc; i += 2) {
  579. optionName = Tcl_GetString(objv[i-1]);
  580. valueName = Tcl_GetString(objv[i]);
  581.         if (Tcl_SetChannelOption(interp, chan, optionName, valueName)
  582. != TCL_OK) {
  583.             return TCL_ERROR;
  584.         }
  585.     }
  586.     return TCL_OK;
  587. }
  588. /*
  589.  *---------------------------------------------------------------------------
  590.  *
  591.  * Tcl_EofObjCmd --
  592.  *
  593.  * This procedure is invoked to process the Tcl "eof" command.
  594.  * See the user documentation for details on what it does.
  595.  *
  596.  * Results:
  597.  * A standard Tcl result.
  598.  *
  599.  * Side effects:
  600.  * Sets interp's result to boolean true or false depending on whether
  601.  * the specified channel has an EOF condition.
  602.  *
  603.  *---------------------------------------------------------------------------
  604.  */
  605. /* ARGSUSED */
  606. int
  607. Tcl_EofObjCmd(unused, interp, objc, objv)
  608.     ClientData unused; /* Not used. */
  609.     Tcl_Interp *interp; /* Current interpreter. */
  610.     int objc; /* Number of arguments. */
  611.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  612. {
  613.     Tcl_Channel chan;
  614.     int dummy;
  615.     char *arg;
  616.     if (objc != 2) {
  617. Tcl_WrongNumArgs(interp, 1, objv, "channelId");
  618.         return TCL_ERROR;
  619.     }
  620.     arg = Tcl_GetString(objv[1]);
  621.     chan = Tcl_GetChannel(interp, arg, &dummy);
  622.     if (chan == NULL) {
  623. return TCL_ERROR;
  624.     }
  625.     Tcl_SetBooleanObj(Tcl_GetObjResult(interp), Tcl_Eof(chan));
  626.     return TCL_OK;
  627. }
  628. /*
  629.  *----------------------------------------------------------------------
  630.  *
  631.  * Tcl_ExecObjCmd --
  632.  *
  633.  * This procedure is invoked to process the "exec" Tcl command.
  634.  * See the user documentation for details on what it does.
  635.  *
  636.  * Results:
  637.  * A standard Tcl result.
  638.  *
  639.  * Side effects:
  640.  * See the user documentation.
  641.  *
  642.  *----------------------------------------------------------------------
  643.  */
  644. /* ARGSUSED */
  645. int
  646. Tcl_ExecObjCmd(dummy, interp, objc, objv)
  647.     ClientData dummy; /* Not used. */
  648.     Tcl_Interp *interp; /* Current interpreter. */
  649.     int objc; /* Number of arguments. */
  650.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  651. {
  652. #ifdef MAC_TCL
  653.     Tcl_AppendResult(interp, "exec not implemented under Mac OS",
  654. (char *)NULL);
  655.     return TCL_ERROR;
  656. #else /* !MAC_TCL */
  657.     /*
  658.      * This procedure generates an argv array for the string arguments. It
  659.      * starts out with stack-allocated space but uses dynamically-allocated
  660.      * storage if needed.
  661.      */
  662. #define NUM_ARGS 20
  663.     Tcl_Obj *resultPtr;
  664.     CONST char **argv;
  665.     char *string;
  666.     Tcl_Channel chan;
  667.     CONST char *argStorage[NUM_ARGS];
  668.     int argc, background, i, index, keepNewline, result, skip, length;
  669.     static CONST char *options[] = {
  670. "-keepnewline", "--", NULL
  671.     };
  672.     enum options {
  673. EXEC_KEEPNEWLINE, EXEC_LAST
  674.     };
  675.     /*
  676.      * Check for a leading "-keepnewline" argument.
  677.      */
  678.     keepNewline = 0;
  679.     for (skip = 1; skip < objc; skip++) {
  680. string = Tcl_GetString(objv[skip]);
  681. if (string[0] != '-') {
  682.     break;
  683. }
  684. if (Tcl_GetIndexFromObj(interp, objv[skip], options, "switch",
  685. TCL_EXACT, &index) != TCL_OK) {
  686.     return TCL_ERROR;
  687. }
  688. if (index == EXEC_KEEPNEWLINE) {
  689.     keepNewline = 1;
  690. } else {
  691.     skip++;
  692.     break;
  693. }
  694.     }
  695.     if (objc <= skip) {
  696. Tcl_WrongNumArgs(interp, 1, objv, "?switches? arg ?arg ...?");
  697. return TCL_ERROR;
  698.     }
  699.     /*
  700.      * See if the command is to be run in background.
  701.      */
  702.     background = 0;
  703.     string = Tcl_GetString(objv[objc - 1]);
  704.     if ((string[0] == '&') && (string[1] == '')) {
  705. objc--;
  706.         background = 1;
  707.     }
  708.     /*
  709.      * Create the string argument array "argv". Make sure argv is large
  710.      * enough to hold the argc arguments plus 1 extra for the zero
  711.      * end-of-argv word.
  712.      */
  713.     argv = argStorage;
  714.     argc = objc - skip;
  715.     if ((argc + 1) > sizeof(argv) / sizeof(argv[0])) {
  716. argv = (CONST char **) ckalloc((unsigned)(argc + 1) * sizeof(char *));
  717.     }
  718.     /*
  719.      * Copy the string conversions of each (post option) object into the
  720.      * argument vector.
  721.      */
  722.     for (i = 0; i < argc; i++) {
  723. argv[i] = Tcl_GetString(objv[i + skip]);
  724.     }
  725.     argv[argc] = NULL;
  726.     chan = Tcl_OpenCommandChannel(interp, argc, argv,
  727.             (background ? 0 : TCL_STDOUT | TCL_STDERR));
  728.     /*
  729.      * Free the argv array if malloc'ed storage was used.
  730.      */
  731.     if (argv != argStorage) {
  732. ckfree((char *)argv);
  733.     }
  734.     if (chan == (Tcl_Channel) NULL) {
  735. return TCL_ERROR;
  736.     }
  737.     if (background) {
  738.         /*
  739.  * Store the list of PIDs from the pipeline in interp's result and
  740.  * detach the PIDs (instead of waiting for them).
  741.  */
  742.         TclGetAndDetachPids(interp, chan);
  743.         if (Tcl_Close(interp, chan) != TCL_OK) {
  744.     return TCL_ERROR;
  745.         }
  746. return TCL_OK;
  747.     }
  748.     resultPtr = Tcl_NewObj();
  749.     if (Tcl_GetChannelHandle(chan, TCL_READABLE, NULL) == TCL_OK) {
  750. if (Tcl_ReadChars(chan, resultPtr, -1, 0) < 0) {
  751.     Tcl_ResetResult(interp);
  752.     Tcl_AppendResult(interp, "error reading output from command: ",
  753.     Tcl_PosixError(interp), (char *) NULL);
  754.     Tcl_DecrRefCount(resultPtr);
  755.     return TCL_ERROR;
  756. }
  757.     }
  758.     /*
  759.      * If the process produced anything on stderr, it will have been
  760.      * returned in the interpreter result.  It needs to be appended to
  761.      * the result string.
  762.      */
  763.     result = Tcl_Close(interp, chan);
  764.     string = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &length);
  765.     Tcl_AppendToObj(resultPtr, string, length);
  766.     /*
  767.      * If the last character of the result is a newline, then remove
  768.      * the newline character.
  769.      */
  770.     
  771.     if (keepNewline == 0) {
  772. string = Tcl_GetStringFromObj(resultPtr, &length);
  773. if ((length > 0) && (string[length - 1] == 'n')) {
  774.     Tcl_SetObjLength(resultPtr, length - 1);
  775. }
  776.     }
  777.     Tcl_SetObjResult(interp, resultPtr);
  778.     return result;
  779. #endif /* !MAC_TCL */
  780. }
  781. /*
  782.  *---------------------------------------------------------------------------
  783.  *
  784.  * Tcl_FblockedObjCmd --
  785.  *
  786.  * This procedure is invoked to process the Tcl "fblocked" command.
  787.  * See the user documentation for details on what it does.
  788.  *
  789.  * Results:
  790.  * A standard Tcl result.
  791.  *
  792.  * Side effects:
  793.  * Sets interp's result to boolean true or false depending on whether
  794.  * the preceeding input operation on the channel would have blocked.
  795.  *
  796.  *---------------------------------------------------------------------------
  797.  */
  798. /* ARGSUSED */
  799. int
  800. Tcl_FblockedObjCmd(unused, interp, objc, objv)
  801.     ClientData unused; /* Not used. */
  802.     Tcl_Interp *interp; /* Current interpreter. */
  803.     int objc; /* Number of arguments. */
  804.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  805. {
  806.     Tcl_Channel chan;
  807.     int mode;
  808.     char *arg;
  809.     if (objc != 2) {
  810. Tcl_WrongNumArgs(interp, 1, objv, "channelId");
  811.         return TCL_ERROR;
  812.     }
  813.     arg = Tcl_GetString(objv[1]);
  814.     chan = Tcl_GetChannel(interp, arg, &mode);
  815.     if (chan == NULL) {
  816.         return TCL_ERROR;
  817.     }
  818.     if ((mode & TCL_READABLE) == 0) {
  819. Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel "",
  820. arg, "" wasn't opened for reading", (char *) NULL);
  821.         return TCL_ERROR;
  822.     }
  823.         
  824.     Tcl_SetBooleanObj(Tcl_GetObjResult(interp), Tcl_InputBlocked(chan));
  825.     return TCL_OK;
  826. }
  827. /*
  828.  *----------------------------------------------------------------------
  829.  *
  830.  * Tcl_OpenObjCmd --
  831.  *
  832.  * This procedure is invoked to process the "open" Tcl command.
  833.  * See the user documentation for details on what it does.
  834.  *
  835.  * Results:
  836.  * A standard Tcl result.
  837.  *
  838.  * Side effects:
  839.  * See the user documentation.
  840.  *
  841.  *----------------------------------------------------------------------
  842.  */
  843. /* ARGSUSED */
  844. int
  845. Tcl_OpenObjCmd(notUsed, interp, objc, objv)
  846.     ClientData notUsed; /* Not used. */
  847.     Tcl_Interp *interp; /* Current interpreter. */
  848.     int objc; /* Number of arguments. */
  849.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  850. {
  851.     int pipeline, prot;
  852.     char *modeString, *what;
  853.     Tcl_Channel chan;
  854.     if ((objc < 2) || (objc > 4)) {
  855. Tcl_WrongNumArgs(interp, 1, objv, "fileName ?access? ?permissions?");
  856. return TCL_ERROR;
  857.     }
  858.     prot = 0666;
  859.     if (objc == 2) {
  860. modeString = "r";
  861.     } else {
  862. modeString = Tcl_GetString(objv[2]);
  863. if (objc == 4) {
  864.     if (Tcl_GetIntFromObj(interp, objv[3], &prot) != TCL_OK) {
  865. return TCL_ERROR;
  866.     }
  867. }
  868.     }
  869.     pipeline = 0;
  870.     what = Tcl_GetString(objv[1]);
  871.     if (what[0] == '|') {
  872. pipeline = 1;
  873.     }
  874.     /*
  875.      * Open the file or create a process pipeline.
  876.      */
  877.     if (!pipeline) {
  878.         chan = Tcl_FSOpenFileChannel(interp, objv[1], modeString, prot);
  879.     } else {
  880. #ifdef MAC_TCL
  881. Tcl_AppendResult(interp,
  882. "command pipelines not supported on Macintosh OS",
  883. (char *)NULL);
  884. return TCL_ERROR;
  885. #else
  886. int mode, seekFlag, cmdObjc;
  887. CONST char **cmdArgv;
  888.         if (Tcl_SplitList(interp, what+1, &cmdObjc, &cmdArgv) != TCL_OK) {
  889.             return TCL_ERROR;
  890.         }
  891.         mode = TclGetOpenMode(interp, modeString, &seekFlag);
  892.         if (mode == -1) {
  893.     chan = NULL;
  894.         } else {
  895.     int flags = TCL_STDERR | TCL_ENFORCE_MODE;
  896.     switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
  897. case O_RDONLY:
  898.     flags |= TCL_STDOUT;
  899.     break;
  900. case O_WRONLY:
  901.     flags |= TCL_STDIN;
  902.     break;
  903. case O_RDWR:
  904.     flags |= (TCL_STDIN | TCL_STDOUT);
  905.     break;
  906. default:
  907.     panic("Tcl_OpenCmd: invalid mode value");
  908.     break;
  909.     }
  910.     chan = Tcl_OpenCommandChannel(interp, cmdObjc, cmdArgv, flags);
  911. }
  912.         ckfree((char *) cmdArgv);
  913. #endif
  914.     }
  915.     if (chan == (Tcl_Channel) NULL) {
  916.         return TCL_ERROR;
  917.     }
  918.     Tcl_RegisterChannel(interp, chan);
  919.     Tcl_AppendResult(interp, Tcl_GetChannelName(chan), (char *) NULL);
  920.     return TCL_OK;
  921. }
  922. /*
  923.  *----------------------------------------------------------------------
  924.  *
  925.  * TcpAcceptCallbacksDeleteProc --
  926.  *
  927.  * Assocdata cleanup routine called when an interpreter is being
  928.  * deleted to set the interp field of all the accept callback records
  929.  * registered with the interpreter to NULL. This will prevent the
  930.  * interpreter from being used in the future to eval accept scripts.
  931.  *
  932.  * Results:
  933.  * None.
  934.  *
  935.  * Side effects:
  936.  * Deallocates memory and sets the interp field of all the accept
  937.  * callback records to NULL to prevent this interpreter from being
  938.  * used subsequently to eval accept scripts.
  939.  *
  940.  *----------------------------------------------------------------------
  941.  */
  942. /* ARGSUSED */
  943. static void
  944. TcpAcceptCallbacksDeleteProc(clientData, interp)
  945.     ClientData clientData; /* Data which was passed when the assocdata
  946.                                  * was registered. */
  947.     Tcl_Interp *interp; /* Interpreter being deleted - not used. */
  948. {
  949.     Tcl_HashTable *hTblPtr;
  950.     Tcl_HashEntry *hPtr;
  951.     Tcl_HashSearch hSearch;
  952.     AcceptCallback *acceptCallbackPtr;
  953.     hTblPtr = (Tcl_HashTable *) clientData;
  954.     for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
  955.              hPtr != (Tcl_HashEntry *) NULL;
  956.              hPtr = Tcl_NextHashEntry(&hSearch)) {
  957.         acceptCallbackPtr = (AcceptCallback *) Tcl_GetHashValue(hPtr);
  958.         acceptCallbackPtr->interp = (Tcl_Interp *) NULL;
  959.     }
  960.     Tcl_DeleteHashTable(hTblPtr);
  961.     ckfree((char *) hTblPtr);
  962. }
  963. /*
  964.  *----------------------------------------------------------------------
  965.  *
  966.  * RegisterTcpServerInterpCleanup --
  967.  *
  968.  * Registers an accept callback record to have its interp
  969.  * field set to NULL when the interpreter is deleted.
  970.  *
  971.  * Results:
  972.  * None.
  973.  *
  974.  * Side effects:
  975.  * When, in the future, the interpreter is deleted, the interp
  976.  * field of the accept callback data structure will be set to
  977.  * NULL. This will prevent attempts to eval the accept script
  978.  * in a deleted interpreter.
  979.  *
  980.  *----------------------------------------------------------------------
  981.  */
  982. static void
  983. RegisterTcpServerInterpCleanup(interp, acceptCallbackPtr)
  984.     Tcl_Interp *interp; /* Interpreter for which we want to be
  985.                                  * informed of deletion. */
  986.     AcceptCallback *acceptCallbackPtr;
  987.      /* The accept callback record whose
  988.                                  * interp field we want set to NULL when
  989.                                  * the interpreter is deleted. */
  990. {
  991.     Tcl_HashTable *hTblPtr; /* Hash table for accept callback
  992.                                  * records to smash when the interpreter
  993.                                  * will be deleted. */
  994.     Tcl_HashEntry *hPtr; /* Entry for this record. */
  995.     int new; /* Is the entry new? */
  996.     hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp,
  997.             "tclTCPAcceptCallbacks",
  998.             NULL);
  999.     if (hTblPtr == (Tcl_HashTable *) NULL) {
  1000.         hTblPtr = (Tcl_HashTable *) ckalloc((unsigned) sizeof(Tcl_HashTable));
  1001.         Tcl_InitHashTable(hTblPtr, TCL_ONE_WORD_KEYS);
  1002.         (void) Tcl_SetAssocData(interp, "tclTCPAcceptCallbacks",
  1003.                 TcpAcceptCallbacksDeleteProc, (ClientData) hTblPtr);
  1004.     }
  1005.     hPtr = Tcl_CreateHashEntry(hTblPtr, (char *) acceptCallbackPtr, &new);
  1006.     if (!new) {
  1007.         panic("RegisterTcpServerCleanup: damaged accept record table");
  1008.     }
  1009.     Tcl_SetHashValue(hPtr, (ClientData) acceptCallbackPtr);
  1010. }
  1011. /*
  1012.  *----------------------------------------------------------------------
  1013.  *
  1014.  * UnregisterTcpServerInterpCleanupProc --
  1015.  *
  1016.  * Unregister a previously registered accept callback record. The
  1017.  * interp field of this record will no longer be set to NULL in
  1018.  * the future when the interpreter is deleted.
  1019.  *
  1020.  * Results:
  1021.  * None.
  1022.  *
  1023.  * Side effects:
  1024.  * Prevents the interp field of the accept callback record from
  1025.  * being set to NULL in the future when the interpreter is deleted.
  1026.  *
  1027.  *----------------------------------------------------------------------
  1028.  */
  1029. static void
  1030. UnregisterTcpServerInterpCleanupProc(interp, acceptCallbackPtr)
  1031.     Tcl_Interp *interp; /* Interpreter in which the accept callback
  1032.                                  * record was registered. */
  1033.     AcceptCallback *acceptCallbackPtr;
  1034.      /* The record for which to delete the
  1035.                                  * registration. */
  1036. {
  1037.     Tcl_HashTable *hTblPtr;
  1038.     Tcl_HashEntry *hPtr;
  1039.     hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp,
  1040.             "tclTCPAcceptCallbacks", NULL);
  1041.     if (hTblPtr == (Tcl_HashTable *) NULL) {
  1042.         return;
  1043.     }
  1044.     hPtr = Tcl_FindHashEntry(hTblPtr, (char *) acceptCallbackPtr);
  1045.     if (hPtr == (Tcl_HashEntry *) NULL) {
  1046.         return;
  1047.     }
  1048.     Tcl_DeleteHashEntry(hPtr);
  1049. }
  1050. /*
  1051.  *----------------------------------------------------------------------
  1052.  *
  1053.  * AcceptCallbackProc --
  1054.  *
  1055.  * This callback is invoked by the TCP channel driver when it
  1056.  * accepts a new connection from a client on a server socket.
  1057.  *
  1058.  * Results:
  1059.  * None.
  1060.  *
  1061.  * Side effects:
  1062.  * Whatever the script does.
  1063.  *
  1064.  *----------------------------------------------------------------------
  1065.  */
  1066. static void
  1067. AcceptCallbackProc(callbackData, chan, address, port)
  1068.     ClientData callbackData; /* The data stored when the callback
  1069.                                          * was created in the call to
  1070.                                          * Tcl_OpenTcpServer. */
  1071.     Tcl_Channel chan; /* Channel for the newly accepted
  1072.                                          * connection. */
  1073.     char *address; /* Address of client that was
  1074.                                          * accepted. */
  1075.     int port; /* Port of client that was accepted. */
  1076. {
  1077.     AcceptCallback *acceptCallbackPtr;
  1078.     Tcl_Interp *interp;
  1079.     char *script;
  1080.     char portBuf[TCL_INTEGER_SPACE];
  1081.     int result;
  1082.     acceptCallbackPtr = (AcceptCallback *) callbackData;
  1083.     /*
  1084.      * Check if the callback is still valid; the interpreter may have gone
  1085.      * away, this is signalled by setting the interp field of the callback
  1086.      * data to NULL.
  1087.      */
  1088.     
  1089.     if (acceptCallbackPtr->interp != (Tcl_Interp *) NULL) {
  1090.         script = acceptCallbackPtr->script;
  1091.         interp = acceptCallbackPtr->interp;
  1092.         
  1093.         Tcl_Preserve((ClientData) script);
  1094.         Tcl_Preserve((ClientData) interp);
  1095. TclFormatInt(portBuf, port);
  1096.         Tcl_RegisterChannel(interp, chan);
  1097.         /*
  1098.          * Artificially bump the refcount to protect the channel from
  1099.          * being deleted while the script is being evaluated.
  1100.          */
  1101.         Tcl_RegisterChannel((Tcl_Interp *) NULL,  chan);
  1102.         
  1103.         result = Tcl_VarEval(interp, script, " ", Tcl_GetChannelName(chan),
  1104.                 " ", address, " ", portBuf, (char *) NULL);
  1105.         if (result != TCL_OK) {
  1106.             Tcl_BackgroundError(interp);
  1107.     Tcl_UnregisterChannel(interp, chan);
  1108.         }
  1109.         /*
  1110.          * Decrement the artificially bumped refcount. After this it is
  1111.          * not safe anymore to use "chan", because it may now be deleted.
  1112.          */
  1113.         Tcl_UnregisterChannel((Tcl_Interp *) NULL, chan);
  1114.         
  1115.         Tcl_Release((ClientData) interp);
  1116.         Tcl_Release((ClientData) script);
  1117.     } else {
  1118.         /*
  1119.          * The interpreter has been deleted, so there is no useful
  1120.          * way to utilize the client socket - just close it.
  1121.          */
  1122.         Tcl_Close((Tcl_Interp *) NULL, chan);
  1123.     }
  1124. }
  1125. /*
  1126.  *----------------------------------------------------------------------
  1127.  *
  1128.  * TcpServerCloseProc --
  1129.  *
  1130.  * This callback is called when the TCP server channel for which it
  1131.  * was registered is being closed. It informs the interpreter in
  1132.  * which the accept script is evaluated (if that interpreter still
  1133.  * exists) that this channel no longer needs to be informed if the
  1134.  * interpreter is deleted.
  1135.  *
  1136.  * Results:
  1137.  * None.
  1138.  *
  1139.  * Side effects:
  1140.  * In the future, if the interpreter is deleted this channel will
  1141.  * no longer be informed.
  1142.  *
  1143.  *----------------------------------------------------------------------
  1144.  */
  1145. static void
  1146. TcpServerCloseProc(callbackData)
  1147.     ClientData callbackData; /* The data passed in the call to
  1148.                                  * Tcl_CreateCloseHandler. */
  1149. {
  1150.     AcceptCallback *acceptCallbackPtr;
  1151.      /* The actual data. */
  1152.     acceptCallbackPtr = (AcceptCallback *) callbackData;
  1153.     if (acceptCallbackPtr->interp != (Tcl_Interp *) NULL) {
  1154.         UnregisterTcpServerInterpCleanupProc(acceptCallbackPtr->interp,
  1155.                 acceptCallbackPtr);
  1156.     }
  1157.     Tcl_EventuallyFree((ClientData) acceptCallbackPtr->script, TCL_DYNAMIC);
  1158.     ckfree((char *) acceptCallbackPtr);
  1159. }
  1160. /*
  1161.  *----------------------------------------------------------------------
  1162.  *
  1163.  * Tcl_SocketObjCmd --
  1164.  *
  1165.  * This procedure is invoked to process the "socket" Tcl command.
  1166.  * See the user documentation for details on what it does.
  1167.  *
  1168.  * Results:
  1169.  * A standard Tcl result.
  1170.  *
  1171.  * Side effects:
  1172.  * Creates a socket based channel.
  1173.  *
  1174.  *----------------------------------------------------------------------
  1175.  */
  1176. int
  1177. Tcl_SocketObjCmd(notUsed, interp, objc, objv)
  1178.     ClientData notUsed; /* Not used. */
  1179.     Tcl_Interp *interp; /* Current interpreter. */
  1180.     int objc; /* Number of arguments. */
  1181.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  1182. {
  1183.     static CONST char *socketOptions[] = {
  1184. "-async", "-myaddr", "-myport","-server", (char *) NULL
  1185.     };
  1186.     enum socketOptions {
  1187. SKT_ASYNC,      SKT_MYADDR,      SKT_MYPORT,      SKT_SERVER  
  1188.     };
  1189.     int optionIndex, a, server, port;
  1190.     char *arg, *copyScript, *host, *script;
  1191.     char *myaddr = NULL;
  1192.     int myport = 0;
  1193.     int async = 0;
  1194.     Tcl_Channel chan;
  1195.     AcceptCallback *acceptCallbackPtr;
  1196.     
  1197.     server = 0;
  1198.     script = NULL;
  1199.     if (TclpHasSockets(interp) != TCL_OK) {
  1200. return TCL_ERROR;
  1201.     }
  1202.     for (a = 1; a < objc; a++) {
  1203. arg = Tcl_GetString(objv[a]);
  1204. if (arg[0] != '-') {
  1205.     break;
  1206. }
  1207. if (Tcl_GetIndexFromObj(interp, objv[a], socketOptions,
  1208. "option", TCL_EXACT, &optionIndex) != TCL_OK) {
  1209.     return TCL_ERROR;
  1210. }
  1211. switch ((enum socketOptions) optionIndex) {
  1212.     case SKT_ASYNC: {
  1213.                 if (server == 1) {
  1214.                     Tcl_AppendResult(interp,
  1215.                             "cannot set -async option for server sockets",
  1216.                             (char *) NULL);
  1217.                     return TCL_ERROR;
  1218.                 }
  1219.                 async = 1;
  1220. break;
  1221.     }
  1222.     case SKT_MYADDR: {
  1223. a++;
  1224.                 if (a >= objc) {
  1225.     Tcl_AppendResult(interp,
  1226.     "no argument given for -myaddr option",
  1227.                             (char *) NULL);
  1228.     return TCL_ERROR;
  1229. }
  1230.                 myaddr = Tcl_GetString(objv[a]);
  1231. break;
  1232.     }
  1233.     case SKT_MYPORT: {
  1234. char *myPortName;
  1235. a++;
  1236.                 if (a >= objc) {
  1237.     Tcl_AppendResult(interp,
  1238.     "no argument given for -myport option",
  1239.                             (char *) NULL);
  1240.     return TCL_ERROR;
  1241. }
  1242. myPortName = Tcl_GetString(objv[a]);
  1243. if (TclSockGetPort(interp, myPortName, "tcp", &myport)
  1244. != TCL_OK) {
  1245.     return TCL_ERROR;
  1246. }
  1247. break;
  1248.     }
  1249.     case SKT_SERVER: {
  1250.                 if (async == 1) {
  1251.                     Tcl_AppendResult(interp,
  1252.                             "cannot set -async option for server sockets",
  1253.                             (char *) NULL);
  1254.                     return TCL_ERROR;
  1255.                 }
  1256. server = 1;
  1257. a++;
  1258. if (a >= objc) {
  1259.     Tcl_AppendResult(interp,
  1260.     "no argument given for -server option",
  1261.                             (char *) NULL);
  1262.     return TCL_ERROR;
  1263. }
  1264.                 script = Tcl_GetString(objv[a]);
  1265. break;
  1266.     }
  1267.     default: {
  1268. panic("Tcl_SocketObjCmd: bad option index to SocketOptions");
  1269.     }
  1270. }
  1271.     }
  1272.     if (server) {
  1273.         host = myaddr; /* NULL implies INADDR_ANY */
  1274. if (myport != 0) {
  1275.     Tcl_AppendResult(interp, "Option -myport is not valid for servers",
  1276.     NULL);
  1277.     return TCL_ERROR;
  1278. }
  1279.     } else if (a < objc) {
  1280. host = Tcl_GetString(objv[a]);
  1281. a++;
  1282.     } else {
  1283. wrongNumArgs:
  1284. Tcl_AppendResult(interp, "wrong # args: should be either:n",
  1285. Tcl_GetString(objv[0]),
  1286.                 " ?-myaddr addr? ?-myport myport? ?-async? host portn",
  1287. Tcl_GetString(objv[0]),
  1288.                 " -server command ?-myaddr addr? port",
  1289.                 (char *) NULL);
  1290.         return TCL_ERROR;
  1291.     }
  1292.     if (a == objc-1) {
  1293. if (TclSockGetPort(interp, Tcl_GetString(objv[a]),
  1294. "tcp", &port) != TCL_OK) {
  1295.     return TCL_ERROR;
  1296. }
  1297.     } else {
  1298. goto wrongNumArgs;
  1299.     }
  1300.     if (server) {
  1301.         acceptCallbackPtr = (AcceptCallback *) ckalloc((unsigned)
  1302.                 sizeof(AcceptCallback));
  1303.         copyScript = ckalloc((unsigned) strlen(script) + 1);
  1304.         strcpy(copyScript, script);
  1305.         acceptCallbackPtr->script = copyScript;
  1306.         acceptCallbackPtr->interp = interp;
  1307.         chan = Tcl_OpenTcpServer(interp, port, host, AcceptCallbackProc,
  1308.                 (ClientData) acceptCallbackPtr);
  1309.         if (chan == (Tcl_Channel) NULL) {
  1310.             ckfree(copyScript);
  1311.             ckfree((char *) acceptCallbackPtr);
  1312.             return TCL_ERROR;
  1313.         }
  1314.         /*
  1315.          * Register with the interpreter to let us know when the
  1316.          * interpreter is deleted (by having the callback set the
  1317.          * acceptCallbackPtr->interp field to NULL). This is to
  1318.          * avoid trying to eval the script in a deleted interpreter.
  1319.          */
  1320.         RegisterTcpServerInterpCleanup(interp, acceptCallbackPtr);
  1321.         
  1322.         /*
  1323.          * Register a close callback. This callback will inform the
  1324.          * interpreter (if it still exists) that this channel does not
  1325.          * need to be informed when the interpreter is deleted.
  1326.          */
  1327.         
  1328.         Tcl_CreateCloseHandler(chan, TcpServerCloseProc,
  1329.                 (ClientData) acceptCallbackPtr);
  1330.     } else {
  1331.         chan = Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async);
  1332.         if (chan == (Tcl_Channel) NULL) {
  1333.             return TCL_ERROR;
  1334.         }
  1335.     }
  1336.     Tcl_RegisterChannel(interp, chan);            
  1337.     Tcl_AppendResult(interp, Tcl_GetChannelName(chan), (char *) NULL);
  1338.     
  1339.     return TCL_OK;
  1340. }
  1341. /*
  1342.  *----------------------------------------------------------------------
  1343.  *
  1344.  * Tcl_FcopyObjCmd --
  1345.  *
  1346.  * This procedure is invoked to process the "fcopy" Tcl command.
  1347.  * See the user documentation for details on what it does.
  1348.  *
  1349.  * Results:
  1350.  * A standard Tcl result.
  1351.  *
  1352.  * Side effects:
  1353.  * Moves data between two channels and possibly sets up a
  1354.  * background copy handler.
  1355.  *
  1356.  *----------------------------------------------------------------------
  1357.  */
  1358. int
  1359. Tcl_FcopyObjCmd(dummy, interp, objc, objv)
  1360.     ClientData dummy; /* Not used. */
  1361.     Tcl_Interp *interp; /* Current interpreter. */
  1362.     int objc; /* Number of arguments. */
  1363.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  1364. {
  1365.     Tcl_Channel inChan, outChan;
  1366.     char *arg;
  1367.     int mode, i;
  1368.     int toRead, index;
  1369.     Tcl_Obj *cmdPtr;
  1370.     static CONST char* switches[] = { "-size", "-command", NULL };
  1371.     enum { FcopySize, FcopyCommand };
  1372.     if ((objc < 3) || (objc > 7) || (objc == 4) || (objc == 6)) {
  1373. Tcl_WrongNumArgs(interp, 1, objv,
  1374. "input output ?-size size? ?-command callback?");
  1375. return TCL_ERROR;
  1376.     }
  1377.     /*
  1378.      * Parse the channel arguments and verify that they are readable
  1379.      * or writable, as appropriate.
  1380.      */
  1381.     arg = Tcl_GetString(objv[1]);
  1382.     inChan = Tcl_GetChannel(interp, arg, &mode);
  1383.     if (inChan == (Tcl_Channel) NULL) {
  1384. return TCL_ERROR;
  1385.     }
  1386.     if ((mode & TCL_READABLE) == 0) {
  1387. Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel "",
  1388. arg, 
  1389.                 "" wasn't opened for reading", (char *) NULL);
  1390.         return TCL_ERROR;
  1391.     }
  1392.     arg = Tcl_GetString(objv[2]);
  1393.     outChan = Tcl_GetChannel(interp, arg, &mode);
  1394.     if (outChan == (Tcl_Channel) NULL) {
  1395. return TCL_ERROR;
  1396.     }
  1397.     if ((mode & TCL_WRITABLE) == 0) {
  1398. Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "channel "",
  1399. arg, 
  1400.                 "" wasn't opened for writing", (char *) NULL);
  1401.         return TCL_ERROR;
  1402.     }
  1403.     toRead = -1;
  1404.     cmdPtr = NULL;
  1405.     for (i = 3; i < objc; i += 2) {
  1406. if (Tcl_GetIndexFromObj(interp, objv[i], switches, "switch", 0,
  1407. (int *) &index) != TCL_OK) {
  1408.     return TCL_ERROR;
  1409. }
  1410. switch (index) {
  1411.     case FcopySize:
  1412. if (Tcl_GetIntFromObj(interp, objv[i+1], &toRead) != TCL_OK) {
  1413.     return TCL_ERROR;
  1414. }
  1415. break;
  1416.     case FcopyCommand:
  1417. cmdPtr = objv[i+1];
  1418. break;
  1419. }
  1420.     }
  1421.     return TclCopyChannel(interp, inChan, outChan, toRead, cmdPtr);
  1422. }