tclInterp.c

Go 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  doxygen 1.5.1