tclInterp.cGo to the documentation of this file.00001 /* 00002 * tclInterp.c -- 00003 * 00004 * This file implements the "interp" command which allows creation and 00005 * manipulation of Tcl interpreters from within Tcl scripts. 00006 * 00007 * Copyright (c) 1995-1997 Sun Microsystems, Inc. 00008 * Copyright (c) 2004 Donal K. Fellows 00009 * 00010 * See the file "license.terms" for information on usage and redistribution 00011 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 00012 * 00013 * RCS: @(#) $Id: tclInterp.c,v 1.83 2008/01/30 10:45:55 msofer Exp $ 00014 */ 00015 00016 #include "tclInt.h" 00017 00018 /* 00019 * A pointer to a string that holds an initialization script that if non-NULL 00020 * is evaluated in Tcl_Init() prior to the built-in initialization script 00021 * above. This variable can be modified by the function below. 00022 */ 00023 00024 static char *tclPreInitScript = NULL; 00025 00026 /* Forward declaration */ 00027 struct Target; 00028 00029 /* 00030 * struct Alias: 00031 * 00032 * Stores information about an alias. Is stored in the slave interpreter and 00033 * used by the source command to find the target command in the master when 00034 * the source command is invoked. 00035 */ 00036 00037 typedef struct Alias { 00038 Tcl_Obj *token; /* Token for the alias command in the slave 00039 * interp. This used to be the command name in 00040 * the slave when the alias was first 00041 * created. */ 00042 Tcl_Interp *targetInterp; /* Interp in which target command will be 00043 * invoked. */ 00044 Tcl_Command slaveCmd; /* Source command in slave interpreter, bound 00045 * to command that invokes the target command 00046 * in the target interpreter. */ 00047 Tcl_HashEntry *aliasEntryPtr; 00048 /* Entry for the alias hash table in slave. 00049 * This is used by alias deletion to remove 00050 * the alias from the slave interpreter alias 00051 * table. */ 00052 struct Target *targetPtr; /* Entry for target command in master. This is 00053 * used in the master interpreter to map back 00054 * from the target command to aliases 00055 * redirecting to it. */ 00056 int objc; /* Count of Tcl_Obj in the prefix of the 00057 * target command to be invoked in the target 00058 * interpreter. Additional arguments specified 00059 * when calling the alias in the slave interp 00060 * will be appended to the prefix before the 00061 * command is invoked. */ 00062 Tcl_Obj *objPtr; /* The first actual prefix object - the target 00063 * command name; this has to be at the end of 00064 * the structure, which will be extended to 00065 * accomodate the remaining objects in the 00066 * prefix. */ 00067 } Alias; 00068 00069 /* 00070 * 00071 * struct Slave: 00072 * 00073 * Used by the "interp" command to record and find information about slave 00074 * interpreters. Maps from a command name in the master to information about a 00075 * slave interpreter, e.g. what aliases are defined in it. 00076 */ 00077 00078 typedef struct Slave { 00079 Tcl_Interp *masterInterp; /* Master interpreter for this slave. */ 00080 Tcl_HashEntry *slaveEntryPtr; 00081 /* Hash entry in masters slave table for this 00082 * slave interpreter. Used to find this 00083 * record, and used when deleting the slave 00084 * interpreter to delete it from the master's 00085 * table. */ 00086 Tcl_Interp *slaveInterp; /* The slave interpreter. */ 00087 Tcl_Command interpCmd; /* Interpreter object command. */ 00088 Tcl_HashTable aliasTable; /* Table which maps from names of commands in 00089 * slave interpreter to struct Alias defined 00090 * below. */ 00091 } Slave; 00092 00093 /* 00094 * struct Target: 00095 * 00096 * Maps from master interpreter commands back to the source commands in slave 00097 * interpreters. This is needed because aliases can be created between sibling 00098 * interpreters and must be deleted when the target interpreter is deleted. In 00099 * case they would not be deleted the source interpreter would be left with a 00100 * "dangling pointer". One such record is stored in the Master record of the 00101 * master interpreter with the master for each alias which directs to a 00102 * command in the master. These records are used to remove the source command 00103 * for an from a slave if/when the master is deleted. They are organized in a 00104 * doubly-linked list attached to the master interpreter. 00105 */ 00106 00107 typedef struct Target { 00108 Tcl_Command slaveCmd; /* Command for alias in slave interp. */ 00109 Tcl_Interp *slaveInterp; /* Slave Interpreter. */ 00110 struct Target *nextPtr; /* Next in list of target records, or NULL if 00111 * at the end of the list of targets. */ 00112 struct Target *prevPtr; /* Previous in list of target records, or NULL 00113 * if at the start of the list of targets. */ 00114 } Target; 00115 00116 /* 00117 * struct Master: 00118 * 00119 * This record is used for two purposes: First, slaveTable (a hashtable) maps 00120 * from names of commands to slave interpreters. This hashtable is used to 00121 * store information about slave interpreters of this interpreter, to map over 00122 * all slaves, etc. The second purpose is to store information about all 00123 * aliases in slaves (or siblings) which direct to target commands in this 00124 * interpreter (using the targetsPtr doubly-linked list). 00125 * 00126 * NB: the flags field in the interp structure, used with SAFE_INTERP mask 00127 * denotes whether the interpreter is safe or not. Safe interpreters have 00128 * restricted functionality, can only create safe slave interpreters and can 00129 * only load safe extensions. 00130 */ 00131 00132 typedef struct Master { 00133 Tcl_HashTable slaveTable; /* Hash table for slave interpreters. Maps 00134 * from command names to Slave records. */ 00135 Target *targetsPtr; /* The head of a doubly-linked list of all the 00136 * target records which denote aliases from 00137 * slaves or sibling interpreters that direct 00138 * to commands in this interpreter. This list 00139 * is used to remove dangling pointers from 00140 * the slave (or sibling) interpreters when 00141 * this interpreter is deleted. */ 00142 } Master; 00143 00144 /* 00145 * The following structure keeps track of all the Master and Slave information 00146 * on a per-interp basis. 00147 */ 00148 00149 typedef struct InterpInfo { 00150 Master master; /* Keeps track of all interps for which this 00151 * interp is the Master. */ 00152 Slave slave; /* Information necessary for this interp to 00153 * function as a slave. */ 00154 } InterpInfo; 00155 00156 /* 00157 * Limit callbacks handled by scripts are modelled as structures which are 00158 * stored in hashes indexed by a two-word key. Note that the type of the 00159 * 'type' field in the key is not int; this is to make sure that things are 00160 * likely to work properly on 64-bit architectures. 00161 */ 00162 00163 typedef struct ScriptLimitCallback { 00164 Tcl_Interp *interp; /* The interpreter in which to execute the 00165 * callback. */ 00166 Tcl_Obj *scriptObj; /* The script to execute to perform the 00167 * user-defined part of the callback. */ 00168 int type; /* What kind of callback is this. */ 00169 Tcl_HashEntry *entryPtr; /* The entry in the hash table maintained by 00170 * the target interpreter that refers to this 00171 * callback record, or NULL if the entry has 00172 * already been deleted from that hash 00173 * table. */ 00174 } ScriptLimitCallback; 00175 00176 typedef struct ScriptLimitCallbackKey { 00177 Tcl_Interp *interp; /* The interpreter that the limit callback was 00178 * attached to. This is not the interpreter 00179 * that the callback runs in! */ 00180 long type; /* The type of callback that this is. */ 00181 } ScriptLimitCallbackKey; 00182 00183 /* 00184 * Prototypes for local static functions: 00185 */ 00186 00187 static int AliasCreate(Tcl_Interp *interp, 00188 Tcl_Interp *slaveInterp, Tcl_Interp *masterInterp, 00189 Tcl_Obj *namePtr, Tcl_Obj *targetPtr, int objc, 00190 Tcl_Obj *const objv[]); 00191 static int AliasDelete(Tcl_Interp *interp, 00192 Tcl_Interp *slaveInterp, Tcl_Obj *namePtr); 00193 static int AliasDescribe(Tcl_Interp *interp, 00194 Tcl_Interp *slaveInterp, Tcl_Obj *objPtr); 00195 static int AliasList(Tcl_Interp *interp, Tcl_Interp *slaveInterp); 00196 static int AliasObjCmd(ClientData dummy, 00197 Tcl_Interp *currentInterp, int objc, 00198 Tcl_Obj *const objv[]); 00199 static void AliasObjCmdDeleteProc(ClientData clientData); 00200 static Tcl_Interp * GetInterp(Tcl_Interp *interp, Tcl_Obj *pathPtr); 00201 static Tcl_Interp * GetInterp2(Tcl_Interp *interp, int objc, 00202 Tcl_Obj *const objv[]); 00203 static void InterpInfoDeleteProc(ClientData clientData, 00204 Tcl_Interp *interp); 00205 static int SlaveBgerror(Tcl_Interp *interp, 00206 Tcl_Interp *slaveInterp, int objc, 00207 Tcl_Obj *const objv[]); 00208 static Tcl_Interp * SlaveCreate(Tcl_Interp *interp, Tcl_Obj *pathPtr, 00209 int safe); 00210 static int SlaveEval(Tcl_Interp *interp, Tcl_Interp *slaveInterp, 00211 int objc, Tcl_Obj *const objv[]); 00212 static int SlaveExpose(Tcl_Interp *interp, 00213 Tcl_Interp *slaveInterp, int objc, 00214 Tcl_Obj *const objv[]); 00215 static int SlaveHide(Tcl_Interp *interp, Tcl_Interp *slaveInterp, 00216 int objc, Tcl_Obj *const objv[]); 00217 static int SlaveHidden(Tcl_Interp *interp, 00218 Tcl_Interp *slaveInterp); 00219 static int SlaveInvokeHidden(Tcl_Interp *interp, 00220 Tcl_Interp *slaveInterp, 00221 const char *namespaceName, 00222 int objc, Tcl_Obj *const objv[]); 00223 static int SlaveMarkTrusted(Tcl_Interp *interp, 00224 Tcl_Interp *slaveInterp); 00225 static int SlaveObjCmd(ClientData dummy, Tcl_Interp *interp, 00226 int objc, Tcl_Obj *const objv[]); 00227 static void SlaveObjCmdDeleteProc(ClientData clientData); 00228 static int SlaveRecursionLimit(Tcl_Interp *interp, 00229 Tcl_Interp *slaveInterp, int objc, 00230 Tcl_Obj *const objv[]); 00231 static int SlaveCommandLimitCmd(Tcl_Interp *interp, 00232 Tcl_Interp *slaveInterp, int consumedObjc, 00233 int objc, Tcl_Obj *const objv[]); 00234 static int SlaveTimeLimitCmd(Tcl_Interp *interp, 00235 Tcl_Interp *slaveInterp, int consumedObjc, 00236 int objc, Tcl_Obj *const objv[]); 00237 static void InheritLimitsFromMaster(Tcl_Interp *slaveInterp, 00238 Tcl_Interp *masterInterp); 00239 static void SetScriptLimitCallback(Tcl_Interp *interp, int type, 00240 Tcl_Interp *targetInterp, Tcl_Obj *scriptObj); 00241 static void CallScriptLimitCallback(ClientData clientData, 00242 Tcl_Interp *interp); 00243 static void DeleteScriptLimitCallback(ClientData clientData); 00244 static void RunLimitHandlers(LimitHandler *handlerPtr, 00245 Tcl_Interp *interp); 00246 static void TimeLimitCallback(ClientData clientData); 00247 00248 /* 00249 *---------------------------------------------------------------------- 00250 * 00251 * TclSetPreInitScript -- 00252 * 00253 * This routine is used to change the value of the internal variable, 00254 * tclPreInitScript. 00255 * 00256 * Results: 00257 * Returns the current value of tclPreInitScript. 00258 * 00259 * Side effects: 00260 * Changes the way Tcl_Init() routine behaves. 00261 * 00262 *---------------------------------------------------------------------- 00263 */ 00264 00265 char * 00266 TclSetPreInitScript( 00267 char *string) /* Pointer to a script. */ 00268 { 00269 char *prevString = tclPreInitScript; 00270 tclPreInitScript = string; 00271 return(prevString); 00272 } 00273 00274 /* 00275 *---------------------------------------------------------------------- 00276 * 00277 * Tcl_Init -- 00278 * 00279 * This function is typically invoked by Tcl_AppInit functions to find 00280 * and source the "init.tcl" script, which should exist somewhere on the 00281 * Tcl library path. 00282 * 00283 * Results: 00284 * Returns a standard Tcl completion code and sets the interp's result if 00285 * there is an error. 00286 * 00287 * Side effects: 00288 * Depends on what's in the init.tcl script. 00289 * 00290 *---------------------------------------------------------------------- 00291 */ 00292 00293 int 00294 Tcl_Init( 00295 Tcl_Interp *interp) /* Interpreter to initialize. */ 00296 { 00297 if (tclPreInitScript != NULL) { 00298 if (Tcl_Eval(interp, tclPreInitScript) == TCL_ERROR) { 00299 return (TCL_ERROR); 00300 }; 00301 } 00302 00303 /* 00304 * In order to find init.tcl during initialization, the following script 00305 * is invoked by Tcl_Init(). It looks in several different directories: 00306 * 00307 * $tcl_library - can specify a primary location, if set, no 00308 * other locations will be checked. This is the 00309 * recommended way for a program that embeds 00310 * Tcl to specifically tell Tcl where to find 00311 * an init.tcl file. 00312 * 00313 * $env(TCL_LIBRARY) - highest priority so user can always override 00314 * the search path unless the application has 00315 * specified an exact directory above 00316 * 00317 * $tclDefaultLibrary - INTERNAL: This variable is set by Tcl on 00318 * those platforms where it can determine at 00319 * runtime the directory where it expects the 00320 * init.tcl file to be. After [tclInit] reads 00321 * and uses this value, it [unset]s it. 00322 * External users of Tcl should not make use of 00323 * the variable to customize [tclInit]. 00324 * 00325 * $tcl_libPath - OBSOLETE: This variable is no longer set by 00326 * Tcl itself, but [tclInit] examines it in 00327 * case some program that embeds Tcl is 00328 * customizing [tclInit] by setting this 00329 * variable to a list of directories in which 00330 * to search. 00331 * 00332 * [tcl::pkgconfig get scriptdir,runtime] 00333 * - the directory determined by configure to be 00334 * the place where Tcl's script library is to 00335 * be installed. 00336 * 00337 * The first directory on this path that contains a valid init.tcl script 00338 * will be set as the value of tcl_library. 00339 * 00340 * Note that this entire search mechanism can be bypassed by defining an 00341 * alternate tclInit command before calling Tcl_Init(). 00342 */ 00343 00344 return Tcl_Eval(interp, 00345 "if {[namespace which -command tclInit] eq \"\"} {\n" 00346 " proc tclInit {} {\n" 00347 " global tcl_libPath tcl_library env tclDefaultLibrary\n" 00348 " rename tclInit {}\n" 00349 " if {[info exists tcl_library]} {\n" 00350 " set scripts {{set tcl_library}}\n" 00351 " } else {\n" 00352 " set scripts {}\n" 00353 " if {[info exists env(TCL_LIBRARY)] && ($env(TCL_LIBRARY) ne {})} {\n" 00354 " lappend scripts {set env(TCL_LIBRARY)}\n" 00355 " lappend scripts {\n" 00356 "if {[regexp ^tcl(.*)$ [file tail $env(TCL_LIBRARY)] -> tail] == 0} continue\n" 00357 "if {$tail eq [info tclversion]} continue\n" 00358 "file join [file dirname $env(TCL_LIBRARY)] tcl[info tclversion]}\n" 00359 " }\n" 00360 " if {[info exists tclDefaultLibrary]} {\n" 00361 " lappend scripts {set tclDefaultLibrary}\n" 00362 " } else {\n" 00363 " lappend scripts {::tcl::pkgconfig get scriptdir,runtime}\n" 00364 " }\n" 00365 " lappend scripts {\n" 00366 "set parentDir [file dirname [file dirname [info nameofexecutable]]]\n" 00367 "set grandParentDir [file dirname $parentDir]\n" 00368 "file join $parentDir lib tcl[info tclversion]} \\\n" 00369 " {file join $grandParentDir lib tcl[info tclversion]} \\\n" 00370 " {file join $parentDir library} \\\n" 00371 " {file join $grandParentDir library} \\\n" 00372 " {file join $grandParentDir tcl[info patchlevel] library} \\\n" 00373 " {\n" 00374 "file join [file dirname $grandParentDir] tcl[info patchlevel] library}\n" 00375 " if {[info exists tcl_libPath]\n" 00376 " && [catch {llength $tcl_libPath} len] == 0} {\n" 00377 " for {set i 0} {$i < $len} {incr i} {\n" 00378 " lappend scripts [list lindex \\$tcl_libPath $i]\n" 00379 " }\n" 00380 " }\n" 00381 " }\n" 00382 " set dirs {}\n" 00383 " set errors {}\n" 00384 " foreach script $scripts {\n" 00385 " lappend dirs [eval $script]\n" 00386 " set tcl_library [lindex $dirs end]\n" 00387 " set tclfile [file join $tcl_library init.tcl]\n" 00388 " if {[file exists $tclfile]} {\n" 00389 " if {[catch {uplevel #0 [list source $tclfile]} msg opts]} {\n" 00390 " append errors \"$tclfile: $msg\n\"\n" 00391 " append errors \"[dict get $opts -errorinfo]\n\"\n" 00392 " continue\n" 00393 " }\n" 00394 " unset -nocomplain tclDefaultLibrary\n" 00395 " return\n" 00396 " }\n" 00397 " }\n" 00398 " unset -nocomplain tclDefaultLibrary\n" 00399 " set msg \"Can't find a usable init.tcl in the following directories: \n\"\n" 00400 " append msg \" $dirs\n\n\"\n" 00401 " append msg \"$errors\n\n\"\n" 00402 " append msg \"This probably means that Tcl wasn't installed properly.\n\"\n" 00403 " error $msg\n" 00404 " }\n" 00405 "}\n" 00406 "tclInit"); 00407 } 00408 00409 /* 00410 *--------------------------------------------------------------------------- 00411 * 00412 * TclInterpInit -- 00413 * 00414 * Initializes the invoking interpreter for using the master, slave and 00415 * safe interp facilities. This is called from inside Tcl_CreateInterp(). 00416 * 00417 * Results: 00418 * Always returns TCL_OK for backwards compatibility. 00419 * 00420 * Side effects: 00421 * Adds the "interp" command to an interpreter and initializes the 00422 * interpInfoPtr field of the invoking interpreter. 00423 * 00424 *--------------------------------------------------------------------------- 00425 */ 00426 00427 int 00428 TclInterpInit( 00429 Tcl_Interp *interp) /* Interpreter to initialize. */ 00430 { 00431 InterpInfo *interpInfoPtr; 00432 Master *masterPtr; 00433 Slave *slavePtr; 00434 00435 interpInfoPtr = (InterpInfo *) ckalloc(sizeof(InterpInfo)); 00436 ((Interp *) interp)->interpInfo = interpInfoPtr; 00437 00438 masterPtr = &interpInfoPtr->master; 00439 Tcl_InitHashTable(&masterPtr->slaveTable, TCL_STRING_KEYS); 00440 masterPtr->targetsPtr = NULL; 00441 00442 slavePtr = &interpInfoPtr->slave; 00443 slavePtr->masterInterp = NULL; 00444 slavePtr->slaveEntryPtr = NULL; 00445 slavePtr->slaveInterp = interp; 00446 slavePtr->interpCmd = NULL; 00447 Tcl_InitHashTable(&slavePtr->aliasTable, TCL_STRING_KEYS); 00448 00449 Tcl_CreateObjCommand(interp, "interp", Tcl_InterpObjCmd, NULL, NULL); 00450 00451 Tcl_CallWhenDeleted(interp, InterpInfoDeleteProc, NULL); 00452 return TCL_OK; 00453 } 00454 00455 /* 00456 *--------------------------------------------------------------------------- 00457 * 00458 * InterpInfoDeleteProc -- 00459 * 00460 * Invoked when an interpreter is being deleted. It releases all storage 00461 * used by the master/slave/safe interpreter facilities. 00462 * 00463 * Results: 00464 * None. 00465 * 00466 * Side effects: 00467 * Cleans up storage. Sets the interpInfoPtr field of the interp to NULL. 00468 * 00469 *--------------------------------------------------------------------------- 00470 */ 00471 00472 static void 00473 InterpInfoDeleteProc( 00474 ClientData clientData, /* Ignored. */ 00475 Tcl_Interp *interp) /* Interp being deleted. All commands for 00476 * slave interps should already be deleted. */ 00477 { 00478 InterpInfo *interpInfoPtr; 00479 Slave *slavePtr; 00480 Master *masterPtr; 00481 Target *targetPtr; 00482 00483 interpInfoPtr = (InterpInfo *) ((Interp *) interp)->interpInfo; 00484 00485 /* 00486 * There shouldn't be any commands left. 00487 */ 00488 00489 masterPtr = &interpInfoPtr->master; 00490 if (masterPtr->slaveTable.numEntries != 0) { 00491 Tcl_Panic("InterpInfoDeleteProc: still exist commands"); 00492 } 00493 Tcl_DeleteHashTable(&masterPtr->slaveTable); 00494 00495 /* 00496 * Tell any interps that have aliases to this interp that they should 00497 * delete those aliases. If the other interp was already dead, it would 00498 * have removed the target record already. 00499 */ 00500 00501 for (targetPtr = masterPtr->targetsPtr; targetPtr != NULL; ) { 00502 Target *tmpPtr = targetPtr->nextPtr; 00503 Tcl_DeleteCommandFromToken(targetPtr->slaveInterp, 00504 targetPtr->slaveCmd); 00505 targetPtr = tmpPtr; 00506 } 00507 00508 slavePtr = &interpInfoPtr->slave; 00509 if (slavePtr->interpCmd != NULL) { 00510 /* 00511 * Tcl_DeleteInterp() was called on this interpreter, rather "interp 00512 * delete" or the equivalent deletion of the command in the master. 00513 * First ensure that the cleanup callback doesn't try to delete the 00514 * interp again. 00515 */ 00516 00517 slavePtr->slaveInterp = NULL; 00518 Tcl_DeleteCommandFromToken(slavePtr->masterInterp, 00519 slavePtr->interpCmd); 00520 } 00521 00522 /* 00523 * There shouldn't be any aliases left. 00524 */ 00525 00526 if (slavePtr->aliasTable.numEntries != 0) { 00527 Tcl_Panic("InterpInfoDeleteProc: still exist aliases"); 00528 } 00529 Tcl_DeleteHashTable(&slavePtr->aliasTable); 00530 00531 ckfree((char *) interpInfoPtr); 00532 } 00533 00534 /* 00535 *---------------------------------------------------------------------- 00536 * 00537 * Tcl_InterpObjCmd -- 00538 * 00539 * This function is invoked to process the "interp" Tcl command. See the 00540 * user documentation for details on what it does. 00541 * 00542 * Results: 00543 * A standard Tcl result. 00544 * 00545 * Side effects: 00546 * See the user documentation. 00547 * 00548 *---------------------------------------------------------------------- 00549 */ 00550 /* ARGSUSED */ 00551 int 00552 Tcl_InterpObjCmd( 00553 ClientData clientData, /* Unused. */ 00554 Tcl_Interp *interp, /* Current interpreter. */ 00555 int objc, /* Number of arguments. */ 00556 Tcl_Obj *const objv[]) /* Argument objects. */ 00557 { 00558 int index; 00559 static const char *options[] = { 00560 "alias", "aliases", "bgerror", "create", 00561 "delete", "eval", "exists", "expose", 00562 "hide", "hidden", "issafe", "invokehidden", 00563 "limit", "marktrusted", "recursionlimit","slaves", 00564 "share", "target", "transfer", 00565 NULL 00566 }; 00567 enum option { 00568 OPT_ALIAS, OPT_ALIASES, OPT_BGERROR, OPT_CREATE, 00569 OPT_DELETE, OPT_EVAL, OPT_EXISTS, OPT_EXPOSE, 00570 OPT_HIDE, OPT_HIDDEN, OPT_ISSAFE, OPT_INVOKEHID, 00571 OPT_LIMIT, OPT_MARKTRUSTED,OPT_RECLIMIT, OPT_SLAVES, 00572 OPT_SHARE, OPT_TARGET, OPT_TRANSFER 00573 }; 00574 00575 if (objc < 2) { 00576 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?arg ...?"); 00577 return TCL_ERROR; 00578 } 00579 if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0, 00580 &index) != TCL_OK) { 00581 return TCL_ERROR; 00582 } 00583 switch ((enum option) index) { 00584 case OPT_ALIAS: { 00585 Tcl_Interp *slaveInterp, *masterInterp; 00586 00587 if (objc < 4) { 00588 aliasArgs: 00589 Tcl_WrongNumArgs(interp, 2, objv, 00590 "slavePath slaveCmd ?masterPath masterCmd? ?args ..?"); 00591 return TCL_ERROR; 00592 } 00593 slaveInterp = GetInterp(interp, objv[2]); 00594 if (slaveInterp == NULL) { 00595 return TCL_ERROR; 00596 } 00597 if (objc == 4) { 00598 return AliasDescribe(interp, slaveInterp, objv[3]); 00599 } 00600 if ((objc == 5) && (TclGetString(objv[4])[0] == '\0')) { 00601 return AliasDelete(interp, slaveInterp, objv[3]); 00602 } 00603 if (objc > 5) { 00604 masterInterp = GetInterp(interp, objv[4]); 00605 if (masterInterp == NULL) { 00606 return TCL_ERROR; 00607 } 00608 if (TclGetString(objv[5])[0] == '\0') { 00609 if (objc == 6) { 00610 return AliasDelete(interp, slaveInterp, objv[3]); 00611 } 00612 } else { 00613 return AliasCreate(interp, slaveInterp, masterInterp, objv[3], 00614 objv[5], objc - 6, objv + 6); 00615 } 00616 } 00617 goto aliasArgs; 00618 } 00619 case OPT_ALIASES: { 00620 Tcl_Interp *slaveInterp; 00621 00622 slaveInterp = GetInterp2(interp, objc, objv); 00623 if (slaveInterp == NULL) { 00624 return TCL_ERROR; 00625 } 00626 return AliasList(interp, slaveInterp); 00627 } 00628 case OPT_BGERROR: { 00629 Tcl_Interp *slaveInterp; 00630 00631 if (objc != 3 && objc != 4) { 00632 Tcl_WrongNumArgs(interp, 2, objv, "path ?cmdPrefix?"); 00633 return TCL_ERROR; 00634 } 00635 slaveInterp = GetInterp(interp, objv[2]); 00636 if (slaveInterp == NULL) { 00637 return TCL_ERROR; 00638 } 00639 return SlaveBgerror(interp, slaveInterp, objc - 3, objv + 3); 00640 } 00641 case OPT_CREATE: { 00642 int i, last, safe; 00643 Tcl_Obj *slavePtr; 00644 char buf[16 + TCL_INTEGER_SPACE]; 00645 static const char *options[] = { 00646 "-safe", "--", NULL 00647 }; 00648 enum option { 00649 OPT_SAFE, OPT_LAST 00650 }; 00651 00652 safe = Tcl_IsSafe(interp); 00653 00654 /* 00655 * Weird historical rules: "-safe" is accepted at the end, too. 00656 */ 00657 00658 slavePtr = NULL; 00659 last = 0; 00660 for (i = 2; i < objc; i++) { 00661 if ((last == 0) && (Tcl_GetString(objv[i])[0] == '-')) { 00662 if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, 00663 &index) != TCL_OK) { 00664 return TCL_ERROR; 00665 } 00666 if (index == OPT_SAFE) { 00667 safe = 1; 00668 continue; 00669 } 00670 i++; 00671 last = 1; 00672 } 00673 if (slavePtr != NULL) { 00674 Tcl_WrongNumArgs(interp, 2, objv, "?-safe? ?--? ?path?"); 00675 return TCL_ERROR; 00676 } 00677 if (i < objc) { 00678 slavePtr = objv[i]; 00679 } 00680 } 00681 buf[0] = '\0'; 00682 if (slavePtr == NULL) { 00683 /* 00684 * Create an anonymous interpreter -- we choose its name and the 00685 * name of the command. We check that the command name that we use 00686 * for the interpreter does not collide with an existing command 00687 * in the master interpreter. 00688 */ 00689 00690 for (i = 0; ; i++) { 00691 Tcl_CmdInfo cmdInfo; 00692 00693 sprintf(buf, "interp%d", i); 00694 if (Tcl_GetCommandInfo(interp, buf, &cmdInfo) == 0) { 00695 break; 00696 } 00697 } 00698 slavePtr = Tcl_NewStringObj(buf, -1); 00699 } 00700 if (SlaveCreate(interp, slavePtr, safe) == NULL) { 00701 if (buf[0] != '\0') { 00702 Tcl_DecrRefCount(slavePtr); 00703 } 00704 return TCL_ERROR; 00705 } 00706 Tcl_SetObjResult(interp, slavePtr); 00707 return TCL_OK; 00708 } 00709 case OPT_DELETE: { 00710 int i; 00711 InterpInfo *iiPtr; 00712 Tcl_Interp *slaveInterp; 00713 00714 for (i = 2; i < objc; i++) { 00715 slaveInterp = GetInterp(interp, objv[i]); 00716 if (slaveInterp == NULL) { 00717 return TCL_ERROR; 00718 } else if (slaveInterp == interp) { 00719 Tcl_SetObjResult(interp, Tcl_NewStringObj( 00720 "cannot delete the current interpreter", -1)); 00721 return TCL_ERROR; 00722 } 00723 iiPtr = (InterpInfo *) ((Interp *) slaveInterp)->interpInfo; 00724 Tcl_DeleteCommandFromToken(iiPtr->slave.masterInterp, 00725 iiPtr->slave.interpCmd); 00726 } 00727 return TCL_OK; 00728 } 00729 case OPT_EVAL: { 00730 Tcl_Interp *slaveInterp; 00731 00732 if (objc < 4) { 00733 Tcl_WrongNumArgs(interp, 2, objv, "path arg ?arg ...?"); 00734 return TCL_ERROR; 00735 } 00736 slaveInterp = GetInterp(interp, objv[2]); 00737 if (slaveInterp == NULL) { 00738 return TCL_ERROR; 00739 } 00740 return SlaveEval(interp, slaveInterp, objc - 3, objv + 3); 00741 } 00742 case OPT_EXISTS: { 00743 int exists; 00744 Tcl_Interp *slaveInterp; 00745 00746 exists = 1; 00747 slaveInterp = GetInterp2(interp, objc, objv); 00748 if (slaveInterp == NULL) { 00749 if (objc > 3) { 00750 return TCL_ERROR; 00751 } 00752 Tcl_ResetResult(interp); 00753 exists = 0; 00754 } 00755 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(exists)); 00756 return TCL_OK; 00757 } 00758 case OPT_EXPOSE: { 00759 Tcl_Interp *slaveInterp; 00760 00761 if ((objc < 4) || (objc > 5)) { 00762 Tcl_WrongNumArgs(interp, 2, objv, "path hiddenCmdName ?cmdName?"); 00763 return TCL_ERROR; 00764 } 00765 slaveInterp = GetInterp(interp, objv[2]); 00766 if (slaveInterp == NULL) { 00767 return TCL_ERROR; 00768 } 00769 return SlaveExpose(interp, slaveInterp, objc - 3, objv + 3); 00770 } 00771 case OPT_HIDE: { 00772 Tcl_Interp *slaveInterp; /* A slave. */ 00773 00774 if ((objc < 4) || (objc > 5)) { 00775 Tcl_WrongNumArgs(interp, 2, objv, "path cmdName ?hiddenCmdName?"); 00776 return TCL_ERROR; 00777 } 00778 slaveInterp = GetInterp(interp, objv[2]); 00779 if (slaveInterp == NULL) { 00780 return TCL_ERROR; 00781 } 00782 return SlaveHide(interp, slaveInterp, objc - 3, objv + 3); 00783 } 00784 case OPT_HIDDEN: { 00785 Tcl_Interp *slaveInterp; /* A slave. */ 00786 00787 slaveInterp = GetInterp2(interp, objc, objv); 00788 if (slaveInterp == NULL) { 00789 return TCL_ERROR; 00790 } 00791 return SlaveHidden(interp, slaveInterp); 00792 } 00793 case OPT_ISSAFE: { 00794 Tcl_Interp *slaveInterp; 00795 00796 slaveInterp = GetInterp2(interp, objc, objv); 00797 if (slaveInterp == NULL) { 00798 return TCL_ERROR; 00799 } 00800 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(Tcl_IsSafe(slaveInterp))); 00801 return TCL_OK; 00802 } 00803 case OPT_INVOKEHID: { 00804 int i, index; 00805 const char *namespaceName; 00806 Tcl_Interp *slaveInterp; 00807 static const char *hiddenOptions[] = { 00808 "-global", "-namespace", "--", NULL 00809 }; 00810 enum hiddenOption { 00811 OPT_GLOBAL, OPT_NAMESPACE, OPT_LAST 00812 }; 00813 00814 namespaceName = NULL; 00815 for (i = 3; i < objc; i++) { 00816 if (TclGetString(objv[i])[0] != '-') { 00817 break; 00818 } 00819 if (Tcl_GetIndexFromObj(interp, objv[i], hiddenOptions, "option", 00820 0, &index) != TCL_OK) { 00821 return TCL_ERROR; 00822 } 00823 if (index == OPT_GLOBAL) { 00824 namespaceName = "::"; 00825 } else if (index == OPT_NAMESPACE) { 00826 if (++i == objc) { /* There must be more arguments. */ 00827 break; 00828 } else { 00829 namespaceName = TclGetString(objv[i]); 00830 } 00831 } else { 00832 i++; 00833 break; 00834 } 00835 } 00836 if (objc - i < 1) { 00837 Tcl_WrongNumArgs(interp, 2, objv, 00838 "path ?-namespace ns? ?-global? ?--? cmd ?arg ..?"); 00839 return TCL_ERROR; 00840 } 00841 slaveInterp = GetInterp(interp, objv[2]); 00842 if (slaveInterp == NULL) { 00843 return TCL_ERROR; 00844 } 00845 return SlaveInvokeHidden(interp, slaveInterp, namespaceName, objc - i, 00846 objv + i); 00847 } 00848 case OPT_LIMIT: { 00849 Tcl_Interp *slaveInterp; 00850 static const char *limitTypes[] = { 00851 "commands", "time", NULL 00852 }; 00853 enum LimitTypes { 00854 LIMIT_TYPE_COMMANDS, LIMIT_TYPE_TIME 00855 }; 00856 int limitType; 00857 00858 if (objc < 4) { 00859 Tcl_WrongNumArgs(interp, 2, objv, "path limitType ?options?"); 00860 return TCL_ERROR; 00861 } 00862 slaveInterp = GetInterp(interp, objv[2]); 00863 if (slaveInterp == NULL) { 00864 return TCL_ERROR; 00865 } 00866 if (Tcl_GetIndexFromObj(interp, objv[3], limitTypes, "limit type", 0, 00867 &limitType) != TCL_OK) { 00868 return TCL_ERROR; 00869 } 00870 switch ((enum LimitTypes) limitType) { 00871 case LIMIT_TYPE_COMMANDS: 00872 return SlaveCommandLimitCmd(interp, slaveInterp, 4, objc,objv); 00873 case LIMIT_TYPE_TIME: 00874 return SlaveTimeLimitCmd(interp, slaveInterp, 4, objc, objv); 00875 } 00876 } 00877 case OPT_MARKTRUSTED: { 00878 Tcl_Interp *slaveInterp; 00879 00880 if (objc != 3) { 00881 Tcl_WrongNumArgs(interp, 2, objv, "path"); 00882 return TCL_ERROR; 00883 } 00884 slaveInterp = GetInterp(interp, objv[2]); 00885 if (slaveInterp == NULL) { 00886 return TCL_ERROR; 00887 } 00888 return SlaveMarkTrusted(interp, slaveInterp); 00889 } 00890 case OPT_RECLIMIT: { 00891 Tcl_Interp *slaveInterp; 00892 00893 if (objc != 3 && objc != 4) { 00894 Tcl_WrongNumArgs(interp, 2, objv, "path ?newlimit?"); 00895 return TCL_ERROR; 00896 } 00897 slaveInterp = GetInterp(interp, objv[2]); 00898 if (slaveInterp == NULL) { 00899 return TCL_ERROR; 00900 } 00901 return SlaveRecursionLimit(interp, slaveInterp, objc - 3, objv + 3); 00902 } 00903 case OPT_SLAVES: { 00904 Tcl_Interp *slaveInterp; 00905 InterpInfo *iiPtr; 00906 Tcl_Obj *resultPtr; 00907 Tcl_HashEntry *hPtr; 00908 Tcl_HashSearch hashSearch; 00909 char *string; 00910 00911 slaveInterp = GetInterp2(interp, objc, objv); 00912 if (slaveInterp == NULL) { 00913 return TCL_ERROR; 00914 } 00915 iiPtr = (InterpInfo *) ((Interp *) slaveInterp)->interpInfo; 00916 resultPtr = Tcl_NewObj(); 00917 hPtr = Tcl_FirstHashEntry(&iiPtr->master.slaveTable, &hashSearch); 00918 for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&hashSearch)) { 00919 string = Tcl_GetHashKey(&iiPtr->master.slaveTable, hPtr); 00920 Tcl_ListObjAppendElement(NULL, resultPtr, 00921 Tcl_NewStringObj(string, -1)); 00922 } 00923 Tcl_SetObjResult(interp, resultPtr); 00924 return TCL_OK; 00925 } 00926 case OPT_TRANSFER: 00927 case OPT_SHARE: { 00928 Tcl_Interp *slaveInterp; /* A slave. */ 00929 Tcl_Interp *masterInterp; /* Its master. */ 00930 Tcl_Channel chan; 00931 00932 if (objc != 5) { 00933 Tcl_WrongNumArgs(interp, 2, objv, "srcPath channelId destPath"); 00934 return TCL_ERROR; 00935 } 00936 masterInterp = GetInterp(interp, objv[2]); 00937 if (masterInterp == NULL) { 00938 return TCL_ERROR; 00939 } 00940 chan = Tcl_GetChannel(masterInterp, TclGetString(objv[3]), NULL); 00941 if (chan == NULL) { 00942 TclTransferResult(masterInterp, TCL_OK, interp); 00943 return TCL_ERROR; 00944 } 00945 slaveInterp = GetInterp(interp, objv[4]); 00946 if (slaveInterp == NULL) { 00947 return TCL_ERROR; 00948 } 00949 Tcl_RegisterChannel(slaveInterp, chan); 00950 if (index == OPT_TRANSFER) { 00951 /* 00952 * When transferring, as opposed to sharing, we must unhitch the 00953 * channel from the interpreter where it started. 00954 */ 00955 00956 if (Tcl_UnregisterChannel(masterInterp, chan) != TCL_OK) { 00957 TclTransferResult(masterInterp, TCL_OK, interp); 00958 return TCL_ERROR; 00959 } 00960 } 00961 return TCL_OK; 00962 } 00963 case OPT_TARGET: { 00964 Tcl_Interp *slaveInterp; 00965 InterpInfo *iiPtr; 00966 Tcl_HashEntry *hPtr; 00967 Alias *aliasPtr; 00968 char *aliasName; 00969 00970 if (objc != 4) { 00971 Tcl_WrongNumArgs(interp, 2, objv, "path alias"); 00972 return TCL_ERROR; 00973 } 00974 00975 slaveInterp = GetInterp(interp, objv[2]); 00976 if (slaveInterp == NULL) { 00977 return TCL_ERROR; 00978 } 00979 00980 aliasName = TclGetString(objv[3]); 00981 00982 iiPtr = (InterpInfo *) ((Interp *) slaveInterp)->interpInfo; 00983 hPtr = Tcl_FindHashEntry(&iiPtr->slave.aliasTable, aliasName); 00984 if (hPtr == NULL) { 00985 Tcl_AppendResult(interp, "alias \"", aliasName, "\" in path \"", 00986 Tcl_GetString(objv[2]), "\" not found", NULL); 00987 Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ALIAS", aliasName, 00988 NULL); 00989 return TCL_ERROR; 00990 } 00991 aliasPtr = Tcl_GetHashValue(hPtr); 00992 if (Tcl_GetInterpPath(interp, aliasPtr->targetInterp) != TCL_OK) { 00993 Tcl_ResetResult(interp); 00994 Tcl_AppendResult(interp, "target interpreter for alias \"", 00995 aliasName, "\" in path \"", Tcl_GetString(objv[2]), 00996 "\" is not my descendant", NULL); 00997 return TCL_ERROR; 00998 } 00999 return TCL_OK; 01000 } 01001 } 01002 return TCL_OK; 01003 } 01004 01005 /* 01006 *--------------------------------------------------------------------------- 01007 * 01008 * GetInterp2 -- 01009 * 01010 * Helper function for Tcl_InterpObjCmd() to convert the interp name 01011 * potentially specified on the command line to an Tcl_Interp. 01012 * 01013 * Results: 01014 * The return value is the interp specified on the command line, or the 01015 * interp argument itself if no interp was specified on the command line. 01016 * If the interp could not be found or the wrong number of arguments was 01017 * specified on the command line, the return value is NULL and an error 01018 * message is left in the interp's result. 01019 * 01020 * Side effects: 01021 * None. 01022 * 01023 *--------------------------------------------------------------------------- 01024 */ 01025 01026 static Tcl_Interp * 01027 GetInterp2( 01028 Tcl_Interp *interp, /* Default interp if no interp was specified 01029 * on the command line. */ 01030 int objc, /* Number of arguments. */ 01031 Tcl_Obj *const objv[]) /* Argument objects. */ 01032 { 01033 if (objc == 2) { 01034 return interp; 01035 } else if (objc == 3) { 01036 return GetInterp(interp, objv[2]); 01037 } else { 01038 Tcl_WrongNumArgs(interp, 2, objv, "?path?"); 01039 return NULL; 01040 } 01041 } 01042 01043 /* 01044 *---------------------------------------------------------------------- 01045 * 01046 * Tcl_CreateAlias -- 01047 * 01048 * Creates an alias between two interpreters. 01049 * 01050 * Results: 01051 * A standard Tcl result. 01052 * 01053 * Side effects: 01054 * Creates a new alias, manipulates the result field of slaveInterp. 01055 * 01056 *---------------------------------------------------------------------- 01057 */ 01058 01059 int 01060 Tcl_CreateAlias( 01061 Tcl_Interp *slaveInterp, /* Interpreter for source command. */ 01062 const char *slaveCmd, /* Command to install in slave. */ 01063 Tcl_Interp *targetInterp, /* Interpreter for target command. */ 01064 const char *targetCmd, /* Name of target command. */ 01065 int argc, /* How many additional arguments? */ 01066 const char *const *argv) /* These are the additional args. */ 01067 { 01068 Tcl_Obj *slaveObjPtr, *targetObjPtr; 01069 Tcl_Obj **objv; 01070 int i; 01071 int result; 01072 01073 objv = (Tcl_Obj **) 01074 TclStackAlloc(slaveInterp, (unsigned) sizeof(Tcl_Obj *) * argc); 01075 for (i = 0; i < argc; i++) { 01076 objv[i] = Tcl_NewStringObj(argv[i], -1); 01077 Tcl_IncrRefCount(objv[i]); 01078 } 01079 01080 slaveObjPtr = Tcl_NewStringObj(slaveCmd, -1); 01081 Tcl_IncrRefCount(slaveObjPtr); 01082 01083 targetObjPtr = Tcl_NewStringObj(targetCmd, -1); 01084 Tcl_IncrRefCount(targetObjPtr); 01085 01086 result = AliasCreate(slaveInterp, slaveInterp, targetInterp, slaveObjPtr, 01087 targetObjPtr, argc, objv); 01088 01089 for (i = 0; i < argc; i++) { 01090 Tcl_DecrRefCount(objv[i]); 01091 } 01092 TclStackFree(slaveInterp, objv); 01093 Tcl_DecrRefCount(targetObjPtr); 01094 Tcl_DecrRefCount(slaveObjPtr); 01095 01096 return result; 01097 } 01098 01099 /* 01100 *---------------------------------------------------------------------- 01101 * 01102 * Tcl_CreateAliasObj -- 01103 * 01104 * Object version: Creates an alias between two interpreters. 01105 * 01106 * Results: 01107 * A standard Tcl result. 01108 * 01109 * Side effects: 01110 * Creates a new alias. 01111 * 01112 *---------------------------------------------------------------------- 01113 */ 01114 01115 int 01116 Tcl_CreateAliasObj( 01117 Tcl_Interp *slaveInterp, /* Interpreter for source command. */ 01118 const char *slaveCmd, /* Command to install in slave. */ 01119 Tcl_Interp *targetInterp, /* Interpreter for target command. */ 01120 const char *targetCmd, /* Name of target command. */ 01121 int objc, /* How many additional arguments? */ 01122 Tcl_Obj *const objv[]) /* Argument vector. */ 01123 { 01124 Tcl_Obj *slaveObjPtr, *targetObjPtr; 01125 int result; 01126 01127 slaveObjPtr = Tcl_NewStringObj(slaveCmd, -1); 01128 Tcl_IncrRefCount(slaveObjPtr); 01129 01130 targetObjPtr = Tcl_NewStringObj(targetCmd, -1); 01131 Tcl_IncrRefCount(targetObjPtr); 01132 01133 result = AliasCreate(slaveInterp, slaveInterp, targetInterp, slaveObjPtr, 01134 targetObjPtr, objc, objv); 01135 01136 Tcl_DecrRefCount(slaveObjPtr); 01137 Tcl_DecrRefCount(targetObjPtr); 01138 return result; 01139 } 01140 01141 /* 01142 *---------------------------------------------------------------------- 01143 * 01144 * Tcl_GetAlias -- 01145 * 01146 * Gets information about an alias. 01147 * 01148 * Results: 01149 * A standard Tcl result. 01150 * 01151 * Side effects: 01152 * None. 01153 * 01154 *---------------------------------------------------------------------- 01155 */ 01156 01157 int 01158 Tcl_GetAlias( 01159 Tcl_Interp *interp, /* Interp to start search from. */ 01160 const char *aliasName, /* Name of alias to find. */ 01161 Tcl_Interp **targetInterpPtr, 01162 /* (Return) target interpreter. */ 01163 const char **targetNamePtr, /* (Return) name of target command. */ 01164 int *argcPtr, /* (Return) count of addnl args. */ 01165 const char ***argvPtr) /* (Return) additional arguments. */ 01166 { 01167 InterpInfo *iiPtr = (InterpInfo *) ((Interp *) interp)->interpInfo; 01168 Tcl_HashEntry *hPtr; 01169 Alias *aliasPtr; 01170 int i, objc; 01171 Tcl_Obj **objv; 01172 01173 hPtr = Tcl_FindHashEntry(&iiPtr->slave.aliasTable, aliasName); 01174 if (hPtr == NULL) { 01175 Tcl_AppendResult(interp, "alias \"", aliasName, "\" not found", NULL); 01176 Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ALIAS", aliasName, NULL); 01177 return TCL_ERROR; 01178 } 01179 aliasPtr = Tcl_GetHashValue(hPtr); 01180 objc = aliasPtr->objc; 01181 objv = &aliasPtr->objPtr; 01182 01183 if (targetInterpPtr != NULL) { 01184 *targetInterpPtr = aliasPtr->targetInterp; 01185 } 01186 if (targetNamePtr != NULL) { 01187 *targetNamePtr = TclGetString(objv[0]); 01188 } 01189 if (argcPtr != NULL) { 01190 *argcPtr = objc - 1; 01191 } 01192 if (argvPtr != NULL) { 01193 *argvPtr = (const char **) 01194 ckalloc((unsigned) sizeof(const char *) * (objc - 1)); 01195 for (i = 1; i < objc; i++) { 01196 (*argvPtr)[i - 1] = TclGetString(objv[i]); 01197 } 01198 } 01199 return TCL_OK; 01200 } 01201 01202 /* 01203 *---------------------------------------------------------------------- 01204 * 01205 * Tcl_GetAliasObj -- 01206 * 01207 * Object version: Gets information about an alias. 01208 * 01209 * Results: 01210 * A standard Tcl result. 01211 * 01212 * Side effects: 01213 * None. 01214 * 01215 *---------------------------------------------------------------------- 01216 */ 01217 01218 int 01219 Tcl_GetAliasObj( 01220 Tcl_Interp *interp, /* Interp to start search from. */ 01221 const char *aliasName, /* Name of alias to find. */ 01222 Tcl_Interp **targetInterpPtr, 01223 /* (Return) target interpreter. */ 01224 const char **targetNamePtr, /* (Return) name of target command. */ 01225 int *objcPtr, /* (Return) count of addnl args. */ 01226 Tcl_Obj ***objvPtr) /* (Return) additional args. */ 01227 { 01228 InterpInfo *iiPtr = (InterpInfo *) ((Interp *) interp)->interpInfo; 01229 Tcl_HashEntry *hPtr; 01230 Alias *aliasPtr; 01231 int objc; 01232 Tcl_Obj **objv; 01233 01234 hPtr = Tcl_FindHashEntry(&iiPtr->slave.aliasTable, aliasName); 01235 if (hPtr == NULL) { 01236 Tcl_AppendResult(interp, "alias \"", aliasName, "\" not found", NULL); 01237 Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ALIAS", aliasName, NULL); 01238 return TCL_ERROR; 01239 } 01240 aliasPtr = Tcl_GetHashValue(hPtr); 01241 objc = aliasPtr->objc; 01242 objv = &aliasPtr->objPtr; 01243 01244 if (targetInterpPtr != NULL) { 01245 *targetInterpPtr = aliasPtr->targetInterp; 01246 } 01247 if (targetNamePtr != NULL) { 01248 *targetNamePtr = TclGetString(objv[0]); 01249 } 01250 if (objcPtr != NULL) { 01251 *objcPtr = objc - 1; 01252 } 01253 if (objvPtr != NULL) { 01254 *objvPtr = objv + 1; 01255 } 01256 return TCL_OK; 01257 } 01258 01259 /* 01260 *---------------------------------------------------------------------- 01261 * 01262 * TclPreventAliasLoop -- 01263 * 01264 * When defining an alias or renaming a command, prevent an alias loop 01265 * from being formed. 01266 * 01267 * Results: 01268 * A standard Tcl object result. 01269 * 01270 * Side effects: 01271 * If TCL_ERROR is returned, the function also stores an error message in 01272 * the interpreter's result object. 01273 * 01274 * NOTE: 01275 * This function is public internal (instead of being static to this 01276 * file) because it is also used from TclRenameCommand. 01277 * 01278 *---------------------------------------------------------------------- 01279 */ 01280 01281 int 01282 TclPreventAliasLoop( 01283 Tcl_Interp *interp, /* Interp in which to report errors. */ 01284 Tcl_Interp *cmdInterp, /* Interp in which the command is being 01285 * defined. */ 01286 Tcl_Command cmd) /* Tcl command we are attempting to define. */ 01287 { 01288 Command *cmdPtr = (Command *) cmd; 01289 Alias *aliasPtr, *nextAliasPtr; 01290 Tcl_Command aliasCmd; 01291 Command *aliasCmdPtr; 01292 01293 /* 01294 * If we are not creating or renaming an alias, then it is always OK to 01295 * create or rename the command. 01296 */ 01297 01298 if (cmdPtr->objProc != AliasObjCmd) { 01299 return TCL_OK; 01300 } 01301 01302 /* 01303 * OK, we are dealing with an alias, so traverse the chain of aliases. If 01304 * we encounter the alias we are defining (or renaming to) any in the 01305 * chain then we have a loop. 01306 */ 01307 01308 aliasPtr = (Alias *) cmdPtr->objClientData; 01309 nextAliasPtr = aliasPtr; 01310 while (1) { 01311 Tcl_Obj *cmdNamePtr; 01312 01313 /* 01314 * If the target of the next alias in the chain is the same as the 01315 * source alias, we have a loop. 01316 */ 01317 01318 if (Tcl_InterpDeleted(nextAliasPtr->targetInterp)) { 01319 /* 01320 * The slave interpreter can be deleted while creating the alias. 01321 * [Bug #641195] 01322 */ 01323 01324 Tcl_AppendResult(interp, "cannot define or rename alias \"", 01325 Tcl_GetCommandName(cmdInterp, cmd), 01326 "\": interpreter deleted", NULL); 01327 return TCL_ERROR; 01328 } 01329 cmdNamePtr = nextAliasPtr->objPtr; 01330 aliasCmd = Tcl_FindCommand(nextAliasPtr->targetInterp, 01331 TclGetString(cmdNamePtr), 01332 Tcl_GetGlobalNamespace(nextAliasPtr->targetInterp), 01333 /*flags*/ 0); 01334 if (aliasCmd == NULL) { 01335 return TCL_OK; 01336 } 01337 aliasCmdPtr = (Command *) aliasCmd; 01338 if (aliasCmdPtr == cmdPtr) { 01339 Tcl_AppendResult(interp, "cannot define or rename alias \"", 01340 Tcl_GetCommandName(cmdInterp, cmd), 01341 "\": would create a loop", NULL); 01342 return TCL_ERROR; 01343 } 01344 01345 /* 01346 * Otherwise, follow the chain one step further. See if the target 01347 * command is an alias - if so, follow the loop to its target command. 01348 * Otherwise we do not have a loop. 01349 */ 01350 01351 if (aliasCmdPtr->objProc != AliasObjCmd) { 01352 return TCL_OK; 01353 } 01354 nextAliasPtr = (Alias *) aliasCmdPtr->objClientData; 01355 } 01356 01357 /* NOTREACHED */ 01358 } 01359 01360 /* 01361 *---------------------------------------------------------------------- 01362 * 01363 * AliasCreate -- 01364 * 01365 * Helper function to do the work to actually create an alias. 01366 * 01367 * Results: 01368 * A standard Tcl result. 01369 * 01370 * Side effects: 01371 * An alias command is created and entered into the alias table for the 01372 * slave interpreter. 01373 * 01374 *---------------------------------------------------------------------- 01375 */ 01376 01377 static int 01378 AliasCreate( 01379 Tcl_Interp *interp, /* Interp for error reporting. */ 01380 Tcl_Interp *slaveInterp, /* Interp where alias cmd will live or from 01381 * which alias will be deleted. */ 01382 Tcl_Interp *masterInterp, /* Interp in which target command will be 01383 * invoked. */ 01384 Tcl_Obj *namePtr, /* Name of alias cmd. */ 01385 Tcl_Obj *targetNamePtr, /* Name of target cmd. */ 01386 int objc, /* Additional arguments to store */ 01387 Tcl_Obj *const objv[]) /* with alias. */ 01388 { 01389 Alias *aliasPtr; 01390 Tcl_HashEntry *hPtr; 01391 Target *targetPtr; 01392 Slave *slavePtr; 01393 Master *masterPtr; 01394 Tcl_Obj **prefv; 01395 int isNew, i; 01396 01397 aliasPtr = (Alias *) ckalloc((unsigned) (sizeof(Alias) 01398 + objc * sizeof(Tcl_Obj *))); 01399 aliasPtr->token = namePtr; 01400 Tcl_IncrRefCount(aliasPtr->token); 01401 aliasPtr->targetInterp = masterInterp; 01402 01403 aliasPtr->objc = objc + 1; 01404 prefv = &aliasPtr->objPtr; 01405 01406 *prefv = targetNamePtr; 01407 Tcl_IncrRefCount(targetNamePtr); 01408 for (i = 0; i < objc; i++) { 01409 *(++prefv) = objv[i]; 01410 Tcl_IncrRefCount(objv[i]); 01411 } 01412 01413 Tcl_Preserve(slaveInterp); 01414 Tcl_Preserve(masterInterp); 01415 01416 aliasPtr->slaveCmd = Tcl_CreateObjCommand(slaveInterp, 01417 TclGetString(namePtr), AliasObjCmd, aliasPtr, 01418 AliasObjCmdDeleteProc); 01419 01420 if (TclPreventAliasLoop(interp, slaveInterp, 01421 aliasPtr->slaveCmd) != TCL_OK) { 01422 /* 01423 * Found an alias loop! The last call to Tcl_CreateObjCommand made the 01424 * alias point to itself. Delete the command and its alias record. Be 01425 * careful to wipe out its client data first, so the command doesn't 01426 * try to delete itself. 01427 */ 01428 01429 Command *cmdPtr; 01430 01431 Tcl_DecrRefCount(aliasPtr->token); 01432 Tcl_DecrRefCount(targetNamePtr); 01433 for (i = 0; i < objc; i++) { 01434 Tcl_DecrRefCount(objv[i]); 01435 } 01436 01437 cmdPtr = (Command *) aliasPtr->slaveCmd; 01438 cmdPtr->clientData = NULL; 01439 cmdPtr->deleteProc = NULL; 01440 cmdPtr->deleteData = NULL; 01441 Tcl_DeleteCommandFromToken(slaveInterp, aliasPtr->slaveCmd); 01442 01443 ckfree((char *) aliasPtr); 01444 01445 /* 01446 * The result was already set by TclPreventAliasLoop. 01447 */ 01448 01449 Tcl_Release(slaveInterp); 01450 Tcl_Release(masterInterp); 01451 return TCL_ERROR; 01452 } 01453 01454 /* 01455 * Make an entry in the alias table. If it already exists, retry. 01456 */ 01457 01458 slavePtr = &((InterpInfo *) ((Interp *) slaveInterp)->interpInfo)->slave; 01459 while (1) { 01460 Tcl_Obj *newToken; 01461 char *string; 01462 01463 string = TclGetString(aliasPtr->token); 01464 hPtr = Tcl_CreateHashEntry(&slavePtr->aliasTable, string, &isNew); 01465 if (isNew != 0) { 01466 break; 01467 } 01468 01469 /* 01470 * The alias name cannot be used as unique token, it is already taken. 01471 * We can produce a unique token by prepending "::" repeatedly. This 01472 * algorithm is a stop-gap to try to maintain the command name as 01473 * token for most use cases, fearful of possible backwards compat 01474 * problems. A better algorithm would produce unique tokens that need 01475 * not be related to the command name. 01476 * 01477 * ATTENTION: the tests in interp.test and possibly safe.test depend 01478 * on the precise definition of these tokens. 01479 */ 01480 01481 TclNewLiteralStringObj(newToken, "::"); 01482 Tcl_AppendObjToObj(newToken, aliasPtr->token); 01483 Tcl_DecrRefCount(aliasPtr->token); 01484 aliasPtr->token = newToken; 01485 Tcl_IncrRefCount(aliasPtr->token); 01486 } 01487 01488 aliasPtr->aliasEntryPtr = hPtr; 01489 Tcl_SetHashValue(hPtr, aliasPtr); 01490 01491 /* 01492 * Create the new command. We must do it after deleting any old command, 01493 * because the alias may be pointing at a renamed alias, as in: 01494 * 01495 * interp alias {} foo {} bar # Create an alias "foo" 01496 * rename foo zop # Now rename the alias 01497 * interp alias {} foo {} zop # Now recreate "foo"... 01498 */ 01499 01500 targetPtr = (Target *) ckalloc((unsigned) sizeof(Target)); 01501 targetPtr->slaveCmd = aliasPtr->slaveCmd; 01502 targetPtr->slaveInterp = slaveInterp; 01503 01504 masterPtr = &((InterpInfo *) ((Interp*) masterInterp)->interpInfo)->master; 01505 targetPtr->nextPtr = masterPtr->targetsPtr; 01506 targetPtr->prevPtr = NULL; 01507 if (masterPtr->targetsPtr != NULL) { 01508 masterPtr->targetsPtr->prevPtr = targetPtr; 01509 } 01510 masterPtr->targetsPtr = targetPtr; 01511 aliasPtr->targetPtr = targetPtr; 01512 01513 Tcl_SetObjResult(interp, aliasPtr->token); 01514 01515 Tcl_Release(slaveInterp); 01516 Tcl_Release(masterInterp); 01517 return TCL_OK; 01518 } 01519 01520 /* 01521 *---------------------------------------------------------------------- 01522 * 01523 * AliasDelete -- 01524 * 01525 * Deletes the given alias from the slave interpreter given. 01526 * 01527 * Results: 01528 * A standard Tcl result. 01529 * 01530 * Side effects: 01531 * Deletes the alias from the slave interpreter. 01532 * 01533 *---------------------------------------------------------------------- 01534 */ 01535 01536 static int 01537 AliasDelete( 01538 Tcl_Interp *interp, /* Interpreter for result & errors. */ 01539 Tcl_Interp *slaveInterp, /* Interpreter containing alias. */ 01540 Tcl_Obj *namePtr) /* Name of alias to delete. */ 01541 { 01542 Slave *slavePtr; 01543 Alias *aliasPtr; 01544 Tcl_HashEntry *hPtr; 01545 01546 /* 01547 * If the alias has been renamed in the slave, the master can still use 01548 * the original name (with which it was created) to find the alias to 01549 * delete it. 01550 */ 01551 01552 slavePtr = &((InterpInfo *) ((Interp *) slaveInterp)->interpInfo)->slave; 01553 hPtr = Tcl_FindHashEntry(&slavePtr->aliasTable, TclGetString(namePtr)); 01554 if (hPtr == NULL) { 01555 Tcl_AppendResult(interp, "alias \"", TclGetString(namePtr), 01556 "\" not found", NULL); 01557 Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ALIAS", 01558 TclGetString(namePtr), NULL); 01559 return TCL_ERROR; 01560 } 01561 aliasPtr = Tcl_GetHashValue(hPtr); 01562 Tcl_DeleteCommandFromToken(slaveInterp, aliasPtr->slaveCmd); 01563 return TCL_OK; 01564 } 01565 01566 /* 01567 *---------------------------------------------------------------------- 01568 * 01569 * AliasDescribe -- 01570 * 01571 * Sets the interpreter's result object to a Tcl list describing the 01572 * given alias in the given interpreter: its target command and the 01573 * additional arguments to prepend to any invocation of the alias. 01574 * 01575 * Results: 01576 * A standard Tcl result. 01577 * 01578 * Side effects: 01579 * None. 01580 * 01581 *---------------------------------------------------------------------- 01582 */ 01583 01584 static int 01585 AliasDescribe( 01586 Tcl_Interp *interp, /* Interpreter for result & errors. */ 01587 Tcl_Interp *slaveInterp, /* Interpreter containing alias. */ 01588 Tcl_Obj *namePtr) /* Name of alias to describe. */ 01589 { 01590 Slave *slavePtr; 01591 Tcl_HashEntry *hPtr; 01592 Alias *aliasPtr; 01593 Tcl_Obj *prefixPtr; 01594 01595 /* 01596 * If the alias has been renamed in the slave, the master can still use 01597 * the original name (with which it was created) to find the alias to 01598 * describe it. 01599 */ 01600 01601 slavePtr = &((InterpInfo *) ((Interp *) slaveInterp)->interpInfo)->slave; 01602 hPtr = Tcl_FindHashEntry(&slavePtr->aliasTable, Tcl_GetString(namePtr)); 01603 if (hPtr == NULL) { 01604 return TCL_OK; 01605 } 01606 aliasPtr = Tcl_GetHashValue(hPtr); 01607 prefixPtr = Tcl_NewListObj(aliasPtr->objc, &aliasPtr->objPtr); 01608 Tcl_SetObjResult(interp, prefixPtr); 01609 return TCL_OK; 01610 } 01611 01612 /* 01613 *---------------------------------------------------------------------- 01614 * 01615 * AliasList -- 01616 * 01617 * Computes a list of aliases defined in a slave interpreter. 01618 * 01619 * Results: 01620 * A standard Tcl result. 01621 * 01622 * Side effects: 01623 * None. 01624 * 01625 *---------------------------------------------------------------------- 01626 */ 01627 01628 static int 01629 AliasList( 01630 Tcl_Interp *interp, /* Interp for data return. */ 01631 Tcl_Interp *slaveInterp) /* Interp whose aliases to compute. */ 01632 { 01633 Tcl_HashEntry *entryPtr; 01634 Tcl_HashSearch hashSearch; 01635 Tcl_Obj *resultPtr = Tcl_NewObj(); 01636 Alias *aliasPtr; 01637 Slave *slavePtr; 01638 01639 slavePtr = &((InterpInfo *) ((Interp *) slaveInterp)->interpInfo)->slave; 01640 01641 entryPtr = Tcl_FirstHashEntry(&slavePtr->aliasTable, &hashSearch); 01642 for ( ; entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&hashSearch)) { 01643 aliasPtr = Tcl_GetHashValue(entryPtr); 01644 Tcl_ListObjAppendElement(NULL, resultPtr, aliasPtr->token); 01645 } 01646 Tcl_SetObjResult(interp, resultPtr); 01647 return TCL_OK; 01648 } 01649 01650 /* 01651 *---------------------------------------------------------------------- 01652 * 01653 * AliasObjCmd -- 01654 * 01655 * This is the function that services invocations of aliases in a slave 01656 * interpreter. One such command exists for each alias. When invoked, 01657 * this function redirects the invocation to the target command in the 01658 * master interpreter as designated by the Alias record associated with 01659 * this command. 01660 * 01661 * Results: 01662 * A standard Tcl result. 01663 * 01664 * Side effects: 01665 * Causes forwarding of the invocation; all possible side effects may 01666 * occur as a result of invoking the command to which the invocation is 01667 * forwarded. 01668 * 01669 *---------------------------------------------------------------------- 01670 */ 01671 01672 static int 01673 AliasObjCmd( 01674 ClientData clientData, /* Alias record. */ 01675 Tcl_Interp *interp, /* Current interpreter. */ 01676 int objc, /* Number of arguments. */ 01677 Tcl_Obj *const objv[]) /* Argument vector. */ 01678 { 01679 #define ALIAS_CMDV_PREALLOC 10 01680 Alias *aliasPtr = clientData; 01681 Tcl_Interp *targetInterp = aliasPtr->targetInterp; 01682 int result, prefc, cmdc, i; 01683 Tcl_Obj **prefv, **cmdv; 01684 Tcl_Obj *cmdArr[ALIAS_CMDV_PREALLOC]; 01685 Interp *tPtr = (Interp *) targetInterp; 01686 int isRootEnsemble = (tPtr->ensembleRewrite.sourceObjs == NULL); 01687 01688 /* 01689 * Append the arguments to the command prefix and invoke the command in 01690 * the target interp's global namespace. 01691 */ 01692 01693 prefc = aliasPtr->objc; 01694 prefv = &aliasPtr->objPtr; 01695 cmdc = prefc + objc - 1; 01696 if (cmdc <= ALIAS_CMDV_PREALLOC) { 01697 cmdv = cmdArr; 01698 } else { 01699 cmdv = (Tcl_Obj **) TclStackAlloc(interp, cmdc*(int)sizeof(Tcl_Obj*)); 01700 } 01701 01702 prefv = &aliasPtr->objPtr; 01703 memcpy(cmdv, prefv, (size_t) (prefc * sizeof(Tcl_Obj *))); 01704 memcpy(cmdv+prefc, objv+1, (size_t) ((objc-1) * sizeof(Tcl_Obj *))); 01705 01706 Tcl_ResetResult(targetInterp); 01707 01708 for (i=0; i<cmdc; i++) { 01709 Tcl_IncrRefCount(cmdv[i]); 01710 } 01711 01712 /* 01713 * Use the ensemble rewriting machinery to ensure correct error messages: 01714 * only the source command should show, not the full target prefix. 01715 */ 01716 01717 if (isRootEnsemble) { 01718 tPtr->ensembleRewrite.sourceObjs = objv; 01719 tPtr->ensembleRewrite.numRemovedObjs = 1; 01720 tPtr->ensembleRewrite.numInsertedObjs = prefc; 01721 } else { 01722 tPtr->ensembleRewrite.numInsertedObjs += prefc - 1; 01723 } 01724 01725 /* 01726 * Protect the target interpreter if it isn't the same as the source 01727 * interpreter so that we can continue to work with it after the target 01728 * command completes. 01729 */ 01730 01731 if (targetInterp != interp) { 01732 Tcl_Preserve(targetInterp); 01733 } 01734 01735 /* 01736 * Execute the target command in the target interpreter. 01737 */ 01738 01739 result = Tcl_EvalObjv(targetInterp, cmdc, cmdv, TCL_EVAL_INVOKE); 01740 01741 /* 01742 * Clean up the ensemble rewrite info if we set it in the first place. 01743 */ 01744 01745 if (isRootEnsemble) { 01746 tPtr->ensembleRewrite.sourceObjs = NULL; 01747 tPtr->ensembleRewrite.numRemovedObjs = 0; 01748 tPtr->ensembleRewrite.numInsertedObjs = 0; 01749 } 01750 01751 /* 01752 * If it was a cross-interpreter alias, we need to transfer the result 01753 * back to the source interpreter and release the lock we previously set 01754 * on the target interpreter. 01755 */ 01756 01757 if (targetInterp != interp) { 01758 TclTransferResult(targetInterp, result, interp); 01759 Tcl_Release(targetInterp); 01760 } 01761 01762 for (i=0; i<cmdc; i++) { 01763 Tcl_DecrRefCount(cmdv[i]); 01764 } 01765 if (cmdv != cmdArr) { 01766 TclStackFree(interp, cmdv); 01767 } 01768 return result; 01769 #undef ALIAS_CMDV_PREALLOC 01770 } 01771 01772 /* 01773 *---------------------------------------------------------------------- 01774 * 01775 * AliasObjCmdDeleteProc -- 01776 * 01777 * Is invoked when an alias command is deleted in a slave. Cleans up all 01778 * storage associated with this alias. 01779 * 01780 * Results: 01781 * None. 01782 * 01783 * Side effects: 01784 * Deletes the alias record and its entry in the alias table for the 01785 * interpreter. 01786 * 01787 *---------------------------------------------------------------------- 01788 */ 01789 01790 static void 01791 AliasObjCmdDeleteProc( 01792 ClientData clientData) /* The alias record for this alias. */ 01793 { 01794 Alias *aliasPtr = clientData; 01795 Target *targetPtr; 01796 int i; 01797 Tcl_Obj **objv; 01798 01799 Tcl_DecrRefCount(aliasPtr->token); 01800 objv = &aliasPtr->objPtr; 01801 for (i = 0; i < aliasPtr->objc; i++) { 01802 Tcl_DecrRefCount(objv[i]); 01803 } 01804 Tcl_DeleteHashEntry(aliasPtr->aliasEntryPtr); 01805 01806 /* 01807 * Splice the target record out of the target interpreter's master list. 01808 */ 01809 01810 targetPtr = aliasPtr->targetPtr; 01811 if (targetPtr->prevPtr != NULL) { 01812 targetPtr->prevPtr->nextPtr = targetPtr->nextPtr; 01813 } else { 01814 Master *masterPtr = &((InterpInfo *) ((Interp *) 01815 aliasPtr->targetInterp)->interpInfo)->master; 01816 01817 masterPtr->targetsPtr = targetPtr->nextPtr; 01818 } 01819 if (targetPtr->nextPtr != NULL) { 01820 targetPtr->nextPtr->prevPtr = targetPtr->prevPtr; 01821 } 01822 01823 ckfree((char *) targetPtr); 01824 ckfree((char *) aliasPtr); 01825 } 01826 01827 /* 01828 *---------------------------------------------------------------------- 01829 * 01830 * Tcl_CreateSlave -- 01831 * 01832 * Creates a slave interpreter. The slavePath argument denotes the name 01833 * of the new slave relative to the current interpreter; the slave is a 01834 * direct descendant of the one-before-last component of the path, 01835 * e.g. it is a descendant of the current interpreter if the slavePath 01836 * argument contains only one component. Optionally makes the slave 01837 * interpreter safe. 01838 * 01839 * Results: 01840 * Returns the interpreter structure created, or NULL if an error 01841 * occurred. 01842 * 01843 * Side effects: 01844 * Creates a new interpreter and a new interpreter object command in the 01845 * interpreter indicated by the slavePath argument. 01846 * 01847 *---------------------------------------------------------------------- 01848 */ 01849 01850 Tcl_Interp * 01851 Tcl_CreateSlave( 01852 Tcl_Interp *interp, /* Interpreter to start search at. */ 01853 const char *slavePath, /* Name of slave to create. */ 01854 int isSafe) /* Should new slave be "safe" ? */ 01855 { 01856 Tcl_Obj *pathPtr; 01857 Tcl_Interp *slaveInterp; 01858 01859 pathPtr = Tcl_NewStringObj(slavePath, -1); 01860 slaveInterp = SlaveCreate(interp, pathPtr, isSafe); 01861 Tcl_DecrRefCount(pathPtr); 01862 01863 return slaveInterp; 01864 } 01865 01866 /* 01867 *---------------------------------------------------------------------- 01868 * 01869 * Tcl_GetSlave -- 01870 * 01871 * Finds a slave interpreter by its path name. 01872 * 01873 * Results: 01874 * Returns a Tcl_Interp * for the named interpreter or NULL if not found. 01875 * 01876 * Side effects: 01877 * None. 01878 * 01879 *---------------------------------------------------------------------- 01880 */ 01881 01882 Tcl_Interp * 01883 Tcl_GetSlave( 01884 Tcl_Interp *interp, /* Interpreter to start search from. */ 01885 const char *slavePath) /* Path of slave to find. */ 01886 { 01887 Tcl_Obj *pathPtr; 01888 Tcl_Interp *slaveInterp; 01889 01890 pathPtr = Tcl_NewStringObj(slavePath, -1); 01891 slaveInterp = GetInterp(interp, pathPtr); 01892 Tcl_DecrRefCount(pathPtr); 01893 01894 return slaveInterp; 01895 } 01896 01897 /* 01898 *---------------------------------------------------------------------- 01899 * 01900 * Tcl_GetMaster -- 01901 * 01902 * Finds the master interpreter of a slave interpreter. 01903 * 01904 * Results: 01905 * Returns a Tcl_Interp * for the master interpreter or NULL if none. 01906 * 01907 * Side effects: 01908 * None. 01909 * 01910 *---------------------------------------------------------------------- 01911 */ 01912 01913 Tcl_Interp * 01914 Tcl_GetMaster( 01915 Tcl_Interp *interp) /* Get the master of this interpreter. */ 01916 { 01917 Slave *slavePtr; /* Slave record of this interpreter. */ 01918 01919 if (interp == NULL) { 01920 return NULL; 01921 } 01922 slavePtr = &((InterpInfo *) ((Interp *) interp)->interpInfo)->slave; 01923 return slavePtr->masterInterp; 01924 } 01925 01926 /* 01927 *---------------------------------------------------------------------- 01928 * 01929 * Tcl_GetInterpPath -- 01930 * 01931 * Sets the result of the asking interpreter to a proper Tcl list 01932 * containing the names of interpreters between the asking and target 01933 * interpreters. The target interpreter must be either the same as the 01934 * asking interpreter or one of its slaves (including recursively). 01935 * 01936 * Results: 01937 * TCL_OK if the target interpreter is the same as, or a descendant of, 01938 * the asking interpreter; TCL_ERROR else. This way one can distinguish 01939 * between the case where the asking and target interps are the same (an 01940 * empty list is the result, and TCL_OK is returned) and when the target 01941 * is not a descendant of the asking interpreter (in which case the Tcl 01942 * result is an error message and the function returns TCL_ERROR). 01943 * 01944 * Side effects: 01945 * None. 01946 * 01947 *---------------------------------------------------------------------- 01948 */ 01949 01950 int 01951 Tcl_GetInterpPath( 01952 Tcl_Interp *askingInterp, /* Interpreter to start search from. */ 01953 Tcl_Interp *targetInterp) /* Interpreter to find. */ 01954 { 01955 InterpInfo *iiPtr; 01956 01957 if (targetInterp == askingInterp) { 01958 return TCL_OK; 01959 } 01960 if (targetInterp == NULL) { 01961 return TCL_ERROR; 01962 } 01963 iiPtr = (InterpInfo *) ((Interp *) targetInterp)->interpInfo; 01964 if (Tcl_GetInterpPath(askingInterp, iiPtr->slave.masterInterp) != TCL_OK) { 01965 return TCL_ERROR; 01966 } 01967 Tcl_AppendElement(askingInterp, Tcl_GetHashKey(&iiPtr->master.slaveTable, 01968 iiPtr->slave.slaveEntryPtr)); 01969 return TCL_OK; 01970 } 01971 01972 /* 01973 *---------------------------------------------------------------------- 01974 * 01975 * GetInterp -- 01976 * 01977 * Helper function to find a slave interpreter given a pathname. 01978 * 01979 * Results: 01980 * Returns the slave interpreter known by that name in the calling 01981 * interpreter, or NULL if no interpreter known by that name exists. 01982 * 01983 * Side effects: 01984 * Assigns to the pointer variable passed in, if not NULL. 01985 * 01986 *---------------------------------------------------------------------- 01987 */ 01988 01989 static Tcl_Interp * 01990 GetInterp( 01991 Tcl_Interp *interp, /* Interp. to start search from. */ 01992 Tcl_Obj *pathPtr) /* List object containing name of interp. to 01993 * be found. */ 01994 { 01995 Tcl_HashEntry *hPtr; /* Search element. */ 01996 Slave *slavePtr; /* Interim slave record. */ 01997 Tcl_Obj **objv; 01998 int objc, i; 01999 Tcl_Interp *searchInterp; /* Interim storage for interp. to find. */ 02000 InterpInfo *masterInfoPtr; 02001 02002 if (TclListObjGetElements(interp, pathPtr, &objc, &objv) != TCL_OK) { 02003 return NULL; 02004 } 02005 02006 searchInterp = interp; 02007 for (i = 0; i < objc; i++) { 02008 masterInfoPtr = (InterpInfo *) ((Interp *) searchInterp)->interpInfo; 02009 hPtr = Tcl_FindHashEntry(&masterInfoPtr->master.slaveTable, 02010 TclGetString(objv[i])); 02011 if (hPtr == NULL) { 02012 searchInterp = NULL; 02013 break; 02014 } 02015 slavePtr = Tcl_GetHashValue(hPtr); 02016 searchInterp = slavePtr->slaveInterp; 02017 if (searchInterp == NULL) { 02018 break; 02019 } 02020 } 02021 if (searchInterp == NULL) { 02022 Tcl_AppendResult(interp, "could not find interpreter \"", 02023 TclGetString(pathPtr), "\"", NULL); 02024 Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "INTERP", 02025 TclGetString(pathPtr), NULL); 02026 } 02027 return searchInterp; 02028 } 02029 02030 /* 02031 *---------------------------------------------------------------------- 02032 * 02033 * SlaveBgerror -- 02034 * 02035 * Helper function to set/query the background error handling command 02036 * prefix of an interp 02037 * 02038 * Results: 02039 * A standard Tcl result. 02040 * 02041 * Side effects: 02042 * When (objc == 1), slaveInterp will be set to a new background handler 02043 * of objv[0]. 02044 * 02045 *---------------------------------------------------------------------- 02046 */ 02047 02048 static int 02049 SlaveBgerror( 02050 Tcl_Interp *interp, /* Interp for error return. */ 02051 Tcl_Interp *slaveInterp, /* Interp in which limit is set/queried. */ 02052 int objc, /* Set or Query. */ 02053 Tcl_Obj *const objv[]) /* Argument strings. */ 02054 { 02055 if (objc) { 02056 int length; 02057 02058 if (TCL_ERROR == TclListObjLength(NULL, objv[0], &length) 02059 || (length < 1)) { 02060 Tcl_AppendResult(interp, "cmdPrefix must be list of length >= 1", 02061 NULL); 02062 return TCL_ERROR; 02063 } 02064 TclSetBgErrorHandler(interp, objv[0]); 02065 } 02066 Tcl_SetObjResult(interp, TclGetBgErrorHandler(interp)); 02067 return TCL_OK; 02068 } 02069 02070 /* 02071 *---------------------------------------------------------------------- 02072 * 02073 * SlaveCreate -- 02074 * 02075 * Helper function to do the actual work of creating a slave interp and 02076 * new object command. Also optionally makes the new slave interpreter 02077 * "safe". 02078 * 02079 * Results: 02080 * Returns the new Tcl_Interp * if successful or NULL if not. If failed, 02081 * the result of the invoking interpreter contains an error message. 02082 * 02083 * Side effects: 02084 * Creates a new slave interpreter and a new object command. 02085 * 02086 *---------------------------------------------------------------------- 02087 */ 02088 02089 static Tcl_Interp * 02090 SlaveCreate( 02091 Tcl_Interp *interp, /* Interp. to start search from. */ 02092 Tcl_Obj *pathPtr, /* Path (name) of slave to create. */ 02093 int safe) /* Should we make it "safe"? */ 02094 { 02095 Tcl_Interp *masterInterp, *slaveInterp; 02096 Slave *slavePtr; 02097 InterpInfo *masterInfoPtr; 02098 Tcl_HashEntry *hPtr; 02099 char *path; 02100 int isNew, objc; 02101 Tcl_Obj **objv; 02102 02103 if (Tcl_ListObjGetElements(interp, pathPtr, &objc, &objv) != TCL_OK) { 02104 return NULL; 02105 } 02106 if (objc < 2) { 02107 masterInterp = interp; 02108 path = TclGetString(pathPtr); 02109 } else { 02110 Tcl_Obj *objPtr; 02111 02112 objPtr = Tcl_NewListObj(objc - 1, objv); 02113 masterInterp = GetInterp(interp, objPtr); 02114 Tcl_DecrRefCount(objPtr); 02115 if (masterInterp == NULL) { 02116 return NULL; 02117 } 02118 path = TclGetString(objv[objc - 1]); 02119 } 02120 if (safe == 0) { 02121 safe = Tcl_IsSafe(masterInterp); 02122 } 02123 02124 masterInfoPtr = (InterpInfo *) ((Interp *) masterInterp)->interpInfo; 02125 hPtr = Tcl_CreateHashEntry(&masterInfoPtr->master.slaveTable, path, 02126 &isNew); 02127 if (isNew == 0) { 02128 Tcl_AppendResult(interp, "interpreter named \"", path, 02129 "\" already exists, cannot create", NULL); 02130 return NULL; 02131 } 02132 02133 slaveInterp = Tcl_CreateInterp(); 02134 slavePtr = &((InterpInfo *) ((Interp *) slaveInterp)->interpInfo)->slave; 02135 slavePtr->masterInterp = masterInterp; 02136 slavePtr->slaveEntryPtr = hPtr; 02137 slavePtr->slaveInterp = slaveInterp; 02138 slavePtr->interpCmd = Tcl_CreateObjCommand(masterInterp, path, 02139 SlaveObjCmd, slaveInterp, SlaveObjCmdDeleteProc); 02140 Tcl_InitHashTable(&slavePtr->aliasTable, TCL_STRING_KEYS); 02141 Tcl_SetHashValue(hPtr, slavePtr); 02142 Tcl_SetVar(slaveInterp, "tcl_interactive", "0", TCL_GLOBAL_ONLY); 02143 02144 /* 02145 * Inherit the recursion limit. 02146 */ 02147 02148 ((Interp *) slaveInterp)->maxNestingDepth = 02149 ((Interp *) masterInterp)->maxNestingDepth; 02150 02151 if (safe) { 02152 if (Tcl_MakeSafe(slaveInterp) == TCL_ERROR) { 02153 goto error; 02154 } 02155 } else { 02156 if (Tcl_Init(slaveInterp) == TCL_ERROR) { 02157 goto error; 02158 } 02159 02160 /* 02161 * This will create the "memory" command in slave interpreters if we 02162 * compiled with TCL_MEM_DEBUG, otherwise it does nothing. 02163 */ 02164 02165 Tcl_InitMemory(slaveInterp); 02166 } 02167 02168 /* 02169 * Inherit the TIP#143 limits. 02170 */ 02171 02172 InheritLimitsFromMaster(slaveInterp, masterInterp); 02173 02174 /* 02175 * The [clock] command presents a safe API, but uses unsafe features in 02176 * its implementation. This means it has to be implemented in safe interps 02177 * as an alias to a version in the (trusted) master. 02178 */ 02179 02180 if (safe) { 02181 Tcl_Obj *clockObj; 02182 int status; 02183 02184 TclNewLiteralStringObj(clockObj, "clock"); 02185 Tcl_IncrRefCount(clockObj); 02186 status = AliasCreate(interp, slaveInterp, masterInterp, clockObj, 02187 clockObj, 0, NULL); 02188 Tcl_DecrRefCount(clockObj); 02189 if (status != TCL_OK) { 02190 goto error2; 02191 } 02192 } 02193 02194 return slaveInterp; 02195 02196 error: 02197 TclTransferResult(slaveInterp, TCL_ERROR, interp); 02198 error2: 02199 Tcl_DeleteInterp(slaveInterp); 02200 02201 return NULL; 02202 } 02203 02204 /* 02205 *---------------------------------------------------------------------- 02206 * 02207 * SlaveObjCmd -- 02208 * 02209 * Command to manipulate an interpreter, e.g. to send commands to it to 02210 * be evaluated. One such command exists for each slave interpreter. 02211 * 02212 * Results: 02213 * A standard Tcl result. 02214 * 02215 * Side effects: 02216 * See user documentation for details. 02217 * 02218 *---------------------------------------------------------------------- 02219 */ 02220 02221 static int 02222 SlaveObjCmd( 02223 ClientData clientData, /* Slave interpreter. */ 02224 Tcl_Interp *interp, /* Current interpreter. */ 02225 int objc, /* Number of arguments. */ 02226 Tcl_Obj *const objv[]) /* Argument objects. */ 02227 { 02228 Tcl_Interp *slaveInterp = clientData; 02229 int index; 02230 static const char *options[] = { 02231 "alias", "aliases", "bgerror", "eval", 02232 "expose", "hide", "hidden", "issafe", 02233 "invokehidden", "limit", "marktrusted", "recursionlimit", NULL 02234 }; 02235 enum options { 02236 OPT_ALIAS, OPT_ALIASES, OPT_BGERROR, OPT_EVAL, 02237 OPT_EXPOSE, OPT_HIDE, OPT_HIDDEN, OPT_ISSAFE, 02238 OPT_INVOKEHIDDEN, OPT_LIMIT, OPT_MARKTRUSTED, OPT_RECLIMIT 02239 }; 02240 02241 if (slaveInterp == NULL) { 02242 Tcl_Panic("SlaveObjCmd: interpreter has been deleted"); 02243 } 02244 02245 if (objc < 2) { 02246 Tcl_WrongNumArgs(interp, 1, objv, "cmd ?arg ...?"); 02247 return TCL_ERROR; 02248 } 02249 if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0, 02250 &index) != TCL_OK) { 02251 return TCL_ERROR; 02252 } 02253 02254 switch ((enum options) index) { 02255 case OPT_ALIAS: 02256 if (objc > 2) { 02257 if (objc == 3) { 02258 return AliasDescribe(interp, slaveInterp, objv[2]); 02259 } 02260 if (TclGetString(objv[3])[0] == '\0') { 02261 if (objc == 4) { 02262 return AliasDelete(interp, slaveInterp, objv[2]); 02263 } 02264 } else { 02265 return AliasCreate(interp, slaveInterp, interp, objv[2], 02266 objv[3], objc - 4, objv + 4); 02267 } 02268 } 02269 Tcl_WrongNumArgs(interp, 2, objv, "aliasName ?targetName? ?args..?"); 02270 return TCL_ERROR; 02271 case OPT_ALIASES: 02272 if (objc != 2) { 02273 Tcl_WrongNumArgs(interp, 2, objv, NULL); 02274 return TCL_ERROR; 02275 } 02276 return AliasList(interp, slaveInterp); 02277 case OPT_BGERROR: 02278 if (objc != 2 && objc != 3) { 02279 Tcl_WrongNumArgs(interp, 2, objv, "?cmdPrefix?"); 02280 return TCL_ERROR; 02281 } 02282 return SlaveBgerror(interp, slaveInterp, objc - 2, objv + 2); 02283 case OPT_EVAL: 02284 if (objc < 3) { 02285 Tcl_WrongNumArgs(interp, 2, objv, "arg ?arg ...?"); 02286 return TCL_ERROR; 02287 } 02288 return SlaveEval(interp, slaveInterp, objc - 2, objv + 2); 02289 case OPT_EXPOSE: 02290 if ((objc < 3) || (objc > 4)) { 02291 Tcl_WrongNumArgs(interp, 2, objv, "hiddenCmdName ?cmdName?"); 02292 return TCL_ERROR; 02293 } 02294 return SlaveExpose(interp, slaveInterp, objc - 2, objv + 2); 02295 case OPT_HIDE: 02296 if ((objc < 3) || (objc > 4)) { 02297 Tcl_WrongNumArgs(interp, 2, objv, "cmdName ?hiddenCmdName?"); 02298 return TCL_ERROR; 02299 } 02300 return SlaveHide(interp, slaveInterp, objc - 2, objv + 2); 02301 case OPT_HIDDEN: 02302 if (objc != 2) { 02303 Tcl_WrongNumArgs(interp, 2, objv, NULL); 02304 return TCL_ERROR; 02305 } 02306 return SlaveHidden(interp, slaveInterp); 02307 case OPT_ISSAFE: 02308 if (objc != 2) { 02309 Tcl_WrongNumArgs(interp, 2, objv, NULL); 02310 return TCL_ERROR; 02311 } 02312 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(Tcl_IsSafe(slaveInterp))); 02313 return TCL_OK; 02314 case OPT_INVOKEHIDDEN: { 02315 int i, index; 02316 const char *namespaceName; 02317 static const char *hiddenOptions[] = { 02318 "-global", "-namespace", "--", NULL 02319 }; 02320 enum hiddenOption { 02321 OPT_GLOBAL, OPT_NAMESPACE, OPT_LAST 02322 }; 02323 02324 namespaceName = NULL; 02325 for (i = 2; i < objc; i++) { 02326 if (TclGetString(objv[i])[0] != '-') { 02327 break; 02328 } 02329 if (Tcl_GetIndexFromObj(interp, objv[i], hiddenOptions, "option", 02330 0, &index) != TCL_OK) { 02331 return TCL_ERROR; 02332 } 02333 if (index == OPT_GLOBAL) { 02334 namespaceName = "::"; 02335 } else if (index == OPT_NAMESPACE) { 02336 if (++i == objc) { /* There must be more arguments. */ 02337 break; 02338 } else { 02339 namespaceName = TclGetString(objv[i]); 02340 } 02341 } else { 02342 i++; 02343 break; 02344 } 02345 } 02346 if (objc - i < 1) { 02347 Tcl_WrongNumArgs(interp, 2, objv, 02348 "?-namespace ns? ?-global? ?--? cmd ?arg ..?"); 02349 return TCL_ERROR; 02350 } 02351 return SlaveInvokeHidden(interp, slaveInterp, namespaceName, 02352 objc - i, objv + i); 02353 } 02354 case OPT_LIMIT: { 02355 static const char *limitTypes[] = { 02356 "commands", "time", NULL 02357 }; 02358 enum LimitTypes { 02359 LIMIT_TYPE_COMMANDS, LIMIT_TYPE_TIME 02360 }; 02361 int limitType; 02362 02363 if (objc < 3) { 02364 Tcl_WrongNumArgs(interp, 2, objv, "limitType ?options?"); 02365 return TCL_ERROR; 02366 } 02367 if (Tcl_GetIndexFromObj(interp, objv[2], limitTypes, "limit type", 0, 02368 &limitType) != TCL_OK) { 02369 return TCL_ERROR; 02370 } 02371 switch ((enum LimitTypes) limitType) { 02372 case LIMIT_TYPE_COMMANDS: 02373 return SlaveCommandLimitCmd(interp, slaveInterp, 3, objc,objv); 02374 case LIMIT_TYPE_TIME: 02375 return SlaveTimeLimitCmd(interp, slaveInterp, 3, objc, objv); 02376 } 02377 } 02378 case OPT_MARKTRUSTED: 02379 if (objc != 2) { 02380 Tcl_WrongNumArgs(interp, 2, objv, NULL); 02381 return TCL_ERROR; 02382 } 02383 return SlaveMarkTrusted(interp, slaveInterp); 02384 case OPT_RECLIMIT: 02385 if (objc != 2 && objc != 3) { 02386 Tcl_WrongNumArgs(interp, 2, objv, "?newlimit?"); 02387 return TCL_ERROR; 02388 } 02389 return SlaveRecursionLimit(interp, slaveInterp, objc - 2, objv + 2); 02390 } 02391 02392 return TCL_ERROR; 02393 } 02394 02395 /* 02396 *---------------------------------------------------------------------- 02397 * 02398 * SlaveObjCmdDeleteProc -- 02399 * 02400 * Invoked when an object command for a slave interpreter is deleted; 02401 * cleans up all state associated with the slave interpreter and destroys 02402 * the slave interpreter. 02403 * 02404 * Results: 02405 * None. 02406 * 02407 * Side effects: 02408 * Cleans up all state associated with the slave interpreter and destroys 02409 * the slave interpreter. 02410 * 02411 *---------------------------------------------------------------------- 02412 */ 02413 02414 static void 02415 SlaveObjCmdDeleteProc( 02416 ClientData clientData) /* The SlaveRecord for the command. */ 02417 { 02418 Slave *slavePtr; /* Interim storage for Slave record. */ 02419 Tcl_Interp *slaveInterp = clientData; 02420 /* And for a slave interp. */ 02421 02422 slavePtr = &((InterpInfo *) ((Interp *) slaveInterp)->interpInfo)->slave; 02423 02424 /* 02425 * Unlink the slave from its master interpreter. 02426 */ 02427 02428 Tcl_DeleteHashEntry(slavePtr->slaveEntryPtr); 02429 02430 /* 02431 * Set to NULL so that when the InterpInfo is cleaned up in the slave it 02432 * does not try to delete the command causing all sorts of grief. See 02433 * SlaveRecordDeleteProc(). 02434 */ 02435 02436 slavePtr->interpCmd = NULL; 02437 02438 if (slavePtr->slaveInterp != NULL) { 02439 Tcl_DeleteInterp(slavePtr->slaveInterp); 02440 } 02441 } 02442 02443 /* 02444 *---------------------------------------------------------------------- 02445 * 02446 * SlaveEval -- 02447 * 02448 * Helper function to evaluate a command in a slave interpreter. 02449 * 02450 * Results: 02451 * A standard Tcl result. 02452 * 02453 * Side effects: 02454 * Whatever the command does. 02455 * 02456 *---------------------------------------------------------------------- 02457 */ 02458 02459 static int 02460 SlaveEval( 02461 Tcl_Interp *interp, /* Interp for error return. */ 02462 Tcl_Interp *slaveInterp, /* The slave interpreter in which command 02463 * will be evaluated. */ 02464 int objc, /* Number of arguments. */ 02465 Tcl_Obj *const objv[]) /* Argument objects. */ 02466 { 02467 int result; 02468 Tcl_Obj *objPtr; 02469 02470 Tcl_Preserve(slaveInterp); 02471 Tcl_AllowExceptions(slaveInterp); 02472 02473 if (objc == 1) { 02474 /* 02475 * TIP #280: Make invoker available to eval'd script. 02476 */ 02477 02478 Interp *iPtr = (Interp *) interp; 02479 result = TclEvalObjEx(slaveInterp, objv[0], 0, iPtr->cmdFramePtr, 0); 02480 } else { 02481 objPtr = Tcl_ConcatObj(objc, objv); 02482 Tcl_IncrRefCount(objPtr); 02483 result = Tcl_EvalObjEx(slaveInterp, objPtr, 0); 02484 Tcl_DecrRefCount(objPtr); 02485 } 02486 TclTransferResult(slaveInterp, result, interp); 02487 02488 Tcl_Release(slaveInterp); 02489 return result; 02490 } 02491 02492 /* 02493 *---------------------------------------------------------------------- 02494 * 02495 * SlaveExpose -- 02496 * 02497 * Helper function to expose a command in a slave interpreter. 02498 * 02499 * Results: 02500 * A standard Tcl result. 02501 * 02502 * Side effects: 02503 * After this call scripts in the slave will be able to invoke the newly 02504 * exposed command. 02505 * 02506 *---------------------------------------------------------------------- 02507 */ 02508 02509 static int 02510 SlaveExpose( 02511 Tcl_Interp *interp, /* Interp for error return. */ 02512 Tcl_Interp *slaveInterp, /* Interp in which command will be exposed. */ 02513 int objc, /* Number of arguments. */ 02514 Tcl_Obj *const objv[]) /* Argument strings. */ 02515 { 02516 char *name; 02517 02518 if (Tcl_IsSafe(interp)) { 02519 Tcl_SetObjResult(interp, Tcl_NewStringObj( 02520 "permission denied: safe interpreter cannot expose commands", 02521 -1)); 02522 return TCL_ERROR; 02523 } 02524 02525 name = TclGetString(objv[(objc == 1) ? 0 : 1]); 02526 if (Tcl_ExposeCommand(slaveInterp, TclGetString(objv[0]), 02527 name) != TCL_OK) { 02528 TclTransferResult(slaveInterp, TCL_ERROR, interp); 02529 return TCL_ERROR; 02530 } 02531 return TCL_OK; 02532 } 02533 02534 /* 02535 *---------------------------------------------------------------------- 02536 * 02537 * SlaveRecursionLimit -- 02538 * 02539 * Helper function to set/query the Recursion limit of an interp 02540 * 02541 * Results: 02542 * A standard Tcl result. 02543 * 02544 * Side effects: 02545 * When (objc == 1), slaveInterp will be set to a new recursion limit of 02546 * objv[0]. 02547 * 02548 *---------------------------------------------------------------------- 02549 */ 02550 02551 static int 02552 SlaveRecursionLimit( 02553 Tcl_Interp *interp, /* Interp for error return. */ 02554 Tcl_Interp *slaveInterp, /* Interp in which limit is set/queried. */ 02555 int objc, /* Set or Query. */ 02556 Tcl_Obj *const objv[]) /* Argument strings. */ 02557 { 02558 Interp *iPtr; 02559 int limit; 02560 02561 if (objc) { 02562 if (Tcl_IsSafe(interp)) { 02563 Tcl_AppendResult(interp, "permission denied: " 02564 "safe interpreters cannot change recursion limit", NULL); 02565 return TCL_ERROR; 02566 } 02567 if (TclGetIntFromObj(interp, objv[0], &limit) == TCL_ERROR) { 02568 return TCL_ERROR; 02569 } 02570 if (limit <= 0) { 02571 Tcl_SetObjResult(interp, Tcl_NewStringObj( 02572 "recursion limit must be > 0", -1)); 02573 return TCL_ERROR; 02574 } 02575 Tcl_SetRecursionLimit(slaveInterp, limit); 02576 iPtr = (Interp *) slaveInterp; 02577 if (interp == slaveInterp && iPtr->numLevels > limit) { 02578 Tcl_SetObjResult(interp, Tcl_NewStringObj( 02579 "falling back due to new recursion limit", -1)); 02580 return TCL_ERROR; 02581 } 02582 Tcl_SetObjResult(interp, objv[0]); 02583 return TCL_OK; 02584 } else { 02585 limit = Tcl_SetRecursionLimit(slaveInterp, 0); 02586 Tcl_SetObjResult(interp, Tcl_NewIntObj(limit)); 02587 return TCL_OK; 02588 } 02589 } 02590 02591 /* 02592 *---------------------------------------------------------------------- 02593 * 02594 * SlaveHide -- 02595 * 02596 * Helper function to hide a command in a slave interpreter. 02597 * 02598 * Results: 02599 * A standard Tcl result. 02600 * 02601 * Side effects: 02602 * After this call scripts in the slave will no longer be able to invoke 02603 * the named command. 02604 * 02605 *---------------------------------------------------------------------- 02606 */ 02607 02608 static int 02609 SlaveHide( 02610 Tcl_Interp *interp, /* Interp for error return. */ 02611 Tcl_Interp *slaveInterp, /* Interp in which command will be exposed. */ 02612 int objc, /* Number of arguments. */ 02613 Tcl_Obj *const objv[]) /* Argument strings. */ 02614 { 02615 char *name; 02616 02617 if (Tcl_IsSafe(interp)) { 02618 Tcl_SetObjResult(interp, Tcl_NewStringObj( 02619 "permission denied: safe interpreter cannot hide commands", 02620 -1)); 02621 return TCL_ERROR; 02622 } 02623 02624 name = TclGetString(objv[(objc == 1) ? 0 : 1]); 02625 if (Tcl_HideCommand(slaveInterp, TclGetString(objv[0]), name) != TCL_OK) { 02626 TclTransferResult(slaveInterp, TCL_ERROR, interp); 02627 return TCL_ERROR; 02628 } 02629 return TCL_OK; 02630 } 02631 02632 /* 02633 *---------------------------------------------------------------------- 02634 * 02635 * SlaveHidden -- 02636 * 02637 * Helper function to compute list of hidden commands in a slave 02638 * interpreter. 02639 * 02640 * Results: 02641 * A standard Tcl result. 02642 * 02643 * Side effects: 02644 * None. 02645 * 02646 *---------------------------------------------------------------------- 02647 */ 02648 02649 static int 02650 SlaveHidden( 02651 Tcl_Interp *interp, /* Interp for data return. */ 02652 Tcl_Interp *slaveInterp) /* Interp whose hidden commands to query. */ 02653 { 02654 Tcl_Obj *listObjPtr = Tcl_NewObj(); /* Local object pointer. */ 02655 Tcl_HashTable *hTblPtr; /* For local searches. */ 02656 Tcl_HashEntry *hPtr; /* For local searches. */ 02657 Tcl_HashSearch hSearch; /* For local searches. */ 02658 02659 hTblPtr = ((Interp *) slaveInterp)->hiddenCmdTablePtr; 02660 if (hTblPtr != NULL) { 02661 for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch); 02662 hPtr != NULL; 02663 hPtr = Tcl_NextHashEntry(&hSearch)) { 02664 Tcl_ListObjAppendElement(NULL, listObjPtr, 02665 Tcl_NewStringObj(Tcl_GetHashKey(hTblPtr, hPtr), -1)); 02666 } 02667 } 02668 Tcl_SetObjResult(interp, listObjPtr); 02669 return TCL_OK; 02670 } 02671 02672 /* 02673 *---------------------------------------------------------------------- 02674 * 02675 * SlaveInvokeHidden -- 02676 * 02677 * Helper function to invoke a hidden command in a slave interpreter. 02678 * 02679 * Results: 02680 * A standard Tcl result. 02681 * 02682 * Side effects: 02683 * Whatever the hidden command does. 02684 * 02685 *---------------------------------------------------------------------- 02686 */ 02687 02688 static int 02689 SlaveInvokeHidden( 02690 Tcl_Interp *interp, /* Interp for error return. */ 02691 Tcl_Interp *slaveInterp, /* The slave interpreter in which command will 02692 * be invoked. */ 02693 const char *namespaceName, /* The namespace to use, if any. */ 02694 int objc, /* Number of arguments. */ 02695 Tcl_Obj *const objv[]) /* Argument objects. */ 02696 { 02697 int result; 02698 02699 if (Tcl_IsSafe(interp)) { 02700 Tcl_SetObjResult(interp, Tcl_NewStringObj( 02701 "not allowed to invoke hidden commands from safe interpreter", 02702 -1)); 02703 return TCL_ERROR; 02704 } 02705 02706 Tcl_Preserve(slaveInterp); 02707 Tcl_AllowExceptions(slaveInterp); 02708 02709 if (namespaceName == NULL) { 02710 result = TclObjInvoke(slaveInterp, objc, objv, TCL_INVOKE_HIDDEN); 02711 } else { 02712 Namespace *nsPtr, *dummy1, *dummy2; 02713 const char *tail; 02714 02715 result = TclGetNamespaceForQualName(slaveInterp, namespaceName, NULL, 02716 TCL_FIND_ONLY_NS | TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG 02717 | TCL_CREATE_NS_IF_UNKNOWN, &nsPtr, &dummy1, &dummy2, &tail); 02718 if (result == TCL_OK) { 02719 result = TclObjInvokeNamespace(slaveInterp, objc, objv, 02720 (Tcl_Namespace *)nsPtr, TCL_INVOKE_HIDDEN); 02721 } 02722 } 02723 02724 TclTransferResult(slaveInterp, result, interp); 02725 02726 Tcl_Release(slaveInterp); 02727 return result; 02728 } 02729 02730 /* 02731 *---------------------------------------------------------------------- 02732 * 02733 * SlaveMarkTrusted -- 02734 * 02735 * Helper function to mark a slave interpreter as trusted (unsafe). 02736 * 02737 * Results: 02738 * A standard Tcl result. 02739 * 02740 * Side effects: 02741 * After this call the hard-wired security checks in the core no longer 02742 * prevent the slave from performing certain operations. 02743 * 02744 *---------------------------------------------------------------------- 02745 */ 02746 02747 static int 02748 SlaveMarkTrusted( 02749 Tcl_Interp *interp, /* Interp for error return. */ 02750 Tcl_Interp *slaveInterp) /* The slave interpreter which will be marked 02751 * trusted. */ 02752 { 02753 if (Tcl_IsSafe(interp)) { 02754 Tcl_SetObjResult(interp, Tcl_NewStringObj( 02755 "permission denied: safe interpreter cannot mark trusted", 02756 -1)); 02757 return TCL_ERROR; 02758 } 02759 ((Interp *) slaveInterp)->flags &= ~SAFE_INTERP; 02760 return TCL_OK; 02761 } 02762 02763 /* 02764 *---------------------------------------------------------------------- 02765 * 02766 * Tcl_IsSafe -- 02767 * 02768 * Determines whether an interpreter is safe 02769 * 02770 * Results: 02771 * 1 if it is safe, 0 if it is not. 02772 * 02773 * Side effects: 02774 * None. 02775 * 02776 *---------------------------------------------------------------------- 02777 */ 02778 02779 int 02780 Tcl_IsSafe( 02781 Tcl_Interp *interp) /* Is this interpreter "safe" ? */ 02782 { 02783 Interp *iPtr = (Interp *) interp; 02784 02785 if (iPtr == NULL) { 02786 return 0; 02787 } 02788 return (iPtr->flags & SAFE_INTERP) ? 1 : 0; 02789 } 02790 02791 /* 02792 *---------------------------------------------------------------------- 02793 * 02794 * Tcl_MakeSafe -- 02795 * 02796 * Makes its argument interpreter contain only functionality that is 02797 * defined to be part of Safe Tcl. Unsafe commands are hidden, the env 02798 * array is unset, and the standard channels are removed. 02799 * 02800 * Results: 02801 * None. 02802 * 02803 * Side effects: 02804 * Hides commands in its argument interpreter, and removes settings and 02805 * channels. 02806 * 02807 *---------------------------------------------------------------------- 02808 */ 02809 02810 int 02811 Tcl_MakeSafe( 02812 Tcl_Interp *interp) /* Interpreter to be made safe. */ 02813 { 02814 Tcl_Channel chan; /* Channel to remove from safe interpreter. */ 02815 Interp *iPtr = (Interp *) interp; 02816 02817 TclHideUnsafeCommands(interp); 02818 02819 iPtr->flags |= SAFE_INTERP; 02820 02821 /* 02822 * Unsetting variables : (which should not have been set in the first 02823 * place, but...) 02824 */ 02825 02826 /* 02827 * No env array in a safe slave. 02828 */ 02829 02830 Tcl_UnsetVar(interp, "env", TCL_GLOBAL_ONLY); 02831 02832 /* 02833 * Remove unsafe parts of tcl_platform 02834 */ 02835 02836 Tcl_UnsetVar2(interp, "tcl_platform", "os", TCL_GLOBAL_ONLY); 02837 Tcl_UnsetVar2(interp, "tcl_platform", "osVersion", TCL_GLOBAL_ONLY); 02838 Tcl_UnsetVar2(interp, "tcl_platform", "machine", TCL_GLOBAL_ONLY); 02839 Tcl_UnsetVar2(interp, "tcl_platform", "user", TCL_GLOBAL_ONLY); 02840 02841 /* 02842 * Unset path informations variables (the only one remaining is [info 02843 * nameofexecutable]) 02844 */ 02845 02846 Tcl_UnsetVar(interp, "tclDefaultLibrary", TCL_GLOBAL_ONLY); 02847 Tcl_UnsetVar(interp, "tcl_library", TCL_GLOBAL_ONLY); 02848 Tcl_UnsetVar(interp, "tcl_pkgPath", TCL_GLOBAL_ONLY); 02849 02850 /* 02851 * Remove the standard channels from the interpreter; safe interpreters do 02852 * not ordinarily have access to stdin, stdout and stderr. 02853 * 02854 * NOTE: These channels are not added to the interpreter by the 02855 * Tcl_CreateInterp call, but may be added later, by another I/O 02856 * operation. We want to ensure that the interpreter does not have these 02857 * channels even if it is being made safe after being used for some time.. 02858 */ 02859 02860 chan = Tcl_GetStdChannel(TCL_STDIN); 02861 if (chan != NULL) { 02862 Tcl_UnregisterChannel(interp, chan); 02863 } 02864 chan = Tcl_GetStdChannel(TCL_STDOUT); 02865 if (chan != NULL) { 02866 Tcl_UnregisterChannel(interp, chan); 02867 } 02868 chan = Tcl_GetStdChannel(TCL_STDERR); 02869 if (chan != NULL) { 02870 Tcl_UnregisterChannel(interp, chan); 02871 } 02872 02873 return TCL_OK; 02874 } 02875 02876 /* 02877 *---------------------------------------------------------------------- 02878 * 02879 * Tcl_LimitExceeded -- 02880 * 02881 * Tests whether any limit has been exceeded in the given interpreter 02882 * (i.e. whether the interpreter is currently unable to process further 02883 * scripts). 02884 * 02885 * Results: 02886 * A boolean value. 02887 * 02888 * Side effects: 02889 * None. 02890 * 02891 * Notes: 02892 * If you change this function, you MUST also update TclLimitExceeded() in 02893 * tclInt.h. 02894 *---------------------------------------------------------------------- 02895 */ 02896 02897 int 02898 Tcl_LimitExceeded( 02899 Tcl_Interp *interp) 02900 { 02901 register Interp *iPtr = (Interp *) interp; 02902 02903 return iPtr->limit.exceeded != 0; 02904 } 02905 02906 /* 02907 *---------------------------------------------------------------------- 02908 * 02909 * Tcl_LimitReady -- 02910 * 02911 * Find out whether any limit has been set on the interpreter, and if so 02912 * check whether the granularity of that limit is such that the full 02913 * limit check should be carried out. 02914 * 02915 * Results: 02916 * A boolean value that indicates whether to call Tcl_LimitCheck. 02917 * 02918 * Side effects: 02919 * Increments the limit granularity counter. 02920 * 02921 * Notes: 02922 * If you change this function, you MUST also update TclLimitReady() in 02923 * tclInt.h. 02924 * 02925 *---------------------------------------------------------------------- 02926 */ 02927 02928 int 02929 Tcl_LimitReady( 02930 Tcl_Interp *interp) 02931 { 02932 register Interp *iPtr = (Interp *) interp; 02933 02934 if (iPtr->limit.active != 0) { 02935 register int ticker = ++iPtr->limit.granularityTicker; 02936 02937 if ((iPtr->limit.active & TCL_LIMIT_COMMANDS) && 02938 ((iPtr->limit.cmdGranularity == 1) || 02939 (ticker % iPtr->limit.cmdGranularity == 0))) { 02940 return 1; 02941 } 02942 if ((iPtr->limit.active & TCL_LIMIT_TIME) && 02943 ((iPtr->limit.timeGranularity == 1) || 02944 (ticker % iPtr->limit.timeGranularity == 0))) { 02945 return 1; 02946 } 02947 } 02948 return 0; 02949 } 02950 02951 /* 02952 *---------------------------------------------------------------------- 02953 * 02954 * Tcl_LimitCheck -- 02955 * 02956 * Check all currently set limits in the interpreter (where permitted by 02957 * granularity). If a limit is exceeded, call its callbacks and, if the 02958 * limit is still exceeded after the callbacks have run, make the 02959 * interpreter generate an error that cannot be caught within the limited 02960 * interpreter. 02961 * 02962 * Results: 02963 * A Tcl result value (TCL_OK if no limit is exceeded, and TCL_ERROR if a 02964 * limit has been exceeded). 02965 * 02966 * Side effects: 02967 * May invoke system calls. May invoke other interpreters. May be 02968 * reentrant. May put the interpreter into a state where it can no longer 02969 * execute commands without outside intervention. 02970 * 02971 *---------------------------------------------------------------------- 02972 */ 02973 02974 int 02975 Tcl_LimitCheck( 02976 Tcl_Interp *interp) 02977 { 02978 Interp *iPtr = (Interp *) interp; 02979 register int ticker = iPtr->limit.granularityTicker; 02980 02981 if (Tcl_InterpDeleted(interp)) { 02982 return TCL_OK; 02983 } 02984 02985 if ((iPtr->limit.active & TCL_LIMIT_COMMANDS) && 02986 ((iPtr->limit.cmdGranularity == 1) || 02987 (ticker % iPtr->limit.cmdGranularity == 0)) && 02988 (iPtr->limit.cmdCount < iPtr->cmdCount)) { 02989 iPtr->limit.exceeded |= TCL_LIMIT_COMMANDS; 02990 Tcl_Preserve(interp); 02991 RunLimitHandlers(iPtr->limit.cmdHandlers, interp); 02992 if (iPtr->limit.cmdCount >= iPtr->cmdCount) { 02993 iPtr->limit.exceeded &= ~TCL_LIMIT_COMMANDS; 02994 } else if (iPtr->limit.exceeded & TCL_LIMIT_COMMANDS) { 02995 Tcl_ResetResult(interp); 02996 Tcl_AppendResult(interp, "command count limit exceeded", NULL); 02997 Tcl_Release(interp); 02998 return TCL_ERROR; 02999 } 03000 Tcl_Release(interp); 03001 } 03002 03003 if ((iPtr->limit.active & TCL_LIMIT_TIME) && 03004 ((iPtr->limit.timeGranularity == 1) || 03005 (ticker % iPtr->limit.timeGranularity == 0))) { 03006 Tcl_Time now; 03007 03008 Tcl_GetTime(&now); 03009 if (iPtr->limit.time.sec < now.sec || 03010 (iPtr->limit.time.sec == now.sec && 03011 iPtr->limit.time.usec < now.usec)) { 03012 iPtr->limit.exceeded |= TCL_LIMIT_TIME; 03013 Tcl_Preserve(interp); 03014 RunLimitHandlers(iPtr->limit.timeHandlers, interp); 03015 if (iPtr->limit.time.sec > now.sec || 03016 (iPtr->limit.time.sec == now.sec && 03017 iPtr->limit.time.usec >= now.usec)) { 03018 iPtr->limit.exceeded &= ~TCL_LIMIT_TIME; 03019 } else if (iPtr->limit.exceeded & TCL_LIMIT_TIME) { 03020 Tcl_ResetResult(interp); 03021 Tcl_AppendResult(interp, "time limit exceeded", NULL); 03022 Tcl_Release(interp); 03023 return TCL_ERROR; 03024 } 03025 Tcl_Release(interp); 03026 } 03027 } 03028 03029 return TCL_OK; 03030 } 03031 03032 /* 03033 *---------------------------------------------------------------------- 03034 * 03035 * RunLimitHandlers -- 03036 * 03037 * Invoke all the limit handlers in a list (for a particular limit). 03038 * Note that no particular limit handler callback will be invoked 03039 * reentrantly. 03040 * 03041 * Results: 03042 * None. 03043 * 03044 * Side effects: 03045 * Depends on the limit handlers. 03046 * 03047 *---------------------------------------------------------------------- 03048 */ 03049 03050 static void 03051 RunLimitHandlers( 03052 LimitHandler *handlerPtr, 03053 Tcl_Interp *interp) 03054 { 03055 LimitHandler *nextPtr; 03056 for (; handlerPtr!=NULL ; handlerPtr=nextPtr) { 03057 if (handlerPtr->flags & (LIMIT_HANDLER_DELETED|LIMIT_HANDLER_ACTIVE)) { 03058 /* 03059 * Reentrant call or something seriously strange in the delete 03060 * code. 03061 */ 03062 03063 nextPtr = handlerPtr->nextPtr; 03064 continue; 03065 } 03066 03067 /* 03068 * Set the ACTIVE flag while running the limit handler itself so we 03069 * cannot reentrantly call this handler and know to use the alternate 03070 * method of deletion if necessary. 03071 */ 03072 03073 handlerPtr->flags |= LIMIT_HANDLER_ACTIVE; 03074 (handlerPtr->handlerProc)(handlerPtr->clientData, interp); 03075 handlerPtr->flags &= ~LIMIT_HANDLER_ACTIVE; 03076 03077 /* 03078 * Rediscover this value; it might have changed during the processing 03079 * of a limit handler. We have to record it here because we might 03080 * delete the structure below, and reading a value out of a deleted 03081 * structure is unsafe (even if actually legal with some 03082 * malloc()/free() implementations.) 03083 */ 03084 03085 nextPtr = handlerPtr->nextPtr; 03086 03087 /* 03088 * If we deleted the current handler while we were executing it, we 03089 * will have spliced it out of the list and set the 03090 * LIMIT_HANDLER_DELETED flag. 03091 */ 03092 03093 if (handlerPtr->flags & LIMIT_HANDLER_DELETED) { 03094 if (handlerPtr->deleteProc != NULL) { 03095 (handlerPtr->deleteProc)(handlerPtr->clientData); 03096 } 03097 ckfree((char *) handlerPtr); 03098 } 03099 } 03100 } 03101 03102 /* 03103 *---------------------------------------------------------------------- 03104 * 03105 * Tcl_LimitAddHandler -- 03106 * 03107 * Add a callback handler for a particular resource limit. 03108 * 03109 * Results: 03110 * None. 03111 * 03112 * Side effects: 03113 * Extends the internal linked list of handlers for a limit. 03114 * 03115 *---------------------------------------------------------------------- 03116 */ 03117 03118 void 03119 Tcl_LimitAddHandler( 03120 Tcl_Interp *interp, 03121 int type, 03122 Tcl_LimitHandlerProc *handlerProc, 03123 ClientData clientData, 03124 Tcl_LimitHandlerDeleteProc *deleteProc) 03125 { 03126 Interp *iPtr = (Interp *) interp; 03127 LimitHandler *handlerPtr; 03128 03129 /* 03130 * Convert everything into a real deletion callback. 03131 */ 03132 03133 if (deleteProc == (Tcl_LimitHandlerDeleteProc *) TCL_DYNAMIC) { 03134 deleteProc = (Tcl_LimitHandlerDeleteProc *) Tcl_Free; 03135 } 03136 if (deleteProc == (Tcl_LimitHandlerDeleteProc *) TCL_STATIC) { 03137 deleteProc = NULL; 03138 } 03139 03140 /* 03141 * Allocate a handler record. 03142 */ 03143 03144 handlerPtr = (LimitHandler *) ckalloc(sizeof(LimitHandler)); 03145 handlerPtr->flags = 0; 03146 handlerPtr->handlerProc = handlerProc; 03147 handlerPtr->clientData = clientData; 03148 handlerPtr->deleteProc = deleteProc; 03149 handlerPtr->prevPtr = NULL; 03150 03151 /* 03152 * Prepend onto the front of the correct linked list. 03153 */ 03154 03155 switch (type) { 03156 case TCL_LIMIT_COMMANDS: 03157 handlerPtr->nextPtr = iPtr->limit.cmdHandlers; 03158 if (handlerPtr->nextPtr != NULL) { 03159 handlerPtr->nextPtr->prevPtr = handlerPtr; 03160 } 03161 iPtr->limit.cmdHandlers = handlerPtr; 03162 return; 03163 03164 case TCL_LIMIT_TIME: 03165 handlerPtr->nextPtr = iPtr->limit.timeHandlers; 03166 if (handlerPtr->nextPtr != NULL) { 03167 handlerPtr->nextPtr->prevPtr = handlerPtr; 03168 } 03169 iPtr->limit.timeHandlers = handlerPtr; 03170 return; 03171 } 03172 03173 Tcl_Panic("unknown type of resource limit"); 03174 } 03175 03176 /* 03177 *---------------------------------------------------------------------- 03178 * 03179 * Tcl_LimitRemoveHandler -- 03180 * 03181 * Remove a callback handler for a particular resource limit. 03182 * 03183 * Results: 03184 * None. 03185 * 03186 * Side effects: 03187 * The handler is spliced out of the internal linked list for the limit, 03188 * and if not currently being invoked, deleted. Otherwise it is just 03189 * marked for deletion and removed when the limit handler has finished 03190 * executing. 03191 * 03192 *---------------------------------------------------------------------- 03193 */ 03194 03195 void 03196 Tcl_LimitRemoveHandler( 03197 Tcl_Interp *interp, 03198 int type, 03199 Tcl_LimitHandlerProc *handlerProc, 03200 ClientData clientData) 03201 { 03202 Interp *iPtr = (Interp *) interp; 03203 LimitHandler *handlerPtr; 03204 03205 switch (type) { 03206 case TCL_LIMIT_COMMANDS: 03207 handlerPtr = iPtr->limit.cmdHandlers; 03208 break; 03209 case TCL_LIMIT_TIME: 03210 handlerPtr = iPtr->limit.timeHandlers; 03211 break; 03212 default: 03213 Tcl_Panic("unknown type of resource limit"); 03214 return; 03215 } 03216 03217 for (; handlerPtr!=NULL ; handlerPtr=handlerPtr->nextPtr) { 03218 if ((handlerPtr->handlerProc != handlerProc) || 03219 (handlerPtr->clientData != clientData)) { 03220 continue; 03221 } 03222 03223 /* 03224 * We've found the handler to delete; mark it as doomed if not already 03225 * so marked (which shouldn't actually happen). 03226 */ 03227 03228 if (handlerPtr->flags & LIMIT_HANDLER_DELETED) { 03229 return; 03230 } 03231 handlerPtr->flags |= LIMIT_HANDLER_DELETED; 03232 03233 /* 03234 * Splice the handler out of the doubly-linked list. 03235 */ 03236 03237 if (handlerPtr->prevPtr == NULL) { 03238 switch (type) { 03239 case TCL_LIMIT_COMMANDS: 03240 iPtr->limit.cmdHandlers = handlerPtr->nextPtr; 03241 break; 03242 case TCL_LIMIT_TIME: 03243 iPtr->limit.timeHandlers = handlerPtr->nextPtr; 03244 break; 03245 } 03246 } else { 03247 handlerPtr->prevPtr->nextPtr = handlerPtr->nextPtr; 03248 } 03249 if (handlerPtr->nextPtr != NULL) { 03250 handlerPtr->nextPtr->prevPtr = handlerPtr->prevPtr; 03251 } 03252 03253 /* 03254 * If nothing is currently executing the handler, delete its client 03255 * data and the overall handler structure now. Otherwise it will all 03256 * go away when the handler returns. 03257 */ 03258 03259 if (!(handlerPtr->flags & LIMIT_HANDLER_ACTIVE)) { 03260 if (handlerPtr->deleteProc != NULL) { 03261 (handlerPtr->deleteProc)(handlerPtr->clientData); 03262 } 03263 ckfree((char *) handlerPtr); 03264 } 03265 return; 03266 } 03267 } 03268 03269 /* 03270 *---------------------------------------------------------------------- 03271 * 03272 * TclLimitRemoveAllHandlers -- 03273 * 03274 * Remove all limit callback handlers for an interpreter. This is invoked 03275 * as part of deleting the interpreter. 03276 * 03277 * Results: 03278 * None. 03279 * 03280 * Side effects: 03281 * Limit handlers are deleted or marked for deletion (as with 03282 * Tcl_LimitRemoveHandler). 03283 * 03284 *---------------------------------------------------------------------- 03285 */ 03286 03287 void 03288 TclLimitRemoveAllHandlers( 03289 Tcl_Interp *interp) 03290 { 03291 Interp *iPtr = (Interp *) interp; 03292 LimitHandler *handlerPtr, *nextHandlerPtr; 03293 03294 /* 03295 * Delete all command-limit handlers. 03296 */ 03297 03298 for (handlerPtr=iPtr->limit.cmdHandlers, iPtr->limit.cmdHandlers=NULL; 03299 handlerPtr!=NULL; handlerPtr=nextHandlerPtr) { 03300 nextHandlerPtr = handlerPtr->nextPtr; 03301 03302 /* 03303 * Do not delete here if it has already been marked for deletion. 03304 */ 03305 03306 if (handlerPtr->flags & LIMIT_HANDLER_DELETED) { 03307 continue; 03308 } 03309 handlerPtr->flags |= LIMIT_HANDLER_DELETED; 03310 handlerPtr->prevPtr = NULL; 03311 handlerPtr->nextPtr = NULL; 03312 03313 /* 03314 * If nothing is currently executing the handler, delete its client 03315 * data and the overall handler structure now. Otherwise it will all 03316 * go away when the handler returns. 03317 */ 03318 03319 if (!(handlerPtr->flags & LIMIT_HANDLER_ACTIVE)) { 03320 if (handlerPtr->deleteProc != NULL) { 03321 (handlerPtr->deleteProc)(handlerPtr->clientData); 03322 } 03323 ckfree((char *) handlerPtr); 03324 } 03325 } 03326 03327 /* 03328 * Delete all time-limit handlers. 03329 */ 03330 03331 for (handlerPtr=iPtr->limit.timeHandlers, iPtr->limit.timeHandlers=NULL; 03332 handlerPtr!=NULL; handlerPtr=nextHandlerPtr) { 03333 nextHandlerPtr = handlerPtr->nextPtr; 03334 03335 /* 03336 * Do not delete here if it has already been marked for deletion. 03337 */ 03338 03339 if (handlerPtr->flags & LIMIT_HANDLER_DELETED) { 03340 continue; 03341 } 03342 handlerPtr->flags |= LIMIT_HANDLER_DELETED; 03343 handlerPtr->prevPtr = NULL; 03344 handlerPtr->nextPtr = NULL; 03345 03346 /* 03347 * If nothing is currently executing the handler, delete its client 03348 * data and the overall handler structure now. Otherwise it will all 03349 * go away when the handler returns. 03350 */ 03351 03352 if (!(handlerPtr->flags & LIMIT_HANDLER_ACTIVE)) { 03353 if (handlerPtr->deleteProc != NULL) { 03354 (handlerPtr->deleteProc)(handlerPtr->clientData); 03355 } 03356 ckfree((char *) handlerPtr); 03357 } 03358 } 03359 03360 /* 03361 * Delete the timer callback that is used to trap limits that occur in 03362 * [vwait]s... 03363 */ 03364 03365 if (iPtr->limit.timeEvent != NULL) { 03366 Tcl_DeleteTimerHandler(iPtr->limit.timeEvent); 03367 iPtr->limit.timeEvent = NULL; 03368 } 03369 } 03370 03371 /* 03372 *---------------------------------------------------------------------- 03373 * 03374 * Tcl_LimitTypeEnabled -- 03375 * 03376 * Check whether a particular limit has been enabled for an interpreter. 03377 * 03378 * Results: 03379 * A boolean value. 03380 * 03381 * Side effects: 03382 * None. 03383 * 03384 *---------------------------------------------------------------------- 03385 */ 03386 03387 int 03388 Tcl_LimitTypeEnabled( 03389 Tcl_Interp *interp, 03390 int type) 03391 { 03392 Interp *iPtr = (Interp *) interp; 03393 03394 return (iPtr->limit.active & type) != 0; 03395 } 03396 03397 /* 03398 *---------------------------------------------------------------------- 03399 * 03400 * Tcl_LimitTypeExceeded -- 03401 * 03402 * Check whether a particular limit has been exceeded for an interpreter. 03403 * 03404 * Results: 03405 * A boolean value (note that Tcl_LimitExceeded will always return 03406 * non-zero when this function returns non-zero). 03407 * 03408 * Side effects: 03409 * None. 03410 * 03411 *---------------------------------------------------------------------- 03412 */ 03413 03414 int 03415 Tcl_LimitTypeExceeded( 03416 Tcl_Interp *interp, 03417 int type) 03418 { 03419 Interp *iPtr = (Interp *) interp; 03420 03421 return (iPtr->limit.exceeded & type) != 0; 03422 } 03423 03424 /* 03425 *---------------------------------------------------------------------- 03426 * 03427 * Tcl_LimitTypeSet -- 03428 * 03429 * Enable a particular limit for an interpreter. 03430 * 03431 * Results: 03432 * None. 03433 * 03434 * Side effects: 03435 * The limit is turned on and will be checked in future at an interval 03436 * determined by the frequency of calling of Tcl_LimitReady and the 03437 * granularity of the limit in question. 03438 * 03439 *---------------------------------------------------------------------- 03440 */ 03441 03442 void 03443 Tcl_LimitTypeSet( 03444 Tcl_Interp *interp, 03445 int type) 03446 { 03447 Interp *iPtr = (Interp *) interp; 03448 03449 iPtr->limit.active |= type; 03450 } 03451 03452 /* 03453 *---------------------------------------------------------------------- 03454 * 03455 * Tcl_LimitTypeReset -- 03456 * 03457 * Disable a particular limit for an interpreter. 03458 * 03459 * Results: 03460 * None. 03461 * 03462 * Side effects: 03463 * The limit is disabled. If the limit was exceeded when this function 03464 * was called, the limit will no longer be exceeded afterwards and the 03465 * interpreter will be free to execute further scripts (assuming it isn't 03466 * also deleted, of course). 03467 * 03468 *---------------------------------------------------------------------- 03469 */ 03470 03471 void 03472 Tcl_LimitTypeReset( 03473 Tcl_Interp *interp, 03474 int type) 03475 { 03476 Interp *iPtr = (Interp *) interp; 03477 03478 iPtr->limit.active &= ~type; 03479 iPtr->limit.exceeded &= ~type; 03480 } 03481 03482 /* 03483 *---------------------------------------------------------------------- 03484 * 03485 * Tcl_LimitSetCommands -- 03486 * 03487 * Set the command limit for an interpreter. 03488 * 03489 * Results: 03490 * None. 03491 * 03492 * Side effects: 03493 * Also resets whether the command limit was exceeded. This might permit 03494 * a small amount of further execution in the interpreter even if the 03495 * limit itself is theoretically exceeded. 03496 * 03497 *---------------------------------------------------------------------- 03498 */ 03499 03500 void 03501 Tcl_LimitSetCommands( 03502 Tcl_Interp *interp, 03503 int commandLimit) 03504 { 03505 Interp *iPtr = (Interp *) interp; 03506 03507 iPtr->limit.cmdCount = commandLimit; 03508 iPtr->limit.exceeded &= ~TCL_LIMIT_COMMANDS; 03509 } 03510 03511 /* 03512 *---------------------------------------------------------------------- 03513 * 03514 * Tcl_LimitGetCommands -- 03515 * 03516 * Get the number of commands that may be executed in the interpreter 03517 * before the command-limit is reached. 03518 * 03519 * Results: 03520 * An upper bound on the number of commands. 03521 * 03522 * Side effects: 03523 * None. 03524 * 03525 *---------------------------------------------------------------------- 03526 */ 03527 03528 int 03529 Tcl_LimitGetCommands( 03530 Tcl_Interp *interp) 03531 { 03532 Interp *iPtr = (Interp *) interp; 03533 03534 return iPtr->limit.cmdCount; 03535 } 03536 03537 /* 03538 *---------------------------------------------------------------------- 03539 * 03540 * Tcl_LimitSetTime -- 03541 * 03542 * Set the time limit for an interpreter by copying it from the value 03543 * pointed to by the timeLimitPtr argument. 03544 * 03545 * Results: 03546 * None. 03547 * 03548 * Side effects: 03549 * Also resets whether the time limit was exceeded. This might permit a 03550 * small amount of further execution in the interpreter even if the limit 03551 * itself is theoretically exceeded. 03552 * 03553 *---------------------------------------------------------------------- 03554 */ 03555 03556 void 03557 Tcl_LimitSetTime( 03558 Tcl_Interp *interp, 03559 Tcl_Time *timeLimitPtr) 03560 { 03561 Interp *iPtr = (Interp *) interp; 03562 Tcl_Time nextMoment; 03563 03564 memcpy(&iPtr->limit.time, timeLimitPtr, sizeof(Tcl_Time)); 03565 if (iPtr->limit.timeEvent != NULL) { 03566 Tcl_DeleteTimerHandler(iPtr->limit.timeEvent); 03567 } 03568 nextMoment.sec = timeLimitPtr->sec; 03569 nextMoment.usec = timeLimitPtr->usec+10; 03570 if (nextMoment.usec >= 1000000) { 03571 nextMoment.sec++; 03572 nextMoment.usec -= 1000000; 03573 } 03574 iPtr->limit.timeEvent = TclCreateAbsoluteTimerHandler(&nextMoment, 03575 TimeLimitCallback, interp); 03576 iPtr->limit.exceeded &= ~TCL_LIMIT_TIME; 03577 } 03578 03579 /* 03580 *---------------------------------------------------------------------- 03581 * 03582 * TimeLimitCallback -- 03583 * 03584 * Callback that allows time limits to be enforced even when doing a 03585 * blocking wait for events. 03586 * 03587 * Results: 03588 * None. 03589 * 03590 * Side effects: 03591 * May put the interpreter into a state where it can no longer execute 03592 * commands. May make callbacks into other interpreters. 03593 * 03594 *---------------------------------------------------------------------- 03595 */ 03596 03597 static void 03598 TimeLimitCallback( 03599 ClientData clientData) 03600 { 03601 Tcl_Interp *interp = clientData; 03602 int code; 03603 03604 Tcl_Preserve(interp); 03605 ((Interp *)interp)->limit.timeEvent = NULL; 03606 code = Tcl_LimitCheck(interp); 03607 if (code != TCL_OK) { 03608 Tcl_AddErrorInfo(interp, "\n (while waiting for event)"); 03609 TclBackgroundException(interp, code); 03610 } 03611 Tcl_Release(interp); 03612 } 03613 03614 /* 03615 *---------------------------------------------------------------------- 03616 * 03617 * Tcl_LimitGetTime -- 03618 * 03619 * Get the current time limit. 03620 * 03621 * Results: 03622 * The time limit (by it being copied into the variable pointed to by the 03623 * timeLimitPtr). 03624 * 03625 * Side effects: 03626 * None. 03627 * 03628 *---------------------------------------------------------------------- 03629 */ 03630 03631 void 03632 Tcl_LimitGetTime( 03633 Tcl_Interp *interp, 03634 Tcl_Time *timeLimitPtr) 03635 { 03636 Interp *iPtr = (Interp *) interp; 03637 03638 memcpy(timeLimitPtr, &iPtr->limit.time, sizeof(Tcl_Time)); 03639 } 03640 03641 /* 03642 *---------------------------------------------------------------------- 03643 * 03644 * Tcl_LimitSetGranularity -- 03645 * 03646 * Set the granularity divisor (which must be positive) for a particular 03647 * limit. 03648 * 03649 * Results: 03650 * None. 03651 * 03652 * Side effects: 03653 * The granularity is updated. 03654 * 03655 *---------------------------------------------------------------------- 03656 */ 03657 03658 void 03659 Tcl_LimitSetGranularity( 03660 Tcl_Interp *interp, 03661 int type, 03662 int granularity) 03663 { 03664 Interp *iPtr = (Interp *) interp; 03665 if (granularity < 1) { 03666 Tcl_Panic("limit granularity must be positive"); 03667 } 03668 03669 switch (type) { 03670 case TCL_LIMIT_COMMANDS: 03671 iPtr->limit.cmdGranularity = granularity; 03672 return; 03673 case TCL_LIMIT_TIME: 03674 iPtr->limit.timeGranularity = granularity; 03675 return; 03676 } 03677 Tcl_Panic("unknown type of resource limit"); 03678 } 03679 03680 /* 03681 *---------------------------------------------------------------------- 03682 * 03683 * Tcl_LimitGetGranularity -- 03684 * 03685 * Get the granularity divisor for a particular limit. 03686 * 03687 * Results: 03688 * The granularity divisor for the given limit. 03689 * 03690 * Side effects: 03691 * None. 03692 * 03693 *---------------------------------------------------------------------- 03694 */ 03695 03696 int 03697 Tcl_LimitGetGranularity( 03698 Tcl_Interp *interp, 03699 int type) 03700 { 03701 Interp *iPtr = (Interp *) interp; 03702 03703 switch (type) { 03704 case TCL_LIMIT_COMMANDS: 03705 return iPtr->limit.cmdGranularity; 03706 case TCL_LIMIT_TIME: 03707 return iPtr->limit.timeGranularity; 03708 } 03709 Tcl_Panic("unknown type of resource limit"); 03710 return -1; /* NOT REACHED */ 03711 } 03712 03713 /* 03714 *---------------------------------------------------------------------- 03715 * 03716 * DeleteScriptLimitCallback -- 03717 * 03718 * Callback for when a script limit (a limit callback implemented as a 03719 * Tcl script in a master interpreter, as set up from Tcl) is deleted. 03720 * 03721 * Results: 03722 * None. 03723 * 03724 * Side effects: 03725 * The reference to the script callback from the controlling interpreter 03726 * is removed. 03727 * 03728 *---------------------------------------------------------------------- 03729 */ 03730 03731 static void 03732 DeleteScriptLimitCallback( 03733 ClientData clientData) 03734 { 03735 ScriptLimitCallback *limitCBPtr = clientData; 03736 03737 Tcl_DecrRefCount(limitCBPtr->scriptObj); 03738 if (limitCBPtr->entryPtr != NULL) { 03739 Tcl_DeleteHashEntry(limitCBPtr->entryPtr); 03740 } 03741 ckfree((char *) limitCBPtr); 03742 } 03743 03744 /* 03745 *---------------------------------------------------------------------- 03746 * 03747 * CallScriptLimitCallback -- 03748 * 03749 * Invoke a script limit callback. Used to implement limit callbacks set 03750 * at the Tcl level on child interpreters. 03751 * 03752 * Results: 03753 * None. 03754 * 03755 * Side effects: 03756 * Depends on the callback script. Errors are reported as background 03757 * errors. 03758 * 03759 *---------------------------------------------------------------------- 03760 */ 03761 03762 static void 03763 CallScriptLimitCallback( 03764 ClientData clientData, 03765 Tcl_Interp *interp) /* Interpreter which failed the limit */ 03766 { 03767 ScriptLimitCallback *limitCBPtr = clientData; 03768 int code; 03769 03770 if (Tcl_InterpDeleted(limitCBPtr->interp)) { 03771 return; 03772 } 03773 Tcl_Preserve(limitCBPtr->interp); 03774 code = Tcl_EvalObjEx(limitCBPtr->interp, limitCBPtr->scriptObj, 03775 TCL_EVAL_GLOBAL); 03776 if (code != TCL_OK && !Tcl_InterpDeleted(limitCBPtr->interp)) { 03777 TclBackgroundException(limitCBPtr->interp, code); 03778 } 03779 Tcl_Release(limitCBPtr->interp); 03780 } 03781 03782 /* 03783 *---------------------------------------------------------------------- 03784 * 03785 * SetScriptLimitCallback -- 03786 * 03787 * Install (or remove, if scriptObj is NULL) a limit callback script that 03788 * is called when the target interpreter exceeds the type of limit 03789 * specified. Each interpreter may only have one callback set on another 03790 * interpreter through this mechanism (though as many interpreters may be 03791 * limited as the programmer chooses overall). 03792 * 03793 * Results: 03794 * None. 03795 * 03796 * Side effects: 03797 * A limit callback implemented as an invokation of a Tcl script in 03798 * another interpreter is either installed or removed. 03799 * 03800 *---------------------------------------------------------------------- 03801 */ 03802 03803 static void 03804 SetScriptLimitCallback( 03805 Tcl_Interp *interp, 03806 int type, 03807 Tcl_Interp *targetInterp, 03808 Tcl_Obj *scriptObj) 03809 { 03810 ScriptLimitCallback *limitCBPtr; 03811 Tcl_HashEntry *hashPtr; 03812 int isNew; 03813 ScriptLimitCallbackKey key; 03814 Interp *iPtr = (Interp *) interp; 03815 03816 if (interp == targetInterp) { 03817 Tcl_Panic("installing limit callback to the limited interpreter"); 03818 } 03819 03820 key.interp = targetInterp; 03821 key.type = type; 03822 03823 if (scriptObj == NULL) { 03824 hashPtr = Tcl_FindHashEntry(&iPtr->limit.callbacks, (char *) &key); 03825 if (hashPtr != NULL) { 03826 Tcl_LimitRemoveHandler(targetInterp, type, CallScriptLimitCallback, 03827 Tcl_GetHashValue(hashPtr)); 03828 } 03829 return; 03830 } 03831 03832 hashPtr = Tcl_CreateHashEntry(&iPtr->limit.callbacks, (char *) &key, 03833 &isNew); 03834 if (!isNew) { 03835 limitCBPtr = Tcl_GetHashValue(hashPtr); 03836 limitCBPtr->entryPtr = NULL; 03837 Tcl_LimitRemoveHandler(targetInterp, type, CallScriptLimitCallback, 03838 limitCBPtr); 03839 } 03840 03841 limitCBPtr = (ScriptLimitCallback *) ckalloc(sizeof(ScriptLimitCallback)); 03842 limitCBPtr->interp = interp; 03843 limitCBPtr->scriptObj = scriptObj; 03844 limitCBPtr->entryPtr = hashPtr; 03845 limitCBPtr->type = type; 03846 Tcl_IncrRefCount(scriptObj); 03847 03848 Tcl_LimitAddHandler(targetInterp, type, CallScriptLimitCallback, 03849 limitCBPtr, DeleteScriptLimitCallback); 03850 Tcl_SetHashValue(hashPtr, limitCBPtr); 03851 } 03852 03853 /* 03854 *---------------------------------------------------------------------- 03855 * 03856 * TclRemoveScriptLimitCallbacks -- 03857 * 03858 * Remove all script-implemented limit callbacks that make calls back 03859 * into the given interpreter. This invoked as part of deleting an 03860 * interpreter. 03861 * 03862 * Results: 03863 * None. 03864 * 03865 * Side effects: 03866 * The script limit callbacks are removed or marked for later removal. 03867 * 03868 *---------------------------------------------------------------------- 03869 */ 03870 03871 void 03872 TclRemoveScriptLimitCallbacks( 03873 Tcl_Interp *interp) 03874 { 03875 Interp *iPtr = (Interp *) interp; 03876 Tcl_HashEntry *hashPtr; 03877 Tcl_HashSearch search; 03878 ScriptLimitCallbackKey *keyPtr; 03879 03880 hashPtr = Tcl_FirstHashEntry(&iPtr->limit.callbacks, &search); 03881 while (hashPtr != NULL) { 03882 keyPtr = (ScriptLimitCallbackKey *) 03883 Tcl_GetHashKey(&iPtr->limit.callbacks, hashPtr); 03884 Tcl_LimitRemoveHandler(keyPtr->interp, keyPtr->type, 03885 CallScriptLimitCallback, Tcl_GetHashValue(hashPtr)); 03886 hashPtr = Tcl_NextHashEntry(&search); 03887 } 03888 Tcl_DeleteHashTable(&iPtr->limit.callbacks); 03889 } 03890 03891 /* 03892 *---------------------------------------------------------------------- 03893 * 03894 * TclInitLimitSupport -- 03895 * 03896 * Initialise all the parts of the interpreter relating to resource limit 03897 * management. This allows an interpreter to both have limits set upon 03898 * itself and set limits upon other interpreters. 03899 * 03900 * Results: 03901 * None. 03902 * 03903 * Side effects: 03904 * The resource limit subsystem is initialised for the interpreter. 03905 * 03906 *---------------------------------------------------------------------- 03907 */ 03908 03909 void 03910 TclInitLimitSupport( 03911 Tcl_Interp *interp) 03912 { 03913 Interp *iPtr = (Interp *) interp; 03914 03915 iPtr->limit.active = 0; 03916 iPtr->limit.granularityTicker = 0; 03917 iPtr->limit.exceeded = 0; 03918 iPtr->limit.cmdCount = 0; 03919 iPtr->limit.cmdHandlers = NULL; 03920 iPtr->limit.cmdGranularity = 1; 03921 memset(&iPtr->limit.time, 0, sizeof(Tcl_Time)); 03922 iPtr->limit.timeHandlers = NULL; 03923 iPtr->limit.timeEvent = NULL; 03924 iPtr->limit.timeGranularity = 10; 03925 Tcl_InitHashTable(&iPtr->limit.callbacks, 03926 sizeof(ScriptLimitCallbackKey)/sizeof(int)); 03927 } 03928 03929 /* 03930 *---------------------------------------------------------------------- 03931 * 03932 * InheritLimitsFromMaster -- 03933 * 03934 * Derive the interpreter limit configuration for a slave interpreter 03935 * from the limit config for the master. 03936 * 03937 * Results: 03938 * None. 03939 * 03940 * Side effects: 03941 * The slave interpreter limits are set so that if the master has a 03942 * limit, it may not exceed it by handing off work to slave interpreters. 03943 * Note that this does not transfer limit callbacks from the master to 03944 * the slave. 03945 * 03946 *---------------------------------------------------------------------- 03947 */ 03948 03949 static void 03950 InheritLimitsFromMaster( 03951 Tcl_Interp *slaveInterp, 03952 Tcl_Interp *masterInterp) 03953 { 03954 Interp *slavePtr = (Interp *) slaveInterp; 03955 Interp *masterPtr = (Interp *) masterInterp; 03956 03957 if (masterPtr->limit.active & TCL_LIMIT_COMMANDS) { 03958 slavePtr->limit.active |= TCL_LIMIT_COMMANDS; 03959 slavePtr->limit.cmdCount = 0; 03960 slavePtr->limit.cmdGranularity = masterPtr->limit.cmdGranularity; 03961 } 03962 if (masterPtr->limit.active & TCL_LIMIT_TIME) { 03963 slavePtr->limit.active |= TCL_LIMIT_TIME; 03964 memcpy(&slavePtr->limit.time, &masterPtr->limit.time, 03965 sizeof(Tcl_Time)); 03966 slavePtr->limit.timeGranularity = masterPtr->limit.timeGranularity; 03967 } 03968 } 03969 03970 /* 03971 *---------------------------------------------------------------------- 03972 * 03973 * SlaveCommandLimitCmd -- 03974 * 03975 * Implementation of the [interp limit $i commands] and [$i limit 03976 * commands] subcommands. See the interp manual page for a full 03977 * description. 03978 * 03979 * Results: 03980 * A standard Tcl result. 03981 * 03982 * Side effects: 03983 * Depends on the arguments. 03984 * 03985 *---------------------------------------------------------------------- 03986 */ 03987 03988 static int 03989 SlaveCommandLimitCmd( 03990 Tcl_Interp *interp, /* Current interpreter. */ 03991 Tcl_Interp *slaveInterp, /* Interpreter being adjusted. */ 03992 int consumedObjc, /* Number of args already parsed. */ 03993 int objc, /* Total number of arguments. */ 03994 Tcl_Obj *const objv[]) /* Argument objects. */ 03995 { 03996 static const char *options[] = { 03997 "-command", "-granularity", "-value", NULL 03998 }; 03999 enum Options { 04000 OPT_CMD, OPT_GRAN, OPT_VAL 04001 }; 04002 Interp *iPtr = (Interp *) interp; 04003 int index; 04004 ScriptLimitCallbackKey key; 04005 ScriptLimitCallback *limitCBPtr; 04006 Tcl_HashEntry *hPtr; 04007 04008 if (objc == consumedObjc) { 04009 Tcl_Obj *dictPtr; 04010 04011 TclNewObj(dictPtr); 04012 key.interp = slaveInterp; 04013 key.type = TCL_LIMIT_COMMANDS; 04014 hPtr = Tcl_FindHashEntry(&iPtr->limit.callbacks, (char *) &key); 04015 if (hPtr != NULL) { 04016 limitCBPtr = Tcl_GetHashValue(hPtr); 04017 if (limitCBPtr != NULL && limitCBPtr->scriptObj != NULL) { 04018 Tcl_DictObjPut(NULL, dictPtr, Tcl_NewStringObj(options[0], -1), 04019 limitCBPtr->scriptObj); 04020 } else { 04021 goto putEmptyCommandInDict; 04022 } 04023 } else { 04024 Tcl_Obj *empty; 04025 04026 putEmptyCommandInDict: 04027 TclNewObj(empty); 04028 Tcl_DictObjPut(NULL, dictPtr, 04029 Tcl_NewStringObj(options[0], -1), empty); 04030 } 04031 Tcl_DictObjPut(NULL, dictPtr, Tcl_NewStringObj(options[1], -1), 04032 Tcl_NewIntObj(Tcl_LimitGetGranularity(slaveInterp, 04033 TCL_LIMIT_COMMANDS))); 04034 04035 if (Tcl_LimitTypeEnabled(slaveInterp, TCL_LIMIT_COMMANDS)) { 04036 Tcl_DictObjPut(NULL, dictPtr, Tcl_NewStringObj(options[2], -1), 04037 Tcl_NewIntObj(Tcl_LimitGetCommands(slaveInterp))); 04038 } else { 04039 Tcl_Obj *empty; 04040 04041 TclNewObj(empty); 04042 Tcl_DictObjPut(NULL, dictPtr, 04043 Tcl_NewStringObj(options[2], -1), empty); 04044 } 04045 Tcl_SetObjResult(interp, dictPtr); 04046 return TCL_OK; 04047 } else if (objc == consumedObjc+1) { 04048 if (Tcl_GetIndexFromObj(interp, objv[consumedObjc], options, "option", 04049 0, &index) != TCL_OK) { 04050 return TCL_ERROR; 04051 } 04052 switch ((enum Options) index) { 04053 case OPT_CMD: 04054 key.interp = slaveInterp; 04055 key.type = TCL_LIMIT_COMMANDS; 04056 hPtr = Tcl_FindHashEntry(&iPtr->limit.callbacks, (char *) &key); 04057 if (hPtr != NULL) { 04058 limitCBPtr = Tcl_GetHashValue(hPtr); 04059 if (limitCBPtr != NULL && limitCBPtr->scriptObj != NULL) { 04060 Tcl_SetObjResult(interp, limitCBPtr->scriptObj); 04061 } 04062 } 04063 break; 04064 case OPT_GRAN: 04065 Tcl_SetObjResult(interp, Tcl_NewIntObj( 04066 Tcl_LimitGetGranularity(slaveInterp, TCL_LIMIT_COMMANDS))); 04067 break; 04068 case OPT_VAL: 04069 if (Tcl_LimitTypeEnabled(slaveInterp, TCL_LIMIT_COMMANDS)) { 04070 Tcl_SetObjResult(interp, 04071 Tcl_NewIntObj(Tcl_LimitGetCommands(slaveInterp))); 04072 } 04073 break; 04074 } 04075 return TCL_OK; 04076 } else if ((objc-consumedObjc) & 1 /* isOdd(objc-consumedObjc) */) { 04077 Tcl_WrongNumArgs(interp, consumedObjc, objv, 04078 "?-option? ?value? ?-option value ...?"); 04079 return TCL_ERROR; 04080 } else { 04081 int i, scriptLen = 0, limitLen = 0; 04082 Tcl_Obj *scriptObj = NULL, *granObj = NULL, *limitObj = NULL; 04083 int gran = 0, limit = 0; 04084 04085 for (i=consumedObjc ; i<objc ; i+=2) { 04086 if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, 04087 &index) != TCL_OK) { 04088 return TCL_ERROR; 04089 } 04090 switch ((enum Options) index) { 04091 case OPT_CMD: 04092 scriptObj = objv[i+1]; 04093 (void) Tcl_GetStringFromObj(objv[i+1], &scriptLen); 04094 break; 04095 case OPT_GRAN: 04096 granObj = objv[i+1]; 04097 if (TclGetIntFromObj(interp, objv[i+1], &gran) != TCL_OK) { 04098 return TCL_ERROR; 04099 } 04100 if (gran < 1) { 04101 Tcl_AppendResult(interp, "granularity must be at " 04102 "least 1", NULL); 04103 return TCL_ERROR; 04104 } 04105 break; 04106 case OPT_VAL: 04107 limitObj = objv[i+1]; 04108 (void) Tcl_GetStringFromObj(objv[i+1], &limitLen); 04109 if (limitLen == 0) { 04110 break; 04111 } 04112 if (TclGetIntFromObj(interp, objv[i+1], &limit) != TCL_OK) { 04113 return TCL_ERROR; 04114 } 04115 if (limit < 0) { 04116 Tcl_AppendResult(interp, "command limit value must be at " 04117 "least 0", NULL); 04118 return TCL_ERROR; 04119 } 04120 break; 04121 } 04122 } 04123 if (scriptObj != NULL) { 04124 SetScriptLimitCallback(interp, TCL_LIMIT_COMMANDS, slaveInterp, 04125 (scriptLen > 0 ? scriptObj : NULL)); 04126 } 04127 if (granObj != NULL) { 04128 Tcl_LimitSetGranularity(slaveInterp, TCL_LIMIT_COMMANDS, gran); 04129 } 04130 if (limitObj != NULL) { 04131 if (limitLen > 0) { 04132 Tcl_LimitSetCommands(slaveInterp, limit); 04133 Tcl_LimitTypeSet(slaveInterp, TCL_LIMIT_COMMANDS); 04134 } else { 04135 Tcl_LimitTypeReset(slaveInterp, TCL_LIMIT_COMMANDS); 04136 } 04137 } 04138 return TCL_OK; 04139 } 04140 } 04141 04142 /* 04143 *---------------------------------------------------------------------- 04144 * 04145 * SlaveTimeLimitCmd -- 04146 * 04147 * Implementation of the [interp limit $i time] and [$i limit time] 04148 * subcommands. See the interp manual page for a full description. 04149 * 04150 * Results: 04151 * A standard Tcl result. 04152 * 04153 * Side effects: 04154 * Depends on the arguments. 04155 * 04156 *---------------------------------------------------------------------- 04157 */ 04158 04159 static int 04160 SlaveTimeLimitCmd( 04161 Tcl_Interp *interp, /* Current interpreter. */ 04162 Tcl_Interp *slaveInterp, /* Interpreter being adjusted. */ 04163 int consumedObjc, /* Number of args already parsed. */ 04164 int objc, /* Total number of arguments. */ 04165 Tcl_Obj *const objv[]) /* Argument objects. */ 04166 { 04167 static const char *options[] = { 04168 "-command", "-granularity", "-milliseconds", "-seconds", NULL 04169 }; 04170 enum Options { 04171 OPT_CMD, OPT_GRAN, OPT_MILLI, OPT_SEC 04172 }; 04173 Interp *iPtr = (Interp *) interp; 04174 int index; 04175 ScriptLimitCallbackKey key; 04176 ScriptLimitCallback *limitCBPtr; 04177 Tcl_HashEntry *hPtr; 04178 04179 if (objc == consumedObjc) { 04180 Tcl_Obj *dictPtr; 04181 04182 TclNewObj(dictPtr); 04183 key.interp = slaveInterp; 04184 key.type = TCL_LIMIT_TIME; 04185 hPtr = Tcl_FindHashEntry(&iPtr->limit.callbacks, (char *) &key); 04186 if (hPtr != NULL) { 04187 limitCBPtr = Tcl_GetHashValue(hPtr); 04188 if (limitCBPtr != NULL && limitCBPtr->scriptObj != NULL) { 04189 Tcl_DictObjPut(NULL, dictPtr, Tcl_NewStringObj(options[0], -1), 04190 limitCBPtr->scriptObj); 04191 } else { 04192 goto putEmptyCommandInDict; 04193 } 04194 } else { 04195 Tcl_Obj *empty; 04196 putEmptyCommandInDict: 04197 TclNewObj(empty); 04198 Tcl_DictObjPut(NULL, dictPtr, 04199 Tcl_NewStringObj(options[0], -1), empty); 04200 } 04201 Tcl_DictObjPut(NULL, dictPtr, Tcl_NewStringObj(options[1], -1), 04202 Tcl_NewIntObj(Tcl_LimitGetGranularity(slaveInterp, 04203 TCL_LIMIT_TIME))); 04204 04205 if (Tcl_LimitTypeEnabled(slaveInterp, TCL_LIMIT_TIME)) { 04206 Tcl_Time limitMoment; 04207 04208 Tcl_LimitGetTime(slaveInterp, &limitMoment); 04209 Tcl_DictObjPut(NULL, dictPtr, Tcl_NewStringObj(options[2], -1), 04210 Tcl_NewLongObj(limitMoment.usec/1000)); 04211 Tcl_DictObjPut(NULL, dictPtr, Tcl_NewStringObj(options[3], -1), 04212 Tcl_NewLongObj(limitMoment.sec)); 04213 } else { 04214 Tcl_Obj *empty; 04215 04216 TclNewObj(empty); 04217 Tcl_DictObjPut(NULL, dictPtr, 04218 Tcl_NewStringObj(options[2], -1), empty); 04219 Tcl_DictObjPut(NULL, dictPtr, 04220 Tcl_NewStringObj(options[3], -1), empty); 04221 } 04222 Tcl_SetObjResult(interp, dictPtr); 04223 return TCL_OK; 04224 } else if (objc == consumedObjc+1) { 04225 if (Tcl_GetIndexFromObj(interp, objv[consumedObjc], options, "option", 04226 0, &index) != TCL_OK) { 04227 return TCL_ERROR; 04228 } 04229 switch ((enum Options) index) { 04230 case OPT_CMD: 04231 key.interp = slaveInterp; 04232 key.type = TCL_LIMIT_TIME; 04233 hPtr = Tcl_FindHashEntry(&iPtr->limit.callbacks, (char *) &key); 04234 if (hPtr != NULL) { 04235 limitCBPtr = Tcl_GetHashValue(hPtr); 04236 if (limitCBPtr != NULL && limitCBPtr->scriptObj != NULL) { 04237 Tcl_SetObjResult(interp, limitCBPtr->scriptObj); 04238 } 04239 } 04240 break; 04241 case OPT_GRAN: 04242 Tcl_SetObjResult(interp, Tcl_NewIntObj( 04243 Tcl_LimitGetGranularity(slaveInterp, TCL_LIMIT_TIME))); 04244 break; 04245 case OPT_MILLI: 04246 if (Tcl_LimitTypeEnabled(slaveInterp, TCL_LIMIT_TIME)) { 04247 Tcl_Time limitMoment; 04248 04249 Tcl_LimitGetTime(slaveInterp, &limitMoment); 04250 Tcl_SetObjResult(interp, 04251 Tcl_NewLongObj(limitMoment.usec/1000)); 04252 } 04253 break; 04254 case OPT_SEC: 04255 if (Tcl_LimitTypeEnabled(slaveInterp, TCL_LIMIT_TIME)) { 04256 Tcl_Time limitMoment; 04257 04258 Tcl_LimitGetTime(slaveInterp, &limitMoment); 04259 Tcl_SetObjResult(interp, Tcl_NewLongObj(limitMoment.sec)); 04260 } 04261 break; 04262 } 04263 return TCL_OK; 04264 } else if ((objc-consumedObjc) & 1 /* isOdd(objc-consumedObjc) */) { 04265 Tcl_WrongNumArgs(interp, consumedObjc, objv, 04266 "?-option? ?value? ?-option value ...?"); 04267 return TCL_ERROR; 04268 } else { 04269 int i, scriptLen = 0, milliLen = 0, secLen = 0; 04270 Tcl_Obj *scriptObj = NULL, *granObj = NULL; 04271 Tcl_Obj *milliObj = NULL, *secObj = NULL; 04272 int gran = 0; 04273 Tcl_Time limitMoment; 04274 int tmp; 04275 04276 Tcl_LimitGetTime(slaveInterp, &limitMoment); 04277 for (i=consumedObjc ; i<objc ; i+=2) { 04278 if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, 04279 &index) != TCL_OK) { 04280 return TCL_ERROR; 04281 } 04282 switch ((enum Options) index) { 04283 case OPT_CMD: 04284 scriptObj = objv[i+1]; 04285 (void) Tcl_GetStringFromObj(objv[i+1], &scriptLen); 04286 break; 04287 case OPT_GRAN: 04288 granObj = objv[i+1]; 04289 if (TclGetIntFromObj(interp, objv[i+1], &gran) != TCL_OK) { 04290 return TCL_ERROR; 04291 } 04292 if (gran < 1) { 04293 Tcl_AppendResult(interp, "granularity must be at " 04294 "least 1", NULL); 04295 return TCL_ERROR; 04296 } 04297 break; 04298 case OPT_MILLI: 04299 milliObj = objv[i+1]; 04300 (void) Tcl_GetStringFromObj(objv[i+1], &milliLen); 04301 if (milliLen == 0) { 04302 break; 04303 } 04304 if (TclGetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK) { 04305 return TCL_ERROR; 04306 } 04307 if (tmp < 0) { 04308 Tcl_AppendResult(interp, "milliseconds must be at least 0", 04309 NULL); 04310 return TCL_ERROR; 04311 } 04312 limitMoment.usec = ((long)tmp)*1000; 04313 break; 04314 case OPT_SEC: 04315 secObj = objv[i+1]; 04316 (void) Tcl_GetStringFromObj(objv[i+1], &secLen); 04317 if (secLen == 0) { 04318 break; 04319 } 04320 if (TclGetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK) { 04321 return TCL_ERROR; 04322 } 04323 if (tmp < 0) { 04324 Tcl_AppendResult(interp, "seconds must be at least 0", 04325 NULL); 04326 return TCL_ERROR; 04327 } 04328 limitMoment.sec = tmp; 04329 break; 04330 } 04331 } 04332 if (milliObj != NULL || secObj != NULL) { 04333 if (milliObj != NULL) { 04334 /* 04335 * Setting -milliseconds but clearing -seconds, or resetting 04336 * -milliseconds but not resetting -seconds? Bad voodoo! 04337 */ 04338 04339 if (secObj != NULL && secLen == 0 && milliLen > 0) { 04340 Tcl_AppendResult(interp, "may only set -milliseconds " 04341 "if -seconds is not also being reset", NULL); 04342 return TCL_ERROR; 04343 } 04344 if (milliLen == 0 && (secObj == NULL || secLen > 0)) { 04345 Tcl_AppendResult(interp, "may only reset -milliseconds " 04346 "if -seconds is also being reset", NULL); 04347 return TCL_ERROR; 04348 } 04349 } 04350 04351 if (milliLen > 0 || secLen > 0) { 04352 /* 04353 * Force usec to be in range [0..1000000), possibly 04354 * incrementing sec in the process. This makes it much easier 04355 * for people to write scripts that do small time increments. 04356 */ 04357 04358 limitMoment.sec += limitMoment.usec / 1000000; 04359 limitMoment.usec %= 1000000; 04360 04361 Tcl_LimitSetTime(slaveInterp, &limitMoment); 04362 Tcl_LimitTypeSet(slaveInterp, TCL_LIMIT_TIME); 04363 } else { 04364 Tcl_LimitTypeReset(slaveInterp, TCL_LIMIT_TIME); 04365 } 04366 } 04367 if (scriptObj != NULL) { 04368 SetScriptLimitCallback(interp, TCL_LIMIT_TIME, slaveInterp, 04369 (scriptLen > 0 ? scriptObj : NULL)); 04370 } 04371 if (granObj != NULL) { 04372 Tcl_LimitSetGranularity(slaveInterp, TCL_LIMIT_TIME, gran); 04373 } 04374 return TCL_OK; 04375 } 04376 } 04377 04378 /* 04379 * Local Variables: 04380 * mode: c 04381 * c-basic-offset: 4 04382 * fill-column: 78 04383 * End: 04384 */
Generated on Wed Mar 12 12:18:17 2008 by 1.5.1 |