tclIOCmd.cGo 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 1.5.1 |