tclIOCmd.c

Go to the documentation of this file.
00001 /*
00002  * tclIOCmd.c --
00003  *
00004  *      Contains the definitions of most of the Tcl commands relating to IO.
00005  *
00006  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
00007  *
00008  * See the file "license.terms" for information on usage and redistribution of
00009  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00010  *
00011  * RCS: @(#) $Id: tclIOCmd.c,v 1.51 2007/12/13 15:23:18 dgp Exp $
00012  */
00013 
00014 #include "tclInt.h"
00015 
00016 /*
00017  * Callback structure for accept callback in a TCP server.
00018  */
00019 
00020 typedef struct AcceptCallback {
00021     char *script;                       /* Script to invoke. */
00022     Tcl_Interp *interp;                 /* Interpreter in which to run it. */
00023 } AcceptCallback;
00024 
00025 /*
00026  * Thread local storage used to maintain a per-thread stdout channel obj.
00027  * It must be per-thread because of std channel limitations.
00028  */
00029 
00030 typedef struct ThreadSpecificData {
00031     int initialized;            /* Set to 1 when the module is initialized. */
00032     Tcl_Obj *stdoutObjPtr;      /* Cached stdout channel Tcl_Obj */
00033 } ThreadSpecificData;
00034 
00035 static Tcl_ThreadDataKey dataKey;
00036 
00037 /*
00038  * Static functions for this file:
00039  */
00040 
00041 static void             FinalizeIOCmdTSD(ClientData clientData);
00042 static void             AcceptCallbackProc(ClientData callbackData,
00043                             Tcl_Channel chan, char *address, int port);
00044 static int              ChanPendingObjCmd(ClientData unused,
00045                             Tcl_Interp *interp, int objc,
00046                             Tcl_Obj *const objv[]);
00047 static int              ChanTruncateObjCmd(ClientData dummy,
00048                             Tcl_Interp *interp, int objc,
00049                             Tcl_Obj *const objv[]);
00050 static void             RegisterTcpServerInterpCleanup(Tcl_Interp *interp,
00051                             AcceptCallback *acceptCallbackPtr);
00052 static void             TcpAcceptCallbacksDeleteProc(ClientData clientData,
00053                             Tcl_Interp *interp);
00054 static void             TcpServerCloseProc(ClientData callbackData);
00055 static void             UnregisterTcpServerInterpCleanupProc(
00056                             Tcl_Interp *interp,
00057                             AcceptCallback *acceptCallbackPtr);
00058 
00059 /*
00060  *----------------------------------------------------------------------
00061  *
00062  * FinalizeIOCmdTSD --
00063  *
00064  *      Release the storage associated with the per-thread cache.
00065  *
00066  * Results:
00067  *      None.
00068  *
00069  * Side effects:
00070  *      None.
00071  *
00072  *----------------------------------------------------------------------
00073  */
00074 
00075 static void
00076 FinalizeIOCmdTSD(
00077     ClientData clientData)      /* Not used. */
00078 {
00079     ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
00080 
00081     if (tsdPtr->stdoutObjPtr != NULL) {
00082         Tcl_DecrRefCount(tsdPtr->stdoutObjPtr);
00083         tsdPtr->stdoutObjPtr = NULL;
00084     }
00085     tsdPtr->initialized = 0;
00086 }
00087 
00088 /*
00089  *----------------------------------------------------------------------
00090  *
00091  * Tcl_PutsObjCmd --
00092  *
00093  *      This function is invoked to process the "puts" Tcl command. See the
00094  *      user documentation for details on what it does.
00095  *
00096  * Results:
00097  *      A standard Tcl result.
00098  *
00099  * Side effects:
00100  *      Produces output on a channel.
00101  *
00102  *----------------------------------------------------------------------
00103  */
00104 
00105         /* ARGSUSED */
00106 int
00107 Tcl_PutsObjCmd(
00108     ClientData dummy,           /* Not used. */
00109     Tcl_Interp *interp,         /* Current interpreter. */
00110     int objc,                   /* Number of arguments. */
00111     Tcl_Obj *const objv[])      /* Argument objects. */
00112 {
00113     Tcl_Channel chan;           /* The channel to puts on. */
00114     Tcl_Obj *string;            /* String to write. */
00115     Tcl_Obj *chanObjPtr = NULL; /* channel object. */
00116     int newline;                /* Add a newline at end? */
00117     int result;                 /* Result of puts operation. */
00118     int mode;                   /* Mode in which channel is opened. */
00119     ThreadSpecificData *tsdPtr;
00120 
00121     switch (objc) {
00122     case 2: /* [puts $x] */
00123         string = objv[1];
00124         newline = 1;
00125         break;
00126 
00127     case 3: /* [puts -nonewline $x] or [puts $chan $x] */
00128         if (strcmp(TclGetString(objv[1]), "-nonewline") == 0) {
00129             newline = 0;
00130         } else {
00131             newline = 1;
00132             chanObjPtr = objv[1];
00133         }
00134         string = objv[2];
00135         break;
00136 
00137     case 4: /* [puts -nonewline $chan $x] or [puts $chan $x nonewline] */
00138         if (strcmp(TclGetString(objv[1]), "-nonewline") == 0) {
00139             chanObjPtr = objv[2];
00140             string = objv[3];
00141         } else {
00142             /*
00143              * The code below provides backwards compatibility with an old
00144              * form of the command that is no longer recommended or
00145              * documented.
00146              */
00147 
00148             char *arg;
00149             int length;
00150 
00151             arg = TclGetStringFromObj(objv[3], &length);
00152             if ((length != 9)
00153                     || (strncmp(arg, "nonewline", (size_t) length) != 0)) {
00154                 Tcl_AppendResult(interp, "bad argument \"", arg,
00155                         "\": should be \"nonewline\"", NULL);
00156                 return TCL_ERROR;
00157             }
00158             chanObjPtr = objv[1];
00159             string = objv[2];
00160         }
00161         newline = 0;
00162         break;
00163 
00164     default:
00165         /* [puts] or [puts some bad number of arguments...] */
00166         Tcl_WrongNumArgs(interp, 1, objv, "?-nonewline? ?channelId? string");
00167         return TCL_ERROR;
00168     }
00169 
00170     if (chanObjPtr == NULL) {
00171         tsdPtr = TCL_TSD_INIT(&dataKey);
00172 
00173         if (!tsdPtr->initialized) {
00174             tsdPtr->initialized = 1;
00175             TclNewLiteralStringObj(tsdPtr->stdoutObjPtr, "stdout");
00176             Tcl_IncrRefCount(tsdPtr->stdoutObjPtr);
00177             Tcl_CreateThreadExitHandler(FinalizeIOCmdTSD, NULL);
00178         }
00179         chanObjPtr = tsdPtr->stdoutObjPtr;
00180     }
00181     if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) {
00182         return TCL_ERROR;
00183     }
00184     if ((mode & TCL_WRITABLE) == 0) {
00185         Tcl_AppendResult(interp, "channel \"", TclGetString(chanObjPtr),
00186                 "\" wasn't opened for writing", NULL);
00187         return TCL_ERROR;
00188     }
00189 
00190     result = Tcl_WriteObj(chan, string);
00191     if (result < 0) {
00192         goto error;
00193     }
00194     if (newline != 0) {
00195         result = Tcl_WriteChars(chan, "\n", 1);
00196         if (result < 0) {
00197             goto error;
00198         }
00199     }
00200     return TCL_OK;
00201 
00202     /*
00203      * TIP #219.
00204      * Capture error messages put by the driver into the bypass area and put
00205      * them into the regular interpreter result. Fall back to the regular
00206      * message if nothing was found in the bypass.
00207      */
00208 
00209   error:
00210     if (!TclChanCaughtErrorBypass(interp, chan)) {
00211         Tcl_AppendResult(interp, "error writing \"",
00212                 TclGetString(chanObjPtr), "\": ",
00213                 Tcl_PosixError(interp), NULL);
00214     }
00215     return TCL_ERROR;
00216 }
00217 
00218 /*
00219  *----------------------------------------------------------------------
00220  *
00221  * Tcl_FlushObjCmd --
00222  *
00223  *      This function is called to process the Tcl "flush" command. See the
00224  *      user documentation for details on what it does.
00225  *
00226  * Results:
00227  *      A standard Tcl result.
00228  *
00229  * Side effects:
00230  *      May cause output to appear on the specified channel.
00231  *
00232  *----------------------------------------------------------------------
00233  */
00234 
00235         /* ARGSUSED */
00236 int
00237 Tcl_FlushObjCmd(
00238     ClientData dummy,           /* Not used. */
00239     Tcl_Interp *interp,         /* Current interpreter. */
00240     int objc,                   /* Number of arguments. */
00241     Tcl_Obj *const objv[])      /* Argument objects. */
00242 {
00243     Tcl_Obj *chanObjPtr;
00244     Tcl_Channel chan;           /* The channel to flush on. */
00245     int mode;
00246 
00247     if (objc != 2) {
00248         Tcl_WrongNumArgs(interp, 1, objv, "channelId");
00249         return TCL_ERROR;
00250     }
00251     chanObjPtr = objv[1];
00252     if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) {
00253         return TCL_ERROR;
00254     }
00255     if ((mode & TCL_WRITABLE) == 0) {
00256         Tcl_AppendResult(interp, "channel \"", TclGetString(chanObjPtr),
00257                 "\" wasn't opened for writing", NULL);
00258         return TCL_ERROR;
00259     }
00260 
00261     if (Tcl_Flush(chan) != TCL_OK) {
00262         /*
00263          * TIP #219.
00264          * Capture error messages put by the driver into the bypass area and
00265          * put them into the regular interpreter result. Fall back to the
00266          * regular message if nothing was found in the bypass.
00267          */
00268 
00269         if (!TclChanCaughtErrorBypass(interp, chan)) {
00270             Tcl_AppendResult(interp, "error flushing \"",
00271                     TclGetString(chanObjPtr), "\": ",
00272                     Tcl_PosixError(interp), NULL);
00273         }
00274         return TCL_ERROR;
00275     }
00276     return TCL_OK;
00277 }
00278 
00279 /*
00280  *----------------------------------------------------------------------
00281  *
00282  * Tcl_GetsObjCmd --
00283  *
00284  *      This function is called to process the Tcl "gets" command. See the
00285  *      user documentation for details on what it does.
00286  *
00287  * Results:
00288  *      A standard Tcl result.
00289  *
00290  * Side effects:
00291  *      May consume input from channel.
00292  *
00293  *----------------------------------------------------------------------
00294  */
00295 
00296         /* ARGSUSED */
00297 int
00298 Tcl_GetsObjCmd(
00299     ClientData dummy,           /* Not used. */
00300     Tcl_Interp *interp,         /* Current interpreter. */
00301     int objc,                   /* Number of arguments. */
00302     Tcl_Obj *const objv[])      /* Argument objects. */
00303 {
00304     Tcl_Channel chan;           /* The channel to read from. */
00305     int lineLen;                /* Length of line just read. */
00306     int mode;                   /* Mode in which channel is opened. */
00307     Tcl_Obj *linePtr, *chanObjPtr;
00308 
00309     if ((objc != 2) && (objc != 3)) {
00310         Tcl_WrongNumArgs(interp, 1, objv, "channelId ?varName?");
00311         return TCL_ERROR;
00312     }
00313     chanObjPtr = objv[1];
00314     if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) {
00315         return TCL_ERROR;
00316     }
00317     if ((mode & TCL_READABLE) == 0) {
00318         Tcl_AppendResult(interp, "channel \"", TclGetString(chanObjPtr),
00319                 "\" wasn't opened for reading", NULL);
00320         return TCL_ERROR;
00321     }
00322 
00323     linePtr = Tcl_NewObj();
00324     lineLen = Tcl_GetsObj(chan, linePtr);
00325     if (lineLen < 0) {
00326         if (!Tcl_Eof(chan) && !Tcl_InputBlocked(chan)) {
00327             Tcl_DecrRefCount(linePtr);
00328 
00329             /*
00330              * TIP #219. Capture error messages put by the driver into the
00331              * bypass area and put them into the regular interpreter result.
00332              * Fall back to the regular message if nothing was found in the
00333              * bypass.
00334              */
00335 
00336             if (!TclChanCaughtErrorBypass(interp, chan)) {
00337                 Tcl_ResetResult(interp);
00338                 Tcl_AppendResult(interp, "error reading \"",
00339                         TclGetString(chanObjPtr), "\": ",
00340                         Tcl_PosixError(interp), NULL);
00341             }
00342             return TCL_ERROR;
00343         }
00344         lineLen = -1;
00345     }
00346     if (objc == 3) {
00347         if (Tcl_ObjSetVar2(interp, objv[2], NULL, linePtr,
00348                 TCL_LEAVE_ERR_MSG) == NULL) {
00349             return TCL_ERROR;
00350         }
00351         Tcl_SetObjResult(interp, Tcl_NewIntObj(lineLen));
00352         return TCL_OK;
00353     } else {
00354         Tcl_SetObjResult(interp, linePtr);
00355     }
00356     return TCL_OK;
00357 }
00358 
00359 /*
00360  *----------------------------------------------------------------------
00361  *
00362  * Tcl_ReadObjCmd --
00363  *
00364  *      This function is invoked to process the Tcl "read" command. See the
00365  *      user documentation for details on what it does.
00366  *
00367  * Results:
00368  *      A standard Tcl result.
00369  *
00370  * Side effects:
00371  *      May consume input from channel.
00372  *
00373  *----------------------------------------------------------------------
00374  */
00375 
00376         /* ARGSUSED */
00377 int
00378 Tcl_ReadObjCmd(
00379     ClientData dummy,           /* Not used. */
00380     Tcl_Interp *interp,         /* Current interpreter. */
00381     int objc,                   /* Number of arguments. */
00382     Tcl_Obj *const objv[])      /* Argument objects. */
00383 {
00384     Tcl_Channel chan;           /* The channel to read from. */
00385     int newline, i;             /* Discard newline at end? */
00386     int toRead;                 /* How many bytes to read? */
00387     int charactersRead;         /* How many characters were read? */
00388     int mode;                   /* Mode in which channel is opened. */
00389     Tcl_Obj *resultPtr, *chanObjPtr;
00390 
00391     if ((objc != 2) && (objc != 3)) {
00392         Interp *iPtr;
00393 
00394     argerror:
00395         iPtr = (Interp *) interp;
00396         Tcl_WrongNumArgs(interp, 1, objv, "channelId ?numChars?");
00397 
00398         /*
00399          * Do not append directly; that makes ensembles using this command as
00400          * a subcommand produce the wrong message.
00401          */
00402 
00403         iPtr->flags |= INTERP_ALTERNATE_WRONG_ARGS;
00404         Tcl_WrongNumArgs(interp, 1, objv, "?-nonewline? channelId");
00405         iPtr->flags &= ~INTERP_ALTERNATE_WRONG_ARGS;
00406         return TCL_ERROR;
00407     }
00408 
00409     i = 1;
00410     newline = 0;
00411     if (strcmp(TclGetString(objv[1]), "-nonewline") == 0) {
00412         newline = 1;
00413         i++;
00414     }
00415 
00416     if (i == objc) {
00417         goto argerror;
00418     }
00419 
00420     chanObjPtr = objv[i];
00421     if (TclGetChannelFromObj(interp, chanObjPtr, &chan, &mode, 0) != TCL_OK) {
00422         return TCL_ERROR;
00423     }
00424     if ((mode & TCL_READABLE) == 0) {
00425         Tcl_AppendResult(interp, "channel \"", TclGetString(chanObjPtr),
00426                 "\" wasn't opened for reading", NULL);
00427         return TCL_ERROR;
00428     }
00429     i++;        /* Consumed channel name. */
00430 
00431     /*
00432      * Compute how many bytes to read, and see whether the final newline
00433      * should be dropped.
00434      */
00435 
00436     toRead = -1;
00437     if (i < objc) {
00438         char *arg;
00439 
00440         arg = TclGetString(objv[i]);
00441         if (isdigit(UCHAR(arg[0]))) { /* INTL: digit */
00442             if (TclGetIntFromObj(interp, objv[i], &toRead) != TCL_OK) {
00443                 return TCL_ERROR;
00444             }
00445         } else if (strcmp(arg, "nonewline") == 0) {
00446             newline = 1;
00447         } else {
00448             Tcl_AppendResult(interp, "bad argument \"", arg,
00449                     "\": should be \"nonewline\"", NULL);
00450             return TCL_ERROR;
00451         }
00452     }
00453 
00454     resultPtr = Tcl_NewObj();
00455     Tcl_IncrRefCount(resultPtr);
00456     charactersRead = Tcl_ReadChars(chan, resultPtr, toRead, 0);
00457     if (charactersRead < 0) {
00458         /*
00459          * TIP #219.
00460          * Capture error messages put by the driver into the bypass area and
00461          * put them into the regular interpreter result. Fall back to the
00462          * regular message if nothing was found in the bypass.
00463          */
00464 
00465         if (!TclChanCaughtErrorBypass(interp, chan)) {
00466             Tcl_ResetResult(interp);
00467             Tcl_AppendResult(interp, "error reading \"",
00468                     TclGetString(chanObjPtr), "\": ",
00469                     Tcl_PosixError(interp), NULL);
00470         }
00471         Tcl_DecrRefCount(resultPtr);
00472         return TCL_ERROR;
00473     }
00474 
00475     /*
00476      * If requested, remove the last newline in the channel if at EOF.
00477      */
00478 
00479     if ((charactersRead > 0) && (newline != 0)) {
00480         char *result;
00481         int length;
00482 
00483         result = TclGetStringFromObj(resultPtr, &length);
00484         if (result[length - 1] == '\n') {
00485             Tcl_SetObjLength(resultPtr, length - 1);
00486         }
00487     }
00488     Tcl_SetObjResult(interp, resultPtr);
00489     Tcl_DecrRefCount(resultPtr);
00490     return TCL_OK;
00491 }
00492 
00493 /*
00494  *----------------------------------------------------------------------
00495  *
00496  * Tcl_SeekObjCmd --
00497  *
00498  *      This function is invoked to process the Tcl "seek" command. See the
00499  *      user documentation for details on what it does.
00500  *
00501  * Results:
00502  *      A standard Tcl result.
00503  *
00504  * Side effects:
00505  *      Moves the position of the access point on the specified channel.  May
00506  *      flush queued output.
00507  *
00508  *----------------------------------------------------------------------
00509  */
00510 
00511         /* ARGSUSED */
00512 int
00513 Tcl_SeekObjCmd(
00514     ClientData clientData,      /* Not used. */
00515     Tcl_Interp *interp,         /* Current interpreter. */
00516     int objc,                   /* Number of arguments. */
00517     Tcl_Obj *const objv[])      /* Argument objects. */
00518 {
00519     Tcl_Channel chan;           /* The channel to tell on. */
00520     Tcl_WideInt offset;         /* Where to seek? */
00521     int mode;                   /* How to seek? */
00522     Tcl_WideInt result;         /* Of calling Tcl_Seek. */
00523     int optionIndex;
00524     static const char *originOptions[] = {
00525         "start", "current", "end", NULL
00526     };
00527     static int modeArray[] = {SEEK_SET, SEEK_CUR, SEEK_END};
00528 
00529     if ((objc != 3) && (objc != 4)) {
00530         Tcl_WrongNumArgs(interp, 1, objv, "channelId offset ?origin?");
00531         return TCL_ERROR;
00532     }
00533     if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
00534         return TCL_ERROR;
00535     }
00536     if (Tcl_GetWideIntFromObj(interp, objv[2], &offset) != TCL_OK) {
00537         return TCL_ERROR;
00538     }
00539     mode = SEEK_SET;
00540     if (objc == 4) {
00541         if (Tcl_GetIndexFromObj(interp, objv[3], originOptions, "origin", 0,
00542                 &optionIndex) != TCL_OK) {
00543             return TCL_ERROR;
00544         }
00545         mode = modeArray[optionIndex];
00546     }
00547 
00548     result = Tcl_Seek(chan, offset, mode);
00549     if (result == Tcl_LongAsWide(-1)) {
00550         /*
00551          * TIP #219.
00552          * Capture error messages put by the driver into the bypass area and
00553          * put them into the regular interpreter result. Fall back to the
00554          * regular message if nothing was found in the bypass.
00555          */
00556         if (!TclChanCaughtErrorBypass(interp, chan)) {
00557             Tcl_AppendResult(interp, "error during seek on \"",
00558                     TclGetString(objv[1]), "\": ",
00559                     Tcl_PosixError(interp), NULL);
00560         }
00561         return TCL_ERROR;
00562     }
00563     return TCL_OK;
00564 }
00565 
00566 /*
00567  *----------------------------------------------------------------------
00568  *
00569  * Tcl_TellObjCmd --
00570  *
00571  *      This function is invoked to process the Tcl "tell" command. See the
00572  *      user documentation for details on what it does.
00573  *
00574  * Results:
00575  *      A standard Tcl result.
00576  *
00577  * Side effects:
00578  *      None.
00579  *
00580  *----------------------------------------------------------------------
00581  */
00582 
00583         /* ARGSUSED */
00584 int
00585 Tcl_TellObjCmd(
00586     ClientData clientData,      /* Not used. */
00587     Tcl_Interp *interp,         /* Current interpreter. */
00588     int objc,                   /* Number of arguments. */
00589     Tcl_Obj *const objv[])      /* Argument objects. */
00590 {
00591     Tcl_Channel chan;           /* The channel to tell on. */
00592     Tcl_WideInt newLoc;
00593 
00594     if (objc != 2) {
00595         Tcl_WrongNumArgs(interp, 1, objv, "channelId");
00596         return TCL_ERROR;
00597     }
00598 
00599     /*
00600      * Try to find a channel with the right name and permissions in the IO
00601      * channel table of this interpreter.
00602      */
00603 
00604     if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
00605         return TCL_ERROR;
00606     }
00607 
00608     newLoc = Tcl_Tell(chan);
00609 
00610     /*
00611      * TIP #219.
00612      * Capture error messages put by the driver into the bypass area and put
00613      * them into the regular interpreter result.
00614      */
00615 
00616     if (TclChanCaughtErrorBypass(interp, chan)) {
00617         return TCL_ERROR;
00618     }
00619 
00620     Tcl_SetObjResult(interp, Tcl_NewWideIntObj(newLoc));
00621     return TCL_OK;
00622 }
00623 
00624 /*
00625  *----------------------------------------------------------------------
00626  *
00627  * Tcl_CloseObjCmd --
00628  *
00629  *      This function is invoked to process the Tcl "close" command. See the
00630  *      user documentation for details on what it does.
00631  *
00632  * Results:
00633  *      A standard Tcl result.
00634  *
00635  * Side effects:
00636  *      May discard queued input; may flush queued output.
00637  *
00638  *----------------------------------------------------------------------
00639  */
00640 
00641         /* ARGSUSED */
00642 int
00643 Tcl_CloseObjCmd(
00644     ClientData clientData,      /* Not used. */
00645     Tcl_Interp *interp,         /* Current interpreter. */
00646     int objc,                   /* Number of arguments. */
00647     Tcl_Obj *const objv[])      /* Argument objects. */
00648 {
00649     Tcl_Channel chan;           /* The channel to close. */
00650 
00651     if (objc != 2) {
00652         Tcl_WrongNumArgs(interp, 1, objv, "channelId");
00653         return TCL_ERROR;
00654     }
00655 
00656     if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
00657         return TCL_ERROR;
00658     }
00659 
00660     if (Tcl_UnregisterChannel(interp, chan) != TCL_OK) {
00661         /*
00662          * If there is an error message and it ends with a newline, remove the
00663          * newline. This is done for command pipeline channels where the error
00664          * output from the subprocesses is stored in interp's result.
00665          *
00666          * NOTE: This is likely to not have any effect on regular error
00667          * messages produced by drivers during the closing of a channel,
00668          * because the Tcl convention is that such error messages do not have
00669          * a terminating newline.
00670          */
00671 
00672         Tcl_Obj *resultPtr = Tcl_GetObjResult(interp);
00673         char *string;
00674         int len;
00675 
00676         if (Tcl_IsShared(resultPtr)) {
00677             resultPtr = Tcl_DuplicateObj(resultPtr);
00678             Tcl_SetObjResult(interp, resultPtr);
00679         }
00680         string = TclGetStringFromObj(resultPtr, &len);
00681         if ((len > 0) && (string[len - 1] == '\n')) {
00682             Tcl_SetObjLength(resultPtr, len - 1);
00683         }
00684         return TCL_ERROR;
00685     }
00686 
00687     return TCL_OK;
00688 }
00689 
00690 /*
00691  *----------------------------------------------------------------------
00692  *
00693  * Tcl_FconfigureObjCmd --
00694  *
00695  *      This function is invoked to process the Tcl "fconfigure" command. See
00696  *      the user documentation for details on what it does.
00697  *
00698  * Results:
00699  *      A standard Tcl result.
00700  *
00701  * Side effects:
00702  *      May modify the behavior of an IO channel.
00703  *
00704  *----------------------------------------------------------------------
00705  */
00706 
00707         /* ARGSUSED */
00708 int
00709 Tcl_FconfigureObjCmd(
00710     ClientData clientData,      /* Not used. */
00711     Tcl_Interp *interp,         /* Current interpreter. */
00712     int objc,                   /* Number of arguments. */
00713     Tcl_Obj *const objv[])      /* Argument objects. */
00714 {
00715     char *optionName, *valueName;
00716     Tcl_Channel chan;           /* The channel to set a mode on. */
00717     int i;                      /* Iterate over arg-value pairs. */
00718 
00719     if ((objc < 2) || (((objc % 2) == 1) && (objc != 3))) {
00720         Tcl_WrongNumArgs(interp, 1, objv,
00721                 "channelId ?optionName? ?value? ?optionName value?...");
00722         return TCL_ERROR;
00723     }
00724 
00725     if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
00726         return TCL_ERROR;
00727     }
00728 
00729     if (objc == 2) {
00730         Tcl_DString ds;         /* DString to hold result of calling
00731                                  * Tcl_GetChannelOption. */
00732 
00733         Tcl_DStringInit(&ds);
00734         if (Tcl_GetChannelOption(interp, chan, NULL, &ds) != TCL_OK) {
00735             Tcl_DStringFree(&ds);
00736             return TCL_ERROR;
00737         }
00738         Tcl_DStringResult(interp, &ds);
00739         return TCL_OK;
00740     } else if (objc == 3) {
00741         Tcl_DString ds;         /* DString to hold result of calling
00742                                  * Tcl_GetChannelOption. */
00743 
00744         Tcl_DStringInit(&ds);
00745         optionName = TclGetString(objv[2]);
00746         if (Tcl_GetChannelOption(interp, chan, optionName, &ds) != TCL_OK) {
00747             Tcl_DStringFree(&ds);
00748             return TCL_ERROR;
00749         }
00750         Tcl_DStringResult(interp, &ds);
00751         return TCL_OK;
00752     }
00753 
00754     for (i = 3; i < objc; i += 2) {
00755         optionName = TclGetString(objv[i-1]);
00756         valueName = TclGetString(objv[i]);
00757         if (Tcl_SetChannelOption(interp, chan, optionName, valueName)
00758                 != TCL_OK) {
00759             return TCL_ERROR;
00760         }
00761     }
00762 
00763     return TCL_OK;
00764 }
00765 
00766 /*
00767  *---------------------------------------------------------------------------
00768  *
00769  * Tcl_EofObjCmd --
00770  *
00771  *      This function is invoked to process the Tcl "eof" command. See the
00772  *      user documentation for details on what it does.
00773  *
00774  * Results:
00775  *      A standard Tcl result.
00776  *
00777  * Side effects:
00778  *      Sets interp's result to boolean true or false depending on whether the
00779  *      specified channel has an EOF condition.
00780  *
00781  *---------------------------------------------------------------------------
00782  */
00783 
00784         /* ARGSUSED */
00785 int
00786 Tcl_EofObjCmd(
00787     ClientData unused,          /* Not used. */
00788     Tcl_Interp *interp,         /* Current interpreter. */
00789     int objc,                   /* Number of arguments. */
00790     Tcl_Obj *const objv[])      /* Argument objects. */
00791 {
00792     Tcl_Channel chan;
00793 
00794     if (objc != 2) {
00795         Tcl_WrongNumArgs(interp, 1, objv, "channelId");
00796         return TCL_ERROR;
00797     }
00798 
00799     if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
00800         return TCL_ERROR;
00801     }
00802 
00803     Tcl_SetObjResult(interp, Tcl_NewBooleanObj(Tcl_Eof(chan)));
00804     return TCL_OK;
00805 }
00806 
00807 /*
00808  *----------------------------------------------------------------------
00809  *
00810  * Tcl_ExecObjCmd --
00811  *
00812  *      This function is invoked to process the "exec" Tcl command. See the
00813  *      user documentation for details on what it does.
00814  *
00815  * Results:
00816  *      A standard Tcl result.
00817  *
00818  * Side effects:
00819  *      See the user documentation.
00820  *
00821  *----------------------------------------------------------------------
00822  */
00823 
00824         /* ARGSUSED */
00825 int
00826 Tcl_ExecObjCmd(
00827     ClientData dummy,           /* Not used. */
00828     Tcl_Interp *interp,         /* Current interpreter. */
00829     int objc,                   /* Number of arguments. */
00830     Tcl_Obj *const objv[])      /* Argument objects. */
00831 {
00832     /*
00833      * This function generates an argv array for the string arguments. It
00834      * starts out with stack-allocated space but uses dynamically-allocated
00835      * storage if needed.
00836      */
00837 
00838     Tcl_Obj *resultPtr;
00839     const char **argv;
00840     char *string;
00841     Tcl_Channel chan;
00842     int argc, background, i, index, keepNewline, result, skip, length;
00843     int ignoreStderr;
00844     static const char *options[] = {
00845         "-ignorestderr", "-keepnewline", "--", NULL
00846     };
00847     enum options {
00848         EXEC_IGNORESTDERR, EXEC_KEEPNEWLINE, EXEC_LAST
00849     };
00850 
00851     /*
00852      * Check for any leading option arguments.
00853      */
00854 
00855     keepNewline = 0;
00856     ignoreStderr = 0;
00857     for (skip = 1; skip < objc; skip++) {
00858         string = TclGetString(objv[skip]);
00859         if (string[0] != '-') {
00860             break;
00861         }
00862         if (Tcl_GetIndexFromObj(interp, objv[skip], options, "switch",
00863                 TCL_EXACT, &index) != TCL_OK) {
00864             return TCL_ERROR;
00865         }
00866         if (index == EXEC_KEEPNEWLINE) {
00867             keepNewline = 1;
00868         } else if (index == EXEC_IGNORESTDERR) {
00869             ignoreStderr = 1;
00870         } else {
00871             skip++;
00872             break;
00873         }
00874     }
00875     if (objc <= skip) {
00876         Tcl_WrongNumArgs(interp, 1, objv, "?switches? arg ?arg ...?");
00877         return TCL_ERROR;
00878     }
00879 
00880     /*
00881      * See if the command is to be run in background.
00882      */
00883 
00884     background = 0;
00885     string = TclGetString(objv[objc - 1]);
00886     if ((string[0] == '&') && (string[1] == '\0')) {
00887         objc--;
00888         background = 1;
00889     }
00890 
00891     /*
00892      * Create the string argument array "argv". Make sure argv is large enough
00893      * to hold the argc arguments plus 1 extra for the zero end-of-argv word.
00894      */
00895 
00896     argc = objc - skip;
00897     argv = (const char **)
00898             TclStackAlloc(interp, (unsigned)(argc + 1) * sizeof(char *));
00899 
00900     /*
00901      * Copy the string conversions of each (post option) object into the
00902      * argument vector.
00903      */
00904 
00905     for (i = 0; i < argc; i++) {
00906         argv[i] = TclGetString(objv[i + skip]);
00907     }
00908     argv[argc] = NULL;
00909     chan = Tcl_OpenCommandChannel(interp, argc, argv, (background ? 0 :
00910             (ignoreStderr ? TCL_STDOUT : TCL_STDOUT|TCL_STDERR)));
00911 
00912     /*
00913      * Free the argv array.
00914      */
00915 
00916     TclStackFree(interp, (void *)argv);
00917 
00918     if (chan == NULL) {
00919         return TCL_ERROR;
00920     }
00921 
00922     if (background) {
00923         /*
00924          * Store the list of PIDs from the pipeline in interp's result and
00925          * detach the PIDs (instead of waiting for them).
00926          */
00927 
00928         TclGetAndDetachPids(interp, chan);
00929         if (Tcl_Close(interp, chan) != TCL_OK) {
00930             return TCL_ERROR;
00931         }
00932         return TCL_OK;
00933     }
00934 
00935     resultPtr = Tcl_NewObj();
00936     if (Tcl_GetChannelHandle(chan, TCL_READABLE, NULL) == TCL_OK) {
00937         if (Tcl_ReadChars(chan, resultPtr, -1, 0) < 0) {
00938             /*
00939              * TIP #219.
00940              * Capture error messages put by the driver into the bypass area
00941              * and put them into the regular interpreter result. Fall back to
00942              * the regular message if nothing was found in the bypass.
00943              */
00944 
00945             if (!TclChanCaughtErrorBypass(interp, chan)) {
00946                 Tcl_ResetResult(interp);
00947                 Tcl_AppendResult(interp, "error reading output from command: ",
00948                         Tcl_PosixError(interp), NULL);
00949                 Tcl_DecrRefCount(resultPtr);
00950             }
00951             return TCL_ERROR;
00952         }
00953     }
00954 
00955     /*
00956      * If the process produced anything on stderr, it will have been returned
00957      * in the interpreter result. It needs to be appended to the result
00958      * string.
00959      */
00960 
00961     result = Tcl_Close(interp, chan);
00962     Tcl_AppendObjToObj(resultPtr, Tcl_GetObjResult(interp));
00963 
00964     /*
00965      * If the last character of the result is a newline, then remove the
00966      * newline character.
00967      */
00968 
00969     if (keepNewline == 0) {
00970         string = TclGetStringFromObj(resultPtr, &length);
00971         if ((length > 0) && (string[length - 1] == '\n')) {
00972             Tcl_SetObjLength(resultPtr, length - 1);
00973         }
00974     }
00975     Tcl_SetObjResult(interp, resultPtr);
00976 
00977     return result;
00978 }
00979 
00980 /*
00981  *---------------------------------------------------------------------------
00982  *
00983  * Tcl_FblockedObjCmd --
00984  *
00985  *      This function is invoked to process the Tcl "fblocked" command. See
00986  *      the user documentation for details on what it does.
00987  *
00988  * Results:
00989  *      A standard Tcl result.
00990  *
00991  * Side effects:
00992  *      Sets interp's result to boolean true or false depending on whether the
00993  *      preceeding input operation on the channel would have blocked.
00994  *
00995  *---------------------------------------------------------------------------
00996  */
00997 
00998         /* ARGSUSED */
00999 int
01000 Tcl_FblockedObjCmd(
01001     ClientData unused,          /* Not used. */
01002     Tcl_Interp *interp,         /* Current interpreter. */
01003     int objc,                   /* Number of arguments. */
01004     Tcl_Obj *const objv[])      /* Argument objects. */
01005 {
01006     Tcl_Channel chan;
01007     int mode;
01008 
01009     if (objc != 2) {
01010         Tcl_WrongNumArgs(interp, 1, objv, "channelId");
01011         return TCL_ERROR;
01012     }
01013 
01014     if (TclGetChannelFromObj(interp, objv[1], &chan, &mode, 0) != TCL_OK) {
01015         return TCL_ERROR;
01016     }
01017     if ((mode & TCL_READABLE) == 0) {
01018         Tcl_AppendResult(interp, "channel \"", TclGetString(objv[1]),
01019                 "\" wasn't opened for reading", NULL);
01020         return TCL_ERROR;
01021     }
01022 
01023     Tcl_SetObjResult(interp, Tcl_NewBooleanObj(Tcl_InputBlocked(chan)));
01024     return TCL_OK;
01025 }
01026 
01027 /*
01028  *----------------------------------------------------------------------
01029  *
01030  * Tcl_OpenObjCmd --
01031  *
01032  *      This function is invoked to process the "open" Tcl command. See the
01033  *      user documentation for details on what it does.
01034  *
01035  * Results:
01036  *      A standard Tcl result.
01037  *
01038  * Side effects:
01039  *      See the user documentation.
01040  *
01041  *----------------------------------------------------------------------
01042  */
01043 
01044         /* ARGSUSED */
01045 int
01046 Tcl_OpenObjCmd(
01047     ClientData notUsed,         /* Not used. */
01048     Tcl_Interp *interp,         /* Current interpreter. */
01049     int objc,                   /* Number of arguments. */
01050     Tcl_Obj *const objv[])      /* Argument objects. */
01051 {
01052     int pipeline, prot;
01053     const char *modeString, *what;
01054     Tcl_Channel chan;
01055 
01056     if ((objc < 2) || (objc > 4)) {
01057         Tcl_WrongNumArgs(interp, 1, objv, "fileName ?access? ?permissions?");
01058         return TCL_ERROR;
01059     }
01060     prot = 0666;
01061     if (objc == 2) {
01062         modeString = "r";
01063     } else {
01064         modeString = TclGetString(objv[2]);
01065         if (objc == 4) {
01066             char *permString = TclGetString(objv[3]);
01067             int code = TCL_ERROR;
01068             int scanned = TclParseAllWhiteSpace(permString, -1);
01069 
01070             /* Support legacy octal numbers */
01071             if ((permString[scanned] == '0')
01072                     && (permString[scanned+1] >= '0')
01073                     && (permString[scanned+1] <= '7')) {
01074 
01075                 Tcl_Obj *permObj;
01076 
01077                 TclNewLiteralStringObj(permObj, "0o");
01078                 Tcl_AppendToObj(permObj, permString+scanned+1, -1);
01079                 code = TclGetIntFromObj(NULL, permObj, &prot);
01080                 Tcl_DecrRefCount(permObj);
01081             }
01082 
01083             if ((code == TCL_ERROR)
01084                     && TclGetIntFromObj(interp, objv[3], &prot) != TCL_OK) {
01085                 return TCL_ERROR;
01086             }
01087         }
01088     }
01089 
01090     pipeline = 0;
01091     what = TclGetString(objv[1]);
01092     if (what[0] == '|') {
01093         pipeline = 1;
01094     }
01095 
01096     /*
01097      * Open the file or create a process pipeline.
01098      */
01099 
01100     if (!pipeline) {
01101         chan = Tcl_FSOpenFileChannel(interp, objv[1], modeString, prot);
01102     } else {
01103         int mode, seekFlag, cmdObjc, binary;
01104         const char **cmdArgv;
01105 
01106         if (Tcl_SplitList(interp, what+1, &cmdObjc, &cmdArgv) != TCL_OK) {
01107             return TCL_ERROR;
01108         }
01109 
01110         mode = TclGetOpenModeEx(interp, modeString, &seekFlag, &binary);
01111         if (mode == -1) {
01112             chan = NULL;
01113         } else {
01114             int flags = TCL_STDERR | TCL_ENFORCE_MODE;
01115 
01116             switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
01117             case O_RDONLY:
01118                 flags |= TCL_STDOUT;
01119                 break;
01120             case O_WRONLY:
01121                 flags |= TCL_STDIN;
01122                 break;
01123             case O_RDWR:
01124                 flags |= (TCL_STDIN | TCL_STDOUT);
01125                 break;
01126             default:
01127                 Tcl_Panic("Tcl_OpenCmd: invalid mode value");
01128                 break;
01129             }
01130             chan = Tcl_OpenCommandChannel(interp, cmdObjc, cmdArgv, flags);
01131             if (binary) {
01132                 Tcl_SetChannelOption(interp, chan, "-translation", "binary");
01133             }
01134         }
01135         ckfree((char *) cmdArgv);
01136     }
01137     if (chan == NULL) {
01138         return TCL_ERROR;
01139     }
01140     Tcl_RegisterChannel(interp, chan);
01141     Tcl_AppendResult(interp, Tcl_GetChannelName(chan), NULL);
01142     return TCL_OK;
01143 }
01144 
01145 /*
01146  *----------------------------------------------------------------------
01147  *
01148  * TcpAcceptCallbacksDeleteProc --
01149  *
01150  *      Assocdata cleanup routine called when an interpreter is being deleted
01151  *      to set the interp field of all the accept callback records registered
01152  *      with the interpreter to NULL. This will prevent the interpreter from
01153  *      being used in the future to eval accept scripts.
01154  *
01155  * Results:
01156  *      None.
01157  *
01158  * Side effects:
01159  *      Deallocates memory and sets the interp field of all the accept
01160  *      callback records to NULL to prevent this interpreter from being used
01161  *      subsequently to eval accept scripts.
01162  *
01163  *----------------------------------------------------------------------
01164  */
01165 
01166         /* ARGSUSED */
01167 static void
01168 TcpAcceptCallbacksDeleteProc(
01169     ClientData clientData,      /* Data which was passed when the assocdata
01170                                  * was registered. */
01171     Tcl_Interp *interp)         /* Interpreter being deleted - not used. */
01172 {
01173     Tcl_HashTable *hTblPtr = clientData;
01174     Tcl_HashEntry *hPtr;
01175     Tcl_HashSearch hSearch;
01176 
01177     for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);
01178             hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) {
01179         AcceptCallback *acceptCallbackPtr = Tcl_GetHashValue(hPtr);
01180 
01181         acceptCallbackPtr->interp = NULL;
01182     }
01183     Tcl_DeleteHashTable(hTblPtr);
01184     ckfree((char *) hTblPtr);
01185 }
01186 
01187 /*
01188  *----------------------------------------------------------------------
01189  *
01190  * RegisterTcpServerInterpCleanup --
01191  *
01192  *      Registers an accept callback record to have its interp field set to
01193  *      NULL when the interpreter is deleted.
01194  *
01195  * Results:
01196  *      None.
01197  *
01198  * Side effects:
01199  *      When, in the future, the interpreter is deleted, the interp field of
01200  *      the accept callback data structure will be set to NULL. This will
01201  *      prevent attempts to eval the accept script in a deleted interpreter.
01202  *
01203  *----------------------------------------------------------------------
01204  */
01205 
01206 static void
01207 RegisterTcpServerInterpCleanup(
01208     Tcl_Interp *interp,         /* Interpreter for which we want to be
01209                                  * informed of deletion. */
01210     AcceptCallback *acceptCallbackPtr)
01211                                 /* The accept callback record whose interp
01212                                  * field we want set to NULL when the
01213                                  * interpreter is deleted. */
01214 {
01215     Tcl_HashTable *hTblPtr;     /* Hash table for accept callback records to
01216                                  * smash when the interpreter will be
01217                                  * deleted. */
01218     Tcl_HashEntry *hPtr;        /* Entry for this record. */
01219     int isNew;                  /* Is the entry new? */
01220 
01221     hTblPtr = (Tcl_HashTable *)
01222             Tcl_GetAssocData(interp, "tclTCPAcceptCallbacks", NULL);
01223 
01224     if (hTblPtr == NULL) {
01225         hTblPtr = (Tcl_HashTable *) ckalloc((unsigned) sizeof(Tcl_HashTable));
01226         Tcl_InitHashTable(hTblPtr, TCL_ONE_WORD_KEYS);
01227         (void) Tcl_SetAssocData(interp, "tclTCPAcceptCallbacks",
01228                 TcpAcceptCallbacksDeleteProc, hTblPtr);
01229     }
01230 
01231     hPtr = Tcl_CreateHashEntry(hTblPtr, (char *) acceptCallbackPtr, &isNew);
01232     if (!isNew) {
01233         Tcl_Panic("RegisterTcpServerCleanup: damaged accept record table");
01234     }
01235     Tcl_SetHashValue(hPtr, acceptCallbackPtr);
01236 }
01237 
01238 /*
01239  *----------------------------------------------------------------------
01240  *
01241  * UnregisterTcpServerInterpCleanupProc --
01242  *
01243  *      Unregister a previously registered accept callback record. The interp
01244  *      field of this record will no longer be set to NULL in the future when
01245  *      the interpreter is deleted.
01246  *
01247  * Results:
01248  *      None.
01249  *
01250  * Side effects:
01251  *      Prevents the interp field of the accept callback record from being set
01252  *      to NULL in the future when the interpreter is deleted.
01253  *
01254  *----------------------------------------------------------------------
01255  */
01256 
01257 static void
01258 UnregisterTcpServerInterpCleanupProc(
01259     Tcl_Interp *interp,         /* Interpreter in which the accept callback
01260                                  * record was registered. */
01261     AcceptCallback *acceptCallbackPtr)
01262                                 /* The record for which to delete the
01263                                  * registration. */
01264 {
01265     Tcl_HashTable *hTblPtr;
01266     Tcl_HashEntry *hPtr;
01267 
01268     hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(interp,
01269             "tclTCPAcceptCallbacks", NULL);
01270     if (hTblPtr == NULL) {
01271         return;
01272     }
01273 
01274     hPtr = Tcl_FindHashEntry(hTblPtr, (char *) acceptCallbackPtr);
01275     if (hPtr != NULL) {
01276         Tcl_DeleteHashEntry(hPtr);
01277     }
01278 }
01279 
01280 /*
01281  *----------------------------------------------------------------------
01282  *
01283  * AcceptCallbackProc --
01284  *
01285  *      This callback is invoked by the TCP channel driver when it accepts a
01286  *      new connection from a client on a server socket.
01287  *
01288  * Results:
01289  *      None.
01290  *
01291  * Side effects:
01292  *      Whatever the script does.
01293  *
01294  *----------------------------------------------------------------------
01295  */
01296 
01297 static void
01298 AcceptCallbackProc(
01299     ClientData callbackData,    /* The data stored when the callback was
01300                                  * created in the call to
01301                                  * Tcl_OpenTcpServer. */
01302     Tcl_Channel chan,           /* Channel for the newly accepted
01303                                  * connection. */
01304     char *address,              /* Address of client that was accepted. */
01305     int port)                   /* Port of client that was accepted. */
01306 {
01307     AcceptCallback *acceptCallbackPtr = (AcceptCallback *) callbackData;
01308 
01309     /*
01310      * Check if the callback is still valid; the interpreter may have gone
01311      * away, this is signalled by setting the interp field of the callback
01312      * data to NULL.
01313      */
01314 
01315     if (acceptCallbackPtr->interp != NULL) {
01316         char portBuf[TCL_INTEGER_SPACE];
01317         char *script = acceptCallbackPtr->script;
01318         Tcl_Interp *interp = acceptCallbackPtr->interp;
01319         int result;
01320 
01321         Tcl_Preserve(script);
01322         Tcl_Preserve(interp);
01323 
01324         TclFormatInt(portBuf, port);
01325         Tcl_RegisterChannel(interp, chan);
01326 
01327         /*
01328          * Artificially bump the refcount to protect the channel from being
01329          * deleted while the script is being evaluated.
01330          */
01331 
01332         Tcl_RegisterChannel(NULL, chan);
01333 
01334         result = Tcl_VarEval(interp, script, " ", Tcl_GetChannelName(chan),
01335                 " ", address, " ", portBuf, NULL);
01336         if (result != TCL_OK) {
01337             TclBackgroundException(interp, result);
01338             Tcl_UnregisterChannel(interp, chan);
01339         }
01340 
01341         /*
01342          * Decrement the artificially bumped refcount. After this it is not
01343          * safe anymore to use "chan", because it may now be deleted.
01344          */
01345 
01346         Tcl_UnregisterChannel(NULL, chan);
01347 
01348         Tcl_Release(interp);
01349         Tcl_Release(script);
01350     } else {
01351         /*
01352          * The interpreter has been deleted, so there is no useful way to
01353          * utilize the client socket - just close it.
01354          */
01355 
01356         Tcl_Close(NULL, chan);
01357     }
01358 }
01359 
01360 /*
01361  *----------------------------------------------------------------------
01362  *
01363  * TcpServerCloseProc --
01364  *
01365  *      This callback is called when the TCP server channel for which it was
01366  *      registered is being closed. It informs the interpreter in which the
01367  *      accept script is evaluated (if that interpreter still exists) that
01368  *      this channel no longer needs to be informed if the interpreter is
01369  *      deleted.
01370  *
01371  * Results:
01372  *      None.
01373  *
01374  * Side effects:
01375  *      In the future, if the interpreter is deleted this channel will no
01376  *      longer be informed.
01377  *
01378  *----------------------------------------------------------------------
01379  */
01380 
01381 static void
01382 TcpServerCloseProc(
01383     ClientData callbackData)    /* The data passed in the call to
01384                                  * Tcl_CreateCloseHandler. */
01385 {
01386     AcceptCallback *acceptCallbackPtr = (AcceptCallback *) callbackData;
01387                                 /* The actual data. */
01388 
01389     if (acceptCallbackPtr->interp != NULL) {
01390         UnregisterTcpServerInterpCleanupProc(acceptCallbackPtr->interp,
01391                 acceptCallbackPtr);
01392     }
01393     Tcl_EventuallyFree(acceptCallbackPtr->script, TCL_DYNAMIC);
01394     ckfree((char *) acceptCallbackPtr);
01395 }
01396 
01397 /*
01398  *----------------------------------------------------------------------
01399  *
01400  * Tcl_SocketObjCmd --
01401  *
01402  *      This function is invoked to process the "socket" Tcl command. See the
01403  *      user documentation for details on what it does.
01404  *
01405  * Results:
01406  *      A standard Tcl result.
01407  *
01408  * Side effects:
01409  *      Creates a socket based channel.
01410  *
01411  *----------------------------------------------------------------------
01412  */
01413 
01414 int
01415 Tcl_SocketObjCmd(
01416     ClientData notUsed,         /* Not used. */
01417     Tcl_Interp *interp,         /* Current interpreter. */
01418     int objc,                   /* Number of arguments. */
01419     Tcl_Obj *const objv[])      /* Argument objects. */
01420 {
01421     static const char *socketOptions[] = {
01422         "-async", "-myaddr", "-myport","-server", NULL
01423     };
01424     enum socketOptions {
01425         SKT_ASYNC, SKT_MYADDR, SKT_MYPORT, SKT_SERVER
01426     };
01427     int optionIndex, a, server = 0, port, myport = 0, async = 0;
01428     char *host, *script = NULL, *myaddr = NULL;
01429     Tcl_Channel chan;
01430 
01431     if (TclpHasSockets(interp) != TCL_OK) {
01432         return TCL_ERROR;
01433     }
01434 
01435     for (a = 1; a < objc; a++) {
01436         const char *arg = Tcl_GetString(objv[a]);
01437 
01438         if (arg[0] != '-') {
01439             break;
01440         }
01441         if (Tcl_GetIndexFromObj(interp, objv[a], socketOptions, "option",
01442                 TCL_EXACT, &optionIndex) != TCL_OK) {
01443             return TCL_ERROR;
01444         }
01445         switch ((enum socketOptions) optionIndex) {
01446         case SKT_ASYNC:
01447             if (server == 1) {
01448                 Tcl_AppendResult(interp,
01449                         "cannot set -async option for server sockets", NULL);
01450                 return TCL_ERROR;
01451             }
01452             async = 1;
01453             break;
01454         case SKT_MYADDR:
01455             a++;
01456             if (a >= objc) {
01457                 Tcl_AppendResult(interp,
01458                         "no argument given for -myaddr option", NULL);
01459                 return TCL_ERROR;
01460             }
01461             myaddr = TclGetString(objv[a]);
01462             break;
01463         case SKT_MYPORT: {
01464             char *myPortName;
01465 
01466             a++;
01467             if (a >= objc) {
01468                 Tcl_AppendResult(interp,
01469                         "no argument given for -myport option", NULL);
01470                 return TCL_ERROR;
01471             }
01472             myPortName = TclGetString(objv[a]);
01473             if (TclSockGetPort(interp, myPortName, "tcp", &myport) != TCL_OK) {
01474                 return TCL_ERROR;
01475             }
01476             break;
01477         }
01478         case SKT_SERVER:
01479             if (async == 1) {
01480                 Tcl_AppendResult(interp,
01481                         "cannot set -async option for server sockets", NULL);
01482                 return TCL_ERROR;
01483             }
01484             server = 1;
01485             a++;
01486             if (a >= objc) {
01487                 Tcl_AppendResult(interp,
01488                         "no argument given for -server option", NULL);
01489                 return TCL_ERROR;
01490             }
01491             script = TclGetString(objv[a]);
01492             break;
01493         default:
01494             Tcl_Panic("Tcl_SocketObjCmd: bad option index to SocketOptions");
01495         }
01496     }
01497     if (server) {
01498         host = myaddr;          /* NULL implies INADDR_ANY */
01499         if (myport != 0) {
01500             Tcl_AppendResult(interp, "option -myport is not valid for servers",
01501                     NULL);
01502             return TCL_ERROR;
01503         }
01504     } else if (a < objc) {
01505         host = TclGetString(objv[a]);
01506         a++;
01507     } else {
01508         Interp *iPtr;
01509 
01510     wrongNumArgs:
01511         iPtr = (Interp *) interp;
01512         Tcl_WrongNumArgs(interp, 1, objv,
01513                 "?-myaddr addr? ?-myport myport? ?-async? host port");
01514         iPtr->flags |= INTERP_ALTERNATE_WRONG_ARGS;
01515         Tcl_WrongNumArgs(interp, 1, objv,
01516                 "-server command ?-myaddr addr? port");
01517         iPtr->flags &= ~INTERP_ALTERNATE_WRONG_ARGS;
01518         return TCL_ERROR;
01519     }
01520 
01521     if (a == objc-1) {
01522         if (TclSockGetPort(interp, TclGetString(objv[a]), "tcp",
01523                 &port) != TCL_OK) {
01524             return TCL_ERROR;
01525         }
01526     } else {
01527         goto wrongNumArgs;
01528     }
01529 
01530     if (server) {
01531         AcceptCallback *acceptCallbackPtr = (AcceptCallback *)
01532                 ckalloc((unsigned) sizeof(AcceptCallback));
01533         unsigned len = strlen(script) + 1;
01534         char *copyScript = ckalloc(len);
01535 
01536         memcpy(copyScript, script, len);
01537         acceptCallbackPtr->script = copyScript;
01538         acceptCallbackPtr->interp = interp;
01539         chan = Tcl_OpenTcpServer(interp, port, host, AcceptCallbackProc,
01540                 acceptCallbackPtr);
01541         if (chan == NULL) {
01542             ckfree(copyScript);
01543             ckfree((char *) acceptCallbackPtr);
01544             return TCL_ERROR;
01545         }
01546 
01547         /*
01548          * Register with the interpreter to let us know when the interpreter
01549          * is deleted (by having the callback set the interp field of the
01550          * acceptCallbackPtr's structure to NULL). This is to avoid trying to
01551          * eval the script in a deleted interpreter.
01552          */
01553 
01554         RegisterTcpServerInterpCleanup(interp, acceptCallbackPtr);
01555 
01556         /*
01557          * Register a close callback. This callback will inform the
01558          * interpreter (if it still exists) that this channel does not need to
01559          * be informed when the interpreter is deleted.
01560          */
01561 
01562         Tcl_CreateCloseHandler(chan, TcpServerCloseProc, acceptCallbackPtr);
01563     } else {
01564         chan = Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async);
01565         if (chan == NULL) {
01566             return TCL_ERROR;
01567         }
01568     }
01569     Tcl_RegisterChannel(interp, chan);
01570     Tcl_AppendResult(interp, Tcl_GetChannelName(chan), NULL);
01571 
01572     return TCL_OK;
01573 }
01574 
01575 /*
01576  *----------------------------------------------------------------------
01577  *
01578  * Tcl_FcopyObjCmd --
01579  *
01580  *      This function is invoked to process the "fcopy" Tcl command. See the
01581  *      user documentation for details on what it does.
01582  *
01583  * Results:
01584  *      A standard Tcl result.
01585  *
01586  * Side effects:
01587  *      Moves data between two channels and possibly sets up a background copy
01588  *      handler.
01589  *
01590  *----------------------------------------------------------------------
01591  */
01592 
01593 int
01594 Tcl_FcopyObjCmd(
01595     ClientData dummy,           /* Not used. */
01596     Tcl_Interp *interp,         /* Current interpreter. */
01597     int objc,                   /* Number of arguments. */
01598     Tcl_Obj *const objv[])      /* Argument objects. */
01599 {
01600     Tcl_Channel inChan, outChan;
01601     int mode, i, toRead, index;
01602     Tcl_Obj *cmdPtr;
01603     static const char* switches[] = { "-size", "-command", NULL };
01604     enum { FcopySize, FcopyCommand };
01605 
01606     if ((objc < 3) || (objc > 7) || (objc == 4) || (objc == 6)) {
01607         Tcl_WrongNumArgs(interp, 1, objv,
01608                 "input output ?-size size? ?-command callback?");
01609         return TCL_ERROR;
01610     }
01611 
01612     /*
01613      * Parse the channel arguments and verify that they are readable or
01614      * writable, as appropriate.
01615      */
01616 
01617     if (TclGetChannelFromObj(interp, objv[1], &inChan, &mode, 0) != TCL_OK) {
01618         return TCL_ERROR;
01619     }
01620     if ((mode & TCL_READABLE) == 0) {
01621         Tcl_AppendResult(interp, "channel \"", TclGetString(objv[1]),
01622                 "\" wasn't opened for reading", NULL);
01623         return TCL_ERROR;
01624     }
01625     if (TclGetChannelFromObj(interp, objv[2], &outChan, &mode, 0) != TCL_OK) {
01626         return TCL_ERROR;
01627     }
01628     if ((mode & TCL_WRITABLE) == 0) {
01629         Tcl_AppendResult(interp, "channel \"", TclGetString(objv[2]),
01630                 "\" wasn't opened for writing", NULL);
01631         return TCL_ERROR;
01632     }
01633 
01634     toRead = -1;
01635     cmdPtr = NULL;
01636     for (i = 3; i < objc; i += 2) {
01637         if (Tcl_GetIndexFromObj(interp, objv[i], switches, "switch", 0,
01638                 &index) != TCL_OK) {
01639             return TCL_ERROR;
01640         }
01641         switch (index) {
01642         case FcopySize:
01643             if (TclGetIntFromObj(interp, objv[i+1], &toRead) != TCL_OK) {
01644                 return TCL_ERROR;
01645             }
01646             break;
01647         case FcopyCommand:
01648             cmdPtr = objv[i+1];
01649             break;
01650         }
01651     }
01652 
01653     return TclCopyChannel(interp, inChan, outChan, toRead, cmdPtr);
01654 }
01655 
01656 /*
01657  *---------------------------------------------------------------------------
01658  *
01659  * ChanPendingObjCmd --
01660  *
01661  *      This function is invoked to process the Tcl "chan pending" command
01662  *      (TIP #287). See the user documentation for details on what it does.
01663  *
01664  * Results:
01665  *      A standard Tcl result.
01666  *
01667  * Side effects:
01668  *      Sets interp's result to the number of bytes of buffered input or
01669  *      output (depending on whether the first argument is "input" or
01670  *      "output"), or -1 if the channel wasn't opened for that mode.
01671  *
01672  *---------------------------------------------------------------------------
01673  */
01674 
01675         /* ARGSUSED */
01676 static int
01677 ChanPendingObjCmd(
01678     ClientData unused,          /* Not used. */
01679     Tcl_Interp *interp,         /* Current interpreter. */
01680     int objc,                   /* Number of arguments. */
01681     Tcl_Obj *const objv[])      /* Argument objects. */
01682 {
01683     Tcl_Channel chan;
01684     int index, mode;
01685     static const char *options[] = {"input", "output", NULL};
01686     enum options {PENDING_INPUT, PENDING_OUTPUT};
01687 
01688     if (objc != 3) {
01689         Tcl_WrongNumArgs(interp, 1, objv, "mode channelId");
01690         return TCL_ERROR;
01691     }
01692 
01693     if (Tcl_GetIndexFromObj(interp, objv[1], options, "mode", 0,
01694             &index) != TCL_OK) {
01695         return TCL_ERROR;
01696     }
01697 
01698     if (TclGetChannelFromObj(interp, objv[2], &chan, &mode, 0) != TCL_OK) {
01699         return TCL_ERROR;
01700     }
01701 
01702     switch ((enum options) index) {
01703     case PENDING_INPUT:
01704         if ((mode & TCL_READABLE) == 0) {
01705             Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
01706         } else {
01707             Tcl_SetObjResult(interp, Tcl_NewIntObj(Tcl_InputBuffered(chan)));
01708         }
01709         break;
01710     case PENDING_OUTPUT:
01711         if ((mode & TCL_WRITABLE) == 0) {
01712             Tcl_SetObjResult(interp, Tcl_NewIntObj(-1));
01713         } else {
01714             Tcl_SetObjResult(interp, Tcl_NewIntObj(Tcl_OutputBuffered(chan)));
01715         }
01716         break;
01717     }
01718     return TCL_OK;
01719 }
01720 
01721 /*
01722  *----------------------------------------------------------------------
01723  *
01724  * ChanTruncateObjCmd --
01725  *
01726  *      This function is invoked to process the "chan truncate" Tcl command.
01727  *      See the user documentation for details on what it does.
01728  *
01729  * Results:
01730  *      A standard Tcl result.
01731  *
01732  * Side effects:
01733  *      Truncates a channel (or rather a file underlying a channel).
01734  *
01735  *----------------------------------------------------------------------
01736  */
01737 
01738 static int
01739 ChanTruncateObjCmd(
01740     ClientData dummy,           /* Not used. */
01741     Tcl_Interp *interp,         /* Current interpreter. */
01742     int objc,                   /* Number of arguments. */
01743     Tcl_Obj *const objv[])      /* Argument objects. */
01744 {
01745     Tcl_Channel chan;
01746     Tcl_WideInt length;
01747 
01748     if ((objc < 2) || (objc > 3)) {
01749         Tcl_WrongNumArgs(interp, 1, objv, "channelId ?length?");
01750         return TCL_ERROR;
01751     }
01752     if (TclGetChannelFromObj(interp, objv[1], &chan, NULL, 0) != TCL_OK) {
01753         return TCL_ERROR;
01754     }
01755 
01756     if (objc == 3) {
01757         /*
01758          * User is supplying an explicit length.
01759          */
01760 
01761         if (Tcl_GetWideIntFromObj(interp, objv[2], &length) != TCL_OK) {
01762             return TCL_ERROR;
01763         }
01764         if (length < 0) {
01765             Tcl_AppendResult(interp,
01766                     "cannot truncate to negative length of file", NULL);
01767             return TCL_ERROR;
01768         }
01769     } else {
01770         /*
01771          * User wants to truncate to the current file position.
01772          */
01773 
01774         length = Tcl_Tell(chan);
01775         if (length == Tcl_WideAsLong(-1)) {
01776             Tcl_AppendResult(interp,
01777                     "could not determine current location in \"",
01778                     TclGetString(objv[1]), "\": ",
01779                     Tcl_PosixError(interp), NULL);
01780             return TCL_ERROR;
01781         }
01782     }
01783 
01784     if (Tcl_TruncateChannel(chan, length) != TCL_OK) {
01785         Tcl_AppendResult(interp, "error during truncate on \"",
01786                 TclGetString(objv[1]), "\": ",
01787                 Tcl_PosixError(interp), NULL);
01788         return TCL_ERROR;
01789     }
01790 
01791     return TCL_OK;
01792 }
01793 
01794 /*
01795  *----------------------------------------------------------------------
01796  *
01797  * TclInitChanCmd --
01798  *
01799  *      This function is invoked to create the "chan" Tcl command. See the
01800  *      user documentation for details on what it does.
01801  *
01802  * Results:
01803  *      A Tcl command handle.
01804  *
01805  * Side effects:
01806  *      None (since nothing is byte-compiled).
01807  *
01808  *----------------------------------------------------------------------
01809  */
01810 
01811 Tcl_Command
01812 TclInitChanCmd(
01813     Tcl_Interp *interp)
01814 {
01815     /*
01816      * Most commands are plugged directly together, but some are done via
01817      * alias-like rewriting; [chan configure] is this way for security reasons
01818      * (want overwriting of [fconfigure] to control that nicely), and [chan
01819      * names] because the functionality isn't available as a separate command
01820      * function at the moment.
01821      */
01822     static const EnsembleImplMap initMap[] = {
01823         {"blocked",     Tcl_FblockedObjCmd},
01824         {"close",       Tcl_CloseObjCmd},
01825         {"copy",        Tcl_FcopyObjCmd},
01826         {"create",      TclChanCreateObjCmd},           /* TIP #219 */
01827         {"eof",         Tcl_EofObjCmd},
01828         {"event",       Tcl_FileEventObjCmd},
01829         {"flush",       Tcl_FlushObjCmd},
01830         {"gets",        Tcl_GetsObjCmd},
01831         {"pending",     ChanPendingObjCmd},             /* TIP #287 */
01832         {"postevent",   TclChanPostEventObjCmd},        /* TIP #219 */
01833         {"puts",        Tcl_PutsObjCmd},
01834         {"read",        Tcl_ReadObjCmd},
01835         {"seek",        Tcl_SeekObjCmd},
01836         {"tell",        Tcl_TellObjCmd},
01837         {"truncate",    ChanTruncateObjCmd},            /* TIP #208 */
01838         {NULL}
01839     };
01840     static const char *extras[] = {
01841         "configure",    "::fconfigure",
01842         "names",        "::file channels",
01843         NULL
01844     };
01845     Tcl_Command ensemble;
01846     Tcl_Obj *mapObj;
01847     int i;
01848 
01849     ensemble = TclMakeEnsemble(interp, "chan", initMap);
01850     Tcl_GetEnsembleMappingDict(NULL, ensemble, &mapObj);
01851     for (i=0 ; extras[i] ; i+=2) {
01852         /*
01853          * Can assume that reference counts are all incremented.
01854          */
01855 
01856         Tcl_DictObjPut(NULL, mapObj, Tcl_NewStringObj(extras[i], -1),
01857                 Tcl_NewStringObj(extras[i+1], -1));
01858     }
01859     Tcl_SetEnsembleMappingDict(interp, ensemble, mapObj);
01860     return ensemble;
01861 }
01862 
01863 /*
01864  * Local Variables:
01865  * mode: c
01866  * c-basic-offset: 4
01867  * fill-column: 78
01868  * End:
01869  */



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