diff options
Diffstat (limited to 'generic/tclInterp.c')
| -rw-r--r-- | generic/tclInterp.c | 6563 | 
1 files changed, 3789 insertions, 2774 deletions
| diff --git a/generic/tclInterp.c b/generic/tclInterp.c index 16f12c7..0da5d47 100644 --- a/generic/tclInterp.c +++ b/generic/tclInterp.c @@ -1,78 +1,94 @@ -/*  +/*   * tclInterp.c --   * - *	This file implements the "interp" command which allows creation - *	and manipulation of Tcl interpreters from within Tcl scripts. + *	This file implements the "interp" command which allows creation and + *	manipulation of Tcl interpreters from within Tcl scripts.   *   * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * Copyright (c) 2004 Donal K. Fellows   *   * See the file "license.terms" for information on usage and redistribution   * of this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclInterp.c,v 1.3 1998/09/14 18:40:00 stanton Exp $   */ -#include <stdio.h>  #include "tclInt.h" -#include "tclPort.h" + +/* + * A pointer to a string that holds an initialization script that if non-NULL + * is evaluated in Tcl_Init() prior to the built-in initialization script + * above. This variable can be modified by the function below. + */ + +static const char *tclPreInitScript = NULL; +/* Forward declaration */ +struct Target; +  /* - * Counter for how many aliases were created (global) + * struct Alias: + * + * Stores information about an alias. Is stored in the slave interpreter and + * used by the source command to find the target command in the master when + * the source command is invoked.   */ -static int aliasCounter = 0; +typedef struct Alias { +    Tcl_Obj *token;		/* Token for the alias command in the slave +				 * interp. This used to be the command name in +				 * the slave when the alias was first +				 * created. */ +    Tcl_Interp *targetInterp;	/* Interp in which target command will be +				 * invoked. */ +    Tcl_Command slaveCmd;	/* Source command in slave interpreter, bound +				 * to command that invokes the target command +				 * in the target interpreter. */ +    Tcl_HashEntry *aliasEntryPtr; +				/* Entry for the alias hash table in slave. +				 * This is used by alias deletion to remove +				 * the alias from the slave interpreter alias +				 * table. */ +    struct Target *targetPtr;	/* Entry for target command in master. This is +				 * used in the master interpreter to map back +				 * from the target command to aliases +				 * redirecting to it. */ +    int objc;			/* Count of Tcl_Obj in the prefix of the +				 * target command to be invoked in the target +				 * interpreter. Additional arguments specified +				 * when calling the alias in the slave interp +				 * will be appended to the prefix before the +				 * command is invoked. */ +    Tcl_Obj *objPtr;		/* The first actual prefix object - the target +				 * command name; this has to be at the end of +				 * the structure, which will be extended to +				 * accomodate the remaining objects in the +				 * prefix. */ +} Alias;  /*   *   * struct Slave:   *   * Used by the "interp" command to record and find information about slave - * interpreters. Maps from a command name in the master to information about - * a slave interpreter, e.g. what aliases are defined in it. + * interpreters. Maps from a command name in the master to information about a + * slave interpreter, e.g. what aliases are defined in it.   */ -typedef struct { +typedef struct Slave {      Tcl_Interp *masterInterp;	/* Master interpreter for this slave. */ -    Tcl_HashEntry *slaveEntry;	/* Hash entry in masters slave table for -                                 * this slave interpreter. Used to find -                                 * this record, and used when deleting the -                                 * slave interpreter to delete it from the -                                 * masters table. */ +    Tcl_HashEntry *slaveEntryPtr; +				/* Hash entry in masters slave table for this +				 * slave interpreter. Used to find this +				 * record, and used when deleting the slave +				 * interpreter to delete it from the master's +				 * table. */      Tcl_Interp	*slaveInterp;	/* The slave interpreter. */      Tcl_Command interpCmd;	/* Interpreter object command. */ -    Tcl_HashTable aliasTable;	/* Table which maps from names of commands -                                 * in slave interpreter to struct Alias -                                 * defined below. */ +    Tcl_HashTable aliasTable;	/* Table which maps from names of commands in +				 * slave interpreter to struct Alias defined +				 * below. */  } Slave;  /* - * struct Alias: - * - * Stores information about an alias. Is stored in the slave interpreter - * and used by the source command to find the target command in the master - * when the source command is invoked. - */ - -typedef struct { -    char	*aliasName;	/* Name of alias command. */ -    char	*targetName;	/* Name of target command in master interp. */ -    Tcl_Interp	*targetInterp;	/* Master interpreter. */ -    int		objc;		/* Count of additional args to pass. */ -    Tcl_Obj	**objv;		/* Actual additional args to pass. */ -    Tcl_HashEntry *aliasEntry;	/* Entry for the alias hash table in slave. -                                 * This is used by alias deletion to remove -                                 * the alias from the slave interpreter -                                 * alias table. */ -    Tcl_HashEntry *targetEntry;	/* Entry for target command in master. -                                 * This is used in the master interpreter to -                                 * map back from the target command to aliases -                                 * redirecting to it. Random access to this -                                 * hash table is never required - we are using -                                 * a hash table only for convenience. */ -    Tcl_Command slaveCmd;	/* Source command in slave interpreter. */ -} Alias; - -/*   * struct Target:   *   * Maps from master interpreter commands back to the source commands in slave @@ -80,893 +96,1517 @@ typedef struct {   * interpreters and must be deleted when the target interpreter is deleted. In   * case they would not be deleted the source interpreter would be left with a   * "dangling pointer". One such record is stored in the Master record of the - * master interpreter (in the targetTable hashtable, see below) with the - * master for each alias which directs to a command in the master. These - * records are used to remove the source command for an from a slave if/when - * the master is deleted. + * master interpreter with the master for each alias which directs to a + * command in the master. These records are used to remove the source command + * for an from a slave if/when the master is deleted. They are organized in a + * doubly-linked list attached to the master interpreter.   */ -typedef struct { +typedef struct Target {      Tcl_Command	slaveCmd;	/* Command for alias in slave interp. */      Tcl_Interp *slaveInterp;	/* Slave Interpreter. */ +    struct Target *nextPtr;	/* Next in list of target records, or NULL if +				 * at the end of the list of targets. */ +    struct Target *prevPtr;	/* Previous in list of target records, or NULL +				 * if at the start of the list of targets. */  } Target;  /*   * struct Master:   * - * This record is used for two purposes: First, slaveTable (a hashtable) - * maps from names of commands to slave interpreters. This hashtable is - * used to store information about slave interpreters of this interpreter, - * to map over all slaves, etc. The second purpose is to store information - * about all aliases in slaves (or siblings) which direct to target commands - * in this interpreter (using the targetTable hashtable). - *  - * NB: the flags field in the interp structure, used with SAFE_INTERP - * mask denotes whether the interpreter is safe or not. Safe - * interpreters have restricted functionality, can only create safe slave - * interpreters and can only load safe extensions. - */ - -typedef struct { -    Tcl_HashTable slaveTable;	/* Hash table for slave interpreters. -                                 * Maps from command names to Slave records. */ -    Tcl_HashTable targetTable;	/* Hash table for Target Records. Contains -                                 * all Target records which denote aliases -                                 * from slaves or sibling interpreters that -                                 * direct to commands in this interpreter. This -                                 * table is used to remove dangling pointers -                                 * from the slave (or sibling) interpreters -                                 * when this interpreter is deleted. */ + * This record is used for two purposes: First, slaveTable (a hashtable) maps + * from names of commands to slave interpreters. This hashtable is used to + * store information about slave interpreters of this interpreter, to map over + * all slaves, etc. The second purpose is to store information about all + * aliases in slaves (or siblings) which direct to target commands in this + * interpreter (using the targetsPtr doubly-linked list). + * + * NB: the flags field in the interp structure, used with SAFE_INTERP mask + * denotes whether the interpreter is safe or not. Safe interpreters have + * restricted functionality, can only create safe slave interpreters and can + * only load safe extensions. + */ + +typedef struct Master { +    Tcl_HashTable slaveTable;	/* Hash table for slave interpreters. Maps +				 * from command names to Slave records. */ +    Target *targetsPtr;		/* The head of a doubly-linked list of all the +				 * target records which denote aliases from +				 * slaves or sibling interpreters that direct +				 * to commands in this interpreter. This list +				 * is used to remove dangling pointers from +				 * the slave (or sibling) interpreters when +				 * this interpreter is deleted. */  } Master;  /* - * Prototypes for local static procedures: + * The following structure keeps track of all the Master and Slave information + * on a per-interp basis.   */ -static int		AliasCmd _ANSI_ARGS_((ClientData dummy, -			    Tcl_Interp *currentInterp, int objc, -		            Tcl_Obj *CONST objv[])); -static void		AliasCmdDeleteProc _ANSI_ARGS_(( -			    ClientData clientData)); -static int		AliasCreationHelper _ANSI_ARGS_((Tcl_Interp *curInterp, +typedef struct InterpInfo { +    Master master;		/* Keeps track of all interps for which this +				 * interp is the Master. */ +    Slave slave;		/* Information necessary for this interp to +				 * function as a slave. */ +} InterpInfo; + +/* + * Limit callbacks handled by scripts are modelled as structures which are + * stored in hashes indexed by a two-word key. Note that the type of the + * 'type' field in the key is not int; this is to make sure that things are + * likely to work properly on 64-bit architectures. + */ + +typedef struct ScriptLimitCallback { +    Tcl_Interp *interp;		/* The interpreter in which to execute the +				 * callback. */ +    Tcl_Obj *scriptObj;		/* The script to execute to perform the +				 * user-defined part of the callback. */ +    int type;			/* What kind of callback is this. */ +    Tcl_HashEntry *entryPtr;	/* The entry in the hash table maintained by +				 * the target interpreter that refers to this +				 * callback record, or NULL if the entry has +				 * already been deleted from that hash +				 * table. */ +} ScriptLimitCallback; + +typedef struct ScriptLimitCallbackKey { +    Tcl_Interp *interp;		/* The interpreter that the limit callback was +				 * attached to. This is not the interpreter +				 * that the callback runs in! */ +    long type;			/* The type of callback that this is. */ +} ScriptLimitCallbackKey; + +/* + * TIP#143 limit handler internal representation. + */ + +struct LimitHandler { +    int flags;			/* The state of this particular handler. */ +    Tcl_LimitHandlerProc *handlerProc; +				/* The handler callback. */ +    ClientData clientData;	/* Opaque argument to the handler callback. */ +    Tcl_LimitHandlerDeleteProc *deleteProc; +				/* How to delete the clientData. */ +    LimitHandler *prevPtr;	/* Previous item in linked list of +				 * handlers. */ +    LimitHandler *nextPtr;	/* Next item in linked list of handlers. */ +}; + +/* + * Values for the LimitHandler flags field. + *      LIMIT_HANDLER_ACTIVE - Whether the handler is currently being + *              processed; handlers are never to be entered reentrantly. + *      LIMIT_HANDLER_DELETED - Whether the handler has been deleted. This + *              should not normally be observed because when a handler is + *              deleted it is also spliced out of the list of handlers, but + *              even so we will be careful. + */ + +#define LIMIT_HANDLER_ACTIVE    0x01 +#define LIMIT_HANDLER_DELETED   0x02 + + + +/* + * Prototypes for local static functions: + */ + +static int		AliasCreate(Tcl_Interp *interp,  			    Tcl_Interp *slaveInterp, Tcl_Interp *masterInterp, -			    Master *masterPtr, char *aliasName, -			    char *targetName, int objc, -			    Tcl_Obj *CONST objv[])); -static int		CreateInterpObject _ANSI_ARGS_((Tcl_Interp *interp, -			    Master *masterPtr, int objc, -        		    Tcl_Obj *CONST objv[])); -static Tcl_Interp	*CreateSlave _ANSI_ARGS_((Tcl_Interp *interp, -		            Master *masterPtr, char *slavePath, int safe)); -static int		DeleteAlias _ANSI_ARGS_((Tcl_Interp *interp, -			    Tcl_Interp *slaveInterp, char *aliasName)); -static int		DescribeAlias _ANSI_ARGS_((Tcl_Interp *interp, -			    Tcl_Interp *slaveInterp, char *aliasName)); -static int		DeleteInterpObject _ANSI_ARGS_((Tcl_Interp *interp, -			    Master *masterPtr, int objc, -        		    Tcl_Obj *CONST objv[])); -static int		DeleteOneInterpObject _ANSI_ARGS_((Tcl_Interp *interp, -			    Master *masterPtr, char *path)); -static Tcl_Interp	*GetInterp _ANSI_ARGS_((Tcl_Interp *interp, -			    Master *masterPtr, char *path, -			    Master **masterPtrPtr)); -static int		GetTarget _ANSI_ARGS_((Tcl_Interp *interp, char *path, -			    char *aliasName)); -static int		InterpAliasHelper _ANSI_ARGS_((Tcl_Interp *interp, -		            Master *masterPtr, int objc, -        		    Tcl_Obj *CONST objv[])); -static int		InterpAliasesHelper _ANSI_ARGS_((Tcl_Interp *interp, -		            Master *masterPtr, int objc, -        		    Tcl_Obj *CONST objv[])); -static int		InterpExistsHelper _ANSI_ARGS_((Tcl_Interp *interp, -			    Master *masterPtr, int objc, -        		    Tcl_Obj *CONST objv[])); -static int		InterpEvalHelper _ANSI_ARGS_((Tcl_Interp *interp, -			    Master *masterPtr, int objc, -        		    Tcl_Obj *CONST objv[])); -static int		InterpExposeHelper _ANSI_ARGS_((Tcl_Interp *interp, -			    Master *masterPtr, int objc, -        		    Tcl_Obj *CONST objv[])); -static int		InterpIsSafeHelper _ANSI_ARGS_((Tcl_Interp *interp, -			    Master *masterPtr, int objc, -        		    Tcl_Obj *CONST objv[])); -static int		InterpHideHelper _ANSI_ARGS_((Tcl_Interp *interp, -			    Master *masterPtr, int objc, -        		    Tcl_Obj *CONST objv[])); -static int		InterpHiddenHelper _ANSI_ARGS_((Tcl_Interp *interp, -			    Master *masterPtr, int objc, -        		    Tcl_Obj *CONST objv[])); -static int		InterpInvokeHiddenHelper _ANSI_ARGS_(( -    			    Tcl_Interp *interp, Master *masterPtr, int objc, -        		    Tcl_Obj *CONST objv[])); -static int		InterpMarkTrustedHelper _ANSI_ARGS_(( -    			    Tcl_Interp *interp, Master *masterPtr, int objc, -        		    Tcl_Obj *CONST objv[])); -static int		InterpSlavesHelper _ANSI_ARGS_((Tcl_Interp *interp, -			    Master *masterPtr, int objc, -        		    Tcl_Obj *CONST objv[])); -static int		InterpShareHelper _ANSI_ARGS_((Tcl_Interp *interp, -			    Master *masterPtr, int objc, -        		    Tcl_Obj *CONST objv[])); -static int		InterpTargetHelper _ANSI_ARGS_((Tcl_Interp *interp, -			    Master *masterPtr, int objc, -        		    Tcl_Obj *CONST objv[])); -static int		InterpTransferHelper _ANSI_ARGS_((Tcl_Interp *interp, -			    Master *masterPtr, int objc, -        		    Tcl_Obj *CONST objv[])); -static int		MarkTrusted _ANSI_ARGS_((Tcl_Interp *interp)); -static void		MasterRecordDeleteProc _ANSI_ARGS_(( -			    ClientData clientData, Tcl_Interp *interp)); -static int		SlaveAliasHelper _ANSI_ARGS_((Tcl_Interp *interp, -		            Tcl_Interp *slaveInterp, Slave *slavePtr, -		            int objc, Tcl_Obj *CONST objv[])); -static int		SlaveAliasesHelper _ANSI_ARGS_((Tcl_Interp *interp, -		            Tcl_Interp *slaveInterp, Slave *slavePtr, -		            int objc, Tcl_Obj *CONST objv[])); -static int		SlaveEvalHelper _ANSI_ARGS_((Tcl_Interp *interp, -		            Tcl_Interp *slaveInterp, Slave *slavePtr, -		            int objc, Tcl_Obj *CONST objv[])); -static int		SlaveExposeHelper _ANSI_ARGS_((Tcl_Interp *interp, -		            Tcl_Interp *slaveInterp, Slave *slavePtr, -		            int objc, Tcl_Obj *CONST objv[])); -static int		SlaveHideHelper _ANSI_ARGS_((Tcl_Interp *interp, -		            Tcl_Interp *slaveInterp, Slave *slavePtr, -		            int objc, Tcl_Obj *CONST objv[])); -static int		SlaveHiddenHelper _ANSI_ARGS_((Tcl_Interp *interp, -		            Tcl_Interp *slaveInterp, Slave *slavePtr, -		            int objc, Tcl_Obj *CONST objv[])); -static int		SlaveIsSafeHelper _ANSI_ARGS_(( -    			    Tcl_Interp *interp, Tcl_Interp *slaveInterp, -                            Slave *slavePtr, int objc, Tcl_Obj *CONST objv[])); -static int		SlaveInvokeHiddenHelper _ANSI_ARGS_(( -    			    Tcl_Interp *interp, Tcl_Interp *slaveInterp, -                            Slave *slavePtr, int objc, Tcl_Obj *CONST objv[])); -static int		SlaveMarkTrustedHelper _ANSI_ARGS_((Tcl_Interp *interp, -		            Tcl_Interp *slaveInterp, Slave *slavePtr, -		            int objc, Tcl_Obj *CONST objv[])); -static int		SlaveObjectCmd _ANSI_ARGS_((ClientData dummy, -			    Tcl_Interp *interp, int objc, -			    Tcl_Obj *CONST objv[])); -static void		SlaveObjectDeleteProc _ANSI_ARGS_(( -			    ClientData clientData)); -static void		SlaveRecordDeleteProc _ANSI_ARGS_(( -			    ClientData clientData, Tcl_Interp *interp)); +			    Tcl_Obj *namePtr, Tcl_Obj *targetPtr, int objc, +			    Tcl_Obj *const objv[]); +static int		AliasDelete(Tcl_Interp *interp, +			    Tcl_Interp *slaveInterp, Tcl_Obj *namePtr); +static int		AliasDescribe(Tcl_Interp *interp, +			    Tcl_Interp *slaveInterp, Tcl_Obj *objPtr); +static int		AliasList(Tcl_Interp *interp, Tcl_Interp *slaveInterp); +static int		AliasObjCmd(ClientData dummy, +			    Tcl_Interp *currentInterp, int objc, +			    Tcl_Obj *const objv[]); +static int		AliasNRCmd(ClientData dummy, +			    Tcl_Interp *currentInterp, int objc, +			    Tcl_Obj *const objv[]); +static void		AliasObjCmdDeleteProc(ClientData clientData); +static Tcl_Interp *	GetInterp(Tcl_Interp *interp, Tcl_Obj *pathPtr); +static Tcl_Interp *	GetInterp2(Tcl_Interp *interp, int objc, +			    Tcl_Obj *const objv[]); +static void		InterpInfoDeleteProc(ClientData clientData, +			    Tcl_Interp *interp); +static int		SlaveBgerror(Tcl_Interp *interp, +			    Tcl_Interp *slaveInterp, int objc, +			    Tcl_Obj *const objv[]); +static Tcl_Interp *	SlaveCreate(Tcl_Interp *interp, Tcl_Obj *pathPtr, +			    int safe); +static int		SlaveDebugCmd(Tcl_Interp *interp, +			    Tcl_Interp *slaveInterp, +			    int objc, Tcl_Obj *const objv[]); +static int		SlaveEval(Tcl_Interp *interp, Tcl_Interp *slaveInterp, +			    int objc, Tcl_Obj *const objv[]); +static int		SlaveExpose(Tcl_Interp *interp, +			    Tcl_Interp *slaveInterp, int objc, +			    Tcl_Obj *const objv[]); +static int		SlaveHide(Tcl_Interp *interp, Tcl_Interp *slaveInterp, +			    int objc, Tcl_Obj *const objv[]); +static int		SlaveHidden(Tcl_Interp *interp, +			    Tcl_Interp *slaveInterp); +static int		SlaveInvokeHidden(Tcl_Interp *interp, +			    Tcl_Interp *slaveInterp, +			    const char *namespaceName, +			    int objc, Tcl_Obj *const objv[]); +static int		SlaveMarkTrusted(Tcl_Interp *interp, +			    Tcl_Interp *slaveInterp); +static int		SlaveObjCmd(ClientData dummy, Tcl_Interp *interp, +			    int objc, Tcl_Obj *const objv[]); +static void		SlaveObjCmdDeleteProc(ClientData clientData); +static int		SlaveRecursionLimit(Tcl_Interp *interp, +			    Tcl_Interp *slaveInterp, int objc, +			    Tcl_Obj *const objv[]); +static int		SlaveCommandLimitCmd(Tcl_Interp *interp, +			    Tcl_Interp *slaveInterp, int consumedObjc, +			    int objc, Tcl_Obj *const objv[]); +static int		SlaveTimeLimitCmd(Tcl_Interp *interp, +			    Tcl_Interp *slaveInterp, int consumedObjc, +			    int objc, Tcl_Obj *const objv[]); +static void		InheritLimitsFromMaster(Tcl_Interp *slaveInterp, +			    Tcl_Interp *masterInterp); +static void		SetScriptLimitCallback(Tcl_Interp *interp, int type, +			    Tcl_Interp *targetInterp, Tcl_Obj *scriptObj); +static void		CallScriptLimitCallback(ClientData clientData, +			    Tcl_Interp *interp); +static void		DeleteScriptLimitCallback(ClientData clientData); +static void		RunLimitHandlers(LimitHandler *handlerPtr, +			    Tcl_Interp *interp); +static void		TimeLimitCallback(ClientData clientData); + +/* NRE enabling */ +static Tcl_NRPostProc	NRPostInvokeHidden; +static Tcl_ObjCmdProc	NRInterpCmd; +static Tcl_ObjCmdProc	NRSlaveCmd; +  /*   *----------------------------------------------------------------------   * - * TclPreventAliasLoop -- + * TclSetPreInitScript --   * - *	When defining an alias or renaming a command, prevent an alias - *	loop from being formed. + *	This routine is used to change the value of the internal variable, + *	tclPreInitScript.   *   * Results: - *	A standard Tcl object result. + *	Returns the current value of tclPreInitScript.   *   * Side effects: - *	If TCL_ERROR is returned, the function also stores an error message - *	in the interpreter's result object. + *	Changes the way Tcl_Init() routine behaves.   * - * NOTE: - *	This function is public internal (instead of being static to - *	this file) because it is also used from TclRenameCommand. + *---------------------------------------------------------------------- + */ + +const char * +TclSetPreInitScript( +    const char *string)		/* Pointer to a script. */ +{ +    const char *prevString = tclPreInitScript; +    tclPreInitScript = string; +    return(prevString); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_Init -- + * + *	This function is typically invoked by Tcl_AppInit functions to find + *	and source the "init.tcl" script, which should exist somewhere on the + *	Tcl library path. + * + * Results: + *	Returns a standard Tcl completion code and sets the interp's result if + *	there is an error. + * + * Side effects: + *	Depends on what's in the init.tcl script.   *   *----------------------------------------------------------------------   */  int -TclPreventAliasLoop(interp, cmdInterp, cmd) -    Tcl_Interp *interp;			/* Interp in which to report errors. */ -    Tcl_Interp *cmdInterp;		/* Interp in which the command is -                                         * being defined. */ -    Tcl_Command cmd;                    /* Tcl command we are attempting -                                         * to define. */ +Tcl_Init( +    Tcl_Interp *interp)		/* Interpreter to initialize. */  { -    Command *cmdPtr = (Command *) cmd; -    Alias *aliasPtr, *nextAliasPtr; -    Tcl_Command aliasCmd; -    Command *aliasCmdPtr; -     -    /* -     * If we are not creating or renaming an alias, then it is -     * always OK to create or rename the command. -     */ -     -    if (cmdPtr->objProc != AliasCmd) { -        return TCL_OK; +    if (tclPreInitScript != NULL) { +	if (Tcl_Eval(interp, tclPreInitScript) == TCL_ERROR) { +	    return TCL_ERROR; +	}      }      /* -     * OK, we are dealing with an alias, so traverse the chain of aliases. -     * If we encounter the alias we are defining (or renaming to) any in -     * the chain then we have a loop. +     * In order to find init.tcl during initialization, the following script +     * is invoked by Tcl_Init(). It looks in several different directories: +     * +     *	$tcl_library		- can specify a primary location, if set, no +     *				  other locations will be checked. This is the +     *				  recommended way for a program that embeds +     *				  Tcl to specifically tell Tcl where to find +     *				  an init.tcl file. +     * +     *	$env(TCL_LIBRARY)	- highest priority so user can always override +     *				  the search path unless the application has +     *				  specified an exact directory above +     * +     *	$tclDefaultLibrary	- INTERNAL: This variable is set by Tcl on +     *				  those platforms where it can determine at +     *				  runtime the directory where it expects the +     *				  init.tcl file to be. After [tclInit] reads +     *				  and uses this value, it [unset]s it. +     *				  External users of Tcl should not make use of +     *				  the variable to customize [tclInit]. +     * +     *	$tcl_libPath		- OBSOLETE: This variable is no longer set by +     *				  Tcl itself, but [tclInit] examines it in +     *				  case some program that embeds Tcl is +     *				  customizing [tclInit] by setting this +     *				  variable to a list of directories in which +     *				  to search. +     * +     *	[tcl::pkgconfig get scriptdir,runtime] +     *				- the directory determined by configure to be +     *				  the place where Tcl's script library is to +     *				  be installed. +     * +     * The first directory on this path that contains a valid init.tcl script +     * will be set as the value of tcl_library. +     * +     * Note that this entire search mechanism can be bypassed by defining an +     * alternate tclInit command before calling Tcl_Init().       */ -    aliasPtr = (Alias *) cmdPtr->objClientData; -    nextAliasPtr = aliasPtr; -    while (1) { - -        /* -         * If the target of the next alias in the chain is the same as -         * the source alias, we have a loop. -	 */ - -	aliasCmd = Tcl_FindCommand(nextAliasPtr->targetInterp, -                nextAliasPtr->targetName, -		Tcl_GetGlobalNamespace(nextAliasPtr->targetInterp), -		/*flags*/ 0); -        if (aliasCmd == (Tcl_Command) NULL) { -            return TCL_OK; -        } -	aliasCmdPtr = (Command *) aliasCmd; -        if (aliasCmdPtr == cmdPtr) { -            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -		"cannot define or rename alias \"", aliasPtr->aliasName, -		"\": would create a loop", (char *) NULL); -            return TCL_ERROR; -        } - -        /* -	 * Otherwise, follow the chain one step further. See if the target -         * command is an alias - if so, follow the loop to its target -         * command. Otherwise we do not have a loop. -	 */ - -        if (aliasCmdPtr->objProc != AliasCmd) { -            return TCL_OK; -        } -        nextAliasPtr = (Alias *) aliasCmdPtr->objClientData; -    } - -    /* NOTREACHED */ +    return Tcl_Eval(interp, +"if {[namespace which -command tclInit] eq \"\"} {\n" +"  proc tclInit {} {\n" +"    global tcl_libPath tcl_library env tclDefaultLibrary\n" +"    rename tclInit {}\n" +"    if {[info exists tcl_library]} {\n" +"	set scripts {{set tcl_library}}\n" +"    } else {\n" +"	set scripts {}\n" +"	if {[info exists env(TCL_LIBRARY)] && ($env(TCL_LIBRARY) ne {})} {\n" +"	    lappend scripts {set env(TCL_LIBRARY)}\n" +"	    lappend scripts {\n" +"if {[regexp ^tcl(.*)$ [file tail $env(TCL_LIBRARY)] -> tail] == 0} continue\n" +"if {$tail eq [info tclversion]} continue\n" +"file join [file dirname $env(TCL_LIBRARY)] tcl[info tclversion]}\n" +"	}\n" +"	if {[info exists tclDefaultLibrary]} {\n" +"	    lappend scripts {set tclDefaultLibrary}\n" +"	} else {\n" +"	    lappend scripts {::tcl::pkgconfig get scriptdir,runtime}\n" +"	}\n" +"	lappend scripts {\n" +"set parentDir [file dirname [file dirname [info nameofexecutable]]]\n" +"set grandParentDir [file dirname $parentDir]\n" +"file join $parentDir lib tcl[info tclversion]} \\\n" +"	{file join $grandParentDir lib tcl[info tclversion]} \\\n" +"	{file join $parentDir library} \\\n" +"	{file join $grandParentDir library} \\\n" +"	{file join $grandParentDir tcl[info patchlevel] library} \\\n" +"	{\n" +"file join [file dirname $grandParentDir] tcl[info patchlevel] library}\n" +"	if {[info exists tcl_libPath]\n" +"		&& [catch {llength $tcl_libPath} len] == 0} {\n" +"	    for {set i 0} {$i < $len} {incr i} {\n" +"		lappend scripts [list lindex \\$tcl_libPath $i]\n" +"	    }\n" +"	}\n" +"    }\n" +"    set dirs {}\n" +"    set errors {}\n" +"    foreach script $scripts {\n" +"	lappend dirs [eval $script]\n" +"	set tcl_library [lindex $dirs end]\n" +"	set tclfile [file join $tcl_library init.tcl]\n" +"	if {[file exists $tclfile]} {\n" +"	    if {[catch {uplevel #0 [list source $tclfile]} msg opts]} {\n" +"		append errors \"$tclfile: $msg\n\"\n" +"		append errors \"[dict get $opts -errorinfo]\n\"\n" +"		continue\n" +"	    }\n" +"	    unset -nocomplain tclDefaultLibrary\n" +"	    return\n" +"	}\n" +"    }\n" +"    unset -nocomplain tclDefaultLibrary\n" +"    set msg \"Can't find a usable init.tcl in the following directories: \n\"\n" +"    append msg \"    $dirs\n\n\"\n" +"    append msg \"$errors\n\n\"\n" +"    append msg \"This probably means that Tcl wasn't installed properly.\n\"\n" +"    error $msg\n" +"  }\n" +"}\n" +"tclInit");  }  /* - *---------------------------------------------------------------------- + *---------------------------------------------------------------------------   * - * MarkTrusted -- + * TclInterpInit --   * - *	Mark an interpreter as unsafe (i.e. remove the "safe" mark). + *	Initializes the invoking interpreter for using the master, slave and + *	safe interp facilities. This is called from inside Tcl_CreateInterp().   *   * Results: - *	A standard Tcl result. + *	Always returns TCL_OK for backwards compatibility.   *   * Side effects: - *	Removes the "safe" mark from an interpreter. + *	Adds the "interp" command to an interpreter and initializes the + *	interpInfoPtr field of the invoking interpreter.   * - *---------------------------------------------------------------------- + *---------------------------------------------------------------------------   */ -static int -MarkTrusted(interp) -    Tcl_Interp *interp;		/* Interpreter to be marked unsafe. */ +int +TclInterpInit( +    Tcl_Interp *interp)		/* Interpreter to initialize. */  { -    Interp *iPtr = (Interp *) interp; +    InterpInfo *interpInfoPtr; +    Master *masterPtr; +    Slave *slavePtr; + +    interpInfoPtr = ckalloc(sizeof(InterpInfo)); +    ((Interp *) interp)->interpInfo = interpInfoPtr; + +    masterPtr = &interpInfoPtr->master; +    Tcl_InitHashTable(&masterPtr->slaveTable, TCL_STRING_KEYS); +    masterPtr->targetsPtr = NULL; + +    slavePtr = &interpInfoPtr->slave; +    slavePtr->masterInterp	= NULL; +    slavePtr->slaveEntryPtr	= NULL; +    slavePtr->slaveInterp	= interp; +    slavePtr->interpCmd		= NULL; +    Tcl_InitHashTable(&slavePtr->aliasTable, TCL_STRING_KEYS); -    iPtr->flags &= ~SAFE_INTERP; +    Tcl_NRCreateCommand(interp, "interp", Tcl_InterpObjCmd, NRInterpCmd, +	    NULL, NULL); + +    Tcl_CallWhenDeleted(interp, InterpInfoDeleteProc, NULL);      return TCL_OK;  }  /* - *---------------------------------------------------------------------- + *---------------------------------------------------------------------------   * - * Tcl_MakeSafe -- + * InterpInfoDeleteProc --   * - *	Makes its argument interpreter contain only functionality that is - *	defined to be part of Safe Tcl. Unsafe commands are hidden, the - *	env array is unset, and the standard channels are removed. + *	Invoked when an interpreter is being deleted. It releases all storage + *	used by the master/slave/safe interpreter facilities.   *   * Results:   *	None.   *   * Side effects: - *	Hides commands in its argument interpreter, and removes settings - *	and channels. + *	Cleans up storage. Sets the interpInfoPtr field of the interp to NULL.   * - *---------------------------------------------------------------------- + *---------------------------------------------------------------------------   */ -int -Tcl_MakeSafe(interp) -    Tcl_Interp *interp;		/* Interpreter to be made safe. */ +static void +InterpInfoDeleteProc( +    ClientData clientData,	/* Ignored. */ +    Tcl_Interp *interp)		/* Interp being deleted. All commands for +				 * slave interps should already be deleted. */  { -    Tcl_Channel chan;				/* Channel to remove from -                                                 * safe interpreter. */ -    Interp *iPtr = (Interp *) interp; +    InterpInfo *interpInfoPtr; +    Slave *slavePtr; +    Master *masterPtr; +    Target *targetPtr; -    TclHideUnsafeCommands(interp); -     -    iPtr->flags |= SAFE_INTERP; +    interpInfoPtr = (InterpInfo *) ((Interp *) interp)->interpInfo;      /* -     *  Unsetting variables : (which should not have been set  -     *  in the first place, but...) +     * There shouldn't be any commands left.       */ +    masterPtr = &interpInfoPtr->master; +    if (masterPtr->slaveTable.numEntries != 0) { +	Tcl_Panic("InterpInfoDeleteProc: still exist commands"); +    } +    Tcl_DeleteHashTable(&masterPtr->slaveTable); +      /* -     * No env array in a safe slave. +     * Tell any interps that have aliases to this interp that they should +     * delete those aliases. If the other interp was already dead, it would +     * have removed the target record already.       */ -    Tcl_UnsetVar(interp, "env", TCL_GLOBAL_ONLY); +    for (targetPtr = masterPtr->targetsPtr; targetPtr != NULL; ) { +	Target *tmpPtr = targetPtr->nextPtr; +	Tcl_DeleteCommandFromToken(targetPtr->slaveInterp, +		targetPtr->slaveCmd); +	targetPtr = tmpPtr; +    } -    /*  -     * Remove unsafe parts of tcl_platform -     */ +    slavePtr = &interpInfoPtr->slave; +    if (slavePtr->interpCmd != NULL) { +	/* +	 * Tcl_DeleteInterp() was called on this interpreter, rather "interp +	 * delete" or the equivalent deletion of the command in the master. +	 * First ensure that the cleanup callback doesn't try to delete the +	 * interp again. +	 */ -    Tcl_UnsetVar2(interp, "tcl_platform", "os", TCL_GLOBAL_ONLY); -    Tcl_UnsetVar2(interp, "tcl_platform", "osVersion", TCL_GLOBAL_ONLY); -    Tcl_UnsetVar2(interp, "tcl_platform", "machine", TCL_GLOBAL_ONLY); +	slavePtr->slaveInterp = NULL; +	Tcl_DeleteCommandFromToken(slavePtr->masterInterp, +		slavePtr->interpCmd); +    }      /* -     * Unset path informations variables -     * (the only one remaining is [info nameofexecutable]) +     * There shouldn't be any aliases left.       */ -    Tcl_UnsetVar(interp, "tclDefaultLibrary", TCL_GLOBAL_ONLY); -    Tcl_UnsetVar(interp, "tcl_library", TCL_GLOBAL_ONLY); -    Tcl_UnsetVar(interp, "tcl_pkgPath", TCL_GLOBAL_ONLY); -     -    /* -     * Remove the standard channels from the interpreter; safe interpreters -     * do not ordinarily have access to stdin, stdout and stderr. -     * -     * NOTE: These channels are not added to the interpreter by the -     * Tcl_CreateInterp call, but may be added later, by another I/O -     * operation. We want to ensure that the interpreter does not have -     * these channels even if it is being made safe after being used for -     * some time.. -     */ +    if (slavePtr->aliasTable.numEntries != 0) { +	Tcl_Panic("InterpInfoDeleteProc: still exist aliases"); +    } +    Tcl_DeleteHashTable(&slavePtr->aliasTable); -    chan = Tcl_GetStdChannel(TCL_STDIN); -    if (chan != (Tcl_Channel) NULL) { -        Tcl_UnregisterChannel(interp, chan); +    ckfree(interpInfoPtr); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_InterpObjCmd -- + * + *	This function is invoked to process the "interp" Tcl command. See the + *	user documentation for details on what it does. + * + * Results: + *	A standard Tcl result. + * + * Side effects: + *	See the user documentation. + * + *---------------------------------------------------------------------- + */ +	/* ARGSUSED */ +int +Tcl_InterpObjCmd( +    ClientData clientData,		/* Unused. */ +    Tcl_Interp *interp,			/* Current interpreter. */ +    int objc,				/* Number of arguments. */ +    Tcl_Obj *const objv[])		/* Argument objects. */ +{ +    return Tcl_NRCallObjProc(interp, NRInterpCmd, clientData, objc, objv); +} + +static int +NRInterpCmd( +    ClientData clientData,		/* Unused. */ +    Tcl_Interp *interp,			/* Current interpreter. */ +    int objc,				/* Number of arguments. */ +    Tcl_Obj *const objv[])		/* Argument objects. */ +{ +    Tcl_Interp *slaveInterp; +    int index; +    static const char *const options[] = { +	"alias",	"aliases",	"bgerror",	"cancel", +	"create",	"debug",	"delete", +	"eval",		"exists",	"expose", +	"hide",		"hidden",	"issafe", +	"invokehidden",	"limit",	"marktrusted",	"recursionlimit", +	"slaves",	"share",	"target",	"transfer", +	NULL +    }; +    enum option { +	OPT_ALIAS,	OPT_ALIASES,	OPT_BGERROR,	OPT_CANCEL, +	OPT_CREATE,	OPT_DEBUG,	OPT_DELETE, +	OPT_EVAL,	OPT_EXISTS,	OPT_EXPOSE, +	OPT_HIDE,	OPT_HIDDEN,	OPT_ISSAFE, +	OPT_INVOKEHID,	OPT_LIMIT,	OPT_MARKTRUSTED,OPT_RECLIMIT, +	OPT_SLAVES,	OPT_SHARE,	OPT_TARGET,	OPT_TRANSFER +    }; + +    if (objc < 2) { +	Tcl_WrongNumArgs(interp, 1, objv, "cmd ?arg ...?"); +	return TCL_ERROR;      } -    chan = Tcl_GetStdChannel(TCL_STDOUT); -    if (chan != (Tcl_Channel) NULL) { -        Tcl_UnregisterChannel(interp, chan); +    if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0, +	    &index) != TCL_OK) { +	return TCL_ERROR;      } -    chan = Tcl_GetStdChannel(TCL_STDERR); -    if (chan != (Tcl_Channel) NULL) { -        Tcl_UnregisterChannel(interp, chan); +    switch ((enum option) index) { +    case OPT_ALIAS: { +	Tcl_Interp *masterInterp; + +	if (objc < 4) { +	aliasArgs: +	    Tcl_WrongNumArgs(interp, 2, objv, +		    "slavePath slaveCmd ?masterPath masterCmd? ?arg ...?"); +	    return TCL_ERROR; +	} +	slaveInterp = GetInterp(interp, objv[2]); +	if (slaveInterp == NULL) { +	    return TCL_ERROR; +	} +	if (objc == 4) { +	    return AliasDescribe(interp, slaveInterp, objv[3]); +	} +	if ((objc == 5) && (TclGetString(objv[4])[0] == '\0')) { +	    return AliasDelete(interp, slaveInterp, objv[3]); +	} +	if (objc > 5) { +	    masterInterp = GetInterp(interp, objv[4]); +	    if (masterInterp == NULL) { +		return TCL_ERROR; +	    } +	    if (TclGetString(objv[5])[0] == '\0') { +		if (objc == 6) { +		    return AliasDelete(interp, slaveInterp, objv[3]); +		} +	    } else { +		return AliasCreate(interp, slaveInterp, masterInterp, objv[3], +			objv[5], objc - 6, objv + 6); +	    } +	} +	goto aliasArgs; +    } +    case OPT_ALIASES: +	slaveInterp = GetInterp2(interp, objc, objv); +	if (slaveInterp == NULL) { +	    return TCL_ERROR; +	} +	return AliasList(interp, slaveInterp); +    case OPT_BGERROR: +	if (objc != 3 && objc != 4) { +	    Tcl_WrongNumArgs(interp, 2, objv, "path ?cmdPrefix?"); +	    return TCL_ERROR; +	} +	slaveInterp = GetInterp(interp, objv[2]); +	if (slaveInterp == NULL) { +	    return TCL_ERROR; +	} +	return SlaveBgerror(interp, slaveInterp, objc - 3, objv + 3); +    case OPT_CANCEL: { +	int i, flags; +	Tcl_Obj *resultObjPtr; +	static const char *const cancelOptions[] = { +	    "-unwind",	"--",	NULL +	}; +	enum option { +	    OPT_UNWIND,	OPT_LAST +	}; + +	flags = 0; + +	for (i = 2; i < objc; i++) { +	    if (TclGetString(objv[i])[0] != '-') { +		break; +	    } +	    if (Tcl_GetIndexFromObj(interp, objv[i], cancelOptions, "option", +		    0, &index) != TCL_OK) { +		return TCL_ERROR; +	    } + +	    switch ((enum option) index) { +	    case OPT_UNWIND: +		/* +		 * The evaluation stack in the target interp is to be unwound. +		 */ + +		flags |= TCL_CANCEL_UNWIND; +		break; +	    case OPT_LAST: +		i++; +		goto endOfForLoop; +	    } +	} + +    endOfForLoop: +	if ((i + 2) < objc) { +	    Tcl_WrongNumArgs(interp, 2, objv, +		    "?-unwind? ?--? ?path? ?result?"); +	    return TCL_ERROR; +	} + +	/* +	 * Did they specify a slave interp to cancel the script in progress +	 * in?  If not, use the current interp. +	 */ + +	if (i < objc) { +	    slaveInterp = GetInterp(interp, objv[i]); +	    if (slaveInterp == NULL) { +		return TCL_ERROR; +	    } +	    i++; +	} else { +	    slaveInterp = interp; +	} + +	if (i < objc) { +	    resultObjPtr = objv[i]; + +	    /* +	     * Tcl_CancelEval removes this reference. +	     */ + +	    Tcl_IncrRefCount(resultObjPtr); +	    i++; +	} else { +	    resultObjPtr = NULL; +	} + +	return Tcl_CancelEval(slaveInterp, resultObjPtr, 0, flags); +    } +    case OPT_CREATE: { +	int i, last, safe; +	Tcl_Obj *slavePtr; +	char buf[16 + TCL_INTEGER_SPACE]; +	static const char *const createOptions[] = { +	    "-safe",	"--", NULL +	}; +	enum option { +	    OPT_SAFE,	OPT_LAST +	}; + +	safe = Tcl_IsSafe(interp); + +	/* +	 * Weird historical rules: "-safe" is accepted at the end, too. +	 */ + +	slavePtr = NULL; +	last = 0; +	for (i = 2; i < objc; i++) { +	    if ((last == 0) && (Tcl_GetString(objv[i])[0] == '-')) { +		if (Tcl_GetIndexFromObj(interp, objv[i], createOptions, +			"option", 0, &index) != TCL_OK) { +		    return TCL_ERROR; +		} +		if (index == OPT_SAFE) { +		    safe = 1; +		    continue; +		} +		i++; +		last = 1; +	    } +	    if (slavePtr != NULL) { +		Tcl_WrongNumArgs(interp, 2, objv, "?-safe? ?--? ?path?"); +		return TCL_ERROR; +	    } +	    if (i < objc) { +		slavePtr = objv[i]; +	    } +	} +	buf[0] = '\0'; +	if (slavePtr == NULL) { +	    /* +	     * Create an anonymous interpreter -- we choose its name and the +	     * name of the command. We check that the command name that we use +	     * for the interpreter does not collide with an existing command +	     * in the master interpreter. +	     */ + +	    for (i = 0; ; i++) { +		Tcl_CmdInfo cmdInfo; + +		sprintf(buf, "interp%d", i); +		if (Tcl_GetCommandInfo(interp, buf, &cmdInfo) == 0) { +		    break; +		} +	    } +	    slavePtr = Tcl_NewStringObj(buf, -1); +	} +	if (SlaveCreate(interp, slavePtr, safe) == NULL) { +	    if (buf[0] != '\0') { +		Tcl_DecrRefCount(slavePtr); +	    } +	    return TCL_ERROR; +	} +	Tcl_SetObjResult(interp, slavePtr); +	return TCL_OK; +    } +    case OPT_DEBUG:		/* TIP #378 */ +	/* +	 * Currently only -frame supported, otherwise ?-option ?value?? +	 */ + +	if (objc < 3 || objc > 5) { +	    Tcl_WrongNumArgs(interp, 2, objv, "path ?-frame ?bool??"); +	    return TCL_ERROR; +	} +	slaveInterp = GetInterp(interp, objv[2]); +	if (slaveInterp == NULL) { +	    return TCL_ERROR; +	} +	return SlaveDebugCmd(interp, slaveInterp, objc - 3, objv + 3); +    case OPT_DELETE: { +	int i; +	InterpInfo *iiPtr; + +	for (i = 2; i < objc; i++) { +	    slaveInterp = GetInterp(interp, objv[i]); +	    if (slaveInterp == NULL) { +		return TCL_ERROR; +	    } else if (slaveInterp == interp) { +		Tcl_SetObjResult(interp, Tcl_NewStringObj( +			"cannot delete the current interpreter", -1)); +		Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", +			"DELETESELF", NULL); +		return TCL_ERROR; +	    } +	    iiPtr = (InterpInfo *) ((Interp *) slaveInterp)->interpInfo; +	    Tcl_DeleteCommandFromToken(iiPtr->slave.masterInterp, +		    iiPtr->slave.interpCmd); +	} +	return TCL_OK; +    } +    case OPT_EVAL: +	if (objc < 4) { +	    Tcl_WrongNumArgs(interp, 2, objv, "path arg ?arg ...?"); +	    return TCL_ERROR; +	} +	slaveInterp = GetInterp(interp, objv[2]); +	if (slaveInterp == NULL) { +	    return TCL_ERROR; +	} +	return SlaveEval(interp, slaveInterp, objc - 3, objv + 3); +    case OPT_EXISTS: { +	int exists = 1; + +	slaveInterp = GetInterp2(interp, objc, objv); +	if (slaveInterp == NULL) { +	    if (objc > 3) { +		return TCL_ERROR; +	    } +	    Tcl_ResetResult(interp); +	    exists = 0; +	} +	Tcl_SetObjResult(interp, Tcl_NewBooleanObj(exists)); +	return TCL_OK; +    } +    case OPT_EXPOSE: +	if ((objc < 4) || (objc > 5)) { +	    Tcl_WrongNumArgs(interp, 2, objv, "path hiddenCmdName ?cmdName?"); +	    return TCL_ERROR; +	} +	slaveInterp = GetInterp(interp, objv[2]); +	if (slaveInterp == NULL) { +	    return TCL_ERROR; +	} +	return SlaveExpose(interp, slaveInterp, objc - 3, objv + 3); +    case OPT_HIDE: +	if ((objc < 4) || (objc > 5)) { +	    Tcl_WrongNumArgs(interp, 2, objv, "path cmdName ?hiddenCmdName?"); +	    return TCL_ERROR; +	} +	slaveInterp = GetInterp(interp, objv[2]); +	if (slaveInterp == NULL) { +	    return TCL_ERROR; +	} +	return SlaveHide(interp, slaveInterp, objc - 3, objv + 3); +    case OPT_HIDDEN: +	slaveInterp = GetInterp2(interp, objc, objv); +	if (slaveInterp == NULL) { +	    return TCL_ERROR; +	} +	return SlaveHidden(interp, slaveInterp); +    case OPT_ISSAFE: +	slaveInterp = GetInterp2(interp, objc, objv); +	if (slaveInterp == NULL) { +	    return TCL_ERROR; +	} +	Tcl_SetObjResult(interp, Tcl_NewBooleanObj(Tcl_IsSafe(slaveInterp))); +	return TCL_OK; +    case OPT_INVOKEHID: { +	int i; +	const char *namespaceName; +	static const char *const hiddenOptions[] = { +	    "-global",	"-namespace",	"--", NULL +	}; +	enum hiddenOption { +	    OPT_GLOBAL,	OPT_NAMESPACE,	OPT_LAST +	}; + +	namespaceName = NULL; +	for (i = 3; i < objc; i++) { +	    if (TclGetString(objv[i])[0] != '-') { +		break; +	    } +	    if (Tcl_GetIndexFromObj(interp, objv[i], hiddenOptions, "option", +		    0, &index) != TCL_OK) { +		return TCL_ERROR; +	    } +	    if (index == OPT_GLOBAL) { +		namespaceName = "::"; +	    } else if (index == OPT_NAMESPACE) { +		if (++i == objc) { /* There must be more arguments. */ +		    break; +		} else { +		    namespaceName = TclGetString(objv[i]); +		} +	    } else { +		i++; +		break; +	    } +	} +	if (objc - i < 1) { +	    Tcl_WrongNumArgs(interp, 2, objv, +		    "path ?-namespace ns? ?-global? ?--? cmd ?arg ..?"); +	    return TCL_ERROR; +	} +	slaveInterp = GetInterp(interp, objv[2]); +	if (slaveInterp == NULL) { +	    return TCL_ERROR; +	} +	return SlaveInvokeHidden(interp, slaveInterp, namespaceName, objc - i, +		objv + i); +    } +    case OPT_LIMIT: { +	static const char *const limitTypes[] = { +	    "commands", "time", NULL +	}; +	enum LimitTypes { +	    LIMIT_TYPE_COMMANDS, LIMIT_TYPE_TIME +	}; +	int limitType; + +	if (objc < 4) { +	    Tcl_WrongNumArgs(interp, 2, objv, +		    "path limitType ?-option value ...?"); +	    return TCL_ERROR; +	} +	slaveInterp = GetInterp(interp, objv[2]); +	if (slaveInterp == NULL) { +	    return TCL_ERROR; +	} +	if (Tcl_GetIndexFromObj(interp, objv[3], limitTypes, "limit type", 0, +		&limitType) != TCL_OK) { +	    return TCL_ERROR; +	} +	switch ((enum LimitTypes) limitType) { +	case LIMIT_TYPE_COMMANDS: +	    return SlaveCommandLimitCmd(interp, slaveInterp, 4, objc,objv); +	case LIMIT_TYPE_TIME: +	    return SlaveTimeLimitCmd(interp, slaveInterp, 4, objc, objv); +	}      } +    case OPT_MARKTRUSTED: +	if (objc != 3) { +	    Tcl_WrongNumArgs(interp, 2, objv, "path"); +	    return TCL_ERROR; +	} +	slaveInterp = GetInterp(interp, objv[2]); +	if (slaveInterp == NULL) { +	    return TCL_ERROR; +	} +	return SlaveMarkTrusted(interp, slaveInterp); +    case OPT_RECLIMIT: +	if (objc != 3 && objc != 4) { +	    Tcl_WrongNumArgs(interp, 2, objv, "path ?newlimit?"); +	    return TCL_ERROR; +	} +	slaveInterp = GetInterp(interp, objv[2]); +	if (slaveInterp == NULL) { +	    return TCL_ERROR; +	} +	return SlaveRecursionLimit(interp, slaveInterp, objc - 3, objv + 3); +    case OPT_SLAVES: { +	InterpInfo *iiPtr; +	Tcl_Obj *resultPtr; +	Tcl_HashEntry *hPtr; +	Tcl_HashSearch hashSearch; +	char *string; + +	slaveInterp = GetInterp2(interp, objc, objv); +	if (slaveInterp == NULL) { +	    return TCL_ERROR; +	} +	iiPtr = (InterpInfo *) ((Interp *) slaveInterp)->interpInfo; +	resultPtr = Tcl_NewObj(); +	hPtr = Tcl_FirstHashEntry(&iiPtr->master.slaveTable, &hashSearch); +	for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&hashSearch)) { +	    string = Tcl_GetHashKey(&iiPtr->master.slaveTable, hPtr); +	    Tcl_ListObjAppendElement(NULL, resultPtr, +		    Tcl_NewStringObj(string, -1)); +	} +	Tcl_SetObjResult(interp, resultPtr); +	return TCL_OK; +    } +    case OPT_TRANSFER: +    case OPT_SHARE: { +	Tcl_Interp *masterInterp;	/* The master of the slave. */ +	Tcl_Channel chan; +	if (objc != 5) { +	    Tcl_WrongNumArgs(interp, 2, objv, "srcPath channelId destPath"); +	    return TCL_ERROR; +	} +	masterInterp = GetInterp(interp, objv[2]); +	if (masterInterp == NULL) { +	    return TCL_ERROR; +	} +	chan = Tcl_GetChannel(masterInterp, TclGetString(objv[3]), NULL); +	if (chan == NULL) { +	    Tcl_TransferResult(masterInterp, TCL_OK, interp); +	    return TCL_ERROR; +	} +	slaveInterp = GetInterp(interp, objv[4]); +	if (slaveInterp == NULL) { +	    return TCL_ERROR; +	} +	Tcl_RegisterChannel(slaveInterp, chan); +	if (index == OPT_TRANSFER) { +	    /* +	     * When transferring, as opposed to sharing, we must unhitch the +	     * channel from the interpreter where it started. +	     */ + +	    if (Tcl_UnregisterChannel(masterInterp, chan) != TCL_OK) { +		Tcl_TransferResult(masterInterp, TCL_OK, interp); +		return TCL_ERROR; +	    } +	} +	return TCL_OK; +    } +    case OPT_TARGET: { +	InterpInfo *iiPtr; +	Tcl_HashEntry *hPtr; +	Alias *aliasPtr; +	const char *aliasName; + +	if (objc != 4) { +	    Tcl_WrongNumArgs(interp, 2, objv, "path alias"); +	    return TCL_ERROR; +	} + +	slaveInterp = GetInterp(interp, objv[2]); +	if (slaveInterp == NULL) { +	    return TCL_ERROR; +	} + +	aliasName = TclGetString(objv[3]); + +	iiPtr = (InterpInfo *) ((Interp *) slaveInterp)->interpInfo; +	hPtr = Tcl_FindHashEntry(&iiPtr->slave.aliasTable, aliasName); +	if (hPtr == NULL) { +	    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		    "alias \"%s\" in path \"%s\" not found", +		    aliasName, Tcl_GetString(objv[2]))); +	    Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ALIAS", aliasName, +		    NULL); +	    return TCL_ERROR; +	} +	aliasPtr = Tcl_GetHashValue(hPtr); +	if (Tcl_GetInterpPath(interp, aliasPtr->targetInterp) != TCL_OK) { +	    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		    "target interpreter for alias \"%s\" in path \"%s\" is " +		    "not my descendant", aliasName, Tcl_GetString(objv[2]))); +	    Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", +		    "TARGETSHROUDED", NULL); +	    return TCL_ERROR; +	} +	return TCL_OK; +    } +    }      return TCL_OK;  }  /* - *---------------------------------------------------------------------- + *---------------------------------------------------------------------------   * - * GetInterp -- + * GetInterp2 --   * - *	Helper function to find a slave interpreter given a pathname. + *	Helper function for Tcl_InterpObjCmd() to convert the interp name + *	potentially specified on the command line to an Tcl_Interp.   *   * Results: - *	Returns the slave interpreter known by that name in the calling - *	interpreter, or NULL if no interpreter known by that name exists.  + *	The return value is the interp specified on the command line, or the + *	interp argument itself if no interp was specified on the command line. + *	If the interp could not be found or the wrong number of arguments was + *	specified on the command line, the return value is NULL and an error + *	message is left in the interp's result.   *   * Side effects: - *	Assigns to the pointer variable passed in, if not NULL. + *	None.   * - *---------------------------------------------------------------------- + *---------------------------------------------------------------------------   */  static Tcl_Interp * -GetInterp(interp, masterPtr, path, masterPtrPtr) -    Tcl_Interp *interp;		/* Interp. to start search from. */ -    Master *masterPtr;		/* Its master record. */ -    char *path;			/* The path (name) of interp. to be found. */ -    Master **masterPtrPtr;	/* (Return) its master record. */ +GetInterp2( +    Tcl_Interp *interp,		/* Default interp if no interp was specified +				 * on the command line. */ +    int objc,			/* Number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument objects. */  { -    Tcl_HashEntry *hPtr;	/* Search element. */ -    Slave *slavePtr;		/* Interim slave record. */ -    char **argv;		/* Split-up path (name) for interp to find. */ -    int argc, i;		/* Loop indices. */ -    Tcl_Interp *searchInterp;	/* Interim storage for interp. to find. */ - -    if (masterPtrPtr != (Master **) NULL) { -        *masterPtrPtr = masterPtr; -    } -     -    if (Tcl_SplitList(interp, path, &argc, &argv) != TCL_OK) { -        return (Tcl_Interp *) NULL; -    } - -    for (searchInterp = interp, i = 0; i < argc; i++) { -         -        hPtr = Tcl_FindHashEntry(&(masterPtr->slaveTable), argv[i]); -        if (hPtr == (Tcl_HashEntry *) NULL) { -            ckfree((char *) argv); -            return (Tcl_Interp *) NULL; -        } -        slavePtr = (Slave *) Tcl_GetHashValue(hPtr); -        searchInterp = slavePtr->slaveInterp; -        if (searchInterp == (Tcl_Interp *) NULL) { -            ckfree((char *) argv); -            return (Tcl_Interp *) NULL; -        } -        masterPtr = (Master *) Tcl_GetAssocData(searchInterp, -                "tclMasterRecord", NULL); -        if (masterPtrPtr != (Master **) NULL) *masterPtrPtr = masterPtr; -        if (masterPtr == (Master *) NULL) { -            ckfree((char *) argv); -            return (Tcl_Interp *) NULL; -        } -    } -    ckfree((char *) argv); -    return searchInterp; +    if (objc == 2) { +	return interp; +    } else if (objc == 3) { +	return GetInterp(interp, objv[2]); +    } else { +	Tcl_WrongNumArgs(interp, 2, objv, "?path?"); +	return NULL; +    }  }  /*   *----------------------------------------------------------------------   * - * CreateSlave -- + * Tcl_CreateAlias --   * - *	Helper function to do the actual work of creating a slave interp - *	and new object command. Also optionally makes the new slave - *	interpreter "safe". + *	Creates an alias between two interpreters.   *   * Results: - *	Returns the new Tcl_Interp * if successful or NULL if not. If failed, - *	the result of the invoking interpreter contains an error message. + *	A standard Tcl result.   *   * Side effects: - *	Creates a new slave interpreter and a new object command. + *	Creates a new alias, manipulates the result field of slaveInterp.   *   *----------------------------------------------------------------------   */ -static Tcl_Interp * -CreateSlave(interp, masterPtr, slavePath, safe) -    Tcl_Interp *interp;			/* Interp. to start search from. */ -    Master *masterPtr;			/* Master record. */ -    char *slavePath;			/* Path (name) of slave to create. */ -    int safe;				/* Should we make it "safe"? */ -{ -    Tcl_Interp *slaveInterp;		/* Ptr to slave interpreter. */ -    Tcl_Interp *masterInterp;		/* Ptr to master interp for slave. */ -    Slave *slavePtr;			/* Slave record. */ -    Tcl_HashEntry *hPtr;		/* Entry into interp hashtable. */ -    int new;				/* Indicates whether new entry. */ -    int argc;				/* Count of elements in slavePath. */ -    char **argv;			/* Elements in slavePath. */ -    char *masterPath;			/* Path to its master. */ - -    if (Tcl_SplitList(interp, slavePath, &argc, &argv) != TCL_OK) { -        return (Tcl_Interp *) NULL; -    } - -    if (argc < 2) { -        masterInterp = interp; -        if (argc == 1) { -            slavePath = argv[0]; -        } -    } else { -        masterPath = Tcl_Merge(argc-1, argv); -        masterInterp = GetInterp(interp, masterPtr, masterPath, &masterPtr); -        if (masterInterp == (Tcl_Interp *) NULL) { -            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                    "interpreter named \"", masterPath, -                    "\" not found", (char *) NULL); -            ckfree((char *) argv); -            ckfree((char *) masterPath); -            return (Tcl_Interp *) NULL; -        } -        ckfree((char *) masterPath); -        slavePath = argv[argc-1]; -        if (!safe) { -            safe = Tcl_IsSafe(masterInterp); -        } -    } -    hPtr = Tcl_CreateHashEntry(&(masterPtr->slaveTable), slavePath, &new); -    if (new == 0) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "interpreter named \"", slavePath, -                "\" already exists, cannot create", (char *) NULL); -        ckfree((char *) argv); -        return (Tcl_Interp *) NULL; -    } -    slaveInterp = Tcl_CreateInterp(); -    if (slaveInterp == (Tcl_Interp *) NULL) { -        panic("CreateSlave: out of memory while creating a new interpreter"); +int +Tcl_CreateAlias( +    Tcl_Interp *slaveInterp,	/* Interpreter for source command. */ +    const char *slaveCmd,	/* Command to install in slave. */ +    Tcl_Interp *targetInterp,	/* Interpreter for target command. */ +    const char *targetCmd,	/* Name of target command. */ +    int argc,			/* How many additional arguments? */ +    const char *const *argv)	/* These are the additional args. */ +{ +    Tcl_Obj *slaveObjPtr, *targetObjPtr; +    Tcl_Obj **objv; +    int i; +    int result; + +    objv = TclStackAlloc(slaveInterp, (unsigned) sizeof(Tcl_Obj *) * argc); +    for (i = 0; i < argc; i++) { +	objv[i] = Tcl_NewStringObj(argv[i], -1); +	Tcl_IncrRefCount(objv[i]);      } -    slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord", NULL); -    slavePtr->masterInterp = masterInterp; -    slavePtr->slaveEntry = hPtr; -    slavePtr->slaveInterp = slaveInterp; -    slavePtr->interpCmd = Tcl_CreateObjCommand(masterInterp, slavePath, -            SlaveObjectCmd, (ClientData) slaveInterp, SlaveObjectDeleteProc); -    Tcl_InitHashTable(&(slavePtr->aliasTable), TCL_STRING_KEYS); -    (void) Tcl_SetAssocData(slaveInterp, "tclSlaveRecord", -            SlaveRecordDeleteProc, (ClientData) slavePtr); -    Tcl_SetHashValue(hPtr, (ClientData) slavePtr); -    Tcl_SetVar(slaveInterp, "tcl_interactive", "0", TCL_GLOBAL_ONLY); -     -    /* -     * Inherit the recursion limit. -     */ -    ((Interp *)slaveInterp)->maxNestingDepth = -	((Interp *)masterInterp)->maxNestingDepth ; -    if (safe) { -        if (Tcl_MakeSafe(slaveInterp) == TCL_ERROR) { -            goto error; -        } -    } else { -        if (Tcl_Init(slaveInterp) == TCL_ERROR) { -            goto error; -        } +    slaveObjPtr = Tcl_NewStringObj(slaveCmd, -1); +    Tcl_IncrRefCount(slaveObjPtr); + +    targetObjPtr = Tcl_NewStringObj(targetCmd, -1); +    Tcl_IncrRefCount(targetObjPtr); + +    result = AliasCreate(slaveInterp, slaveInterp, targetInterp, slaveObjPtr, +	    targetObjPtr, argc, objv); + +    for (i = 0; i < argc; i++) { +	Tcl_DecrRefCount(objv[i]);      } +    TclStackFree(slaveInterp, objv); +    Tcl_DecrRefCount(targetObjPtr); +    Tcl_DecrRefCount(slaveObjPtr); -    ckfree((char *) argv); -    return slaveInterp; +    return result; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_CreateAliasObj -- + * + *	Object version: Creates an alias between two interpreters. + * + * Results: + *	A standard Tcl result. + * + * Side effects: + *	Creates a new alias. + * + *---------------------------------------------------------------------- + */ -error: +int +Tcl_CreateAliasObj( +    Tcl_Interp *slaveInterp,	/* Interpreter for source command. */ +    const char *slaveCmd,	/* Command to install in slave. */ +    Tcl_Interp *targetInterp,	/* Interpreter for target command. */ +    const char *targetCmd,	/* Name of target command. */ +    int objc,			/* How many additional arguments? */ +    Tcl_Obj *const objv[])	/* Argument vector. */ +{ +    Tcl_Obj *slaveObjPtr, *targetObjPtr; +    int result; -    Tcl_AddErrorInfo(interp, Tcl_GetVar2(slaveInterp, "errorInfo", (char *) -            NULL, TCL_GLOBAL_ONLY)); -    Tcl_SetVar2(interp, "errorCode", (char *) NULL, -            Tcl_GetVar2(slaveInterp, "errorCode", (char *) NULL, -                    TCL_GLOBAL_ONLY), -            TCL_GLOBAL_ONLY); +    slaveObjPtr = Tcl_NewStringObj(slaveCmd, -1); +    Tcl_IncrRefCount(slaveObjPtr); -    Tcl_SetObjResult(interp, Tcl_GetObjResult(slaveInterp)); -    Tcl_ResetResult(slaveInterp); +    targetObjPtr = Tcl_NewStringObj(targetCmd, -1); +    Tcl_IncrRefCount(targetObjPtr); -    (void) Tcl_DeleteCommand(masterInterp, slavePath); +    result = AliasCreate(slaveInterp, slaveInterp, targetInterp, slaveObjPtr, +	    targetObjPtr, objc, objv); -    ckfree((char *) argv); -    return (Tcl_Interp *) NULL; +    Tcl_DecrRefCount(slaveObjPtr); +    Tcl_DecrRefCount(targetObjPtr); +    return result;  }  /*   *----------------------------------------------------------------------   * - * CreateInterpObject - + * Tcl_GetAlias --   * - *	Helper function to do the actual work of creating a new interpreter - *	and an object command.  + *	Gets information about an alias.   *   * Results: - *	A Tcl result. + *	A standard Tcl result.   *   * Side effects: - *	See user documentation for details. + *	None.   *   *----------------------------------------------------------------------   */ -static int -CreateInterpObject(interp, masterPtr, objc, objv) -    Tcl_Interp *interp;			/* Invoking interpreter. */ -    Master *masterPtr;			/* Master record for same. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* with alias. */ -{ -    int safe;				/* Create a safe interpreter? */ -    int moreFlags;			/* Expecting more flag args? */ -    char *string;			/* Local pointer to object string. */ -    char *slavePath;			/* Name of slave. */ -    char localSlaveName[200];		/* Local area for creating names. */ -    int i;				/* Loop counter. */ -    int len;				/* Length of option argument. */ -    static int interpCounter = 0;	/* Unique id for created names. */ - -    moreFlags = 1; -    slavePath = NULL; -    safe = Tcl_IsSafe(interp); -     -    if ((objc < 2) || (objc > 5)) { -        Tcl_WrongNumArgs(interp, 2, objv, "?-safe? ?--? ?path?"); -        return TCL_ERROR; -    } -    for (i = 2; i < objc; i++) { -        string = Tcl_GetStringFromObj(objv[i], &len); -        if ((string[0] == '-') && (moreFlags != 0)) { -            if ((string[1] == 's') && -                (strncmp(string, "-safe", (size_t) len) == 0) && -                (len > 1)){ -                safe = 1; -            } else if ((strncmp(string, "--", (size_t) len) == 0) && -                       (len > 1)) { -                moreFlags = 0; -            } else { -                Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                        "bad option \"", string, "\": should be -safe", -                        (char *) NULL); -                return TCL_ERROR; -            } -        } else { -            slavePath = string; -        } -    } -    if (slavePath == (char *) NULL) { - -        /* -         * Create an anonymous interpreter -- we choose its name and -         * the name of the command. We check that the command name that -         * we use for the interpreter does not collide with an existing -         * command in the master interpreter. -         */ -         -        while (1) { -            Tcl_CmdInfo cmdInfo; -             -            sprintf(localSlaveName, "interp%d", interpCounter); -            interpCounter++; -            if (!(Tcl_GetCommandInfo(interp, localSlaveName, &cmdInfo))) { -                break; -            } -        } -        slavePath = localSlaveName; -    } -    if (CreateSlave(interp, masterPtr, slavePath, safe) != NULL) { -        Tcl_SetObjResult(interp, Tcl_NewStringObj(slavePath, -1)); -        return TCL_OK; -    } else { -        /* -         * CreateSlave already set the result if there was an error, -         * so we do not do it here. -         */ -        return TCL_ERROR; +int +Tcl_GetAlias( +    Tcl_Interp *interp,		/* Interp to start search from. */ +    const char *aliasName,	/* Name of alias to find. */ +    Tcl_Interp **targetInterpPtr, +				/* (Return) target interpreter. */ +    const char **targetNamePtr,	/* (Return) name of target command. */ +    int *argcPtr,		/* (Return) count of addnl args. */ +    const char ***argvPtr)	/* (Return) additional arguments. */ +{ +    InterpInfo *iiPtr = (InterpInfo *) ((Interp *) interp)->interpInfo; +    Tcl_HashEntry *hPtr; +    Alias *aliasPtr; +    int i, objc; +    Tcl_Obj **objv; + +    hPtr = Tcl_FindHashEntry(&iiPtr->slave.aliasTable, aliasName); +    if (hPtr == NULL) { +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		"alias \"%s\" not found", aliasName)); +	Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ALIAS", aliasName, NULL); +	return TCL_ERROR; +    } +    aliasPtr = Tcl_GetHashValue(hPtr); +    objc = aliasPtr->objc; +    objv = &aliasPtr->objPtr; + +    if (targetInterpPtr != NULL) { +	*targetInterpPtr = aliasPtr->targetInterp;      } +    if (targetNamePtr != NULL) { +	*targetNamePtr = TclGetString(objv[0]); +    } +    if (argcPtr != NULL) { +	*argcPtr = objc - 1; +    } +    if (argvPtr != NULL) { +	*argvPtr = (const char **) +		ckalloc(sizeof(const char *) * (objc - 1)); +	for (i = 1; i < objc; i++) { +	    (*argvPtr)[i - 1] = TclGetString(objv[i]); +	} +    } +    return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * DeleteOneInterpObject -- + * Tcl_GetAliasObj --   * - *	Helper function for DeleteInterpObject. It deals with deleting one - *	interpreter at a time. + *	Object version: Gets information about an alias.   *   * Results:   *	A standard Tcl result.   *   * Side effects: - *	Deletes an interpreter and its interpreter object command. + *	None.   *   *----------------------------------------------------------------------   */ -static int -DeleteOneInterpObject(interp, masterPtr, path) -    Tcl_Interp *interp;			/* Interpreter for reporting errors. */ -    Master *masterPtr;			/* Interim storage for master record.*/ -    char *path;				/* Path of interpreter to delete. */ -{ -    Slave *slavePtr;			/* Interim storage for slave record. */ -    Tcl_Interp *masterInterp;		/* Master of interp. to delete. */ -    Tcl_HashEntry *hPtr;		/* Search element. */ -    int localArgc;			/* Local copy of count of elements in -                                         * path (name) of interp. to delete. */ -    char **localArgv;			/* Local copy of path. */ -    char *slaveName;			/* Last component in path. */ -    char *masterPath;			/* One-before-last component in path.*/ - -    if (Tcl_SplitList(interp, path, &localArgc, &localArgv) != TCL_OK) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "bad interpreter path \"", path, "\"", (char *) NULL); -        return TCL_ERROR; -    } -    if (localArgc < 2) { -        masterInterp = interp; -        if (localArgc == 0) { -            slaveName = ""; -        } else { -            slaveName = localArgv[0]; -        } -    } else { -        masterPath = Tcl_Merge(localArgc-1, localArgv); -        masterInterp = GetInterp(interp, masterPtr, masterPath, &masterPtr); -        if (masterInterp == (Tcl_Interp *) NULL) { -            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                    "interpreter named \"", masterPath, "\" not found", -                    (char *) NULL); -            ckfree((char *) localArgv); -            ckfree((char *) masterPath); -            return TCL_ERROR; -        } -        ckfree((char *) masterPath); -        slaveName = localArgv[localArgc-1]; -    } -    hPtr = Tcl_FindHashEntry(&(masterPtr->slaveTable), slaveName); -    if (hPtr == (Tcl_HashEntry *) NULL) { -        ckfree((char *) localArgv); -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "interpreter named \"", path, "\" not found", (char *) NULL); -        return TCL_ERROR; -    } -    slavePtr = (Slave *) Tcl_GetHashValue(hPtr); -    if (Tcl_DeleteCommandFromToken(masterInterp, slavePtr->interpCmd) != 0) { -        ckfree((char *) localArgv); -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "interpreter named \"", path, "\" not found", (char *) NULL); -        return TCL_ERROR; -    } -    ckfree((char *) localArgv); +int +Tcl_GetAliasObj( +    Tcl_Interp *interp,		/* Interp to start search from. */ +    const char *aliasName,	/* Name of alias to find. */ +    Tcl_Interp **targetInterpPtr, +				/* (Return) target interpreter. */ +    const char **targetNamePtr,	/* (Return) name of target command. */ +    int *objcPtr,		/* (Return) count of addnl args. */ +    Tcl_Obj ***objvPtr)		/* (Return) additional args. */ +{ +    InterpInfo *iiPtr = (InterpInfo *) ((Interp *) interp)->interpInfo; +    Tcl_HashEntry *hPtr; +    Alias *aliasPtr; +    int objc; +    Tcl_Obj **objv; +    hPtr = Tcl_FindHashEntry(&iiPtr->slave.aliasTable, aliasName); +    if (hPtr == NULL) { +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		"alias \"%s\" not found", aliasName)); +	Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ALIAS", aliasName, NULL); +	return TCL_ERROR; +    } +    aliasPtr = Tcl_GetHashValue(hPtr); +    objc = aliasPtr->objc; +    objv = &aliasPtr->objPtr; + +    if (targetInterpPtr != NULL) { +	*targetInterpPtr = aliasPtr->targetInterp; +    } +    if (targetNamePtr != NULL) { +	*targetNamePtr = TclGetString(objv[0]); +    } +    if (objcPtr != NULL) { +	*objcPtr = objc - 1; +    } +    if (objvPtr != NULL) { +	*objvPtr = objv + 1; +    }      return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * DeleteInterpObject -- + * TclPreventAliasLoop --   * - *	Helper function to do the work of deleting zero or more - *	interpreters and their interpreter object commands. + *	When defining an alias or renaming a command, prevent an alias loop + *	from being formed.   *   * Results: - *	A standard Tcl result. + *	A standard Tcl object result.   *   * Side effects: - *	Deletes interpreters and their interpreter object command. + *	If TCL_ERROR is returned, the function also stores an error message in + *	the interpreter's result object. + * + * NOTE: + *	This function is public internal (instead of being static to this + *	file) because it is also used from TclRenameCommand.   *   *----------------------------------------------------------------------   */ -static int -DeleteInterpObject(interp, masterPtr, objc, objv) -    Tcl_Interp *interp;			/* Interpreter start search from. */ -    Master *masterPtr;			/* Interim storage for master record.*/ -    int objc;				/* Number of arguments in vector. */ -    Tcl_Obj *CONST objv[];		/* with alias. */ +int +TclPreventAliasLoop( +    Tcl_Interp *interp,		/* Interp in which to report errors. */ +    Tcl_Interp *cmdInterp,	/* Interp in which the command is being +				 * defined. */ +    Tcl_Command cmd)		/* Tcl command we are attempting to define. */  { -    int i; -    int len; -     -    for (i = 2; i < objc; i++) { -        if (DeleteOneInterpObject(interp, masterPtr, -                Tcl_GetStringFromObj(objv[i], &len)) -                != TCL_OK) { -            return TCL_ERROR; -        } +    Command *cmdPtr = (Command *) cmd; +    Alias *aliasPtr, *nextAliasPtr; +    Tcl_Command aliasCmd; +    Command *aliasCmdPtr; + +    /* +     * If we are not creating or renaming an alias, then it is always OK to +     * create or rename the command. +     */ + +    if (cmdPtr->objProc != AliasObjCmd) { +	return TCL_OK;      } -    return TCL_OK; + +    /* +     * OK, we are dealing with an alias, so traverse the chain of aliases. If +     * we encounter the alias we are defining (or renaming to) any in the +     * chain then we have a loop. +     */ + +    aliasPtr = cmdPtr->objClientData; +    nextAliasPtr = aliasPtr; +    while (1) { +	Tcl_Obj *cmdNamePtr; + +	/* +	 * If the target of the next alias in the chain is the same as the +	 * source alias, we have a loop. +	 */ + +	if (Tcl_InterpDeleted(nextAliasPtr->targetInterp)) { +	    /* +	     * The slave interpreter can be deleted while creating the alias. +	     * [Bug #641195] +	     */ + +	    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		    "cannot define or rename alias \"%s\": interpreter deleted", +		    Tcl_GetCommandName(cmdInterp, cmd))); +	    return TCL_ERROR; +	} +	cmdNamePtr = nextAliasPtr->objPtr; +	aliasCmd = Tcl_FindCommand(nextAliasPtr->targetInterp, +		TclGetString(cmdNamePtr), +		Tcl_GetGlobalNamespace(nextAliasPtr->targetInterp), +		/*flags*/ 0); +	if (aliasCmd == NULL) { +	    return TCL_OK; +	} +	aliasCmdPtr = (Command *) aliasCmd; +	if (aliasCmdPtr == cmdPtr) { +	    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		    "cannot define or rename alias \"%s\": would create a loop", +		    Tcl_GetCommandName(cmdInterp, cmd))); +	    Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", +		    "ALIASLOOP", NULL); +	    return TCL_ERROR; +	} + +	/* +	 * Otherwise, follow the chain one step further. See if the target +	 * command is an alias - if so, follow the loop to its target command. +	 * Otherwise we do not have a loop. +	 */ + +	if (aliasCmdPtr->objProc != AliasObjCmd) { +	    return TCL_OK; +	} +	nextAliasPtr = aliasCmdPtr->objClientData; +    } + +    /* NOTREACHED */  }  /*   *----------------------------------------------------------------------   * - * AliasCreationHelper -- + * AliasCreate --   * - *	Helper function to do the work to actually create an alias or - *	delete an alias. + *	Helper function to do the work to actually create an alias.   *   * Results:   *	A standard Tcl result.   *   * Side effects: - *	An alias command is created and entered into the alias table - *	for the slave interpreter. + *	An alias command is created and entered into the alias table for the + *	slave interpreter.   *   *----------------------------------------------------------------------   */  static int -AliasCreationHelper(curInterp, slaveInterp, masterInterp, masterPtr, -     aliasName, targetName, objc, objv) -    Tcl_Interp *curInterp;		/* Interp that invoked this proc. */ -    Tcl_Interp *slaveInterp;		/* Interp where alias cmd will live -                                         * or from which alias will be -                                         * deleted. */ -    Tcl_Interp *masterInterp;		/* Interp where target cmd will be. */ -    Master *masterPtr;			/* Master record for target interp. */ -    char *aliasName;			/* Name of alias cmd. */ -    char *targetName;			/* Name of target cmd. */ -    int objc;				/* Additional arguments to store */ -    Tcl_Obj *CONST objv[];		/* with alias. */ -{ -    Alias *aliasPtr;			/* Storage for alias data. */ -    Alias *tmpAliasPtr;			/* Temp storage for alias to delete. */ -    Tcl_HashEntry *hPtr;		/* Entry into interp hashtable. */ -    int i;				/* Loop index. */ -    int new;				/* Is it a new hash entry? */ -    Target *targetPtr;			/* Maps from target command in master -                                         * to source command in slave. */ -    Slave *slavePtr;			/* Maps from source command in slave -                                         * to target command in master. */ - -    slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord", NULL); +AliasCreate( +    Tcl_Interp *interp,		/* Interp for error reporting. */ +    Tcl_Interp *slaveInterp,	/* Interp where alias cmd will live or from +				 * which alias will be deleted. */ +    Tcl_Interp *masterInterp,	/* Interp in which target command will be +				 * invoked. */ +    Tcl_Obj *namePtr,		/* Name of alias cmd. */ +    Tcl_Obj *targetNamePtr,	/* Name of target cmd. */ +    int objc,			/* Additional arguments to store */ +    Tcl_Obj *const objv[])	/* with alias. */ +{ +    Alias *aliasPtr; +    Tcl_HashEntry *hPtr; +    Target *targetPtr; +    Slave *slavePtr; +    Master *masterPtr; +    Tcl_Obj **prefv; +    int isNew, i; -    /* -     * Slave record should be always present because it is created when -     * the interpreter is created. -     */ -     -    if (slavePtr == (Slave *) NULL) { -        panic("AliasCreationHelper: could not find slave record"); -    } - -    if ((targetName == (char *) NULL) || (targetName[0] == '\0')) { -        if (objc != 0) { -            Tcl_AppendStringsToObj(Tcl_GetObjResult(curInterp), -                    "malformed command: should be", -                    " \"alias ",  aliasName, " {}\"", (char *) NULL); -            return TCL_ERROR; -        } - -        return DeleteAlias(curInterp, slaveInterp, aliasName); -    } -     -    aliasPtr = (Alias *) ckalloc((unsigned) sizeof(Alias)); -    aliasPtr->aliasName = (char *) ckalloc((unsigned) strlen(aliasName)+1); -    aliasPtr->targetName = (char *) ckalloc((unsigned) strlen(targetName)+1); -    strcpy(aliasPtr->aliasName, aliasName); -    strcpy(aliasPtr->targetName, targetName); +    aliasPtr = ckalloc(sizeof(Alias) + objc * sizeof(Tcl_Obj *)); +    aliasPtr->token = namePtr; +    Tcl_IncrRefCount(aliasPtr->token);      aliasPtr->targetInterp = masterInterp; -    aliasPtr->objv = NULL; -    aliasPtr->objc = objc; +    aliasPtr->objc = objc + 1; +    prefv = &aliasPtr->objPtr; -    if (aliasPtr->objc > 0) { -        aliasPtr->objv = -            (Tcl_Obj **) ckalloc((unsigned) sizeof(Tcl_Obj *) * -                    aliasPtr->objc); -        for (i = 0; i < objc; i++) { -            aliasPtr->objv[i] = objv[i]; -            Tcl_IncrRefCount(objv[i]); -        } +    *prefv = targetNamePtr; +    Tcl_IncrRefCount(targetNamePtr); +    for (i = 0; i < objc; i++) { +	*(++prefv) = objv[i]; +	Tcl_IncrRefCount(objv[i]);      } -    aliasPtr->slaveCmd = Tcl_CreateObjCommand(slaveInterp, aliasName, -            AliasCmd, (ClientData) aliasPtr, AliasCmdDeleteProc); +    Tcl_Preserve(slaveInterp); +    Tcl_Preserve(masterInterp); -    if (TclPreventAliasLoop(curInterp, slaveInterp,  -            aliasPtr->slaveCmd) != TCL_OK) { +    if (slaveInterp == masterInterp) { +	aliasPtr->slaveCmd = Tcl_NRCreateCommand(slaveInterp, +		TclGetString(namePtr), AliasObjCmd, AliasNRCmd, aliasPtr, +		AliasObjCmdDeleteProc); +    } else { +    aliasPtr->slaveCmd = Tcl_CreateObjCommand(slaveInterp, +	    TclGetString(namePtr), AliasObjCmd, aliasPtr, +	    AliasObjCmdDeleteProc); +    } +    if (TclPreventAliasLoop(interp, slaveInterp, +	    aliasPtr->slaveCmd) != TCL_OK) {  	/* -         *  Found an alias loop!  The last call to Tcl_CreateObjCommand -         *  made the alias point to itself.  Delete the command and -         *  its alias record.  Be careful to wipe out its client data -         *  first, so the command doesn't try to delete itself. -         */ -	 -        Command *cmdPtr = (Command*) aliasPtr->slaveCmd; -        cmdPtr->clientData = NULL; -        cmdPtr->deleteProc = NULL; -        cmdPtr->deleteData = NULL; -        Tcl_DeleteCommandFromToken(slaveInterp, aliasPtr->slaveCmd); - -        for (i = 0; i < objc; i++) { -            Tcl_DecrRefCount(aliasPtr->objv[i]); -        } -        if (aliasPtr->objv != (Tcl_Obj *CONST *) NULL) { -            ckfree((char *) aliasPtr->objv); -        } -        ckfree(aliasPtr->aliasName); -        ckfree(aliasPtr->targetName); -        ckfree((char *) aliasPtr); - -        /* -         * The result was already set by TclPreventAliasLoop. -         */ - -        return TCL_ERROR; -    } -     +	 * Found an alias loop! The last call to Tcl_CreateObjCommand made the +	 * alias point to itself. Delete the command and its alias record. Be +	 * careful to wipe out its client data first, so the command doesn't +	 * try to delete itself. +	 */ + +	Command *cmdPtr; + +	Tcl_DecrRefCount(aliasPtr->token); +	Tcl_DecrRefCount(targetNamePtr); +	for (i = 0; i < objc; i++) { +	    Tcl_DecrRefCount(objv[i]); +	} + +	cmdPtr = (Command *) aliasPtr->slaveCmd; +	cmdPtr->clientData = NULL; +	cmdPtr->deleteProc = NULL; +	cmdPtr->deleteData = NULL; +	Tcl_DeleteCommandFromToken(slaveInterp, aliasPtr->slaveCmd); + +	ckfree(aliasPtr); + +	/* +	 * The result was already set by TclPreventAliasLoop. +	 */ + +	Tcl_Release(slaveInterp); +	Tcl_Release(masterInterp); +	return TCL_ERROR; +    } +      /* -     * Make an entry in the alias table. If it already exists delete -     * the alias command. Then retry. +     * Make an entry in the alias table. If it already exists, retry.       */ -    do { -        hPtr = Tcl_CreateHashEntry(&(slavePtr->aliasTable), aliasName, &new); -        if (!new) { -            tmpAliasPtr = (Alias *) Tcl_GetHashValue(hPtr); -            (void) Tcl_DeleteCommandFromToken(slaveInterp, -	            tmpAliasPtr->slaveCmd); - -            /* -             * The hash entry should be deleted by the Tcl_DeleteCommand -             * above, in its command deletion callback (most likely this -             * will be AliasCmdDeleteProc, which does the deletion). -             */ -        } -    } while (new == 0); -    aliasPtr->aliasEntry = hPtr; -    Tcl_SetHashValue(hPtr, (ClientData) aliasPtr); -     +    slavePtr = &((InterpInfo *) ((Interp *) slaveInterp)->interpInfo)->slave; +    while (1) { +	Tcl_Obj *newToken; +	const char *string; + +	string = TclGetString(aliasPtr->token); +	hPtr = Tcl_CreateHashEntry(&slavePtr->aliasTable, string, &isNew); +	if (isNew != 0) { +	    break; +	} + +	/* +	 * The alias name cannot be used as unique token, it is already taken. +	 * We can produce a unique token by prepending "::" repeatedly. This +	 * algorithm is a stop-gap to try to maintain the command name as +	 * token for most use cases, fearful of possible backwards compat +	 * problems. A better algorithm would produce unique tokens that need +	 * not be related to the command name. +	 * +	 * ATTENTION: the tests in interp.test and possibly safe.test depend +	 * on the precise definition of these tokens. +	 */ + +	TclNewLiteralStringObj(newToken, "::"); +	Tcl_AppendObjToObj(newToken, aliasPtr->token); +	Tcl_DecrRefCount(aliasPtr->token); +	aliasPtr->token = newToken; +	Tcl_IncrRefCount(aliasPtr->token); +    } + +    aliasPtr->aliasEntryPtr = hPtr; +    Tcl_SetHashValue(hPtr, aliasPtr); +      /*       * Create the new command. We must do it after deleting any old command,       * because the alias may be pointing at a renamed alias, as in: @@ -976,181 +1616,124 @@ AliasCreationHelper(curInterp, slaveInterp, masterInterp, masterPtr,       * interp alias {} foo {} zop		# Now recreate "foo"...       */ -    targetPtr = (Target *) ckalloc((unsigned) sizeof(Target)); +    targetPtr = ckalloc(sizeof(Target));      targetPtr->slaveCmd = aliasPtr->slaveCmd;      targetPtr->slaveInterp = slaveInterp; -    do { -        hPtr = Tcl_CreateHashEntry(&(masterPtr->targetTable), -                (char *) aliasCounter, &new); -	aliasCounter++; -    } while (new == 0); - -    Tcl_SetHashValue(hPtr, (ClientData) targetPtr); +    masterPtr = &((InterpInfo*) ((Interp*) masterInterp)->interpInfo)->master; +    targetPtr->nextPtr = masterPtr->targetsPtr; +    targetPtr->prevPtr = NULL; +    if (masterPtr->targetsPtr != NULL) { +	masterPtr->targetsPtr->prevPtr = targetPtr; +    } +    masterPtr->targetsPtr = targetPtr; +    aliasPtr->targetPtr = targetPtr; -    aliasPtr->targetEntry = hPtr; +    Tcl_SetObjResult(interp, aliasPtr->token); -    /* -     * Make sure we clear out the object result when setting the string -     * result. -     */ - -    Tcl_SetObjResult(curInterp, Tcl_NewStringObj(aliasPtr->aliasName, -1)); -     +    Tcl_Release(slaveInterp); +    Tcl_Release(masterInterp);      return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * InterpAliasesHelper -- + * AliasDelete --   * - *	Computes a list of aliases defined in an interpreter. + *	Deletes the given alias from the slave interpreter given.   *   * Results:   *	A standard Tcl result.   *   * Side effects: - *	None. + *	Deletes the alias from the slave interpreter.   *   *----------------------------------------------------------------------   */  static int -InterpAliasesHelper(interp, masterPtr, objc, objv) -    Tcl_Interp *interp;			/* Invoking interpreter. */ -    Master *masterPtr;			/* Master record for current interp. */ -    int objc;				/* How many arguments? */ -    Tcl_Obj *CONST objv[];		/* Actual arguments. */ -{ -    Tcl_Interp *slaveInterp;		/* A slave. */ -    Slave *slavePtr;			/* Record for slave interp. */ -    Tcl_HashEntry *hPtr;		/* Search variable. */ -    Tcl_HashSearch hSearch;		/* Iteration variable. */ -    int len;				/* Dummy length variable. */ -    Tcl_Obj *listObjPtr, *elemObjPtr;	/* Local object pointers. */ -     -    if ((objc != 2) && (objc != 3)) { -        Tcl_WrongNumArgs(interp, 2, objv, "?path?"); -        return TCL_ERROR; -    } -    if (objc == 3) { -        slaveInterp = GetInterp(interp, masterPtr, -                Tcl_GetStringFromObj(objv[2], &len), NULL); -        if (slaveInterp == (Tcl_Interp *) NULL) { -            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                    "interpreter \"", Tcl_GetStringFromObj(objv[2], &len), -                    "\" not found", (char *) NULL); -            return TCL_ERROR; -        } -    } else { -        slaveInterp = interp; -    } -    slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, -            "tclSlaveRecord", NULL); -    if (slavePtr == (Slave *) NULL) { -        return TCL_OK; -    } +AliasDelete( +    Tcl_Interp *interp,		/* Interpreter for result & errors. */ +    Tcl_Interp *slaveInterp,	/* Interpreter containing alias. */ +    Tcl_Obj *namePtr)		/* Name of alias to delete. */ +{ +    Slave *slavePtr; +    Alias *aliasPtr; +    Tcl_HashEntry *hPtr;      /* -     * Build a list to return the aliases: +     * If the alias has been renamed in the slave, the master can still use +     * the original name (with which it was created) to find the alias to +     * delete it.       */ -             -    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); -    for (hPtr = Tcl_FirstHashEntry(&(slavePtr->aliasTable), &hSearch); -         hPtr != NULL; -         hPtr = Tcl_NextHashEntry(&hSearch)) { -        elemObjPtr = Tcl_NewStringObj( -            Tcl_GetHashKey(&(slavePtr->aliasTable), hPtr), -1); -        Tcl_ListObjAppendElement(interp, listObjPtr, elemObjPtr); +    slavePtr = &((InterpInfo *) ((Interp *) slaveInterp)->interpInfo)->slave; +    hPtr = Tcl_FindHashEntry(&slavePtr->aliasTable, TclGetString(namePtr)); +    if (hPtr == NULL) { +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		"alias \"%s\" not found", TclGetString(namePtr))); +	Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ALIAS", +		TclGetString(namePtr), NULL); +	return TCL_ERROR;      } -    Tcl_SetObjResult(interp, listObjPtr); - +    aliasPtr = Tcl_GetHashValue(hPtr); +    Tcl_DeleteCommandFromToken(slaveInterp, aliasPtr->slaveCmd);      return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * InterpAliasHelper - + * AliasDescribe --   * - *	Handles the different forms of the "interp alias" command: - *	- interp alias slavePath aliasName - *		Describes an alias. - *	- interp alias slavePath aliasName {} - *		Deletes an alias. - *	- interp alias slavePath srcCmd masterPath targetCmd args... - *		Creates an alias. + *	Sets the interpreter's result object to a Tcl list describing the + *	given alias in the given interpreter: its target command and the + *	additional arguments to prepend to any invocation of the alias.   *   * Results: - *	A Tcl result. + *	A standard Tcl result.   *   * Side effects: - *	See user documentation for details. + *	None.   *   *----------------------------------------------------------------------   */  static int -InterpAliasHelper(interp, masterPtr, objc, objv) -    Tcl_Interp *interp;			/* Current interpreter. */ -    Master *masterPtr;			/* Master record for current interp. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* Argument objects. */ +AliasDescribe( +    Tcl_Interp *interp,		/* Interpreter for result & errors. */ +    Tcl_Interp *slaveInterp,	/* Interpreter containing alias. */ +    Tcl_Obj *namePtr)		/* Name of alias to describe. */  { -    Tcl_Interp *slaveInterp,		/* Interpreters used when */ -        *masterInterp;			/* creating an alias btn siblings. */ -    Master *masterMasterPtr;		/* Master record for master interp. */ -    int len; +    Slave *slavePtr; +    Tcl_HashEntry *hPtr; +    Alias *aliasPtr; +    Tcl_Obj *prefixPtr; -    if (objc < 4) { -        Tcl_WrongNumArgs(interp, 2, objv, -                "slavePath slaveCmd masterPath masterCmd ?args ..?"); -        return TCL_ERROR; -    } -    slaveInterp = GetInterp(interp, masterPtr, -            Tcl_GetStringFromObj(objv[2], &len), NULL); -    if (slaveInterp == (Tcl_Interp *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "could not find interpreter \"", -                Tcl_GetStringFromObj(objv[2], &len), "\"", -                (char *) NULL); -        return TCL_ERROR; -    } -    if (objc == 4) { -        return DescribeAlias(interp, slaveInterp, -                Tcl_GetStringFromObj(objv[3], &len)); -    } -    if (objc == 5 && strcmp(Tcl_GetStringFromObj(objv[4], &len), "") == 0) { -        return DeleteAlias(interp, slaveInterp, -                Tcl_GetStringFromObj(objv[3], &len)); -    } -    if (objc < 6) { -        Tcl_WrongNumArgs(interp, 2, objv, -                "slavePath slaveCmd masterPath masterCmd ?args ..?"); -        return TCL_ERROR; -    } -    masterInterp = GetInterp(interp, masterPtr, -            Tcl_GetStringFromObj(objv[4], &len), &masterMasterPtr); -    if (masterInterp == (Tcl_Interp *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "could not find interpreter \"", -                Tcl_GetStringFromObj(objv[4], &len), "\"", (char *) NULL); -        return TCL_ERROR; +    /* +     * If the alias has been renamed in the slave, the master can still use +     * the original name (with which it was created) to find the alias to +     * describe it. +     */ + +    slavePtr = &((InterpInfo *) ((Interp *) slaveInterp)->interpInfo)->slave; +    hPtr = Tcl_FindHashEntry(&slavePtr->aliasTable, Tcl_GetString(namePtr)); +    if (hPtr == NULL) { +	return TCL_OK;      } -    return AliasCreationHelper(interp, slaveInterp, masterInterp, -            masterMasterPtr, Tcl_GetStringFromObj(objv[3], &len), -            Tcl_GetStringFromObj(objv[5], &len), -            objc-6, objv+6); +    aliasPtr = Tcl_GetHashValue(hPtr); +    prefixPtr = Tcl_NewListObj(aliasPtr->objc, &aliasPtr->objPtr); +    Tcl_SetObjResult(interp, prefixPtr); +    return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * InterpExistsHelper -- + * AliasList --   * - *	Computes whether a named interpreter exists or not. + *	Computes a list of aliases defined in a slave interpreter.   *   * Results:   *	A standard Tcl result. @@ -1162,266 +1745,316 @@ InterpAliasHelper(interp, masterPtr, objc, objv)   */  static int -InterpExistsHelper(interp, masterPtr, objc, objv) -    Tcl_Interp *interp;			/* Current interpreter. */ -    Master *masterPtr;			/* Master record for current interp. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* Argument objects. */ -{ -    Tcl_Obj *objPtr; -    int len; - -    if (objc > 3) { -        Tcl_WrongNumArgs(interp, 2, objv, "?path?"); -        return TCL_ERROR; -    } -    if (objc == 3) { -        if (GetInterp(interp, masterPtr, -                Tcl_GetStringFromObj(objv[2], &len), NULL) == -                (Tcl_Interp *) NULL) { -            objPtr = Tcl_NewIntObj(0); -        } else { -            objPtr = Tcl_NewIntObj(1); -        } -    } else { -        objPtr = Tcl_NewIntObj(1); +AliasList( +    Tcl_Interp *interp,		/* Interp for data return. */ +    Tcl_Interp *slaveInterp)	/* Interp whose aliases to compute. */ +{ +    Tcl_HashEntry *entryPtr; +    Tcl_HashSearch hashSearch; +    Tcl_Obj *resultPtr = Tcl_NewObj(); +    Alias *aliasPtr; +    Slave *slavePtr; + +    slavePtr = &((InterpInfo *) ((Interp *) slaveInterp)->interpInfo)->slave; + +    entryPtr = Tcl_FirstHashEntry(&slavePtr->aliasTable, &hashSearch); +    for ( ; entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&hashSearch)) { +	aliasPtr = Tcl_GetHashValue(entryPtr); +	Tcl_ListObjAppendElement(NULL, resultPtr, aliasPtr->token);      } -    Tcl_SetObjResult(interp, objPtr); -     +    Tcl_SetObjResult(interp, resultPtr);      return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * InterpEvalHelper -- + * AliasObjCmd --   * - *	Helper function to handle all the details of evaluating a - *	command in another interpreter. + *	This is the function that services invocations of aliases in a slave + *	interpreter. One such command exists for each alias. When invoked, + *	this function redirects the invocation to the target command in the + *	master interpreter as designated by the Alias record associated with + *	this command.   *   * Results:   *	A standard Tcl result.   *   * Side effects: - *	Whatever the command itself does. + *	Causes forwarding of the invocation; all possible side effects may + *	occur as a result of invoking the command to which the invocation is + *	forwarded.   *   *----------------------------------------------------------------------   */  static int -InterpEvalHelper(interp, masterPtr, objc, objv) -    Tcl_Interp *interp;			/* Current interpreter. */ -    Master *masterPtr;			/* Master record for current interp. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* Argument objects. */ -{ -    Tcl_Interp *slaveInterp;		/* A slave. */ -    Interp *iPtr;			/* Internal data type for slave. */ -    int len;				/* Dummy length variable. */ -    int result; -    Tcl_Obj *namePtr, *objPtr;		/* Local object pointer. */ -    char *string; +AliasNRCmd( +    ClientData clientData,	/* Alias record. */ +    Tcl_Interp *interp,		/* Current interpreter. */ +    int objc,			/* Number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument vector. */ +{ +    Interp *iPtr = (Interp *) interp; +    Alias *aliasPtr = clientData; +    int prefc, cmdc, i; +    Tcl_Obj **prefv, **cmdv; +    int isRootEnsemble = (iPtr->ensembleRewrite.sourceObjs == NULL); +    Tcl_Obj *listPtr; +    List *listRep; +    int flags = TCL_EVAL_INVOKE; + +    /* +     * Append the arguments to the command prefix and invoke the command in +     * the target interp's global namespace. +     */ + +    prefc = aliasPtr->objc; +    prefv = &aliasPtr->objPtr; +    cmdc = prefc + objc - 1; + +    listPtr = Tcl_NewListObj(cmdc, NULL); +    listRep = listPtr->internalRep.twoPtrValue.ptr1; +    listRep->elemCount = cmdc; +    cmdv = &listRep->elements; + +    prefv = &aliasPtr->objPtr; +    memcpy(cmdv, prefv, (size_t) (prefc * sizeof(Tcl_Obj *))); +    memcpy(cmdv+prefc, objv+1, (size_t) ((objc-1) * sizeof(Tcl_Obj *))); + +    for (i=0; i<cmdc; i++) { +	Tcl_IncrRefCount(cmdv[i]); +    } + +    /* +     * Use the ensemble rewriting machinery to ensure correct error messages: +     * only the source command should show, not the full target prefix. +     */ + +    if (isRootEnsemble) { +	iPtr->ensembleRewrite.sourceObjs = objv; +	iPtr->ensembleRewrite.numRemovedObjs = 1; +	iPtr->ensembleRewrite.numInsertedObjs = prefc; +    } else { +	iPtr->ensembleRewrite.numInsertedObjs += prefc - 1; +    } + +    /* +     * We are sending a 0-refCount obj, do not need a callback: it will be +     * cleaned up automatically. But we may need to clear the rootEnsemble +     * stuff ... +     */ -    if (objc < 4) { -        Tcl_WrongNumArgs(interp, 2, objv, "path arg ?arg ...?"); -        return TCL_ERROR; +    if (isRootEnsemble) { +	TclNRAddCallback(interp, TclClearRootEnsemble, NULL, NULL, NULL, NULL);      } -    slaveInterp = GetInterp(interp, masterPtr, -            Tcl_GetStringFromObj(objv[2], &len), NULL); -    if (slaveInterp == (Tcl_Interp *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "interpreter named \"", Tcl_GetStringFromObj(objv[2], &len), -                "\" not found", (char *) NULL); -        return TCL_ERROR; +    TclSkipTailcall(interp); +    return Tcl_NREvalObj(interp, listPtr, flags); +} + +static int +AliasObjCmd( +    ClientData clientData,	/* Alias record. */ +    Tcl_Interp *interp,		/* Current interpreter. */ +    int objc,			/* Number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument vector. */ +{ +#define ALIAS_CMDV_PREALLOC 10 +    Alias *aliasPtr = clientData; +    Tcl_Interp *targetInterp = aliasPtr->targetInterp; +    int result, prefc, cmdc, i; +    Tcl_Obj **prefv, **cmdv; +    Tcl_Obj *cmdArr[ALIAS_CMDV_PREALLOC]; +    Interp *tPtr = (Interp *) targetInterp; +    int isRootEnsemble = (tPtr->ensembleRewrite.sourceObjs == NULL); + +    /* +     * Append the arguments to the command prefix and invoke the command in +     * the target interp's global namespace. +     */ + +    prefc = aliasPtr->objc; +    prefv = &aliasPtr->objPtr; +    cmdc = prefc + objc - 1; +    if (cmdc <= ALIAS_CMDV_PREALLOC) { +	cmdv = cmdArr; +    } else { +	cmdv = TclStackAlloc(interp, cmdc * sizeof(Tcl_Obj *));      } -    objPtr = Tcl_ConcatObj(objc-3, objv+3); -    Tcl_IncrRefCount(objPtr); -     -    Tcl_Preserve((ClientData) slaveInterp); -    result = Tcl_EvalObj(slaveInterp, objPtr); -    Tcl_DecrRefCount(objPtr); +    prefv = &aliasPtr->objPtr; +    memcpy(cmdv, prefv, (size_t) (prefc * sizeof(Tcl_Obj *))); +    memcpy(cmdv+prefc, objv+1, (size_t) ((objc-1) * sizeof(Tcl_Obj *))); + +    Tcl_ResetResult(targetInterp); + +    for (i=0; i<cmdc; i++) { +	Tcl_IncrRefCount(cmdv[i]); +    }      /* -     * Now make the result and any error information accessible. We -     * have to be careful because the slave interpreter and the current -     * interpreter can be the same - do not destroy the result.. This -     * can happen if an interpreter contains an alias which is directed -     * at a target command in the same interpreter. +     * Use the ensemble rewriting machinery to ensure correct error messages: +     * only the source command should show, not the full target prefix.       */ -    if (interp != slaveInterp) { -        if (result == TCL_ERROR) { - -            /* -             * An error occurred, so transfer error information from -             * the target interpreter back to our interpreter. -             */ - -            iPtr = (Interp *) slaveInterp; -            if (!(iPtr->flags & ERR_ALREADY_LOGGED)) { -                Tcl_AddErrorInfo(slaveInterp, ""); -            } -            iPtr->flags &= (~(ERR_ALREADY_LOGGED)); -             -            Tcl_ResetResult(interp); -            namePtr = Tcl_NewStringObj("errorInfo", -1); -            objPtr = Tcl_ObjGetVar2(slaveInterp, namePtr, -                    (Tcl_Obj *) NULL, TCL_GLOBAL_ONLY); -            string = Tcl_GetStringFromObj(objPtr, &len); -            Tcl_AddObjErrorInfo(interp, string, len); -            Tcl_SetVar2(interp, "errorCode", (char *) NULL, -                    Tcl_GetVar2(slaveInterp, "errorCode", (char *) -                            NULL, TCL_GLOBAL_ONLY), -                    TCL_GLOBAL_ONLY); -            Tcl_DecrRefCount(namePtr); -        } +    if (isRootEnsemble) { +	tPtr->ensembleRewrite.sourceObjs = objv; +	tPtr->ensembleRewrite.numRemovedObjs = 1; +	tPtr->ensembleRewrite.numInsertedObjs = prefc; +    } else { +	tPtr->ensembleRewrite.numInsertedObjs += prefc - 1; +    } -	/* -         * Move the result object from one interpreter to the -         * other. -         */ -                 -        Tcl_SetObjResult(interp, Tcl_GetObjResult(slaveInterp)); -        Tcl_ResetResult(slaveInterp); +    /* +     * Protect the target interpreter if it isn't the same as the source +     * interpreter so that we can continue to work with it after the target +     * command completes. +     */ +    if (targetInterp != interp) { +	Tcl_Preserve(targetInterp);      } -    Tcl_Release((ClientData) slaveInterp); -    return result;         + +    /* +     * Execute the target command in the target interpreter. +     */ + +    result = Tcl_EvalObjv(targetInterp, cmdc, cmdv, TCL_EVAL_INVOKE); + +    /* +     * Clean up the ensemble rewrite info if we set it in the first place. +     */ + +    if (isRootEnsemble) { +	tPtr->ensembleRewrite.sourceObjs = NULL; +	tPtr->ensembleRewrite.numRemovedObjs = 0; +	tPtr->ensembleRewrite.numInsertedObjs = 0; +    } + +    /* +     * If it was a cross-interpreter alias, we need to transfer the result +     * back to the source interpreter and release the lock we previously set +     * on the target interpreter. +     */ + +    if (targetInterp != interp) { +	Tcl_TransferResult(targetInterp, result, interp); +	Tcl_Release(targetInterp); +    } + +    for (i=0; i<cmdc; i++) { +	Tcl_DecrRefCount(cmdv[i]); +    } +    if (cmdv != cmdArr) { +	TclStackFree(interp, cmdv); +    } +    return result; +#undef ALIAS_CMDV_PREALLOC  }  /*   *----------------------------------------------------------------------   * - * InterpExposeHelper -- + * AliasObjCmdDeleteProc --   * - *	Helper function to handle the details of exposing a command in - *	another interpreter. + *	Is invoked when an alias command is deleted in a slave. Cleans up all + *	storage associated with this alias.   *   * Results: - *	Standard Tcl result. + *	None.   *   * Side effects: - *	Exposes a command. From now on the command can be called by scripts - *	in the interpreter in which it was exposed. + *	Deletes the alias record and its entry in the alias table for the + *	interpreter.   *   *----------------------------------------------------------------------   */ -static int -InterpExposeHelper(interp, masterPtr, objc, objv) -    Tcl_Interp *interp;			/* Current interpreter. */ -    Master *masterPtr;			/* Master record for current interp. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* Argument objects. */ +static void +AliasObjCmdDeleteProc( +    ClientData clientData)	/* The alias record for this alias. */  { -    Tcl_Interp *slaveInterp;		/* A slave. */ -    int len;				/* Dummy length variable. */ +    Alias *aliasPtr = clientData; +    Target *targetPtr; +    int i; +    Tcl_Obj **objv; -    if ((objc != 4) && (objc != 5)) { -        Tcl_WrongNumArgs(interp, 2, objv, -                "path hiddenCmdName ?cmdName?"); -        return TCL_ERROR; +    Tcl_DecrRefCount(aliasPtr->token); +    objv = &aliasPtr->objPtr; +    for (i = 0; i < aliasPtr->objc; i++) { +	Tcl_DecrRefCount(objv[i]);      } -    if (Tcl_IsSafe(interp)) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "permission denied: safe interpreter cannot expose commands", -                (char *) NULL); -        return TCL_ERROR; -    } -    slaveInterp = GetInterp(interp, masterPtr, -            Tcl_GetStringFromObj(objv[2], &len), &masterPtr); -    if (slaveInterp == (Tcl_Interp *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "interpreter \"", Tcl_GetStringFromObj(objv[2], &len), -                "\" not found", (char *) NULL); -        return TCL_ERROR; -    } -    if (Tcl_ExposeCommand(slaveInterp, -            Tcl_GetStringFromObj(objv[3], &len), -                (objc == 5 ? -                        Tcl_GetStringFromObj(objv[4], &len) : -                        Tcl_GetStringFromObj(objv[3], &len))) -            == TCL_ERROR) { -        if (interp != slaveInterp) { -            Tcl_SetObjResult(interp, Tcl_GetObjResult(slaveInterp)); -            Tcl_ResetResult(slaveInterp); -        } -        return TCL_ERROR; +    Tcl_DeleteHashEntry(aliasPtr->aliasEntryPtr); + +    /* +     * Splice the target record out of the target interpreter's master list. +     */ + +    targetPtr = aliasPtr->targetPtr; +    if (targetPtr->prevPtr != NULL) { +	targetPtr->prevPtr->nextPtr = targetPtr->nextPtr; +    } else { +	Master *masterPtr = &((InterpInfo *) ((Interp *) +		aliasPtr->targetInterp)->interpInfo)->master; + +	masterPtr->targetsPtr = targetPtr->nextPtr;      } -    return TCL_OK; +    if (targetPtr->nextPtr != NULL) { +	targetPtr->nextPtr->prevPtr = targetPtr->prevPtr; +    } + +    ckfree(targetPtr); +    ckfree(aliasPtr);  }  /*   *----------------------------------------------------------------------   * - * InterpHideHelper -- + * Tcl_CreateSlave --   * - *	Helper function that handles the details of hiding a command in - *	another interpreter. + *	Creates a slave interpreter. The slavePath argument denotes the name + *	of the new slave relative to the current interpreter; the slave is a + *	direct descendant of the one-before-last component of the path, + *	e.g. it is a descendant of the current interpreter if the slavePath + *	argument contains only one component. Optionally makes the slave + *	interpreter safe.   *   * Results: - *	A standard Tcl result. + *	Returns the interpreter structure created, or NULL if an error + *	occurred.   *   * Side effects: - *	Hides a command. From now on the command cannot be called by - *	scripts in that interpreter. + *	Creates a new interpreter and a new interpreter object command in the + *	interpreter indicated by the slavePath argument.   *   *----------------------------------------------------------------------   */ -static int -InterpHideHelper(interp, masterPtr, objc, objv) -    Tcl_Interp *interp;			/* Current interpreter. */ -    Master *masterPtr;			/* Master record for interp. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* Argument objects. */ +Tcl_Interp * +Tcl_CreateSlave( +    Tcl_Interp *interp,		/* Interpreter to start search at. */ +    const char *slavePath,	/* Name of slave to create. */ +    int isSafe)			/* Should new slave be "safe" ? */  { -    Tcl_Interp *slaveInterp;		/* A slave. */ -    int len;				/* Dummy length variable. */ +    Tcl_Obj *pathPtr; +    Tcl_Interp *slaveInterp; -    if ((objc != 4) && (objc != 5)) { -        Tcl_WrongNumArgs(interp, 2, objv, -                "path cmdName ?hiddenCmdName?"); -        return TCL_ERROR; -    } -    if (Tcl_IsSafe(interp)) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "permission denied: safe interpreter cannot hide commands", -                (char *) NULL); -        return TCL_ERROR; -    } -    slaveInterp = GetInterp(interp, masterPtr, -            Tcl_GetStringFromObj(objv[2], &len), &masterPtr); -    if (slaveInterp == (Tcl_Interp *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "interpreter \"", Tcl_GetStringFromObj(objv[2], &len), -                "\" not found", (char *) NULL); -        return TCL_ERROR; -    } -    if (Tcl_HideCommand(slaveInterp, Tcl_GetStringFromObj(objv[3], &len), -            (objc == 5 ? -                    Tcl_GetStringFromObj(objv[4], &len) : -                    Tcl_GetStringFromObj(objv[3], &len))) -            == TCL_ERROR) { -        if (interp != slaveInterp) { -            Tcl_SetObjResult(interp, Tcl_GetObjResult(slaveInterp)); -            Tcl_ResetResult(slaveInterp); -        } -        return TCL_ERROR; -    } -    return TCL_OK; +    pathPtr = Tcl_NewStringObj(slavePath, -1); +    slaveInterp = SlaveCreate(interp, pathPtr, isSafe); +    Tcl_DecrRefCount(pathPtr); + +    return slaveInterp;  }  /*   *----------------------------------------------------------------------   * - * InterpHiddenHelper -- + * Tcl_GetSlave --   * - *	Computes the list of hidden commands in a named interpreter. + *	Finds a slave interpreter by its path name.   *   * Results: - *	A standard Tcl result. + *	Returns a Tcl_Interp * for the named interpreter or NULL if not found.   *   * Side effects:   *	None. @@ -1429,229 +2062,133 @@ InterpHideHelper(interp, masterPtr, objc, objv)   *----------------------------------------------------------------------   */ -static int -InterpHiddenHelper(interp, masterPtr, objc, objv) -    Tcl_Interp *interp;			/* Current interpreter. */ -    Master *masterPtr;			/* Master record for interp. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* Argument objects. */ -{ -    Tcl_Interp *slaveInterp;		/* A slave. */ -    int len; -    Tcl_HashTable *hTblPtr;		/* Hidden command table. */ -    Tcl_HashEntry *hPtr;		/* Search variable. */ -    Tcl_HashSearch hSearch;		/* Iteration variable. */ -    Tcl_Obj *listObjPtr;		/* Local object pointer. */ - -    if (objc > 3) { -        Tcl_WrongNumArgs(interp, 2, objv, "?path?"); -        return TCL_ERROR; -    } -    if (objc == 3) { -        slaveInterp = GetInterp(interp, masterPtr, -                Tcl_GetStringFromObj(objv[2], &len), -                &masterPtr); -        if (slaveInterp == (Tcl_Interp *) NULL) { -            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                    "interpreter \"", Tcl_GetStringFromObj(objv[2], &len), -                    "\" not found", (char *) NULL); -            return TCL_ERROR; -        } -    } else { -        slaveInterp = interp; -    } -             -    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); -    hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(slaveInterp, -            "tclHiddenCmds", NULL); -    if (hTblPtr != (Tcl_HashTable *) NULL) { -        for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch); -             hPtr != (Tcl_HashEntry *) NULL; -             hPtr = Tcl_NextHashEntry(&hSearch)) { +Tcl_Interp * +Tcl_GetSlave( +    Tcl_Interp *interp,		/* Interpreter to start search from. */ +    const char *slavePath)	/* Path of slave to find. */ +{ +    Tcl_Obj *pathPtr; +    Tcl_Interp *slaveInterp; -            Tcl_ListObjAppendElement(interp, listObjPtr, -                    Tcl_NewStringObj(Tcl_GetHashKey(hTblPtr, hPtr), -1)); -        } -    } -    Tcl_SetObjResult(interp, listObjPtr); -             -    return TCL_OK; +    pathPtr = Tcl_NewStringObj(slavePath, -1); +    slaveInterp = GetInterp(interp, pathPtr); +    Tcl_DecrRefCount(pathPtr); + +    return slaveInterp;  }  /*   *----------------------------------------------------------------------   * - * InterpInvokeHiddenHelper -- + * Tcl_GetMaster --   * - *	Helper routine to handle the details of invoking a hidden - *	command in another interpreter. + *	Finds the master interpreter of a slave interpreter.   *   * Results: - *	A standard Tcl result. + *	Returns a Tcl_Interp * for the master interpreter or NULL if none.   *   * Side effects: - *	Whatever the hidden command does. + *	None.   *   *----------------------------------------------------------------------   */ -static int -InterpInvokeHiddenHelper(interp, masterPtr, objc, objv) -    Tcl_Interp *interp;			/* Current interpreter. */ -    Master *masterPtr;			/* Master record for interp. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* Argument objects. */ -{ -    int doGlobal = 0; -    int len; -    int result; -    Tcl_Obj *namePtr, *objPtr; -    Tcl_Interp *slaveInterp; -    Interp *iPtr; -    char *string; -             -    if (objc < 4) { -        Tcl_WrongNumArgs(interp, 2, objv, -                "path ?-global? cmd ?arg ..?"); -        return TCL_ERROR; -    } -    if (Tcl_IsSafe(interp)) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "not allowed to invoke hidden commands from safe interpreter", -                (char *) NULL); -        return TCL_ERROR; -    } -    if (strcmp(Tcl_GetStringFromObj(objv[3], &len), "-global") == 0) { -        doGlobal = 1; -        if (objc < 5) { -            Tcl_WrongNumArgs(interp, 2, objv, -                    "path ?-global? cmd ?arg ..?"); -            return TCL_ERROR; -        } -    } -    slaveInterp = GetInterp(interp, masterPtr, -            Tcl_GetStringFromObj(objv[2], &len), &masterPtr); -    if (slaveInterp == (Tcl_Interp *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "interpreter \"", Tcl_GetStringFromObj(objv[2], &len), -                "\" not found", (char *) NULL); -        return TCL_ERROR; -    } -    Tcl_Preserve((ClientData) slaveInterp); -    if (doGlobal) { -        result = TclObjInvokeGlobal(slaveInterp, objc-4, objv+4, -                TCL_INVOKE_HIDDEN); -    } else { -        result = TclObjInvoke(slaveInterp, objc-3, objv+3, TCL_INVOKE_HIDDEN); -    } - -    /* -     * Now make the result and any error information accessible. We -     * have to be careful because the slave interpreter and the current -     * interpreter can be the same - do not destroy the result.. This -     * can happen if an interpreter contains an alias which is directed -     * at a target command in the same interpreter. -     */ - -    if (interp != slaveInterp) { -        if (result == TCL_ERROR) { - -            /* -             * An error occurred, so transfer error information from -             * the target interpreter back to our interpreter. -             */ - -            iPtr = (Interp *) slaveInterp; -            if (!(iPtr->flags & ERR_ALREADY_LOGGED)) { -                Tcl_AddErrorInfo(slaveInterp, ""); -            } -            iPtr->flags &= (~(ERR_ALREADY_LOGGED)); - -            Tcl_ResetResult(interp); -            namePtr = Tcl_NewStringObj("errorInfo", -1); -            objPtr = Tcl_ObjGetVar2(slaveInterp, namePtr, -                    (Tcl_Obj *) NULL, TCL_GLOBAL_ONLY); -            Tcl_DecrRefCount(namePtr); -            string = Tcl_GetStringFromObj(objPtr, &len); -            Tcl_AddObjErrorInfo(interp, string, len); -            Tcl_SetVar2(interp, "errorCode", (char *) NULL, -                    Tcl_GetVar2(slaveInterp, "errorCode", (char *) -                            NULL, TCL_GLOBAL_ONLY), -                    TCL_GLOBAL_ONLY); -        } +Tcl_Interp * +Tcl_GetMaster( +    Tcl_Interp *interp)		/* Get the master of this interpreter. */ +{ +    Slave *slavePtr;		/* Slave record of this interpreter. */ -	/* -         * Move the result object from the slave to the master. -         */ -                 -        Tcl_SetObjResult(interp, Tcl_GetObjResult(slaveInterp)); -        Tcl_ResetResult(slaveInterp); +    if (interp == NULL) { +	return NULL;      } -    Tcl_Release((ClientData) slaveInterp); -    return result;         +    slavePtr = &((InterpInfo *) ((Interp *) interp)->interpInfo)->slave; +    return slavePtr->masterInterp;  }  /*   *----------------------------------------------------------------------   * - * InterpMarkTrustedHelper -- + * TclSetSlaveCancelFlags --   * - *	Helper function to handle the details of marking another - *	interpreter as trusted (unsafe). + *	This function marks all slave interpreters belonging to a given + *	interpreter as being canceled or not canceled, depending on the + *	provided flags.   *   * Results: - *	A standard Tcl result. + *	None.   *   * Side effects: - *	Henceforth the hard-wired checks for safety will not prevent - *	this interpreter from performing certain operations. + *	None.   *   *----------------------------------------------------------------------   */ -static int -InterpMarkTrustedHelper(interp, masterPtr, objc, objv) -    Tcl_Interp *interp;			/* Current interpreter. */ -    Master *masterPtr;			/* Master record for interp. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* Argument objects. */ +void +TclSetSlaveCancelFlags( +    Tcl_Interp *interp,		/* Set cancel flags of this interpreter. */ +    int flags,			/* Collection of OR-ed bits that control +				 * the cancellation of the script. Only +				 * TCL_CANCEL_UNWIND is currently +				 * supported. */ +    int force)			/* Non-zero to ignore numLevels for the purpose +				 * of resetting the cancellation flags. */  { -    Tcl_Interp *slaveInterp;		/* A slave. */ -    int len;				/* Dummy length variable. */ +    Master *masterPtr;		/* Master record of given interpreter. */ +    Tcl_HashEntry *hPtr;	/* Search element. */ +    Tcl_HashSearch hashSearch;	/* Search variable. */ +    Slave *slavePtr;		/* Slave record of interpreter. */ +    Interp *iPtr; -    if (objc != 3) { -        Tcl_WrongNumArgs(interp, 2, objv, "path"); -        return TCL_ERROR; -    } -    if (Tcl_IsSafe(interp)) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "\"", Tcl_GetStringFromObj(objv[0], &len), -                " marktrusted\" can only", -                " be invoked from a trusted interpreter", -                (char *) NULL); -        return TCL_ERROR; +    if (interp == NULL) { +	return;      } -    slaveInterp = GetInterp(interp, masterPtr, -            Tcl_GetStringFromObj(objv[2], &len), &masterPtr); -    if (slaveInterp == (Tcl_Interp *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "interpreter \"", Tcl_GetStringFromObj(objv[2], &len), -                "\" not found", (char *) NULL); -        return TCL_ERROR; +    flags &= (CANCELED | TCL_CANCEL_UNWIND); + +    masterPtr = &((InterpInfo *) ((Interp *) interp)->interpInfo)->master; + +    hPtr = Tcl_FirstHashEntry(&masterPtr->slaveTable, &hashSearch); +    for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&hashSearch)) { +	slavePtr = Tcl_GetHashValue(hPtr); +	iPtr = (Interp *) slavePtr->slaveInterp; + +	if (iPtr == NULL) { +	    continue; +	} + +	if (flags == 0) { +	    TclResetCancellation((Tcl_Interp *) iPtr, force); +	} else { +	    TclSetCancelFlags(iPtr, flags); +	} + +	/* +	 * Now, recursively handle this for the slaves of this slave +	 * interpreter. +	 */ + +	TclSetSlaveCancelFlags((Tcl_Interp *) iPtr, flags, force);      } -    return MarkTrusted(slaveInterp);  }  /*   *----------------------------------------------------------------------   * - * InterpIsSafeHelper -- + * Tcl_GetInterpPath --   * - *	Computes whether a named interpreter is safe. + *	Sets the result of the asking interpreter to a proper Tcl list + *	containing the names of interpreters between the asking and target + *	interpreters. The target interpreter must be either the same as the + *	asking interpreter or one of its slaves (including recursively).   *   * Results: - *	A standard Tcl result. + *	TCL_OK if the target interpreter is the same as, or a descendant of, + *	the asking interpreter; TCL_ERROR else. This way one can distinguish + *	between the case where the asking and target interps are the same (an + *	empty list is the result, and TCL_OK is returned) and when the target + *	is not a descendant of the asking interpreter (in which case the Tcl + *	result is an error message and the function returns TCL_ERROR).   *   * Side effects:   *	None. @@ -1659,705 +2196,816 @@ InterpMarkTrustedHelper(interp, masterPtr, objc, objv)   *----------------------------------------------------------------------   */ -static int -InterpIsSafeHelper(interp, masterPtr, objc, objv) -    Tcl_Interp *interp;			/* Current interpreter. */ -    Master *masterPtr;			/* Master record for interp. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* Argument objects. */ -{ -    Tcl_Interp *slaveInterp;		/* A slave. */ -    int len;				/* Dummy length variable. */ -    Tcl_Obj *objPtr;			/* Local object pointer. */ - -    if (objc > 3) { -        Tcl_WrongNumArgs(interp, 2, objv, "?path?"); -        return TCL_ERROR; -    } -    if (objc == 3) { -        slaveInterp = GetInterp(interp, masterPtr, -                Tcl_GetStringFromObj(objv[2], &len), &masterPtr); -        if (slaveInterp == (Tcl_Interp *) NULL) { -            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                    "interpreter \"", -                    Tcl_GetStringFromObj(objv[2], &len), "\" not found", -                    (char *) NULL); -            return TCL_ERROR; -        } -	objPtr = Tcl_NewIntObj(Tcl_IsSafe(slaveInterp)); -    } else { -	objPtr = Tcl_NewIntObj(Tcl_IsSafe(interp)); +int +Tcl_GetInterpPath( +    Tcl_Interp *askingInterp,	/* Interpreter to start search from. */ +    Tcl_Interp *targetInterp)	/* Interpreter to find. */ +{ +    InterpInfo *iiPtr; + +    if (targetInterp == askingInterp) { +	Tcl_SetObjResult(askingInterp, Tcl_NewObj()); +	return TCL_OK; +    } +    if (targetInterp == NULL) { +	return TCL_ERROR;      } -    Tcl_SetObjResult(interp, objPtr); +    iiPtr = (InterpInfo *) ((Interp *) targetInterp)->interpInfo; +    if (Tcl_GetInterpPath(askingInterp, iiPtr->slave.masterInterp) != TCL_OK){ +	return TCL_ERROR; +    } +    Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(askingInterp), +	    Tcl_NewStringObj(Tcl_GetHashKey(&iiPtr->master.slaveTable, +		    iiPtr->slave.slaveEntryPtr), -1));      return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * InterpSlavesHelper -- + * GetInterp --   * - *	Computes a list of slave interpreters of a named interpreter. + *	Helper function to find a slave interpreter given a pathname.   *   * Results: - *	A standard Tcl result. + *	Returns the slave interpreter known by that name in the calling + *	interpreter, or NULL if no interpreter known by that name exists.   *   * Side effects: - *	None. + *	Assigns to the pointer variable passed in, if not NULL.   *   *----------------------------------------------------------------------   */ -static int -InterpSlavesHelper(interp, masterPtr, objc, objv) -    Tcl_Interp *interp;			/* Current interpreter. */ -    Master *masterPtr;			/* Master record for interp. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* Argument objects. */ -{ -    int len; -    Tcl_HashEntry *hPtr;		/* Search variable. */ -    Tcl_HashSearch hSearch;		/* Iteration variable. */ -    Tcl_Obj *listObjPtr;		/* Local object pointers. */ - -    if ((objc != 2) && (objc != 3)) { -        Tcl_WrongNumArgs(interp, 2, objv, "?path?"); -        return TCL_ERROR; -    } -    if (objc == 3) { -        if (GetInterp(interp, masterPtr, -                Tcl_GetStringFromObj(objv[2], &len), &masterPtr) == -                (Tcl_Interp *) NULL) { -            Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                    "interpreter \"", Tcl_GetStringFromObj(objv[2], &len), -                    "\" not found", (char *) NULL); -            return TCL_ERROR; -        } -    } - -    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); -    for (hPtr = Tcl_FirstHashEntry(&(masterPtr->slaveTable), &hSearch); -         hPtr != NULL; -         hPtr = Tcl_NextHashEntry(&hSearch)) { - -        Tcl_ListObjAppendElement(interp, listObjPtr, -                Tcl_NewStringObj( -                    Tcl_GetHashKey(&(masterPtr->slaveTable), hPtr), -1)); +static Tcl_Interp * +GetInterp( +    Tcl_Interp *interp,		/* Interp. to start search from. */ +    Tcl_Obj *pathPtr)		/* List object containing name of interp. to +				 * be found. */ +{ +    Tcl_HashEntry *hPtr;	/* Search element. */ +    Slave *slavePtr;		/* Interim slave record. */ +    Tcl_Obj **objv; +    int objc, i; +    Tcl_Interp *searchInterp;	/* Interim storage for interp. to find. */ +    InterpInfo *masterInfoPtr; + +    if (TclListObjGetElements(interp, pathPtr, &objc, &objv) != TCL_OK) { +	return NULL;      } -    Tcl_SetObjResult(interp, listObjPtr); -    return TCL_OK; + +    searchInterp = interp; +    for (i = 0; i < objc; i++) { +	masterInfoPtr = (InterpInfo *) ((Interp *) searchInterp)->interpInfo; +	hPtr = Tcl_FindHashEntry(&masterInfoPtr->master.slaveTable, +		TclGetString(objv[i])); +	if (hPtr == NULL) { +	    searchInterp = NULL; +	    break; +	} +	slavePtr = Tcl_GetHashValue(hPtr); +	searchInterp = slavePtr->slaveInterp; +	if (searchInterp == NULL) { +	    break; +	} +    } +    if (searchInterp == NULL) { +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		"could not find interpreter \"%s\"", TclGetString(pathPtr))); +	Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "INTERP", +		TclGetString(pathPtr), NULL); +    } +    return searchInterp;  }  /*   *----------------------------------------------------------------------   * - * InterpShareHelper -- + * SlaveBgerror --   * - *	Helper function to handle the details of sharing a channel between - *	interpreters. + *	Helper function to set/query the background error handling command + *	prefix of an interp   *   * Results:   *	A standard Tcl result.   *   * Side effects: - *	After this call the named channel will be shared between the - *	interpreters named in the arguments. + *	When (objc == 1), slaveInterp will be set to a new background handler + *	of objv[0].   *   *----------------------------------------------------------------------   */  static int -InterpShareHelper(interp, masterPtr, objc, objv) -    Tcl_Interp *interp;			/* Current interpreter. */ -    Master *masterPtr;			/* Master record for interp. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* Argument objects. */ -{ -    Tcl_Interp *slaveInterp;		/* A slave. */ -    Tcl_Interp *masterInterp;		/* Its master. */ -    int len; -    Tcl_Channel chan; - -    if (objc != 5) { -        Tcl_WrongNumArgs(interp, 2, objv, "srcPath channelId destPath"); -        return TCL_ERROR; -    } -    masterInterp = GetInterp(interp, masterPtr, -            Tcl_GetStringFromObj(objv[2], &len), NULL); -    if (masterInterp == (Tcl_Interp *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "interpreter \"", Tcl_GetStringFromObj(objv[2], &len), -                "\" not found", (char *) NULL); -        return TCL_ERROR; -    } -    slaveInterp = GetInterp(interp, masterPtr, -            Tcl_GetStringFromObj(objv[4], &len), NULL); -    if (slaveInterp == (Tcl_Interp *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "interpreter \"", Tcl_GetStringFromObj(objv[4], &len), -                "\" not found", (char *) NULL); -        return TCL_ERROR; -    } -    chan = Tcl_GetChannel(masterInterp, Tcl_GetStringFromObj(objv[3], &len), -            NULL); -    if (chan == (Tcl_Channel) NULL) { -        if (interp != masterInterp) { -            Tcl_SetObjResult(interp, Tcl_GetObjResult(masterInterp)); -            Tcl_ResetResult(masterInterp); -        } -        return TCL_ERROR; -    } -    Tcl_RegisterChannel(slaveInterp, chan); +SlaveBgerror( +    Tcl_Interp *interp,		/* Interp for error return. */ +    Tcl_Interp *slaveInterp,	/* Interp in which limit is set/queried. */ +    int objc,			/* Set or Query. */ +    Tcl_Obj *const objv[])	/* Argument strings. */ +{ +    if (objc) { +	int length; + +	if (TCL_ERROR == TclListObjLength(NULL, objv[0], &length) +		|| (length < 1)) { +	    Tcl_SetObjResult(interp, Tcl_NewStringObj( +		    "cmdPrefix must be list of length >= 1", -1)); +	    Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", +		    "BGERRORFORMAT", NULL); +	    return TCL_ERROR; +	} +	TclSetBgErrorHandler(slaveInterp, objv[0]); +    } +    Tcl_SetObjResult(interp, TclGetBgErrorHandler(slaveInterp));      return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * InterpTargetHelper -- + * SlaveCreate --   * - *	Helper function to compute the target of an alias. + *	Helper function to do the actual work of creating a slave interp and + *	new object command. Also optionally makes the new slave interpreter + *	"safe".   *   * Results: - *	A standard Tcl result. + *	Returns the new Tcl_Interp * if successful or NULL if not. If failed, + *	the result of the invoking interpreter contains an error message.   *   * Side effects: - *	None. + *	Creates a new slave interpreter and a new object command.   *   *----------------------------------------------------------------------   */ -static int -InterpTargetHelper(interp, masterPtr, objc, objv) -    Tcl_Interp *interp;			/* Current interpreter. */ -    Master *masterPtr;			/* Master record for interp. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* Argument objects. */ +static Tcl_Interp * +SlaveCreate( +    Tcl_Interp *interp,		/* Interp. to start search from. */ +    Tcl_Obj *pathPtr,		/* Path (name) of slave to create. */ +    int safe)			/* Should we make it "safe"? */  { -    int len; -     -    if (objc != 4) { -        Tcl_WrongNumArgs(interp, 2, objv, "path alias"); -        return TCL_ERROR; +    Tcl_Interp *masterInterp, *slaveInterp; +    Slave *slavePtr; +    InterpInfo *masterInfoPtr; +    Tcl_HashEntry *hPtr; +    const char *path; +    int isNew, objc; +    Tcl_Obj **objv; + +    if (Tcl_ListObjGetElements(interp, pathPtr, &objc, &objv) != TCL_OK) { +	return NULL; +    } +    if (objc < 2) { +	masterInterp = interp; +	path = TclGetString(pathPtr); +    } else { +	Tcl_Obj *objPtr; + +	objPtr = Tcl_NewListObj(objc - 1, objv); +	masterInterp = GetInterp(interp, objPtr); +	Tcl_DecrRefCount(objPtr); +	if (masterInterp == NULL) { +	    return NULL; +	} +	path = TclGetString(objv[objc - 1]);      } -    return GetTarget(interp, -            Tcl_GetStringFromObj(objv[2], &len), -            Tcl_GetStringFromObj(objv[3], &len)); +    if (safe == 0) { +	safe = Tcl_IsSafe(masterInterp); +    } + +    masterInfoPtr = (InterpInfo *) ((Interp *) masterInterp)->interpInfo; +    hPtr = Tcl_CreateHashEntry(&masterInfoPtr->master.slaveTable, path, +	    &isNew); +    if (isNew == 0) { +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		"interpreter named \"%s\" already exists, cannot create", +		path)); +	return NULL; +    } + +    slaveInterp = Tcl_CreateInterp(); +    slavePtr = &((InterpInfo *) ((Interp *) slaveInterp)->interpInfo)->slave; +    slavePtr->masterInterp = masterInterp; +    slavePtr->slaveEntryPtr = hPtr; +    slavePtr->slaveInterp = slaveInterp; +    slavePtr->interpCmd = Tcl_NRCreateCommand(masterInterp, path, +	    SlaveObjCmd, NRSlaveCmd, slaveInterp, SlaveObjCmdDeleteProc); +    Tcl_InitHashTable(&slavePtr->aliasTable, TCL_STRING_KEYS); +    Tcl_SetHashValue(hPtr, slavePtr); +    Tcl_SetVar(slaveInterp, "tcl_interactive", "0", TCL_GLOBAL_ONLY); + +    /* +     * Inherit the recursion limit. +     */ + +    ((Interp *) slaveInterp)->maxNestingDepth = +	    ((Interp *) masterInterp)->maxNestingDepth; + +    if (safe) { +	if (Tcl_MakeSafe(slaveInterp) == TCL_ERROR) { +	    goto error; +	} +    } else { +	if (Tcl_Init(slaveInterp) == TCL_ERROR) { +	    goto error; +	} + +	/* +	 * This will create the "memory" command in slave interpreters if we +	 * compiled with TCL_MEM_DEBUG, otherwise it does nothing. +	 */ + +	Tcl_InitMemory(slaveInterp); +    } + +    /* +     * Inherit the TIP#143 limits. +     */ + +    InheritLimitsFromMaster(slaveInterp, masterInterp); + +    /* +     * The [clock] command presents a safe API, but uses unsafe features in +     * its implementation. This means it has to be implemented in safe interps +     * as an alias to a version in the (trusted) master. +     */ + +    if (safe) { +	Tcl_Obj *clockObj; +	int status; + +	TclNewLiteralStringObj(clockObj, "clock"); +	Tcl_IncrRefCount(clockObj); +	status = AliasCreate(interp, slaveInterp, masterInterp, clockObj, +		clockObj, 0, NULL); +	Tcl_DecrRefCount(clockObj); +	if (status != TCL_OK) { +	    goto error2; +	} +    } + +    return slaveInterp; + +  error: +    Tcl_TransferResult(slaveInterp, TCL_ERROR, interp); +  error2: +    Tcl_DeleteInterp(slaveInterp); + +    return NULL;  }  /*   *----------------------------------------------------------------------   * - * InterpTransferHelper -- + * SlaveObjCmd --   * - *	Helper function to handle the details of transferring ownership - *	of a channel between interpreters. + *	Command to manipulate an interpreter, e.g. to send commands to it to + *	be evaluated. One such command exists for each slave interpreter.   *   * Results:   *	A standard Tcl result.   *   * Side effects: - *	After the call, the named channel will be registered in the target - *	interpreter and no longer available for use in the source interpreter. + *	See user documentation for details.   *   *----------------------------------------------------------------------   */  static int -InterpTransferHelper(interp, masterPtr, objc, objv) -    Tcl_Interp *interp;			/* Current interpreter. */ -    Master *masterPtr;			/* Master record for interp. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* Argument objects. */ -{ -    Tcl_Interp *slaveInterp;		/* A slave. */ -    Tcl_Interp *masterInterp;		/* Its master. */ -    int len; -    Tcl_Channel chan; -             -    if (objc != 5) { -        Tcl_WrongNumArgs(interp, 2, objv, -                "srcPath channelId destPath"); -        return TCL_ERROR; -    } -    masterInterp = GetInterp(interp, masterPtr, -            Tcl_GetStringFromObj(objv[2], &len), NULL); -    if (masterInterp == (Tcl_Interp *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "interpreter \"", Tcl_GetStringFromObj(objv[2], &len), -                "\" not found", (char *) NULL); -        return TCL_ERROR; -    } -    slaveInterp = GetInterp(interp, masterPtr, -            Tcl_GetStringFromObj(objv[4], &len), NULL); -    if (slaveInterp == (Tcl_Interp *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "interpreter \"", Tcl_GetStringFromObj(objv[4], &len), -                "\" not found", (char *) NULL); -        return TCL_ERROR; -    } -    chan = Tcl_GetChannel(masterInterp, -            Tcl_GetStringFromObj(objv[3], &len), NULL); -    if (chan == (Tcl_Channel) NULL) { -        if (interp != masterInterp) { - -            /* -             * After fixing objresult, this code will change to: -             * Tcl_SetObjResult(interp, Tcl_GetObjResult(masterInterp)); -             */ -             -            Tcl_SetObjResult(interp, Tcl_GetObjResult(masterInterp)); -            Tcl_ResetResult(masterInterp); -        } -        return TCL_ERROR; -    } -    Tcl_RegisterChannel(slaveInterp, chan); -    if (Tcl_UnregisterChannel(masterInterp, chan) != TCL_OK) { -        if (interp != masterInterp) { -            Tcl_SetObjResult(interp, Tcl_GetObjResult(masterInterp)); -            Tcl_ResetResult(masterInterp); -        } -        return TCL_ERROR; +SlaveObjCmd( +    ClientData clientData,	/* Slave interpreter. */ +    Tcl_Interp *interp,		/* Current interpreter. */ +    int objc,			/* Number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument objects. */ +{ +    return Tcl_NRCallObjProc(interp, NRSlaveCmd, clientData, objc, objv); +} + +static int +NRSlaveCmd( +    ClientData clientData,	/* Slave interpreter. */ +    Tcl_Interp *interp,		/* Current interpreter. */ +    int objc,			/* Number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument objects. */ +{ +    Tcl_Interp *slaveInterp = clientData; +    int index; +    static const char *const options[] = { +	"alias",	"aliases",	"bgerror",	"debug", +	"eval",		"expose",	"hide",		"hidden", +	"issafe",	"invokehidden",	"limit",	"marktrusted", +	"recursionlimit", NULL +    }; +    enum options { +	OPT_ALIAS,	OPT_ALIASES,	OPT_BGERROR,	OPT_DEBUG, +	OPT_EVAL,	OPT_EXPOSE,	OPT_HIDE,	OPT_HIDDEN, +	OPT_ISSAFE,	OPT_INVOKEHIDDEN, OPT_LIMIT,	OPT_MARKTRUSTED, +	OPT_RECLIMIT +    }; + +    if (slaveInterp == NULL) { +	Tcl_Panic("SlaveObjCmd: interpreter has been deleted");      } -    return TCL_OK; + +    if (objc < 2) { +	Tcl_WrongNumArgs(interp, 1, objv, "cmd ?arg ...?"); +	return TCL_ERROR; +    } +    if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0, +	    &index) != TCL_OK) { +	return TCL_ERROR; +    } + +    switch ((enum options) index) { +    case OPT_ALIAS: +	if (objc > 2) { +	    if (objc == 3) { +		return AliasDescribe(interp, slaveInterp, objv[2]); +	    } +	    if (TclGetString(objv[3])[0] == '\0') { +		if (objc == 4) { +		    return AliasDelete(interp, slaveInterp, objv[2]); +		} +	    } else { +		return AliasCreate(interp, slaveInterp, interp, objv[2], +			objv[3], objc - 4, objv + 4); +	    } +	} +	Tcl_WrongNumArgs(interp, 2, objv, "aliasName ?targetName? ?arg ...?"); +	return TCL_ERROR; +    case OPT_ALIASES: +	if (objc != 2) { +	    Tcl_WrongNumArgs(interp, 2, objv, NULL); +	    return TCL_ERROR; +	} +	return AliasList(interp, slaveInterp); +    case OPT_BGERROR: +	if (objc != 2 && objc != 3) { +	    Tcl_WrongNumArgs(interp, 2, objv, "?cmdPrefix?"); +	    return TCL_ERROR; +	} +	return SlaveBgerror(interp, slaveInterp, objc - 2, objv + 2); +    case OPT_DEBUG: +	/* +	 * TIP #378 +	 * Currently only -frame supported, otherwise ?-option ?value? ...? +	 */ +	if (objc > 4) { +	    Tcl_WrongNumArgs(interp, 2, objv, "?-frame ?bool??"); +	    return TCL_ERROR; +	} +	return SlaveDebugCmd(interp, slaveInterp, objc - 2, objv + 2); +    case OPT_EVAL: +	if (objc < 3) { +	    Tcl_WrongNumArgs(interp, 2, objv, "arg ?arg ...?"); +	    return TCL_ERROR; +	} +	return SlaveEval(interp, slaveInterp, objc - 2, objv + 2); +    case OPT_EXPOSE: +	if ((objc < 3) || (objc > 4)) { +	    Tcl_WrongNumArgs(interp, 2, objv, "hiddenCmdName ?cmdName?"); +	    return TCL_ERROR; +	} +	return SlaveExpose(interp, slaveInterp, objc - 2, objv + 2); +    case OPT_HIDE: +	if ((objc < 3) || (objc > 4)) { +	    Tcl_WrongNumArgs(interp, 2, objv, "cmdName ?hiddenCmdName?"); +	    return TCL_ERROR; +	} +	return SlaveHide(interp, slaveInterp, objc - 2, objv + 2); +    case OPT_HIDDEN: +	if (objc != 2) { +	    Tcl_WrongNumArgs(interp, 2, objv, NULL); +	    return TCL_ERROR; +	} +	return SlaveHidden(interp, slaveInterp); +    case OPT_ISSAFE: +	if (objc != 2) { +	    Tcl_WrongNumArgs(interp, 2, objv, NULL); +	    return TCL_ERROR; +	} +	Tcl_SetObjResult(interp, Tcl_NewBooleanObj(Tcl_IsSafe(slaveInterp))); +	return TCL_OK; +    case OPT_INVOKEHIDDEN: { +	int i; +	const char *namespaceName; +	static const char *const hiddenOptions[] = { +	    "-global",	"-namespace",	"--", NULL +	}; +	enum hiddenOption { +	    OPT_GLOBAL,	OPT_NAMESPACE,	OPT_LAST +	}; + +	namespaceName = NULL; +	for (i = 2; i < objc; i++) { +	    if (TclGetString(objv[i])[0] != '-') { +		break; +	    } +	    if (Tcl_GetIndexFromObj(interp, objv[i], hiddenOptions, "option", +		    0, &index) != TCL_OK) { +		return TCL_ERROR; +	    } +	    if (index == OPT_GLOBAL) { +		namespaceName = "::"; +	    } else if (index == OPT_NAMESPACE) { +		if (++i == objc) { /* There must be more arguments. */ +		    break; +		} else { +		    namespaceName = TclGetString(objv[i]); +		} +	    } else { +		i++; +		break; +	    } +	} +	if (objc - i < 1) { +	    Tcl_WrongNumArgs(interp, 2, objv, +		    "?-namespace ns? ?-global? ?--? cmd ?arg ..?"); +	    return TCL_ERROR; +	} +	return SlaveInvokeHidden(interp, slaveInterp, namespaceName, +		objc - i, objv + i); +    } +    case OPT_LIMIT: { +	static const char *const limitTypes[] = { +	    "commands", "time", NULL +	}; +	enum LimitTypes { +	    LIMIT_TYPE_COMMANDS, LIMIT_TYPE_TIME +	}; +	int limitType; + +	if (objc < 3) { +	    Tcl_WrongNumArgs(interp, 2, objv, "limitType ?-option value ...?"); +	    return TCL_ERROR; +	} +	if (Tcl_GetIndexFromObj(interp, objv[2], limitTypes, "limit type", 0, +		&limitType) != TCL_OK) { +	    return TCL_ERROR; +	} +	switch ((enum LimitTypes) limitType) { +	case LIMIT_TYPE_COMMANDS: +	    return SlaveCommandLimitCmd(interp, slaveInterp, 3, objc,objv); +	case LIMIT_TYPE_TIME: +	    return SlaveTimeLimitCmd(interp, slaveInterp, 3, objc, objv); +	} +    } +    case OPT_MARKTRUSTED: +	if (objc != 2) { +	    Tcl_WrongNumArgs(interp, 2, objv, NULL); +	    return TCL_ERROR; +	} +	return SlaveMarkTrusted(interp, slaveInterp); +    case OPT_RECLIMIT: +	if (objc != 2 && objc != 3) { +	    Tcl_WrongNumArgs(interp, 2, objv, "?newlimit?"); +	    return TCL_ERROR; +	} +	return SlaveRecursionLimit(interp, slaveInterp, objc - 2, objv + 2); +    } + +    return TCL_ERROR;  }  /*   *----------------------------------------------------------------------   * - * DescribeAlias -- + * SlaveObjCmdDeleteProc --   * - *	Sets the interpreter's result object to a Tcl list describing - *	the given alias in the given interpreter: its target command - *	and the additional arguments to prepend to any invocation - *	of the alias. + *	Invoked when an object command for a slave interpreter is deleted; + *	cleans up all state associated with the slave interpreter and destroys + *	the slave interpreter.   *   * Results: - *	A standard Tcl result. + *	None.   *   * Side effects: - *	None. + *	Cleans up all state associated with the slave interpreter and destroys + *	the slave interpreter.   *   *----------------------------------------------------------------------   */ -static int -DescribeAlias(interp, slaveInterp, aliasName) -    Tcl_Interp *interp;			/* Interpreter for result & errors. */ -    Tcl_Interp *slaveInterp;		/* Interpreter defining alias. */ -    char *aliasName;			/* Name of alias to describe. */ +static void +SlaveObjCmdDeleteProc( +    ClientData clientData)	/* The SlaveRecord for the command. */  { -    Slave *slavePtr;			/* Slave interp slave record. */ -    Tcl_HashEntry *hPtr;		/* Search variable. */ -    Alias *aliasPtr;			/* Structure describing alias. */ -    int i;				/* Loop variable. */ -    Tcl_Obj *listObjPtr;		/* Local object pointer. */ +    Slave *slavePtr;		/* Interim storage for Slave record. */ +    Tcl_Interp *slaveInterp = clientData; +				/* And for a slave interp. */ -    slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord", -            NULL); +    slavePtr = &((InterpInfo *) ((Interp *) slaveInterp)->interpInfo)->slave;      /* -     * The slave record should always be present because it is created -     * by Tcl_CreateInterp. +     * Unlink the slave from its master interpreter.       */ -     -    if (slavePtr == (Slave *) NULL) { -        panic("DescribeAlias: could not find slave record"); -    } -    hPtr = Tcl_FindHashEntry(&(slavePtr->aliasTable), aliasName); -    if (hPtr == (Tcl_HashEntry *) NULL) { -        return TCL_OK; -    } -    aliasPtr = (Alias *) Tcl_GetHashValue(hPtr); -    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); -    Tcl_ListObjAppendElement(interp, listObjPtr, -            Tcl_NewStringObj(aliasPtr->targetName, -1)); -    for (i = 0; i < aliasPtr->objc; i++) { -        Tcl_ListObjAppendElement(interp, listObjPtr, aliasPtr->objv[i]); +    Tcl_DeleteHashEntry(slavePtr->slaveEntryPtr); + +    /* +     * Set to NULL so that when the InterpInfo is cleaned up in the slave it +     * does not try to delete the command causing all sorts of grief. See +     * SlaveRecordDeleteProc(). +     */ + +    slavePtr->interpCmd = NULL; + +    if (slavePtr->slaveInterp != NULL) { +	Tcl_DeleteInterp(slavePtr->slaveInterp);      } -    Tcl_SetObjResult(interp, listObjPtr); -    return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * DeleteAlias -- + * SlaveDebugCmd -- TIP #378   * - *	Deletes the given alias from the slave interpreter given. + *	Helper function to handle 'debug' command in a slave interpreter.   *   * Results:   *	A standard Tcl result.   *   * Side effects: - *	Deletes the alias from the slave interpreter. + *	May modify INTERP_DEBUG_FRAME flag in the slave.   *   *----------------------------------------------------------------------   */  static int -DeleteAlias(interp, slaveInterp, aliasName) -    Tcl_Interp *interp;		/* Interpreter for result and errors. */ -    Tcl_Interp *slaveInterp;	/* Interpreter defining alias. */ -    char *aliasName;		/* Name of alias to delete. */ -{ -    Slave *slavePtr;		/* Slave record for slave interpreter. */ -    Alias *aliasPtr;		/* Points at alias structure to delete. */ -    Tcl_HashEntry *hPtr;	/* Search variable. */ -    char *tmpPtr, *namePtr;	/* Local pointers to name of command to -                                 * be deleted. */ - -    slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord", -            NULL); -    if (slavePtr == (Slave *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "alias \"", aliasName, "\" not found", (char *) NULL); -        return TCL_ERROR; -    } -     -    /* -     * Get the alias from the alias table, then delete the command. The -     * deleteProc on the alias command will take care of removing the entry -     * from the alias table. -     */ - -    hPtr = Tcl_FindHashEntry(&(slavePtr->aliasTable), aliasName); -    if (hPtr == (Tcl_HashEntry *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "alias \"", aliasName, "\" not found", (char *) NULL); -        return TCL_ERROR; -    } -    aliasPtr = (Alias *) Tcl_GetHashValue(hPtr); - -    /* -     * Get a copy of the real name of the command -- it might have -     * been renamed, and we want to delete the renamed command, not -     * the current command (if any) by the name of the original alias. -     * We need the local copy because the name may get smashed when the -     * command to delete is exposed, if it was hidden. -     */ - -    tmpPtr = Tcl_GetCommandName(slaveInterp, aliasPtr->slaveCmd); -    namePtr = (char *) ckalloc((unsigned) strlen(tmpPtr) + 1); -    strcpy(namePtr, tmpPtr); - -    /* -     * NOTE: The deleteProc for this command will delete the -     * alias from the hash table. The deleteProc will also -     * delete the target information from the master interpreter -     * target table. -     */ - -    if (Tcl_DeleteCommand(slaveInterp, namePtr) != 0) { -        if (Tcl_ExposeCommand(slaveInterp, namePtr, namePtr) != TCL_OK) { -            panic("DeleteAlias: did not find alias to be deleted"); -        } -        if (Tcl_DeleteCommand(slaveInterp, namePtr) != 0) { -            panic("DeleteAlias: did not find alias to be deleted"); -        } +SlaveDebugCmd( +    Tcl_Interp *interp,		/* Interp for error return. */ +    Tcl_Interp *slaveInterp,	/* The slave interpreter in which command +				 * will be evaluated. */ +    int objc,			/* Number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument objects. */ +{ +    static const char *const debugTypes[] = { +	"-frame", NULL +    }; +    enum DebugTypes { +	DEBUG_TYPE_FRAME +    }; +    int debugType; +    Interp *iPtr; +    Tcl_Obj *resultPtr; + +    iPtr = (Interp *) slaveInterp; +    if (objc == 0) { +	resultPtr = Tcl_NewObj(); +	Tcl_ListObjAppendElement(NULL, resultPtr, +		Tcl_NewStringObj("-frame", -1)); +	Tcl_ListObjAppendElement(NULL, resultPtr, +		Tcl_NewBooleanObj(iPtr->flags & INTERP_DEBUG_FRAME)); +	Tcl_SetObjResult(interp, resultPtr); +    } else { +	if (Tcl_GetIndexFromObj(interp, objv[0], debugTypes, "debug option", +		0, &debugType) != TCL_OK) { +	    return TCL_ERROR; +	} +	if (debugType == DEBUG_TYPE_FRAME) { +	    if (objc == 2) { /* set */ +		if (Tcl_GetBooleanFromObj(interp, objv[1], &debugType) +			!= TCL_OK) { +		    return TCL_ERROR; +		} + +		/* +		 * Quietly ignore attempts to disable interp debugging.  This +		 * is a one-way switch as frame debug info is maintained in a +		 * stack that must be consistent once turned on. +		 */ + +		if (debugType) { +		    iPtr->flags |= INTERP_DEBUG_FRAME; +		} +	    } +	    Tcl_SetObjResult(interp, +		    Tcl_NewBooleanObj(iPtr->flags & INTERP_DEBUG_FRAME)); +	}      } -    ckfree(namePtr); -      return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * Tcl_GetInterpPath -- + * SlaveEval --   * - *	Sets the result of the asking interpreter to a proper Tcl list - *	containing the names of interpreters between the asking and - *	target interpreters. The target interpreter must be either the - *	same as the asking interpreter or one of its slaves (including - *	recursively). + *	Helper function to evaluate a command in a slave interpreter.   *   * Results: - *	TCL_OK if the target interpreter is the same as, or a descendant - *	of, the asking interpreter; TCL_ERROR else. This way one can - *	distinguish between the case where the asking and target interps - *	are the same (an empty list is the result, and TCL_OK is returned) - *	and when the target is not a descendant of the asking interpreter - *	(in which case the Tcl result is an error message and the function - *	returns TCL_ERROR). + *	A standard Tcl result.   *   * Side effects: - *	None. + *	Whatever the command does.   *   *----------------------------------------------------------------------   */ -int -Tcl_GetInterpPath(askingInterp, targetInterp) -    Tcl_Interp *askingInterp;	/* Interpreter to start search from. */ -    Tcl_Interp *targetInterp;	/* Interpreter to find. */ +static int +SlaveEval( +    Tcl_Interp *interp,		/* Interp for error return. */ +    Tcl_Interp *slaveInterp,	/* The slave interpreter in which command +				 * will be evaluated. */ +    int objc,			/* Number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument objects. */  { -    Master *masterPtr;		/* Interim storage for Master record. */ -    Slave *slavePtr;		/* Interim storage for Slave record. */ -     -    if (targetInterp == askingInterp) { -        return TCL_OK; -    } -    if (targetInterp == (Tcl_Interp *) NULL) { -        return TCL_ERROR; -    } -    slavePtr = (Slave *) Tcl_GetAssocData(targetInterp, "tclSlaveRecord", -            NULL); -    if (slavePtr == (Slave *) NULL) { -        return TCL_ERROR; -    } -    if (Tcl_GetInterpPath(askingInterp, slavePtr->masterInterp) == TCL_ERROR) { +    int result; -        /* -         * The result of askingInterp was set by recursive call. -         */ +    /* +     * TIP #285: If necessary, reset the cancellation flags for the slave +     * interpreter now; otherwise, canceling a script in a master interpreter +     * can result in a situation where a slave interpreter can no longer +     * evaluate any scripts unless somebody calls the TclResetCancellation +     * function for that particular Tcl_Interp. +     */ -        return TCL_ERROR; -    } -    masterPtr = (Master *) Tcl_GetAssocData(slavePtr->masterInterp, -            "tclMasterRecord", NULL); -    if (masterPtr == (Master *) NULL) { -        panic("Tcl_GetInterpPath: could not find master record"); +    TclSetSlaveCancelFlags(slaveInterp, 0, 0); + +    Tcl_Preserve(slaveInterp); +    Tcl_AllowExceptions(slaveInterp); + +    if (objc == 1) { +	/* +	 * TIP #280: Make actual argument location available to eval'd script. +	 */ + +	Interp *iPtr = (Interp *) interp; +	CmdFrame *invoker = iPtr->cmdFramePtr; +	int word = 0; + +	TclArgumentGet(interp, objv[0], &invoker, &word); + +	result = TclEvalObjEx(slaveInterp, objv[0], 0, invoker, word); +    } else { +	Tcl_Obj *objPtr = Tcl_ConcatObj(objc, objv); +	Tcl_IncrRefCount(objPtr); +	result = Tcl_EvalObjEx(slaveInterp, objPtr, 0); +	Tcl_DecrRefCount(objPtr);      } -    Tcl_AppendElement(askingInterp, Tcl_GetHashKey(&(masterPtr->slaveTable), -            slavePtr->slaveEntry)); -    return TCL_OK; +    Tcl_TransferResult(slaveInterp, result, interp); + +    Tcl_Release(slaveInterp); +    return result;  }  /*   *----------------------------------------------------------------------   * - * GetTarget -- + * SlaveExpose --   * - *	Sets the result of the invoking interpreter to a path name for - *	the target interpreter of an alias in one of the slaves. + *	Helper function to expose a command in a slave interpreter.   *   * Results: - *	TCL_OK if the target interpreter of the alias is a slave of the - *	invoking interpreter, TCL_ERROR else. + *	A standard Tcl result.   *   * Side effects: - *	Sets the result of the invoking interpreter. + *	After this call scripts in the slave will be able to invoke the newly + *	exposed command.   *   *----------------------------------------------------------------------   */  static int -GetTarget(askingInterp, path, aliasName) -    Tcl_Interp *askingInterp;	/* Interpreter to start search from. */ -    char *path;			/* The path of the interp to find. */ -    char *aliasName;		/* The target of this allias. */ -{ -    Tcl_Interp *slaveInterp;	/* Interim storage for slave. */ -    Slave *slaveSlavePtr;	/* Its Slave record. */ -    Master *masterPtr;		/* Interim storage for Master record. */ -    Tcl_HashEntry *hPtr;	/* Search element. */ -    Alias *aliasPtr;		/* Data describing the alias. */ - -    Tcl_ResetResult(askingInterp); -    masterPtr = (Master *) Tcl_GetAssocData(askingInterp, "tclMasterRecord", -            NULL); -    if (masterPtr == (Master *) NULL) { -        panic("GetTarget: could not find master record"); -    } -    slaveInterp = GetInterp(askingInterp, masterPtr, path, NULL); -    if (slaveInterp == (Tcl_Interp *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(askingInterp), -                "could not find interpreter \"", path, "\"", (char *) NULL); -        return TCL_ERROR; -    } -    slaveSlavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord", -            NULL); -    if (slaveSlavePtr == (Slave *) NULL) { -        panic("GetTarget: could not find slave record"); -    } -    hPtr = Tcl_FindHashEntry(&(slaveSlavePtr->aliasTable), aliasName); -    if (hPtr == (Tcl_HashEntry *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(askingInterp), -                "alias \"", aliasName, "\" in path \"", path, "\" not found", -                (char *) NULL); -        return TCL_ERROR; -    } -    aliasPtr = (Alias *) Tcl_GetHashValue(hPtr); -    if (aliasPtr == (Alias *) NULL) { -        panic("GetTarget: could not find alias record"); -    } -     -    if (Tcl_GetInterpPath(askingInterp, aliasPtr->targetInterp) == TCL_ERROR) { -        Tcl_ResetResult(askingInterp); -        Tcl_AppendStringsToObj(Tcl_GetObjResult(askingInterp), -                "target interpreter for alias \"", -                aliasName, "\" in path \"", path, "\" is not my descendant", -                (char *) NULL); -        return TCL_ERROR; -    } -     +SlaveExpose( +    Tcl_Interp *interp,		/* Interp for error return. */ +    Tcl_Interp *slaveInterp,	/* Interp in which command will be exposed. */ +    int objc,			/* Number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument strings. */ +{ +    const char *name; + +    if (Tcl_IsSafe(interp)) { +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +		"permission denied: safe interpreter cannot expose commands", +		-1)); +	Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", "UNSAFE", +		NULL); +	return TCL_ERROR; +    } + +    name = TclGetString(objv[(objc == 1) ? 0 : 1]); +    if (Tcl_ExposeCommand(slaveInterp, TclGetString(objv[0]), +	    name) != TCL_OK) { +	Tcl_TransferResult(slaveInterp, TCL_ERROR, interp); +	return TCL_ERROR; +    }      return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * Tcl_InterpCmd -- + * SlaveRecursionLimit --   * - *	This procedure is invoked to process the "interp" Tcl command. - *	See the user documentation for details on what it does. + *	Helper function to set/query the Recursion limit of an interp   *   * Results:   *	A standard Tcl result.   *   * Side effects: - *	See the user documentation. + *	When (objc == 1), slaveInterp will be set to a new recursion limit of + *	objv[0].   *   *----------------------------------------------------------------------   */ -	/* ARGSUSED */ -int -Tcl_InterpObjCmd(clientData, interp, objc, objv) -    ClientData clientData;		/* Unused. */ -    Tcl_Interp *interp;			/* Current interpreter. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* Argument objects. */ -{ -    Master *masterPtr;			/* Master record for current interp. */ -    int result;				/* Local result variable. */ - -    /* -     * These are all the different subcommands for this command: -     */ -     -    static char *subCmds[] = { -        "alias", "aliases", "create", "delete", "eval", "exists", -	"expose", "hide", "hidden", "issafe", "invokehidden", -        "marktrusted", "slaves", "share", "target", "transfer", -        (char *) NULL}; -    enum ISubCmdIdx { -        IAliasIdx, IAliasesIdx, ICreateIdx, IDeleteIdx, IEvalIdx, -	IExistsIdx, IExposeIdx, IHideIdx, IHiddenIdx, IIsSafeIdx, -	IInvokeHiddenIdx, IMarkTrustedIdx, ISlavesIdx, IShareIdx, -        ITargetIdx, ITransferIdx -    } index; -    if (objc < 2) { -        Tcl_WrongNumArgs(interp, 1, objv, "cmd ?arg ...?"); -        return TCL_ERROR; -    } - -    masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL); -    if (masterPtr == (Master *) NULL) { -        panic("Tcl_InterpCmd: could not find master record"); -    } - -    result = Tcl_GetIndexFromObj(interp, objv[1], subCmds, "option", -            0, (int *) &index); -    if (result != TCL_OK) { -        return result; -    } -     -    switch (index) { -        case IAliasIdx: -            return InterpAliasHelper(interp, masterPtr, objc, objv); -        case IAliasesIdx: -            return InterpAliasesHelper(interp, masterPtr, objc, objv); -        case ICreateIdx: -            return CreateInterpObject(interp, masterPtr, objc, objv); -        case IDeleteIdx: -            return DeleteInterpObject(interp, masterPtr, objc, objv); -        case IEvalIdx: -            return InterpEvalHelper(interp, masterPtr, objc, objv); -        case IExistsIdx: -            return InterpExistsHelper(interp, masterPtr, objc, objv); -        case IExposeIdx: -            return InterpExposeHelper(interp, masterPtr, objc, objv); -        case IHideIdx: -            return InterpHideHelper(interp, masterPtr, objc, objv); -        case IHiddenIdx: -            return InterpHiddenHelper(interp, masterPtr, objc, objv); -        case IIsSafeIdx: -            return InterpIsSafeHelper(interp, masterPtr, objc, objv); -        case IInvokeHiddenIdx: -            return InterpInvokeHiddenHelper(interp, masterPtr, objc, objv); -        case IMarkTrustedIdx: -            return InterpMarkTrustedHelper(interp, masterPtr, objc, objv); -        case ISlavesIdx: -            return InterpSlavesHelper(interp, masterPtr, objc, objv); -        case IShareIdx: -            return InterpShareHelper(interp, masterPtr, objc, objv); -        case ITargetIdx: -            return InterpTargetHelper(interp, masterPtr, objc, objv); -        case ITransferIdx: -            return InterpTransferHelper(interp, masterPtr, objc, objv); -    } - -    return TCL_ERROR;     -} - -/* - *---------------------------------------------------------------------- - * - * SlaveAliasHelper -- - * - *	Helper function to construct or query an alias for a slave - *	interpreter. +static int +SlaveRecursionLimit( +    Tcl_Interp *interp,		/* Interp for error return. */ +    Tcl_Interp *slaveInterp,	/* Interp in which limit is set/queried. */ +    int objc,			/* Set or Query. */ +    Tcl_Obj *const objv[])	/* Argument strings. */ +{ +    Interp *iPtr; +    int limit; + +    if (objc) { +	if (Tcl_IsSafe(interp)) { +	    Tcl_SetObjResult(interp, Tcl_NewStringObj("permission denied: " +		    "safe interpreters cannot change recursion limit", -1)); +	    Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", "UNSAFE", +		    NULL); +	    return TCL_ERROR; +	} +	if (TclGetIntFromObj(interp, objv[0], &limit) == TCL_ERROR) { +	    return TCL_ERROR; +	} +	if (limit <= 0) { +	    Tcl_SetObjResult(interp, Tcl_NewStringObj( +		    "recursion limit must be > 0", -1)); +	    Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", "BADLIMIT", +		    NULL); +	    return TCL_ERROR; +	} +	Tcl_SetRecursionLimit(slaveInterp, limit); +	iPtr = (Interp *) slaveInterp; +	if (interp == slaveInterp && iPtr->numLevels > limit) { +	    Tcl_SetObjResult(interp, Tcl_NewStringObj( +		    "falling back due to new recursion limit", -1)); +	    Tcl_SetErrorCode(interp, "TCL", "RECURSION", NULL); +	    return TCL_ERROR; +	} +	Tcl_SetObjResult(interp, objv[0]); +	return TCL_OK; +    } else { +	limit = Tcl_SetRecursionLimit(slaveInterp, 0); +	Tcl_SetObjResult(interp, Tcl_NewIntObj(limit)); +	return TCL_OK; +    } +} + +/* + *---------------------------------------------------------------------- + * + * SlaveHide -- + * + *	Helper function to hide a command in a slave interpreter.   *   * Results:   *	A standard Tcl result.   *   * Side effects: - *	Potentially creates a new alias. + *	After this call scripts in the slave will no longer be able to invoke + *	the named command.   *   *----------------------------------------------------------------------   */  static int -SlaveAliasHelper(interp, slaveInterp, slavePtr, objc, objv) -    Tcl_Interp	*interp;		/* Current interpreter. */ -    Tcl_Interp	*slaveInterp;		/* The slave interpreter. */ -    Slave *slavePtr;			/* Its slave record. */ -    int objc;				/* Count of arguments. */ -    Tcl_Obj *CONST objv[];		/* Vector of arguments. */ +SlaveHide( +    Tcl_Interp *interp,		/* Interp for error return. */ +    Tcl_Interp *slaveInterp,	/* Interp in which command will be exposed. */ +    int objc,			/* Number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument strings. */  { -    Master *masterPtr; -    int len; - -    switch (objc-2) { -        case 0: -            Tcl_WrongNumArgs(interp, 2, objv, -                    "aliasName ?targetName? ?args..?"); -            return TCL_ERROR; - -        case 1: - -            /* -             * Return the name of the command in the current -             * interpreter for which the argument is an alias in the -             * slave interpreter, and the list of saved arguments -             */ +    const char *name; -            return DescribeAlias(interp, slaveInterp, -                    Tcl_GetStringFromObj(objv[2], &len)); +    if (Tcl_IsSafe(interp)) { +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +		"permission denied: safe interpreter cannot hide commands", +		-1)); +	Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", "UNSAFE", +		NULL); +	return TCL_ERROR; +    } -        default: -            masterPtr = (Master *) Tcl_GetAssocData(interp, -                    "tclMasterRecord", NULL); -            if (masterPtr == (Master *) NULL) { -                panic("SlaveObjectCmd: could not find master record"); -            } -            return AliasCreationHelper(interp, slaveInterp, interp, -                    masterPtr, -                    Tcl_GetStringFromObj(objv[2], &len), -                    Tcl_GetStringFromObj(objv[3], &len), -                    objc-4, objv+4); +    name = TclGetString(objv[(objc == 1) ? 0 : 1]); +    if (Tcl_HideCommand(slaveInterp, TclGetString(objv[0]), name) != TCL_OK) { +	Tcl_TransferResult(slaveInterp, TCL_ERROR, interp); +	return TCL_ERROR;      } +    return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * SlaveAliasesHelper -- + * SlaveHidden --   * - *	Computes a list of aliases defined in a slave interpreter. + *	Helper function to compute list of hidden commands in a slave + *	interpreter.   *   * Results:   *	A standard Tcl result. @@ -2369,31 +3017,23 @@ SlaveAliasHelper(interp, slaveInterp, slavePtr, objc, objv)   */  static int -SlaveAliasesHelper(interp, slaveInterp, slavePtr, objc, objv) -    Tcl_Interp	*interp;		/* Current interpreter. */ -    Tcl_Interp	*slaveInterp;		/* The slave interpreter. */ -    Slave *slavePtr;			/* Its slave record. */ -    int objc;				/* Count of arguments. */ -    Tcl_Obj *CONST objv[];		/* Vector of arguments. */ +SlaveHidden( +    Tcl_Interp *interp,		/* Interp for data return. */ +    Tcl_Interp *slaveInterp)	/* Interp whose hidden commands to query. */  { +    Tcl_Obj *listObjPtr = Tcl_NewObj();	/* Local object pointer. */ +    Tcl_HashTable *hTblPtr;		/* For local searches. */      Tcl_HashEntry *hPtr;		/* For local searches. */      Tcl_HashSearch hSearch;		/* For local searches. */ -    Tcl_Obj *listObjPtr;		/* Local object pointer. */ -    Alias *aliasPtr;			/* Alias information. */ -    /* -     * Return the names of all the aliases created in the -     * slave interpreter. -     */ - -    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); -    for (hPtr = Tcl_FirstHashEntry(&(slavePtr->aliasTable), -            &hSearch); -         hPtr != (Tcl_HashEntry *) NULL; -         hPtr = Tcl_NextHashEntry(&hSearch)) { -        aliasPtr = (Alias *) Tcl_GetHashValue(hPtr); -        Tcl_ListObjAppendElement(interp, listObjPtr, -                Tcl_NewStringObj(aliasPtr->aliasName, -1)); +    hTblPtr = ((Interp *) slaveInterp)->hiddenCmdTablePtr; +    if (hTblPtr != NULL) { +	for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch); +		hPtr != NULL; +		hPtr = Tcl_NextHashEntry(&hSearch)) { +	    Tcl_ListObjAppendElement(NULL, listObjPtr, +		    Tcl_NewStringObj(Tcl_GetHashKey(hTblPtr, hPtr), -1)); +	}      }      Tcl_SetObjResult(interp, listObjPtr);      return TCL_OK; @@ -2402,1088 +3042,1074 @@ SlaveAliasesHelper(interp, slaveInterp, slavePtr, objc, objv)  /*   *----------------------------------------------------------------------   * - * SlaveEvalHelper -- + * SlaveInvokeHidden --   * - *	Helper function to evaluate a command in a slave interpreter. + *	Helper function to invoke a hidden command in a slave interpreter.   *   * Results:   *	A standard Tcl result.   *   * Side effects: - *	Whatever the command does. + *	Whatever the hidden command does.   *   *----------------------------------------------------------------------   */  static int -SlaveEvalHelper(interp, slaveInterp, slavePtr, objc, objv) -    Tcl_Interp	*interp;		/* Current interpreter. */ -    Tcl_Interp	*slaveInterp;		/* The slave interpreter. */ -    Slave *slavePtr;			/* Its slave record. */ -    int objc;				/* Count of arguments. */ -    Tcl_Obj *CONST objv[];		/* Vector of arguments. */ -{ -    Interp *iPtr;			/* Internal data type for slave. */ -    Tcl_Obj *objPtr;			/* Local object pointer. */ -    Tcl_Obj *namePtr;			/* Local object pointer. */ -    int len; -    char *string; +SlaveInvokeHidden( +    Tcl_Interp *interp,		/* Interp for error return. */ +    Tcl_Interp *slaveInterp,	/* The slave interpreter in which command will +				 * be invoked. */ +    const char *namespaceName,	/* The namespace to use, if any. */ +    int objc,			/* Number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument objects. */ +{      int result; -     -    if (objc < 3) { -        Tcl_WrongNumArgs(interp, 2, objv, "arg ?arg ...?"); -        return TCL_ERROR; + +    if (Tcl_IsSafe(interp)) { +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +		"not allowed to invoke hidden commands from safe interpreter", +		-1)); +	Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", "UNSAFE", +		NULL); +	return TCL_ERROR;      } -    objPtr = Tcl_ConcatObj(objc-2, objv+2); -    Tcl_IncrRefCount(objPtr); -     -    Tcl_Preserve((ClientData) slaveInterp); -    result = Tcl_EvalObj(slaveInterp, objPtr); +    Tcl_Preserve(slaveInterp); +    Tcl_AllowExceptions(slaveInterp); -    Tcl_DecrRefCount(objPtr); +    if (namespaceName == NULL) { +	NRE_callback *rootPtr = TOP_CB(slaveInterp); -    /* -     * Make the result and any error information accessible. We have -     * to be careful because the slave interpreter and the current -     * interpreter can be the same - do not destroy the result.. This -     * can happen if an interpreter contains an alias which is directed -     * at a target command in the same interpreter. -     */ +	Tcl_NRAddCallback(interp, NRPostInvokeHidden, slaveInterp, +		rootPtr, NULL, NULL); +	return TclNRInvoke(NULL, slaveInterp, objc, objv); +    } else { +	Namespace *nsPtr, *dummy1, *dummy2; +	const char *tail; + +	result = TclGetNamespaceForQualName(slaveInterp, namespaceName, NULL, +		TCL_FIND_ONLY_NS | TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG +		| TCL_CREATE_NS_IF_UNKNOWN, &nsPtr, &dummy1, &dummy2, &tail); +	if (result == TCL_OK) { +	    result = TclObjInvokeNamespace(slaveInterp, objc, objv, +		    (Tcl_Namespace *) nsPtr, TCL_INVOKE_HIDDEN); +	} +    } -    if (interp != slaveInterp) { -        if (result == TCL_ERROR) { - -            /* -             * An error occurred, so transfer error information from the -             * destination interpreter back to our interpreter.  -             */ - -            iPtr = (Interp *) slaveInterp; -            if (!(iPtr->flags & ERR_ALREADY_LOGGED)) { -                Tcl_AddErrorInfo(slaveInterp, ""); -            } -            iPtr->flags &= (~(ERR_ALREADY_LOGGED)); - -            Tcl_ResetResult(interp); -            namePtr = Tcl_NewStringObj("errorInfo", -1); -            objPtr = Tcl_ObjGetVar2(slaveInterp, namePtr, -                    (Tcl_Obj *) NULL, TCL_GLOBAL_ONLY); -            string = Tcl_GetStringFromObj(objPtr, &len); -            Tcl_AddObjErrorInfo(interp, string, len); -            Tcl_SetVar2(interp, "errorCode", (char *) NULL, -                    Tcl_GetVar2(slaveInterp, "errorCode", (char *) -                            NULL, TCL_GLOBAL_ONLY), -                    TCL_GLOBAL_ONLY); -            Tcl_DecrRefCount(namePtr); -        } +    Tcl_TransferResult(slaveInterp, result, interp); -	/* -         * Move the result object from one interpreter to the -         * other. -         */ -                 -        Tcl_SetObjResult(interp, Tcl_GetObjResult(slaveInterp)); -        Tcl_ResetResult(slaveInterp); +    Tcl_Release(slaveInterp); +    return result; +} + +static int +NRPostInvokeHidden( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    Tcl_Interp *slaveInterp = (Tcl_Interp *)data[0]; +    NRE_callback *rootPtr = (NRE_callback *)data[1]; + +    if (interp != slaveInterp) { +	result = TclNRRunCallbacks(slaveInterp, result, rootPtr); +	Tcl_TransferResult(slaveInterp, result, interp);      } -    Tcl_Release((ClientData) slaveInterp); -    return result;         +    Tcl_Release(slaveInterp); +    return result;  }  /*   *----------------------------------------------------------------------   * - * SlaveExposeHelper -- + * SlaveMarkTrusted --   * - *	Helper function to expose a command in a slave interpreter. + *	Helper function to mark a slave interpreter as trusted (unsafe).   *   * Results:   *	A standard Tcl result.   *   * Side effects: - *	After this call scripts in the slave will be able to invoke - *	the newly exposed command. + *	After this call the hard-wired security checks in the core no longer + *	prevent the slave from performing certain operations.   *   *----------------------------------------------------------------------   */  static int -SlaveExposeHelper(interp, slaveInterp, slavePtr, objc, objv) -    Tcl_Interp	*interp;		/* Current interpreter. */ -    Tcl_Interp	*slaveInterp;		/* The slave interpreter. */ -    Slave *slavePtr;			/* Its slave record. */ -    int objc;				/* Count of arguments. */ -    Tcl_Obj *CONST objv[];		/* Vector of arguments. */ -{ -    int len; -     -    if ((objc != 3) && (objc != 4)) { -        Tcl_WrongNumArgs(interp, 2, objv, "hiddenCmdName ?cmdName?"); -        return TCL_ERROR; -    } +SlaveMarkTrusted( +    Tcl_Interp *interp,		/* Interp for error return. */ +    Tcl_Interp *slaveInterp)	/* The slave interpreter which will be marked +				 * trusted. */ +{      if (Tcl_IsSafe(interp)) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "permission denied: safe interpreter cannot expose commands", -                (char *) NULL); -        return TCL_ERROR; -    } -    if (Tcl_ExposeCommand(slaveInterp, Tcl_GetStringFromObj(objv[2], &len), -            (objc == 4 ? -                    Tcl_GetStringFromObj(objv[3], &len) : -                    Tcl_GetStringFromObj(objv[2], &len))) -            == TCL_ERROR) { -        Tcl_SetObjResult(interp, Tcl_GetObjResult(slaveInterp)); -        Tcl_ResetResult(slaveInterp); -        return TCL_ERROR; +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +		"permission denied: safe interpreter cannot mark trusted", +		-1)); +	Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", "UNSAFE", +		NULL); +	return TCL_ERROR;      } +    ((Interp *) slaveInterp)->flags &= ~SAFE_INTERP;      return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * SlaveHideHelper -- + * Tcl_IsSafe --   * - *	Helper function to hide a command in a slave interpreter. + *	Determines whether an interpreter is safe   *   * Results: - *	A standard Tcl result. + *	1 if it is safe, 0 if it is not.   *   * Side effects: - *	After this call scripts in the slave will no longer be able - *	to invoke the named command. + *	None.   *   *----------------------------------------------------------------------   */ -static int -SlaveHideHelper(interp, slaveInterp, slavePtr, objc, objv) -    Tcl_Interp	*interp;		/* Current interpreter. */ -    Tcl_Interp	*slaveInterp;		/* The slave interpreter. */ -    Slave *slavePtr;			/* Its slave record. */ -    int objc;				/* Count of arguments. */ -    Tcl_Obj *CONST objv[];		/* Vector of arguments. */ +int +Tcl_IsSafe( +    Tcl_Interp *interp)		/* Is this interpreter "safe" ? */  { -    int len; +    Interp *iPtr = (Interp *) interp; -    if ((objc != 3) && (objc != 4)) { -        Tcl_WrongNumArgs(interp, 2, objv, "cmdName ?hiddenCmdName?"); -        return TCL_ERROR; +    if (iPtr == NULL) { +	return 0;      } -    if (Tcl_IsSafe(interp)) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "permission denied: safe interpreter cannot hide commands", -                (char *) NULL); -        return TCL_ERROR; -    } -    if (Tcl_HideCommand(slaveInterp, Tcl_GetStringFromObj(objv[2], &len), -            (objc == 4 ? -                    Tcl_GetStringFromObj(objv[3], &len) : -                    Tcl_GetStringFromObj(objv[2], &len))) -            == TCL_ERROR) { -        Tcl_SetObjResult(interp, Tcl_GetObjResult(slaveInterp)); -        Tcl_ResetResult(slaveInterp); -        return TCL_ERROR; -    } -    return TCL_OK; +    return (iPtr->flags & SAFE_INTERP) ? 1 : 0;  }  /*   *----------------------------------------------------------------------   * - * SlaveHiddenHelper -- + * Tcl_MakeSafe --   * - *	Helper function to compute list of hidden commands in a slave - *	interpreter. + *	Makes its argument interpreter contain only functionality that is + *	defined to be part of Safe Tcl. Unsafe commands are hidden, the env + *	array is unset, and the standard channels are removed.   *   * Results: - *	A standard Tcl result. + *	None.   *   * Side effects: - *	None. + *	Hides commands in its argument interpreter, and removes settings and + *	channels.   *   *----------------------------------------------------------------------   */ -static int -SlaveHiddenHelper(interp, slaveInterp, slavePtr, objc, objv) -    Tcl_Interp	*interp;		/* Current interpreter. */ -    Tcl_Interp	*slaveInterp;		/* The slave interpreter. */ -    Slave *slavePtr;			/* Its slave record. */ -    int objc;				/* Count of arguments. */ -    Tcl_Obj *CONST objv[];		/* Vector of arguments. */ -{ -    Tcl_Obj *listObjPtr;		/* Local object pointer. */ -    Tcl_HashTable *hTblPtr;		/* For local searches. */ -    Tcl_HashEntry *hPtr;		/* For local searches. */ -    Tcl_HashSearch hSearch;		/* For local searches. */ -     -    if (objc != 2) { -        Tcl_WrongNumArgs(interp, 2, objv, NULL); -        return TCL_ERROR; -    } - -    listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); -    hTblPtr = (Tcl_HashTable *) Tcl_GetAssocData(slaveInterp, -            "tclHiddenCmds", NULL); -    if (hTblPtr != (Tcl_HashTable *) NULL) { -        for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch); -             hPtr != (Tcl_HashEntry *) NULL; -             hPtr = Tcl_NextHashEntry(&hSearch)) { -            Tcl_ListObjAppendElement(interp, listObjPtr, -                    Tcl_NewStringObj(Tcl_GetHashKey(hTblPtr, hPtr), -1)); -        } +int +Tcl_MakeSafe( +    Tcl_Interp *interp)		/* Interpreter to be made safe. */ +{ +    Tcl_Channel chan;		/* Channel to remove from safe interpreter. */ +    Interp *iPtr = (Interp *) interp; +    Tcl_Interp *master = ((InterpInfo*) iPtr->interpInfo)->slave.masterInterp; + +    TclHideUnsafeCommands(interp); + +    if (master != NULL) { +	/* +	 * Alias these function implementations in the slave to those in the +	 * master; the overall implementations are safe, but they're normally +	 * defined by init.tcl which is not sourced by safe interpreters. +	 * Assume these functions all work. [Bug 2895741] +	 */ + +	(void) Tcl_Eval(interp, +		"namespace eval ::tcl {namespace eval mathfunc {}}"); +	(void) Tcl_CreateAlias(interp, "::tcl::mathfunc::min", master, +		"::tcl::mathfunc::min", 0, NULL); +	(void) Tcl_CreateAlias(interp, "::tcl::mathfunc::max", master, +		"::tcl::mathfunc::max", 0, NULL);      } -    Tcl_SetObjResult(interp, listObjPtr); + +    iPtr->flags |= SAFE_INTERP; + +    /* +     * Unsetting variables : (which should not have been set in the first +     * place, but...) +     */ + +    /* +     * No env array in a safe slave. +     */ + +    Tcl_UnsetVar(interp, "env", TCL_GLOBAL_ONLY); + +    /* +     * Remove unsafe parts of tcl_platform +     */ + +    Tcl_UnsetVar2(interp, "tcl_platform", "os", TCL_GLOBAL_ONLY); +    Tcl_UnsetVar2(interp, "tcl_platform", "osVersion", TCL_GLOBAL_ONLY); +    Tcl_UnsetVar2(interp, "tcl_platform", "machine", TCL_GLOBAL_ONLY); +    Tcl_UnsetVar2(interp, "tcl_platform", "user", TCL_GLOBAL_ONLY); + +    /* +     * Unset path informations variables (the only one remaining is [info +     * nameofexecutable]) +     */ + +    Tcl_UnsetVar(interp, "tclDefaultLibrary", TCL_GLOBAL_ONLY); +    Tcl_UnsetVar(interp, "tcl_library", TCL_GLOBAL_ONLY); +    Tcl_UnsetVar(interp, "tcl_pkgPath", TCL_GLOBAL_ONLY); + +    /* +     * Remove the standard channels from the interpreter; safe interpreters do +     * not ordinarily have access to stdin, stdout and stderr. +     * +     * NOTE: These channels are not added to the interpreter by the +     * Tcl_CreateInterp call, but may be added later, by another I/O +     * operation. We want to ensure that the interpreter does not have these +     * channels even if it is being made safe after being used for some time.. +     */ + +    chan = Tcl_GetStdChannel(TCL_STDIN); +    if (chan != NULL) { +	Tcl_UnregisterChannel(interp, chan); +    } +    chan = Tcl_GetStdChannel(TCL_STDOUT); +    if (chan != NULL) { +	Tcl_UnregisterChannel(interp, chan); +    } +    chan = Tcl_GetStdChannel(TCL_STDERR); +    if (chan != NULL) { +	Tcl_UnregisterChannel(interp, chan); +    } +      return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * SlaveIsSafeHelper -- + * Tcl_LimitExceeded --   * - *	Helper function to compute whether a slave interpreter is safe. + *	Tests whether any limit has been exceeded in the given interpreter + *	(i.e. whether the interpreter is currently unable to process further + *	scripts).   *   * Results: - *	A standard Tcl result. + *	A boolean value.   *   * Side effects:   *	None.   * + * Notes: + *	If you change this function, you MUST also update TclLimitExceeded() in + *	tclInt.h.   *----------------------------------------------------------------------   */ -static int -SlaveIsSafeHelper(interp, slaveInterp, slavePtr, objc, objv) -    Tcl_Interp	*interp;		/* Current interpreter. */ -    Tcl_Interp	*slaveInterp;		/* The slave interpreter. */ -    Slave *slavePtr;			/* Its slave record. */ -    int objc;				/* Count of arguments. */ -    Tcl_Obj *CONST objv[];		/* Vector of arguments. */ +int +Tcl_LimitExceeded( +    Tcl_Interp *interp)  { -    Tcl_Obj *resultPtr;			/* Local object pointer. */ +    register Interp *iPtr = (Interp *) interp; -    if (objc > 2) { -        Tcl_WrongNumArgs(interp, 2, objv, NULL); -        return TCL_ERROR; -    } -    resultPtr = Tcl_NewIntObj(Tcl_IsSafe(slaveInterp)); - -    Tcl_SetObjResult(interp, resultPtr); -    return TCL_OK; +    return iPtr->limit.exceeded != 0;  }  /*   *----------------------------------------------------------------------   * - * SlaveInvokeHiddenHelper -- + * Tcl_LimitReady --   * - *	Helper function to invoke a hidden command in a slave interpreter. + *	Find out whether any limit has been set on the interpreter, and if so + *	check whether the granularity of that limit is such that the full + *	limit check should be carried out.   *   * Results: - *	A standard Tcl result. + *	A boolean value that indicates whether to call Tcl_LimitCheck.   *   * Side effects: - *	Whatever the hidden command does. + *	Increments the limit granularity counter. + * + * Notes: + *	If you change this function, you MUST also update TclLimitReady() in + *	tclInt.h.   *   *----------------------------------------------------------------------   */ -static int -SlaveInvokeHiddenHelper(interp, slaveInterp, slavePtr, objc, objv) -    Tcl_Interp	*interp;		/* Current interpreter. */ -    Tcl_Interp	*slaveInterp;		/* The slave interpreter. */ -    Slave *slavePtr;			/* Its slave record. */ -    int objc;				/* Count of arguments. */ -    Tcl_Obj *CONST objv[];		/* Vector of arguments. */ +int +Tcl_LimitReady( +    Tcl_Interp *interp)  { -    Interp *iPtr; -    Master *masterPtr; -    int doGlobal = 0; -    int result; -    int len; -    char *string; -    Tcl_Obj *namePtr, *objPtr; -             -    if (objc < 3) { -        Tcl_WrongNumArgs(interp, 2, objv, -                "?-global? cmd ?arg ..?"); -        return TCL_ERROR; -    } -    if (Tcl_IsSafe(interp)) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "not allowed to invoke hidden commands from safe interpreter", -                (char *) NULL); -        return TCL_ERROR; -    } -    if (strcmp(Tcl_GetStringFromObj(objv[2], &len), "-global") == 0) { -        doGlobal = 1; -        if (objc < 4) { -            Tcl_WrongNumArgs(interp, 2, objv, -                    "path ?-global? cmd ?arg ..?"); -            return TCL_ERROR; -        } -    } -    masterPtr = (Master *) Tcl_GetAssocData(slaveInterp, -            "tclMasterRecord", NULL); -    if (masterPtr == (Master *) NULL) { -        panic("SlaveObjectCmd: could not find master record"); -    } -    Tcl_Preserve((ClientData) slaveInterp); -    if (doGlobal) { -        result = TclObjInvokeGlobal(slaveInterp, objc-3, objv+3, -                TCL_INVOKE_HIDDEN); -    } else { -        result = TclObjInvoke(slaveInterp, objc-2, objv+2, -                TCL_INVOKE_HIDDEN); -    } +    register Interp *iPtr = (Interp *) interp; -    /* -     * Now make the result and any error information accessible. We -     * have to be careful because the slave interpreter and the current -     * interpreter can be the same - do not destroy the result.. This -     * can happen if an interpreter contains an alias which is directed -     * at a target command in the same interpreter. -     */ - -    if (interp != slaveInterp) { -        if (result == TCL_ERROR) { - -            /* -             * An error occurred, so transfer error information from -             * the target interpreter back to our interpreter. -             */ - -            iPtr = (Interp *) slaveInterp; -            if (!(iPtr->flags & ERR_ALREADY_LOGGED)) { -                Tcl_AddErrorInfo(slaveInterp, ""); -            } -            iPtr->flags &= (~(ERR_ALREADY_LOGGED)); - -            Tcl_ResetResult(interp); -            namePtr = Tcl_NewStringObj("errorInfo", -1); -            objPtr = Tcl_ObjGetVar2(slaveInterp, namePtr, -                    (Tcl_Obj *) NULL, TCL_GLOBAL_ONLY); -            string = Tcl_GetStringFromObj(objPtr, &len); -            Tcl_AddObjErrorInfo(interp, string, len); -            Tcl_SetVar2(interp, "errorCode", (char *) NULL, -                    Tcl_GetVar2(slaveInterp, "errorCode", (char *) -                            NULL, TCL_GLOBAL_ONLY), -                    TCL_GLOBAL_ONLY); -            Tcl_DecrRefCount(namePtr); -        } +    if (iPtr->limit.active != 0) { +	register int ticker = ++iPtr->limit.granularityTicker; -	/* -         * Move the result object from the slave to the master. -         */ -                 -        Tcl_SetObjResult(interp, Tcl_GetObjResult(slaveInterp)); -        Tcl_ResetResult(slaveInterp); +	if ((iPtr->limit.active & TCL_LIMIT_COMMANDS) && +		((iPtr->limit.cmdGranularity == 1) || +		    (ticker % iPtr->limit.cmdGranularity == 0))) { +	    return 1; +	} +	if ((iPtr->limit.active & TCL_LIMIT_TIME) && +		((iPtr->limit.timeGranularity == 1) || +		    (ticker % iPtr->limit.timeGranularity == 0))) { +	    return 1; +	}      } -    Tcl_Release((ClientData) slaveInterp); -    return result;         +    return 0;  }  /*   *----------------------------------------------------------------------   * - * SlaveMarkTrustedHelper -- + * Tcl_LimitCheck --   * - *	Helper function to mark a slave interpreter as trusted (unsafe). + *	Check all currently set limits in the interpreter (where permitted by + *	granularity). If a limit is exceeded, call its callbacks and, if the + *	limit is still exceeded after the callbacks have run, make the + *	interpreter generate an error that cannot be caught within the limited + *	interpreter.   *   * Results: - *	A standard Tcl result. + *	A Tcl result value (TCL_OK if no limit is exceeded, and TCL_ERROR if a + *	limit has been exceeded).   *   * Side effects: - *	After this call the hard-wired security checks in the core no - *	longer prevent the slave from performing certain operations. + *	May invoke system calls. May invoke other interpreters. May be + *	reentrant. May put the interpreter into a state where it can no longer + *	execute commands without outside intervention.   *   *----------------------------------------------------------------------   */ -static int -SlaveMarkTrustedHelper(interp, slaveInterp, slavePtr, objc, objv) -    Tcl_Interp	*interp;		/* Current interpreter. */ -    Tcl_Interp	*slaveInterp;		/* The slave interpreter. */ -    Slave *slavePtr;			/* Its slave record. */ -    int objc;				/* Count of arguments. */ -    Tcl_Obj *CONST objv[];		/* Vector of arguments. */ -{ -    int len; -     -    if (objc != 2) { -        Tcl_WrongNumArgs(interp, 2, objv, NULL); -        return TCL_ERROR; -    } -    if (Tcl_IsSafe(interp)) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "\"", Tcl_GetStringFromObj(objv[0], &len), " marktrusted\"", -                " can only be invoked from a trusted interpreter", -                (char *) NULL); -        return TCL_ERROR; +int +Tcl_LimitCheck( +    Tcl_Interp *interp) +{ +    Interp *iPtr = (Interp *) interp; +    register int ticker = iPtr->limit.granularityTicker; + +    if (Tcl_InterpDeleted(interp)) { +	return TCL_OK; +    } + +    if ((iPtr->limit.active & TCL_LIMIT_COMMANDS) && +	    ((iPtr->limit.cmdGranularity == 1) || +		    (ticker % iPtr->limit.cmdGranularity == 0)) && +	    (iPtr->limit.cmdCount < iPtr->cmdCount)) { +	iPtr->limit.exceeded |= TCL_LIMIT_COMMANDS; +	Tcl_Preserve(interp); +	RunLimitHandlers(iPtr->limit.cmdHandlers, interp); +	if (iPtr->limit.cmdCount >= iPtr->cmdCount) { +	    iPtr->limit.exceeded &= ~TCL_LIMIT_COMMANDS; +	} else if (iPtr->limit.exceeded & TCL_LIMIT_COMMANDS) { +	    Tcl_SetObjResult(interp, Tcl_NewStringObj( +		    "command count limit exceeded", -1)); +	    Tcl_SetErrorCode(interp, "TCL", "LIMIT", "COMMANDS", NULL); +	    Tcl_Release(interp); +	    return TCL_ERROR; +	} +	Tcl_Release(interp); +    } + +    if ((iPtr->limit.active & TCL_LIMIT_TIME) && +	    ((iPtr->limit.timeGranularity == 1) || +		(ticker % iPtr->limit.timeGranularity == 0))) { +	Tcl_Time now; + +	Tcl_GetTime(&now); +	if (iPtr->limit.time.sec < now.sec || +		(iPtr->limit.time.sec == now.sec && +		iPtr->limit.time.usec < now.usec)) { +	    iPtr->limit.exceeded |= TCL_LIMIT_TIME; +	    Tcl_Preserve(interp); +	    RunLimitHandlers(iPtr->limit.timeHandlers, interp); +	    if (iPtr->limit.time.sec > now.sec || +		    (iPtr->limit.time.sec == now.sec && +		    iPtr->limit.time.usec >= now.usec)) { +		iPtr->limit.exceeded &= ~TCL_LIMIT_TIME; +	    } else if (iPtr->limit.exceeded & TCL_LIMIT_TIME) { +		Tcl_SetObjResult(interp, Tcl_NewStringObj( +			"time limit exceeded", -1)); +		Tcl_SetErrorCode(interp, "TCL", "LIMIT", "TIME", NULL); +		Tcl_Release(interp); +		return TCL_ERROR; +	    } +	    Tcl_Release(interp); +	}      } -    return MarkTrusted(slaveInterp); + +    return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * SlaveObjectCmd -- + * RunLimitHandlers --   * - *	Command to manipulate an interpreter, e.g. to send commands to it - *	to be evaluated. One such command exists for each slave interpreter. + *	Invoke all the limit handlers in a list (for a particular limit). + *	Note that no particular limit handler callback will be invoked + *	reentrantly.   *   * Results: - *	A standard Tcl result. + *	None.   *   * Side effects: - *	See user documentation for details. + *	Depends on the limit handlers.   *   *----------------------------------------------------------------------   */ -static int -SlaveObjectCmd(clientData, interp, objc, objv) -    ClientData clientData;		/* Slave interpreter. */ -    Tcl_Interp *interp;			/* Current interpreter. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* The argument vector. */ +static void +RunLimitHandlers( +    LimitHandler *handlerPtr, +    Tcl_Interp *interp)  { -    Slave *slavePtr;			/* Slave record. */ -    Tcl_Interp *slaveInterp;		/* Slave interpreter. */ -    int result;				/* Loop counter, status return. */ -    int len;				/* Length of command name. */ +    LimitHandler *nextPtr; +    for (; handlerPtr!=NULL ; handlerPtr=nextPtr) { +	if (handlerPtr->flags & (LIMIT_HANDLER_DELETED|LIMIT_HANDLER_ACTIVE)) { +	    /* +	     * Reentrant call or something seriously strange in the delete +	     * code. +	     */ + +	    nextPtr = handlerPtr->nextPtr; +	    continue; +	} -    /* -     * These are all the different subcommands for this command: -     */ -     -    static char *subCmds[] = { -        "alias", "aliases", -        "eval", "expose", -        "hide", "hidden", -        "issafe", "invokehidden", -        "marktrusted", -        (char *) NULL}; -    enum ISubCmdIdx { -        IAliasIdx, IAliasesIdx, -        IEvalIdx, IExposeIdx, -        IHideIdx, IHiddenIdx, -        IIsSafeIdx, IInvokeHiddenIdx, -        IMarkTrustedIdx -    } index; -     -    if (objc < 2) { -        Tcl_WrongNumArgs(interp, 1, objv, "cmd ?arg ...?"); -        return TCL_ERROR; -    } +	/* +	 * Set the ACTIVE flag while running the limit handler itself so we +	 * cannot reentrantly call this handler and know to use the alternate +	 * method of deletion if necessary. +	 */ -    slaveInterp = (Tcl_Interp *) clientData; -    if (slaveInterp == (Tcl_Interp *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "interpreter ", Tcl_GetStringFromObj(objv[0], &len), -                " has been deleted", (char *) NULL); -	return TCL_ERROR; -    } +	handlerPtr->flags |= LIMIT_HANDLER_ACTIVE; +	handlerPtr->handlerProc(handlerPtr->clientData, interp); +	handlerPtr->flags &= ~LIMIT_HANDLER_ACTIVE; -    slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, -            "tclSlaveRecord", NULL); -    if (slavePtr == (Slave *) NULL) { -        panic("SlaveObjectCmd: could not find slave record"); -    } - -    result = Tcl_GetIndexFromObj(interp, objv[1], subCmds, "option", -            0, (int *) &index); -    if (result != TCL_OK) { -        return result; -    } - -    switch (index) { -        case IAliasIdx: -            return SlaveAliasHelper(interp, slaveInterp, slavePtr, objc, objv); -        case IAliasesIdx: -            return SlaveAliasesHelper(interp, slaveInterp, slavePtr, -                    objc, objv); -        case IEvalIdx: -            return SlaveEvalHelper(interp, slaveInterp, slavePtr, objc, objv); -        case IExposeIdx: -            return SlaveExposeHelper(interp, slaveInterp, slavePtr, -                    objc, objv); -        case IHideIdx: -            return SlaveHideHelper(interp, slaveInterp, slavePtr, -                    objc, objv); -        case IHiddenIdx: -            return SlaveHiddenHelper(interp, slaveInterp, slavePtr, -                    objc, objv); -        case IIsSafeIdx: -            return SlaveIsSafeHelper(interp, slaveInterp, slavePtr, -                    objc, objv); -        case IInvokeHiddenIdx: -            return SlaveInvokeHiddenHelper(interp, slaveInterp, slavePtr, -                    objc, objv); -        case IMarkTrustedIdx: -            return SlaveMarkTrustedHelper(interp, slaveInterp, slavePtr, -                    objc, objv); -    } +	/* +	 * Rediscover this value; it might have changed during the processing +	 * of a limit handler. We have to record it here because we might +	 * delete the structure below, and reading a value out of a deleted +	 * structure is unsafe (even if actually legal with some +	 * malloc()/free() implementations.) +	 */ -    return TCL_ERROR; +	nextPtr = handlerPtr->nextPtr; + +	/* +	 * If we deleted the current handler while we were executing it, we +	 * will have spliced it out of the list and set the +	 * LIMIT_HANDLER_DELETED flag. +	 */ + +	if (handlerPtr->flags & LIMIT_HANDLER_DELETED) { +	    if (handlerPtr->deleteProc != NULL) { +		handlerPtr->deleteProc(handlerPtr->clientData); +	    } +	    ckfree(handlerPtr); +	} +    }  }  /*   *----------------------------------------------------------------------   * - * SlaveObjectDeleteProc -- + * Tcl_LimitAddHandler --   * - *	Invoked when an object command for a slave interpreter is deleted; - *	cleans up all state associated with the slave interpreter and destroys - *	the slave interpreter. + *	Add a callback handler for a particular resource limit.   *   * Results:   *	None.   *   * Side effects: - *	Cleans up all state associated with the slave interpreter and - *	destroys the slave interpreter. + *	Extends the internal linked list of handlers for a limit.   *   *----------------------------------------------------------------------   */ -static void -SlaveObjectDeleteProc(clientData) -    ClientData clientData;		/* The SlaveRecord for the command. */ +void +Tcl_LimitAddHandler( +    Tcl_Interp *interp, +    int type, +    Tcl_LimitHandlerProc *handlerProc, +    ClientData clientData, +    Tcl_LimitHandlerDeleteProc *deleteProc)  { -    Slave *slavePtr;			/* Interim storage for Slave record. */ -    Tcl_Interp *slaveInterp;		/* And for a slave interp. */ - -    slaveInterp = (Tcl_Interp *) clientData; -    slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord",NULL);  -    if (slavePtr == (Slave *) NULL) { -        panic("SlaveObjectDeleteProc: could not find slave record"); -    } +    Interp *iPtr = (Interp *) interp; +    LimitHandler *handlerPtr;      /* -     * Delete the entry in the slave table in the master interpreter now. -     * This is to avoid an infinite loop in the Master hash table cleanup in -     * the master interpreter. This can happen if this slave is being deleted -     * because the master is being deleted and the slave deletion is deferred -     * because it is still active. +     * Convert everything into a real deletion callback.       */ -    Tcl_DeleteHashEntry(slavePtr->slaveEntry); +    if (deleteProc == (Tcl_LimitHandlerDeleteProc *) TCL_DYNAMIC) { +	deleteProc = (Tcl_LimitHandlerDeleteProc *) Tcl_Free; +    } +    if (deleteProc == (Tcl_LimitHandlerDeleteProc *) TCL_STATIC) { +	deleteProc = NULL; +    }      /* -     * Set to NULL so that when the slave record is cleaned up in the slave -     * it does not try to delete the command causing all sorts of grief. -     * See SlaveRecordDeleteProc(). +     * Allocate a handler record.       */ -    slavePtr->interpCmd = NULL; +    handlerPtr = ckalloc(sizeof(LimitHandler)); +    handlerPtr->flags = 0; +    handlerPtr->handlerProc = handlerProc; +    handlerPtr->clientData = clientData; +    handlerPtr->deleteProc = deleteProc; +    handlerPtr->prevPtr = NULL;      /* -     * Destroy the interpreter - this will cause all the deleteProcs for -     * all commands (including aliases) to run. -     * -     * NOTE: WE ASSUME THAT THE INTERPRETER HAS NOT BEEN DELETED YET!! +     * Prepend onto the front of the correct linked list.       */ -    Tcl_DeleteInterp(slavePtr->slaveInterp); +    switch (type) { +    case TCL_LIMIT_COMMANDS: +	handlerPtr->nextPtr = iPtr->limit.cmdHandlers; +	if (handlerPtr->nextPtr != NULL) { +	    handlerPtr->nextPtr->prevPtr = handlerPtr; +	} +	iPtr->limit.cmdHandlers = handlerPtr; +	return; + +    case TCL_LIMIT_TIME: +	handlerPtr->nextPtr = iPtr->limit.timeHandlers; +	if (handlerPtr->nextPtr != NULL) { +	    handlerPtr->nextPtr->prevPtr = handlerPtr; +	} +	iPtr->limit.timeHandlers = handlerPtr; +	return; +    } + +    Tcl_Panic("unknown type of resource limit");  }  /*   *----------------------------------------------------------------------   * - * AliasCmd -- + * Tcl_LimitRemoveHandler --   * - *	This is the procedure that services invocations of aliases in a - *	slave interpreter. One such command exists for each alias. When - *	invoked, this procedure redirects the invocation to the target - *	command in the master interpreter as designated by the Alias - *	record associated with this command. + *	Remove a callback handler for a particular resource limit.   *   * Results: - *	A standard Tcl result. + *	None.   *   * Side effects: - *	Causes forwarding of the invocation; all possible side effects - *	may occur as a result of invoking the command to which the - *	invocation is forwarded. + *	The handler is spliced out of the internal linked list for the limit, + *	and if not currently being invoked, deleted. Otherwise it is just + *	marked for deletion and removed when the limit handler has finished + *	executing.   *   *----------------------------------------------------------------------   */ -static int -AliasCmd(clientData, interp, objc, objv) -    ClientData clientData;		/* Alias record. */ -    Tcl_Interp *interp;			/* Current interpreter. */ -    int objc;				/* Number of arguments. */ -    Tcl_Obj *CONST objv[];		/* Argument vector. */	 -{ -    Tcl_Interp *targetInterp;		/* Target for alias exec. */ -    Interp *iPtr;			/* Internal type of target. */ -    Alias *aliasPtr;			/* Describes the alias. */ -    Tcl_Command cmd;			/* The target command. */ -    Command *cmdPtr;			/* Points to target command. */ -    Tcl_Namespace *targetNsPtr;	        /* Target command's namespace. */ -    int result;				/* Result of execution. */ -    int i, j, addObjc;			/* Loop counters. */ -    int localObjc;			/* Local argument count. */ -    Tcl_Obj **localObjv;		/* Local argument vector. */ -    Tcl_Obj *namePtr, *objPtr;		/* Local object pointers. */ -    char *string;			/* Local object string rep. */ -    int len;				/* Dummy length arg. */ -     -    aliasPtr = (Alias *) clientData; -    targetInterp = aliasPtr->targetInterp; +void +Tcl_LimitRemoveHandler( +    Tcl_Interp *interp, +    int type, +    Tcl_LimitHandlerProc *handlerProc, +    ClientData clientData) +{ +    Interp *iPtr = (Interp *) interp; +    LimitHandler *handlerPtr; + +    switch (type) { +    case TCL_LIMIT_COMMANDS: +	handlerPtr = iPtr->limit.cmdHandlers; +	break; +    case TCL_LIMIT_TIME: +	handlerPtr = iPtr->limit.timeHandlers; +	break; +    default: +	Tcl_Panic("unknown type of resource limit"); +	return; +    } + +    for (; handlerPtr!=NULL ; handlerPtr=handlerPtr->nextPtr) { +	if ((handlerPtr->handlerProc != handlerProc) || +		(handlerPtr->clientData != clientData)) { +	    continue; +	} -    /* -     * Look for the target command in the global namespace of the target -     * interpreter. -     */ +	/* +	 * We've found the handler to delete; mark it as doomed if not already +	 * so marked (which shouldn't actually happen). +	 */ -    cmdPtr = NULL; -    targetNsPtr = Tcl_GetGlobalNamespace(aliasPtr->targetInterp); -    cmd = Tcl_FindCommand(targetInterp, aliasPtr->targetName, -            targetNsPtr, /*flags*/ 0); -    if (cmd != (Tcl_Command) NULL) { -        cmdPtr = (Command *) cmd; -    } +	if (handlerPtr->flags & LIMIT_HANDLER_DELETED) { +	    return; +	} +	handlerPtr->flags |= LIMIT_HANDLER_DELETED; -    iPtr = (Interp *) targetInterp; +	/* +	 * Splice the handler out of the doubly-linked list. +	 */ -    /* -     * If the command does not exist, invoke "unknown" in the master. -     */ -     -    if (cmdPtr == NULL) { -        addObjc = aliasPtr->objc; -        localObjc = addObjc + objc + 1; -        localObjv = (Tcl_Obj **) ckalloc((unsigned) sizeof(Tcl_Obj *) -                * localObjc); -         -        localObjv[0] = Tcl_NewStringObj("unknown", -1); -        localObjv[1] = Tcl_NewStringObj(aliasPtr->targetName, -1); -        Tcl_IncrRefCount(localObjv[0]); -        Tcl_IncrRefCount(localObjv[1]); -         -        for (i = 0, j = 2; i < addObjc; i++, j++) { -            localObjv[j] = aliasPtr->objv[i]; -        } -        for (i = 1; i < objc; i++, j++) { -            localObjv[j] = objv[i]; -        } -        Tcl_Preserve((ClientData) targetInterp); -        result = TclObjInvoke(targetInterp, localObjc, localObjv, 0); - -        Tcl_DecrRefCount(localObjv[0]); -        Tcl_DecrRefCount(localObjv[1]); -         -        ckfree((char *) localObjv); -         -        if (targetInterp != interp) { -            if (result == TCL_ERROR) { -                 -                /* -                 * An error occurred, so transfer error information from -                 * the target interpreter back to our interpreter. -                 */ - -                if (!(iPtr->flags & ERR_ALREADY_LOGGED)) { -                    Tcl_AddErrorInfo((Tcl_Interp *) iPtr, ""); -                } -                iPtr->flags &= (~(ERR_ALREADY_LOGGED)); - -                Tcl_ResetResult(interp); -                namePtr = Tcl_NewStringObj("errorInfo", -1); -                objPtr = Tcl_ObjGetVar2(targetInterp, namePtr, -                        (Tcl_Obj *) NULL, TCL_GLOBAL_ONLY); -                string = Tcl_GetStringFromObj(objPtr, &len); -                Tcl_AddObjErrorInfo(interp, string, len); -                Tcl_SetVar2(interp, "errorCode", (char *) NULL, -                        Tcl_GetVar2(targetInterp, "errorCode", (char *) -                                NULL, TCL_GLOBAL_ONLY), -                        TCL_GLOBAL_ONLY); -                Tcl_DecrRefCount(namePtr); -            } - -            /* -             * Transfer the result from the target interpreter to the -             * calling interpreter. -             */ -             -            Tcl_SetObjResult(interp, Tcl_GetObjResult(targetInterp)); -            Tcl_ResetResult(targetInterp); -        } - -	Tcl_Release((ClientData) targetInterp); -        return result; +	if (handlerPtr->prevPtr == NULL) { +	    switch (type) { +	    case TCL_LIMIT_COMMANDS: +		iPtr->limit.cmdHandlers = handlerPtr->nextPtr; +		break; +	    case TCL_LIMIT_TIME: +		iPtr->limit.timeHandlers = handlerPtr->nextPtr; +		break; +	    } +	} else { +	    handlerPtr->prevPtr->nextPtr = handlerPtr->nextPtr; +	} +	if (handlerPtr->nextPtr != NULL) { +	    handlerPtr->nextPtr->prevPtr = handlerPtr->prevPtr; +	} + +	/* +	 * If nothing is currently executing the handler, delete its client +	 * data and the overall handler structure now. Otherwise it will all +	 * go away when the handler returns. +	 */ + +	if (!(handlerPtr->flags & LIMIT_HANDLER_ACTIVE)) { +	    if (handlerPtr->deleteProc != NULL) { +		handlerPtr->deleteProc(handlerPtr->clientData); +	    } +	    ckfree(handlerPtr); +	} +	return;      } +} + +/* + *---------------------------------------------------------------------- + * + * TclLimitRemoveAllHandlers -- + * + *	Remove all limit callback handlers for an interpreter. This is invoked + *	as part of deleting the interpreter. + * + * Results: + *	None. + * + * Side effects: + *	Limit handlers are deleted or marked for deletion (as with + *	Tcl_LimitRemoveHandler). + * + *---------------------------------------------------------------------- + */ -    /* -     * Otherwise invoke the regular target command. -     */ -     -    if (aliasPtr->objc <= 0) { -        localObjv = (Tcl_Obj **) objv; -        localObjc = objc; -    } else { -        addObjc = aliasPtr->objc; -        localObjc = objc + addObjc; -        localObjv = -            (Tcl_Obj **) ckalloc((unsigned) sizeof(Tcl_Obj *) * localObjc); -        localObjv[0] = objv[0]; -        for (i = 0, j = 1; i < addObjc; i++, j++) { -            localObjv[j] = aliasPtr->objv[i]; -        } -        for (i = 1; i < objc; i++, j++) { -            localObjv[j] = objv[i]; -        } -    } - -    iPtr->numLevels++; -    Tcl_Preserve((ClientData) targetInterp); +void +TclLimitRemoveAllHandlers( +    Tcl_Interp *interp) +{ +    Interp *iPtr = (Interp *) interp; +    LimitHandler *handlerPtr, *nextHandlerPtr;      /* -     * Reset the interpreter to its clean state; we do not know what state -     * it is in now.. +     * Delete all command-limit handlers.       */ -     -    Tcl_ResetResult(targetInterp); -    result = (cmdPtr->objProc)(cmdPtr->objClientData, targetInterp, -            localObjc, localObjv); -    iPtr->numLevels--; -     -    /* -     * Check if we are at the bottom of the stack for the target interpreter. -     * If so, check for special return codes. -     */ -     -    if (iPtr->numLevels == 0) { -	if (result == TCL_RETURN) { -	    result = TclUpdateReturnInfo(iPtr); -	} -	if ((result != TCL_OK) && (result != TCL_ERROR)) { -	    Tcl_ResetResult(targetInterp); -	    if (result == TCL_BREAK) { -                Tcl_SetObjResult(targetInterp, -                        Tcl_NewStringObj("invoked \"break\" outside of a loop", -                                -1)); -	    } else if (result == TCL_CONTINUE) { -                Tcl_SetObjResult(targetInterp, -                        Tcl_NewStringObj( -                            "invoked \"continue\" outside of a loop", -                            -1)); -	    } else { -                char buf[128]; +    for (handlerPtr=iPtr->limit.cmdHandlers, iPtr->limit.cmdHandlers=NULL; +	    handlerPtr!=NULL; handlerPtr=nextHandlerPtr) { +	nextHandlerPtr = handlerPtr->nextPtr; + +	/* +	 * Do not delete here if it has already been marked for deletion. +	 */ -                sprintf(buf, "command returned bad code: %d", result); -                Tcl_SetObjResult(targetInterp, Tcl_NewStringObj(buf, -1)); +	if (handlerPtr->flags & LIMIT_HANDLER_DELETED) { +	    continue; +	} +	handlerPtr->flags |= LIMIT_HANDLER_DELETED; +	handlerPtr->prevPtr = NULL; +	handlerPtr->nextPtr = NULL; + +	/* +	 * If nothing is currently executing the handler, delete its client +	 * data and the overall handler structure now. Otherwise it will all +	 * go away when the handler returns. +	 */ + +	if (!(handlerPtr->flags & LIMIT_HANDLER_ACTIVE)) { +	    if (handlerPtr->deleteProc != NULL) { +		handlerPtr->deleteProc(handlerPtr->clientData);  	    } -	    result = TCL_ERROR; +	    ckfree(handlerPtr);  	}      }      /* -     * Clean up any locally allocated argument vector structure. +     * Delete all time-limit handlers.       */ -     -    if (localObjv != objv) { -        ckfree((char *) localObjv); + +    for (handlerPtr=iPtr->limit.timeHandlers, iPtr->limit.timeHandlers=NULL; +	    handlerPtr!=NULL; handlerPtr=nextHandlerPtr) { +	nextHandlerPtr = handlerPtr->nextPtr; + +	/* +	 * Do not delete here if it has already been marked for deletion. +	 */ + +	if (handlerPtr->flags & LIMIT_HANDLER_DELETED) { +	    continue; +	} +	handlerPtr->flags |= LIMIT_HANDLER_DELETED; +	handlerPtr->prevPtr = NULL; +	handlerPtr->nextPtr = NULL; + +	/* +	 * If nothing is currently executing the handler, delete its client +	 * data and the overall handler structure now. Otherwise it will all +	 * go away when the handler returns. +	 */ + +	if (!(handlerPtr->flags & LIMIT_HANDLER_ACTIVE)) { +	    if (handlerPtr->deleteProc != NULL) { +		handlerPtr->deleteProc(handlerPtr->clientData); +	    } +	    ckfree(handlerPtr); +	}      } -     +      /* -     * Move the result from the target interpreter to the invoking -     * interpreter if they are different. -     * -     * Note: We cannot use aliasPtr any more because the alias may have -     * been deleted. +     * Delete the timer callback that is used to trap limits that occur in +     * [vwait]s...       */ -    if (interp != targetInterp) { -        if (result == TCL_ERROR) { - -            /* -             * An error occurred, so transfer the error information from -             * the target interpreter back to our interpreter. -             */ - -            if (!(iPtr->flags & ERR_ALREADY_LOGGED)) { -                Tcl_AddErrorInfo(targetInterp, ""); -            } -            iPtr->flags &= (~(ERR_ALREADY_LOGGED)); -             -            Tcl_ResetResult(interp); -            namePtr = Tcl_NewStringObj("errorInfo", -1); -            objPtr = Tcl_ObjGetVar2(targetInterp, namePtr, (Tcl_Obj *) NULL, -                    TCL_GLOBAL_ONLY); -            string = Tcl_GetStringFromObj(objPtr, &len); -            Tcl_AddObjErrorInfo(interp, string, len); -            Tcl_SetVar2(interp, "errorCode", (char *) NULL, -                    Tcl_GetVar2(targetInterp, "errorCode", (char *) NULL, -                            TCL_GLOBAL_ONLY), -                    TCL_GLOBAL_ONLY); -            Tcl_DecrRefCount(namePtr); -        } - -	/* -         * Move the result object from one interpreter to the -         * other. -         */ -                 -        Tcl_SetObjResult(interp, Tcl_GetObjResult(targetInterp)); -        Tcl_ResetResult(targetInterp); +    if (iPtr->limit.timeEvent != NULL) { +	Tcl_DeleteTimerHandler(iPtr->limit.timeEvent); +	iPtr->limit.timeEvent = NULL;      } -    Tcl_Release((ClientData) targetInterp); -    return result;          }  /*   *----------------------------------------------------------------------   * - * AliasCmdDeleteProc -- + * Tcl_LimitTypeEnabled --   * - *	Is invoked when an alias command is deleted in a slave. Cleans up - *	all storage associated with this alias. + *	Check whether a particular limit has been enabled for an interpreter.   *   * Results: - *	None. + *	A boolean value.   *   * Side effects: - *	Deletes the alias record and its entry in the alias table for - *	the interpreter. + *	None.   *   *----------------------------------------------------------------------   */ -static void -AliasCmdDeleteProc(clientData) -    ClientData clientData;		/* The alias record for this alias. */ +int +Tcl_LimitTypeEnabled( +    Tcl_Interp *interp, +    int type)  { -    Alias *aliasPtr;			/* Alias record for alias to delete. */ -    Target *targetPtr;			/* Record for target of this alias. */ -    int i;				/* Loop counter. */ - -    aliasPtr = (Alias *) clientData; -     -    targetPtr = (Target *) Tcl_GetHashValue(aliasPtr->targetEntry); -    ckfree((char *) targetPtr); -    Tcl_DeleteHashEntry(aliasPtr->targetEntry); +    Interp *iPtr = (Interp *) interp; -    ckfree((char *) aliasPtr->targetName); -    ckfree((char *) aliasPtr->aliasName); -    for (i = 0; i < aliasPtr->objc; i++) { -        Tcl_DecrRefCount(aliasPtr->objv[i]); -    } -    if (aliasPtr->objv != (Tcl_Obj **) NULL) { -        ckfree((char *) aliasPtr->objv); -    } +    return (iPtr->limit.active & type) != 0; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitTypeExceeded -- + * + *	Check whether a particular limit has been exceeded for an interpreter. + * + * Results: + *	A boolean value (note that Tcl_LimitExceeded will always return + *	non-zero when this function returns non-zero). + * + * Side effects: + *	None. + * + *---------------------------------------------------------------------- + */ -    Tcl_DeleteHashEntry(aliasPtr->aliasEntry); +int +Tcl_LimitTypeExceeded( +    Tcl_Interp *interp, +    int type) +{ +    Interp *iPtr = (Interp *) interp; -    ckfree((char *) aliasPtr); +    return (iPtr->limit.exceeded & type) != 0;  }  /*   *----------------------------------------------------------------------   * - * MasterRecordDeleteProc - + * Tcl_LimitTypeSet --   * - *	Is invoked when an interpreter (which is using the "interp" facility) - *	is deleted, and it cleans up the storage associated with the - *	"tclMasterRecord" assoc-data entry. + *	Enable a particular limit for an interpreter.   *   * Results:   *	None.   *   * Side effects: - *	Cleans up storage. + *	The limit is turned on and will be checked in future at an interval + *	determined by the frequency of calling of Tcl_LimitReady and the + *	granularity of the limit in question.   *   *----------------------------------------------------------------------   */ -static void -MasterRecordDeleteProc(clientData, interp) -    ClientData	clientData;		/* Master record for deleted interp. */ -    Tcl_Interp *interp;			/* Interpreter being deleted. */ +void +Tcl_LimitTypeSet( +    Tcl_Interp *interp, +    int type)  { -    Target *targetPtr;			/* Loop variable. */ -    Tcl_HashEntry *hPtr;		/* Search element. */ -    Tcl_HashSearch hSearch;		/* Search record (internal). */ -    Slave *slavePtr;			/* Loop variable. */ -    Master *masterPtr;			/* Interim storage. */ +    Interp *iPtr = (Interp *) interp; -    masterPtr = (Master *) clientData; -    for (hPtr = Tcl_FirstHashEntry(&(masterPtr->slaveTable), &hSearch); -         hPtr != NULL; -         hPtr = Tcl_NextHashEntry(&hSearch)) { -        slavePtr = (Slave *) Tcl_GetHashValue(hPtr); -        (void) Tcl_DeleteCommandFromToken(interp, slavePtr->interpCmd); -    } -    Tcl_DeleteHashTable(&(masterPtr->slaveTable)); +    iPtr->limit.active |= type; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitTypeReset -- + * + *	Disable a particular limit for an interpreter. + * + * Results: + *	None. + * + * Side effects: + *	The limit is disabled. If the limit was exceeded when this function + *	was called, the limit will no longer be exceeded afterwards and the + *	interpreter will be free to execute further scripts (assuming it isn't + *	also deleted, of course). + * + *---------------------------------------------------------------------- + */ -    for (hPtr = Tcl_FirstHashEntry(&(masterPtr->targetTable), &hSearch); -         hPtr != NULL; -         hPtr = Tcl_FirstHashEntry(&(masterPtr->targetTable), &hSearch)) { -        targetPtr = (Target *) Tcl_GetHashValue(hPtr); -        (void) Tcl_DeleteCommandFromToken(targetPtr->slaveInterp, -	        targetPtr->slaveCmd); -    } -    Tcl_DeleteHashTable(&(masterPtr->targetTable)); +void +Tcl_LimitTypeReset( +    Tcl_Interp *interp, +    int type) +{ +    Interp *iPtr = (Interp *) interp; -    ckfree((char *) masterPtr); +    iPtr->limit.active &= ~type; +    iPtr->limit.exceeded &= ~type;  }  /*   *----------------------------------------------------------------------   * - * SlaveRecordDeleteProc -- + * Tcl_LimitSetCommands --   * - *	Is invoked when an interpreter (which is using the interp facility) - *	is deleted, and it cleans up the storage associated with the - *	tclSlaveRecord assoc-data entry. + *	Set the command limit for an interpreter.   *   * Results: - *	None + *	None.   *   * Side effects: - *	Cleans up storage. + *	Also resets whether the command limit was exceeded. This might permit + *	a small amount of further execution in the interpreter even if the + *	limit itself is theoretically exceeded.   *   *----------------------------------------------------------------------   */ -static void -SlaveRecordDeleteProc(clientData, interp) -    ClientData	clientData;		/* Slave record for deleted interp. */ -    Tcl_Interp *interp;			/* Interpreter being deleted. */ +void +Tcl_LimitSetCommands( +    Tcl_Interp *interp, +    int commandLimit)  { -    Slave *slavePtr;			/* Interim storage. */ -    Alias *aliasPtr; -    Tcl_HashTable *hTblPtr; -    Tcl_HashEntry *hPtr; -    Tcl_HashSearch hSearch; -     -    slavePtr = (Slave *) clientData; - -    /* -     * In every case that we call SetAssocData on "tclSlaveRecord", -     * slavePtr is not NULL. Otherwise we panic. -     */ +    Interp *iPtr = (Interp *) interp; -    if (slavePtr == NULL) { -	panic("SlaveRecordDeleteProc: NULL slavePtr"); -    } +    iPtr->limit.cmdCount = commandLimit; +    iPtr->limit.exceeded &= ~TCL_LIMIT_COMMANDS; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitGetCommands -- + * + *	Get the number of commands that may be executed in the interpreter + *	before the command-limit is reached. + * + * Results: + *	An upper bound on the number of commands. + * + * Side effects: + *	None. + * + *---------------------------------------------------------------------- + */ -    if (slavePtr->interpCmd != (Tcl_Command) NULL) { -	Command *cmdPtr = (Command *) slavePtr->interpCmd; +int +Tcl_LimitGetCommands( +    Tcl_Interp *interp) +{ +    Interp *iPtr = (Interp *) interp; -	/* -	 * The interpCmd has not been deleted in the master yet,  since -	 * it's callback sets interpCmd to NULL. -	 * -	 * Probably Tcl_DeleteInterp() was called on this interpreter directly, -	 * rather than via "interp delete", or equivalent (deletion of the -	 * command in the master). -	 * -	 * Perform the cleanup done by SlaveObjectDeleteProc() directly, -	 * and turn off the callback now (since we are about to free slavePtr -	 * and this interpreter is going away, while the deletion of commands -	 * in the master may be deferred). -	 */ +    return iPtr->limit.cmdCount; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitSetTime -- + * + *	Set the time limit for an interpreter by copying it from the value + *	pointed to by the timeLimitPtr argument. + * + * Results: + *	None. + * + * Side effects: + *	Also resets whether the time limit was exceeded. This might permit a + *	small amount of further execution in the interpreter even if the limit + *	itself is theoretically exceeded. + * + *---------------------------------------------------------------------- + */ -	Tcl_DeleteHashEntry(slavePtr->slaveEntry); -	cmdPtr->clientData = NULL; -	cmdPtr->deleteProc = NULL; -	cmdPtr->deleteData = NULL; +void +Tcl_LimitSetTime( +    Tcl_Interp *interp, +    Tcl_Time *timeLimitPtr) +{ +    Interp *iPtr = (Interp *) interp; +    Tcl_Time nextMoment; -        Tcl_DeleteCommandFromToken(slavePtr->masterInterp, -	        slavePtr->interpCmd); +    memcpy(&iPtr->limit.time, timeLimitPtr, sizeof(Tcl_Time)); +    if (iPtr->limit.timeEvent != NULL) { +	Tcl_DeleteTimerHandler(iPtr->limit.timeEvent);      } +    nextMoment.sec = timeLimitPtr->sec; +    nextMoment.usec = timeLimitPtr->usec+10; +    if (nextMoment.usec >= 1000000) { +	nextMoment.sec++; +	nextMoment.usec -= 1000000; +    } +    iPtr->limit.timeEvent = TclCreateAbsoluteTimerHandler(&nextMoment, +	    TimeLimitCallback, interp); +    iPtr->limit.exceeded &= ~TCL_LIMIT_TIME; +} + +/* + *---------------------------------------------------------------------- + * + * TimeLimitCallback -- + * + *	Callback that allows time limits to be enforced even when doing a + *	blocking wait for events. + * + * Results: + *	None. + * + * Side effects: + *	May put the interpreter into a state where it can no longer execute + *	commands. May make callbacks into other interpreters. + * + *---------------------------------------------------------------------- + */ -    /* -     * If there are any aliases, delete those now. This removes any -     * dependency on the order of deletion between commands and the -     * slave record. -     */ - -    hTblPtr = (Tcl_HashTable *) &(slavePtr->aliasTable); -    for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch); -             hPtr != (Tcl_HashEntry *) NULL; -             hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch)) { -        aliasPtr = (Alias *) Tcl_GetHashValue(hPtr); +static void +TimeLimitCallback( +    ClientData clientData) +{ +    Tcl_Interp *interp = clientData; +    Interp *iPtr = clientData; +    int code; -        /* -         * The call to Tcl_DeleteCommand will release the storage -         * occupied by the hash entry and the alias record. -         */ +    Tcl_Preserve(interp); +    iPtr->limit.timeEvent = NULL; -        Tcl_DeleteCommandFromToken(interp, aliasPtr->slaveCmd); -    } -              /* -     * Finally dispose of the hash table and the slave record. +     * Must reset the granularity ticker here to force an immediate full +     * check. This is OK because we're swallowing the cost in the overall cost +     * of the event loop. [Bug 2891362]       */ -    Tcl_DeleteHashTable(hTblPtr); -    ckfree((char *) slavePtr);     +    iPtr->limit.granularityTicker = 0; + +    code = Tcl_LimitCheck(interp); +    if (code != TCL_OK) { +	Tcl_AddErrorInfo(interp, "\n    (while waiting for event)"); +	Tcl_BackgroundException(interp, code); +    } +    Tcl_Release(interp);  }  /*   *----------------------------------------------------------------------   * - * TclInterpInit -- + * Tcl_LimitGetTime --   * - *	Initializes the invoking interpreter for using the "interp" - *	facility. This is called from inside Tcl_Init. + *	Get the current time limit.   *   * Results: - *	None. + *	The time limit (by it being copied into the variable pointed to by the + *	timeLimitPtr).   *   * Side effects: - *	Adds the "interp" command to an interpreter and initializes several - *	records in the associated data of the invoking interpreter. + *	None.   *   *----------------------------------------------------------------------   */ -int -TclInterpInit(interp) -    Tcl_Interp *interp;			/* Interpreter to initialize. */ +void +Tcl_LimitGetTime( +    Tcl_Interp *interp, +    Tcl_Time *timeLimitPtr)  { -    Master *masterPtr;			/* Its Master record. */ -    Slave *slavePtr;			/* And its slave record. */ - -    masterPtr = (Master *) ckalloc((unsigned) sizeof(Master)); - -    Tcl_InitHashTable(&(masterPtr->slaveTable), TCL_STRING_KEYS); -    Tcl_InitHashTable(&(masterPtr->targetTable), TCL_ONE_WORD_KEYS); - -    (void) Tcl_SetAssocData(interp, "tclMasterRecord", MasterRecordDeleteProc, -            (ClientData) masterPtr); +    Interp *iPtr = (Interp *) interp; -    slavePtr = (Slave *) ckalloc((unsigned) sizeof(Slave)); +    memcpy(timeLimitPtr, &iPtr->limit.time, sizeof(Tcl_Time)); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_LimitSetGranularity -- + * + *	Set the granularity divisor (which must be positive) for a particular + *	limit. + * + * Results: + *	None. + * + * Side effects: + *	The granularity is updated. + * + *---------------------------------------------------------------------- + */ -    slavePtr->masterInterp = (Tcl_Interp *) NULL; -    slavePtr->slaveEntry = (Tcl_HashEntry *) NULL; -    slavePtr->slaveInterp = interp; -    slavePtr->interpCmd = (Tcl_Command) NULL; -    Tcl_InitHashTable(&(slavePtr->aliasTable), TCL_STRING_KEYS); +void +Tcl_LimitSetGranularity( +    Tcl_Interp *interp, +    int type, +    int granularity) +{ +    Interp *iPtr = (Interp *) interp; +    if (granularity < 1) { +	Tcl_Panic("limit granularity must be positive"); +    } -    (void) Tcl_SetAssocData(interp, "tclSlaveRecord", SlaveRecordDeleteProc, -            (ClientData) slavePtr); -     -    return TCL_OK; +    switch (type) { +    case TCL_LIMIT_COMMANDS: +	iPtr->limit.cmdGranularity = granularity; +	return; +    case TCL_LIMIT_TIME: +	iPtr->limit.timeGranularity = granularity; +	return; +    } +    Tcl_Panic("unknown type of resource limit");  }  /*   *----------------------------------------------------------------------   * - * Tcl_IsSafe -- + * Tcl_LimitGetGranularity --   * - *	Determines whether an interpreter is safe + *	Get the granularity divisor for a particular limit.   *   * Results: - *	1 if it is safe, 0 if it is not. + *	The granularity divisor for the given limit.   *   * Side effects:   *	None. @@ -3492,344 +4118,733 @@ TclInterpInit(interp)   */  int -Tcl_IsSafe(interp) -    Tcl_Interp *interp;		/* Is this interpreter "safe" ? */ +Tcl_LimitGetGranularity( +    Tcl_Interp *interp, +    int type)  { -    Interp *iPtr; +    Interp *iPtr = (Interp *) interp; -    if (interp == (Tcl_Interp *) NULL) { -        return 0; +    switch (type) { +    case TCL_LIMIT_COMMANDS: +	return iPtr->limit.cmdGranularity; +    case TCL_LIMIT_TIME: +	return iPtr->limit.timeGranularity;      } -    iPtr = (Interp *) interp; - -    return ( (iPtr->flags) & SAFE_INTERP ) ? 1 : 0 ; +    Tcl_Panic("unknown type of resource limit"); +    return -1; /* NOT REACHED */  }  /*   *----------------------------------------------------------------------   * - * Tcl_CreateSlave -- + * DeleteScriptLimitCallback --   * - *	Creates a slave interpreter. The slavePath argument denotes the - *	name of the new slave relative to the current interpreter; the - *	slave is a direct descendant of the one-before-last component of - *	the path, e.g. it is a descendant of the current interpreter if - *	the slavePath argument contains only one component. Optionally makes - *	the slave interpreter safe. + *	Callback for when a script limit (a limit callback implemented as a + *	Tcl script in a master interpreter, as set up from Tcl) is deleted.   *   * Results: - *	Returns the interpreter structure created, or NULL if an error - *	occurred. + *	None.   *   * Side effects: - *	Creates a new interpreter and a new interpreter object command in - *	the interpreter indicated by the slavePath argument. + *	The reference to the script callback from the controlling interpreter + *	is removed.   *   *----------------------------------------------------------------------   */ -Tcl_Interp * -Tcl_CreateSlave(interp, slavePath, isSafe) -    Tcl_Interp *interp;		/* Interpreter to start search at. */ -    char *slavePath;		/* Name of slave to create. */ -    int isSafe;			/* Should new slave be "safe" ? */ +static void +DeleteScriptLimitCallback( +    ClientData clientData)  { -    Master *masterPtr;			/* Master record for same. */ +    ScriptLimitCallback *limitCBPtr = clientData; -    if ((interp == (Tcl_Interp *) NULL) || (slavePath == (char *) NULL)) { -        return NULL; +    Tcl_DecrRefCount(limitCBPtr->scriptObj); +    if (limitCBPtr->entryPtr != NULL) { +	Tcl_DeleteHashEntry(limitCBPtr->entryPtr);      } -    masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", -            NULL);  -    if (masterPtr == (Master *) NULL) { -        panic("CreatSlave: could not find master record"); -    } -    return CreateSlave(interp, masterPtr, slavePath, isSafe); +    ckfree(limitCBPtr);  }  /*   *----------------------------------------------------------------------   * - * Tcl_GetSlave -- + * CallScriptLimitCallback --   * - *	Finds a slave interpreter by its path name. + *	Invoke a script limit callback. Used to implement limit callbacks set + *	at the Tcl level on child interpreters.   *   * Results: - *	Returns a Tcl_Interp * for the named interpreter or NULL if not - *	found. + *	None.   *   * Side effects: - *	None. + *	Depends on the callback script. Errors are reported as background + *	errors.   *   *----------------------------------------------------------------------   */ -Tcl_Interp * -Tcl_GetSlave(interp, slavePath) -    Tcl_Interp *interp;		/* Interpreter to start search from. */ -    char *slavePath;		/* Path of slave to find. */ +static void +CallScriptLimitCallback( +    ClientData clientData, +    Tcl_Interp *interp)		/* Interpreter which failed the limit */  { -    Master *masterPtr;		/* Interim storage for Master record. */ +    ScriptLimitCallback *limitCBPtr = clientData; +    int code; -    if ((interp == (Tcl_Interp *) NULL) || (slavePath == (char *) NULL)) { -        return NULL; +    if (Tcl_InterpDeleted(limitCBPtr->interp)) { +	return;      } -    masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL); -    if (masterPtr == (Master *) NULL) { -        panic("Tcl_GetSlave: could not find master record"); +    Tcl_Preserve(limitCBPtr->interp); +    code = Tcl_EvalObjEx(limitCBPtr->interp, limitCBPtr->scriptObj, +	    TCL_EVAL_GLOBAL); +    if (code != TCL_OK && !Tcl_InterpDeleted(limitCBPtr->interp)) { +	Tcl_BackgroundException(limitCBPtr->interp, code);      } -    return GetInterp(interp, masterPtr, slavePath, NULL); +    Tcl_Release(limitCBPtr->interp);  }  /*   *----------------------------------------------------------------------   * - * Tcl_GetMaster -- + * SetScriptLimitCallback --   * - *	Finds the master interpreter of a slave interpreter. + *	Install (or remove, if scriptObj is NULL) a limit callback script that + *	is called when the target interpreter exceeds the type of limit + *	specified. Each interpreter may only have one callback set on another + *	interpreter through this mechanism (though as many interpreters may be + *	limited as the programmer chooses overall).   *   * Results: - *	Returns a Tcl_Interp * for the master interpreter or NULL if none. + *	None.   *   * Side effects: - *	None. + *	A limit callback implemented as an invokation of a Tcl script in + *	another interpreter is either installed or removed.   *   *----------------------------------------------------------------------   */ -Tcl_Interp * -Tcl_GetMaster(interp) -    Tcl_Interp *interp;		/* Get the master of this interpreter. */ +static void +SetScriptLimitCallback( +    Tcl_Interp *interp, +    int type, +    Tcl_Interp *targetInterp, +    Tcl_Obj *scriptObj)  { -    Slave *slavePtr;		/* Slave record of this interpreter. */ +    ScriptLimitCallback *limitCBPtr; +    Tcl_HashEntry *hashPtr; +    int isNew; +    ScriptLimitCallbackKey key; +    Interp *iPtr = (Interp *) interp; + +    if (interp == targetInterp) { +	Tcl_Panic("installing limit callback to the limited interpreter"); +    } + +    key.interp = targetInterp; +    key.type = type; -    if (interp == (Tcl_Interp *) NULL) { -        return NULL; +    if (scriptObj == NULL) { +	hashPtr = Tcl_FindHashEntry(&iPtr->limit.callbacks, (char *) &key); +	if (hashPtr != NULL) { +	    Tcl_LimitRemoveHandler(targetInterp, type, CallScriptLimitCallback, +		    Tcl_GetHashValue(hashPtr)); +	} +	return;      } -    slavePtr = (Slave *) Tcl_GetAssocData(interp, "tclSlaveRecord", NULL); -    if (slavePtr == (Slave *) NULL) { -        return NULL; + +    hashPtr = Tcl_CreateHashEntry(&iPtr->limit.callbacks, &key, +	    &isNew); +    if (!isNew) { +	limitCBPtr = Tcl_GetHashValue(hashPtr); +	limitCBPtr->entryPtr = NULL; +	Tcl_LimitRemoveHandler(targetInterp, type, CallScriptLimitCallback, +		limitCBPtr);      } -    return slavePtr->masterInterp; + +    limitCBPtr = ckalloc(sizeof(ScriptLimitCallback)); +    limitCBPtr->interp = interp; +    limitCBPtr->scriptObj = scriptObj; +    limitCBPtr->entryPtr = hashPtr; +    limitCBPtr->type = type; +    Tcl_IncrRefCount(scriptObj); + +    Tcl_LimitAddHandler(targetInterp, type, CallScriptLimitCallback, +	    limitCBPtr, DeleteScriptLimitCallback); +    Tcl_SetHashValue(hashPtr, limitCBPtr);  }  /*   *----------------------------------------------------------------------   * - * Tcl_CreateAlias -- + * TclRemoveScriptLimitCallbacks --   * - *	Creates an alias between two interpreters. + *	Remove all script-implemented limit callbacks that make calls back + *	into the given interpreter. This invoked as part of deleting an + *	interpreter.   *   * Results: - *	A standard Tcl result. + *	None.   *   * Side effects: - *	Creates a new alias, manipulates the result field of slaveInterp. + *	The script limit callbacks are removed or marked for later removal.   *   *----------------------------------------------------------------------   */ -int -Tcl_CreateAlias(slaveInterp, slaveCmd, targetInterp, targetCmd, argc, argv) -    Tcl_Interp *slaveInterp;		/* Interpreter for source command. */ -    char *slaveCmd;			/* Command to install in slave. */ -    Tcl_Interp *targetInterp;		/* Interpreter for target command. */ -    char *targetCmd;			/* Name of target command. */ -    int argc;				/* How many additional arguments? */ -    char **argv;			/* These are the additional args. */ -{ -    Master *masterPtr;			/* Master record for target interp. */ -    Tcl_Obj **objv; -    int i; -    int result; -     -    if ((slaveInterp == (Tcl_Interp *) NULL) || -            (targetInterp == (Tcl_Interp *) NULL) || -            (slaveCmd == (char *) NULL) || -            (targetCmd == (char *) NULL)) { -        return TCL_ERROR; -    } -    masterPtr = (Master *) Tcl_GetAssocData(targetInterp, "tclMasterRecord", -            NULL); -    if (masterPtr == (Master *) NULL) { -        panic("Tcl_CreateAlias: could not find master record"); -    } -    objv = (Tcl_Obj **) ckalloc((unsigned) sizeof(Tcl_Obj *) * argc); -    for (i = 0; i < argc; i++) { -        objv[i] = Tcl_NewStringObj(argv[i], -1); -        Tcl_IncrRefCount(objv[i]); -    } -     -    result = AliasCreationHelper(slaveInterp, slaveInterp, targetInterp, -            masterPtr, slaveCmd, targetCmd, argc, objv); +void +TclRemoveScriptLimitCallbacks( +    Tcl_Interp *interp) +{ +    Interp *iPtr = (Interp *) interp; +    Tcl_HashEntry *hashPtr; +    Tcl_HashSearch search; +    ScriptLimitCallbackKey *keyPtr; + +    hashPtr = Tcl_FirstHashEntry(&iPtr->limit.callbacks, &search); +    while (hashPtr != NULL) { +	keyPtr = (ScriptLimitCallbackKey *) +		Tcl_GetHashKey(&iPtr->limit.callbacks, hashPtr); +	Tcl_LimitRemoveHandler(keyPtr->interp, keyPtr->type, +		CallScriptLimitCallback, Tcl_GetHashValue(hashPtr)); +	hashPtr = Tcl_NextHashEntry(&search); +    } +    Tcl_DeleteHashTable(&iPtr->limit.callbacks); +} + +/* + *---------------------------------------------------------------------- + * + * TclInitLimitSupport -- + * + *	Initialise all the parts of the interpreter relating to resource limit + *	management. This allows an interpreter to both have limits set upon + *	itself and set limits upon other interpreters. + * + * Results: + *	None. + * + * Side effects: + *	The resource limit subsystem is initialised for the interpreter. + * + *---------------------------------------------------------------------- + */ -    ckfree((char *) objv); +void +TclInitLimitSupport( +    Tcl_Interp *interp) +{ +    Interp *iPtr = (Interp *) interp; -    return result; +    iPtr->limit.active = 0; +    iPtr->limit.granularityTicker = 0; +    iPtr->limit.exceeded = 0; +    iPtr->limit.cmdCount = 0; +    iPtr->limit.cmdHandlers = NULL; +    iPtr->limit.cmdGranularity = 1; +    memset(&iPtr->limit.time, 0, sizeof(Tcl_Time)); +    iPtr->limit.timeHandlers = NULL; +    iPtr->limit.timeEvent = NULL; +    iPtr->limit.timeGranularity = 10; +    Tcl_InitHashTable(&iPtr->limit.callbacks, +	    sizeof(ScriptLimitCallbackKey)/sizeof(int));  }  /*   *----------------------------------------------------------------------   * - * Tcl_CreateAliasObj -- + * InheritLimitsFromMaster --   * - *	Object version: Creates an alias between two interpreters. + *	Derive the interpreter limit configuration for a slave interpreter + *	from the limit config for the master.   *   * Results: - *	A standard Tcl result. + *	None.   *   * Side effects: - *	Creates a new alias. + *	The slave interpreter limits are set so that if the master has a + *	limit, it may not exceed it by handing off work to slave interpreters. + *	Note that this does not transfer limit callbacks from the master to + *	the slave.   *   *----------------------------------------------------------------------   */ -int -Tcl_CreateAliasObj(slaveInterp, slaveCmd, targetInterp, targetCmd, objc, objv) -    Tcl_Interp *slaveInterp;		/* Interpreter for source command. */ -    char *slaveCmd;			/* Command to install in slave. */ -    Tcl_Interp *targetInterp;		/* Interpreter for target command. */ -    char *targetCmd;			/* Name of target command. */ -    int objc;				/* How many additional arguments? */ -    Tcl_Obj *CONST objv[];		/* Argument vector. */ +static void +InheritLimitsFromMaster( +    Tcl_Interp *slaveInterp, +    Tcl_Interp *masterInterp)  { -    Master *masterPtr;			/* Master record for target interp. */ +    Interp *slavePtr = (Interp *) slaveInterp; +    Interp *masterPtr = (Interp *) masterInterp; -    if ((slaveInterp == (Tcl_Interp *) NULL) || -            (targetInterp == (Tcl_Interp *) NULL) || -            (slaveCmd == (char *) NULL) || -            (targetCmd == (char *) NULL)) { -        return TCL_ERROR; +    if (masterPtr->limit.active & TCL_LIMIT_COMMANDS) { +	slavePtr->limit.active |= TCL_LIMIT_COMMANDS; +	slavePtr->limit.cmdCount = 0; +	slavePtr->limit.cmdGranularity = masterPtr->limit.cmdGranularity;      } -    masterPtr = (Master *) Tcl_GetAssocData(targetInterp, "tclMasterRecord", -            NULL); -    if (masterPtr == (Master *) NULL) { -        panic("Tcl_CreateAlias: could not find master record"); +    if (masterPtr->limit.active & TCL_LIMIT_TIME) { +	slavePtr->limit.active |= TCL_LIMIT_TIME; +	memcpy(&slavePtr->limit.time, &masterPtr->limit.time, +		sizeof(Tcl_Time)); +	slavePtr->limit.timeGranularity = masterPtr->limit.timeGranularity;      } -    return AliasCreationHelper(slaveInterp, slaveInterp, targetInterp, -            masterPtr, slaveCmd, targetCmd, objc, objv);  }  /*   *----------------------------------------------------------------------   * - * Tcl_GetAlias -- + * SlaveCommandLimitCmd --   * - *	Gets information about an alias. + *	Implementation of the [interp limit $i commands] and [$i limit + *	commands] subcommands. See the interp manual page for a full + *	description.   *   * Results: - *	A standard Tcl result.  + *	A standard Tcl result.   *   * Side effects: - *	None. + *	Depends on the arguments.   *   *----------------------------------------------------------------------   */ -int -Tcl_GetAlias(interp, aliasName, targetInterpPtr, targetNamePtr, argcPtr, -        argvPtr) -    Tcl_Interp *interp;			/* Interp to start search from. */ -    char *aliasName;			/* Name of alias to find. */ -    Tcl_Interp **targetInterpPtr;	/* (Return) target interpreter. */ -    char **targetNamePtr;		/* (Return) name of target command. */ -    int *argcPtr;			/* (Return) count of addnl args. */ -    char ***argvPtr;			/* (Return) additional arguments. */ -{ -    Slave *slavePtr;			/* Slave record for slave interp. */ -    Tcl_HashEntry *hPtr;		/* Search element. */ -    Alias *aliasPtr;			/* Storage for alias found. */ -    int len; -    int i; +static int +SlaveCommandLimitCmd( +    Tcl_Interp *interp,		/* Current interpreter. */ +    Tcl_Interp *slaveInterp,	/* Interpreter being adjusted. */ +    int consumedObjc,		/* Number of args already parsed. */ +    int objc,			/* Total number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument objects. */ +{ +    static const char *const options[] = { +	"-command", "-granularity", "-value", NULL +    }; +    enum Options { +	OPT_CMD, OPT_GRAN, OPT_VAL +    }; +    Interp *iPtr = (Interp *) interp; +    int index; +    ScriptLimitCallbackKey key; +    ScriptLimitCallback *limitCBPtr; +    Tcl_HashEntry *hPtr; -    if ((interp == (Tcl_Interp *) NULL) || (aliasName == (char *) NULL)) { -        return TCL_ERROR; -    } -    slavePtr = (Slave *) Tcl_GetAssocData(interp, "tclSlaveRecord", NULL); -    if (slavePtr == (Slave *) NULL) { -        panic("Tcl_GetAlias: could not find slave record"); -    } -    hPtr = Tcl_FindHashEntry(&(slavePtr->aliasTable), aliasName); -    if (hPtr == (Tcl_HashEntry *) NULL) { -        Tcl_AppendResult(interp, "alias \"", aliasName, "\" not found", -                (char *) NULL); -        return TCL_ERROR; -    } -    aliasPtr = (Alias *) Tcl_GetHashValue(hPtr); -    if (targetInterpPtr != (Tcl_Interp **) NULL) { -        *targetInterpPtr = aliasPtr->targetInterp; -    } -    if (targetNamePtr != (char **) NULL) { -        *targetNamePtr = aliasPtr->targetName; -    } -    if (argcPtr != (int *) NULL) { -        *argcPtr = aliasPtr->objc; +    /* +     * First, ensure that we are not reading or writing the calling +     * interpreter's limits; it may only manipulate its children. Note that +     * the low level API enforces this with Tcl_Panic, which we want to +     * avoid. [Bug 3398794] +     */ + +    if (interp == slaveInterp) { +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +		"limits on current interpreter inaccessible", -1)); +	Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", "SELF", NULL); +	return TCL_ERROR;      } -    if (argvPtr != (char ***) NULL) { -        *argvPtr = (char **) ckalloc((unsigned) sizeof(char *) * -                aliasPtr->objc); -        for (i = 0; i < aliasPtr->objc; i++) { -            *argvPtr[i] = Tcl_GetStringFromObj(aliasPtr->objv[i], &len); -        } + +    if (objc == consumedObjc) { +	Tcl_Obj *dictPtr; + +	TclNewObj(dictPtr); +	key.interp = slaveInterp; +	key.type = TCL_LIMIT_COMMANDS; +	hPtr = Tcl_FindHashEntry(&iPtr->limit.callbacks, (char *) &key); +	if (hPtr != NULL) { +	    limitCBPtr = Tcl_GetHashValue(hPtr); +	    if (limitCBPtr != NULL && limitCBPtr->scriptObj != NULL) { +		Tcl_DictObjPut(NULL, dictPtr, Tcl_NewStringObj(options[0], -1), +			limitCBPtr->scriptObj); +	    } else { +		goto putEmptyCommandInDict; +	    } +	} else { +	    Tcl_Obj *empty; + +	putEmptyCommandInDict: +	    TclNewObj(empty); +	    Tcl_DictObjPut(NULL, dictPtr, +		    Tcl_NewStringObj(options[0], -1), empty); +	} +	Tcl_DictObjPut(NULL, dictPtr, Tcl_NewStringObj(options[1], -1), +		Tcl_NewIntObj(Tcl_LimitGetGranularity(slaveInterp, +		TCL_LIMIT_COMMANDS))); + +	if (Tcl_LimitTypeEnabled(slaveInterp, TCL_LIMIT_COMMANDS)) { +	    Tcl_DictObjPut(NULL, dictPtr, Tcl_NewStringObj(options[2], -1), +		    Tcl_NewIntObj(Tcl_LimitGetCommands(slaveInterp))); +	} else { +	    Tcl_Obj *empty; + +	    TclNewObj(empty); +	    Tcl_DictObjPut(NULL, dictPtr, +		    Tcl_NewStringObj(options[2], -1), empty); +	} +	Tcl_SetObjResult(interp, dictPtr); +	return TCL_OK; +    } else if (objc == consumedObjc+1) { +	if (Tcl_GetIndexFromObj(interp, objv[consumedObjc], options, "option", +		0, &index) != TCL_OK) { +	    return TCL_ERROR; +	} +	switch ((enum Options) index) { +	case OPT_CMD: +	    key.interp = slaveInterp; +	    key.type = TCL_LIMIT_COMMANDS; +	    hPtr = Tcl_FindHashEntry(&iPtr->limit.callbacks, (char *) &key); +	    if (hPtr != NULL) { +		limitCBPtr = Tcl_GetHashValue(hPtr); +		if (limitCBPtr != NULL && limitCBPtr->scriptObj != NULL) { +		    Tcl_SetObjResult(interp, limitCBPtr->scriptObj); +		} +	    } +	    break; +	case OPT_GRAN: +	    Tcl_SetObjResult(interp, Tcl_NewIntObj( +		    Tcl_LimitGetGranularity(slaveInterp, TCL_LIMIT_COMMANDS))); +	    break; +	case OPT_VAL: +	    if (Tcl_LimitTypeEnabled(slaveInterp, TCL_LIMIT_COMMANDS)) { +		Tcl_SetObjResult(interp, +			Tcl_NewIntObj(Tcl_LimitGetCommands(slaveInterp))); +	    } +	    break; +	} +	return TCL_OK; +    } else if ((objc-consumedObjc) & 1 /* isOdd(objc-consumedObjc) */) { +	Tcl_WrongNumArgs(interp, consumedObjc, objv, "?-option value ...?"); +	return TCL_ERROR; +    } else { +	int i, scriptLen = 0, limitLen = 0; +	Tcl_Obj *scriptObj = NULL, *granObj = NULL, *limitObj = NULL; +	int gran = 0, limit = 0; + +	for (i=consumedObjc ; i<objc ; i+=2) { +	    if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, +		    &index) != TCL_OK) { +		return TCL_ERROR; +	    } +	    switch ((enum Options) index) { +	    case OPT_CMD: +		scriptObj = objv[i+1]; +		(void) Tcl_GetStringFromObj(objv[i+1], &scriptLen); +		break; +	    case OPT_GRAN: +		granObj = objv[i+1]; +		if (TclGetIntFromObj(interp, objv[i+1], &gran) != TCL_OK) { +		    return TCL_ERROR; +		} +		if (gran < 1) { +		    Tcl_SetObjResult(interp, Tcl_NewStringObj( +			    "granularity must be at least 1", -1)); +		    Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", +			    "BADVALUE", NULL); +		    return TCL_ERROR; +		} +		break; +	    case OPT_VAL: +		limitObj = objv[i+1]; +		(void) Tcl_GetStringFromObj(objv[i+1], &limitLen); +		if (limitLen == 0) { +		    break; +		} +		if (TclGetIntFromObj(interp, objv[i+1], &limit) != TCL_OK) { +		    return TCL_ERROR; +		} +		if (limit < 0) { +		    Tcl_SetObjResult(interp, Tcl_NewStringObj( +			    "command limit value must be at least 0", -1)); +		    Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", +			    "BADVALUE", NULL); +		    return TCL_ERROR; +		} +		break; +	    } +	} +	if (scriptObj != NULL) { +	    SetScriptLimitCallback(interp, TCL_LIMIT_COMMANDS, slaveInterp, +		    (scriptLen > 0 ? scriptObj : NULL)); +	} +	if (granObj != NULL) { +	    Tcl_LimitSetGranularity(slaveInterp, TCL_LIMIT_COMMANDS, gran); +	} +	if (limitObj != NULL) { +	    if (limitLen > 0) { +		Tcl_LimitSetCommands(slaveInterp, limit); +		Tcl_LimitTypeSet(slaveInterp, TCL_LIMIT_COMMANDS); +	    } else { +		Tcl_LimitTypeReset(slaveInterp, TCL_LIMIT_COMMANDS); +	    } +	} +	return TCL_OK;      } -    return TCL_OK;  }  /*   *----------------------------------------------------------------------   * - * Tcl_ObjGetAlias -- + * SlaveTimeLimitCmd --   * - *	Object version: Gets information about an alias. + *	Implementation of the [interp limit $i time] and [$i limit time] + *	subcommands. See the interp manual page for a full description.   *   * Results:   *	A standard Tcl result.   *   * Side effects: - *	None. + *	Depends on the arguments.   *   *----------------------------------------------------------------------   */ -int -Tcl_GetAliasObj(interp, aliasName, targetInterpPtr, targetNamePtr, objcPtr, -        objvPtr) -    Tcl_Interp *interp;			/* Interp to start search from. */ -    char *aliasName;			/* Name of alias to find. */ -    Tcl_Interp **targetInterpPtr;	/* (Return) target interpreter. */ -    char **targetNamePtr;		/* (Return) name of target command. */ -    int *objcPtr;			/* (Return) count of addnl args. */ -    Tcl_Obj ***objvPtr;			/* (Return) additional args. */ +static int +SlaveTimeLimitCmd( +    Tcl_Interp *interp,			/* Current interpreter. */ +    Tcl_Interp *slaveInterp,		/* Interpreter being adjusted. */ +    int consumedObjc,			/* Number of args already parsed. */ +    int objc,				/* Total number of arguments. */ +    Tcl_Obj *const objv[])		/* Argument objects. */  { -    Slave *slavePtr;			/* Slave record for slave interp. */ -    Tcl_HashEntry *hPtr;		/* Search element. */ -    Alias *aliasPtr;			/* Storage for alias found. */ +    static const char *const options[] = { +	"-command", "-granularity", "-milliseconds", "-seconds", NULL +    }; +    enum Options { +	OPT_CMD, OPT_GRAN, OPT_MILLI, OPT_SEC +    }; +    Interp *iPtr = (Interp *) interp; +    int index; +    ScriptLimitCallbackKey key; +    ScriptLimitCallback *limitCBPtr; +    Tcl_HashEntry *hPtr; -    if ((interp == (Tcl_Interp *) NULL) || (aliasName == (char *) NULL)) { -        return TCL_ERROR; -    } -    slavePtr = (Slave *) Tcl_GetAssocData(interp, "tclSlaveRecord", NULL); -    if (slavePtr == (Slave *) NULL) { -        panic("Tcl_GetAlias: could not find slave record"); -    } -    hPtr = Tcl_FindHashEntry(&(slavePtr->aliasTable), aliasName); -    if (hPtr == (Tcl_HashEntry *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "alias \"", aliasName, "\" not found", (char *) NULL); -        return TCL_ERROR; -    } -    aliasPtr = (Alias *) Tcl_GetHashValue(hPtr); -    if (targetInterpPtr != (Tcl_Interp **) NULL) { -        *targetInterpPtr = aliasPtr->targetInterp; -    } -    if (targetNamePtr != (char **) NULL) { -        *targetNamePtr = aliasPtr->targetName; -    } -    if (objcPtr != (int *) NULL) { -        *objcPtr = aliasPtr->objc; +    /* +     * First, ensure that we are not reading or writing the calling +     * interpreter's limits; it may only manipulate its children. Note that +     * the low level API enforces this with Tcl_Panic, which we want to +     * avoid. [Bug 3398794] +     */ + +    if (interp == slaveInterp) { +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +		"limits on current interpreter inaccessible", -1)); +	Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", "SELF", NULL); +	return TCL_ERROR;      } -    if (objvPtr != (Tcl_Obj ***) NULL) { -        *objvPtr = aliasPtr->objv; + +    if (objc == consumedObjc) { +	Tcl_Obj *dictPtr; + +	TclNewObj(dictPtr); +	key.interp = slaveInterp; +	key.type = TCL_LIMIT_TIME; +	hPtr = Tcl_FindHashEntry(&iPtr->limit.callbacks, (char *) &key); +	if (hPtr != NULL) { +	    limitCBPtr = Tcl_GetHashValue(hPtr); +	    if (limitCBPtr != NULL && limitCBPtr->scriptObj != NULL) { +		Tcl_DictObjPut(NULL, dictPtr, Tcl_NewStringObj(options[0], -1), +			limitCBPtr->scriptObj); +	    } else { +		goto putEmptyCommandInDict; +	    } +	} else { +	    Tcl_Obj *empty; +	putEmptyCommandInDict: +	    TclNewObj(empty); +	    Tcl_DictObjPut(NULL, dictPtr, +		    Tcl_NewStringObj(options[0], -1), empty); +	} +	Tcl_DictObjPut(NULL, dictPtr, Tcl_NewStringObj(options[1], -1), +		Tcl_NewIntObj(Tcl_LimitGetGranularity(slaveInterp, +		TCL_LIMIT_TIME))); + +	if (Tcl_LimitTypeEnabled(slaveInterp, TCL_LIMIT_TIME)) { +	    Tcl_Time limitMoment; + +	    Tcl_LimitGetTime(slaveInterp, &limitMoment); +	    Tcl_DictObjPut(NULL, dictPtr, Tcl_NewStringObj(options[2], -1), +		    Tcl_NewLongObj(limitMoment.usec/1000)); +	    Tcl_DictObjPut(NULL, dictPtr, Tcl_NewStringObj(options[3], -1), +		    Tcl_NewLongObj(limitMoment.sec)); +	} else { +	    Tcl_Obj *empty; + +	    TclNewObj(empty); +	    Tcl_DictObjPut(NULL, dictPtr, +		    Tcl_NewStringObj(options[2], -1), empty); +	    Tcl_DictObjPut(NULL, dictPtr, +		    Tcl_NewStringObj(options[3], -1), empty); +	} +	Tcl_SetObjResult(interp, dictPtr); +	return TCL_OK; +    } else if (objc == consumedObjc+1) { +	if (Tcl_GetIndexFromObj(interp, objv[consumedObjc], options, "option", +		0, &index) != TCL_OK) { +	    return TCL_ERROR; +	} +	switch ((enum Options) index) { +	case OPT_CMD: +	    key.interp = slaveInterp; +	    key.type = TCL_LIMIT_TIME; +	    hPtr = Tcl_FindHashEntry(&iPtr->limit.callbacks, (char *) &key); +	    if (hPtr != NULL) { +		limitCBPtr = Tcl_GetHashValue(hPtr); +		if (limitCBPtr != NULL && limitCBPtr->scriptObj != NULL) { +		    Tcl_SetObjResult(interp, limitCBPtr->scriptObj); +		} +	    } +	    break; +	case OPT_GRAN: +	    Tcl_SetObjResult(interp, Tcl_NewIntObj( +		    Tcl_LimitGetGranularity(slaveInterp, TCL_LIMIT_TIME))); +	    break; +	case OPT_MILLI: +	    if (Tcl_LimitTypeEnabled(slaveInterp, TCL_LIMIT_TIME)) { +		Tcl_Time limitMoment; + +		Tcl_LimitGetTime(slaveInterp, &limitMoment); +		Tcl_SetObjResult(interp, +			Tcl_NewLongObj(limitMoment.usec/1000)); +	    } +	    break; +	case OPT_SEC: +	    if (Tcl_LimitTypeEnabled(slaveInterp, TCL_LIMIT_TIME)) { +		Tcl_Time limitMoment; + +		Tcl_LimitGetTime(slaveInterp, &limitMoment); +		Tcl_SetObjResult(interp, Tcl_NewLongObj(limitMoment.sec)); +	    } +	    break; +	} +	return TCL_OK; +    } else if ((objc-consumedObjc) & 1 /* isOdd(objc-consumedObjc) */) { +	Tcl_WrongNumArgs(interp, consumedObjc, objv, "?-option value ...?"); +	return TCL_ERROR; +    } else { +	int i, scriptLen = 0, milliLen = 0, secLen = 0; +	Tcl_Obj *scriptObj = NULL, *granObj = NULL; +	Tcl_Obj *milliObj = NULL, *secObj = NULL; +	int gran = 0; +	Tcl_Time limitMoment; +	int tmp; + +	Tcl_LimitGetTime(slaveInterp, &limitMoment); +	for (i=consumedObjc ; i<objc ; i+=2) { +	    if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, +		    &index) != TCL_OK) { +		return TCL_ERROR; +	    } +	    switch ((enum Options) index) { +	    case OPT_CMD: +		scriptObj = objv[i+1]; +		(void) Tcl_GetStringFromObj(objv[i+1], &scriptLen); +		break; +	    case OPT_GRAN: +		granObj = objv[i+1]; +		if (TclGetIntFromObj(interp, objv[i+1], &gran) != TCL_OK) { +		    return TCL_ERROR; +		} +		if (gran < 1) { +		    Tcl_SetObjResult(interp, Tcl_NewStringObj( +			    "granularity must be at least 1", -1)); +		    Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", +			    "BADVALUE", NULL); +		    return TCL_ERROR; +		} +		break; +	    case OPT_MILLI: +		milliObj = objv[i+1]; +		(void) Tcl_GetStringFromObj(objv[i+1], &milliLen); +		if (milliLen == 0) { +		    break; +		} +		if (TclGetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK) { +		    return TCL_ERROR; +		} +		if (tmp < 0) { +		    Tcl_SetObjResult(interp, Tcl_NewStringObj( +			    "milliseconds must be at least 0", -1)); +		    Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", +			    "BADVALUE", NULL); +		    return TCL_ERROR; +		} +		limitMoment.usec = ((long) tmp)*1000; +		break; +	    case OPT_SEC: +		secObj = objv[i+1]; +		(void) Tcl_GetStringFromObj(objv[i+1], &secLen); +		if (secLen == 0) { +		    break; +		} +		if (TclGetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK) { +		    return TCL_ERROR; +		} +		if (tmp < 0) { +		    Tcl_SetObjResult(interp, Tcl_NewStringObj( +			    "seconds must be at least 0", -1)); +		    Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", +			    "BADVALUE", NULL); +		    return TCL_ERROR; +		} +		limitMoment.sec = tmp; +		break; +	    } +	} +	if (milliObj != NULL || secObj != NULL) { +	    if (milliObj != NULL) { +		/* +		 * Setting -milliseconds but clearing -seconds, or resetting +		 * -milliseconds but not resetting -seconds? Bad voodoo! +		 */ + +		if (secObj != NULL && secLen == 0 && milliLen > 0) { +		    Tcl_SetObjResult(interp, Tcl_NewStringObj( +			    "may only set -milliseconds if -seconds is not " +			    "also being reset", -1)); +		    Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", +			    "BADUSAGE", NULL); +		    return TCL_ERROR; +		} +		if (milliLen == 0 && (secObj == NULL || secLen > 0)) { +		    Tcl_SetObjResult(interp, Tcl_NewStringObj( +			    "may only reset -milliseconds if -seconds is " +			    "also being reset", -1)); +		    Tcl_SetErrorCode(interp, "TCL", "OPERATION", "INTERP", +			    "BADUSAGE", NULL); +		    return TCL_ERROR; +		} +	    } + +	    if (milliLen > 0 || secLen > 0) { +		/* +		 * Force usec to be in range [0..1000000), possibly +		 * incrementing sec in the process. This makes it much easier +		 * for people to write scripts that do small time increments. +		 */ + +		limitMoment.sec += limitMoment.usec / 1000000; +		limitMoment.usec %= 1000000; + +		Tcl_LimitSetTime(slaveInterp, &limitMoment); +		Tcl_LimitTypeSet(slaveInterp, TCL_LIMIT_TIME); +	    } else { +		Tcl_LimitTypeReset(slaveInterp, TCL_LIMIT_TIME); +	    } +	} +	if (scriptObj != NULL) { +	    SetScriptLimitCallback(interp, TCL_LIMIT_TIME, slaveInterp, +		    (scriptLen > 0 ? scriptObj : NULL)); +	} +	if (granObj != NULL) { +	    Tcl_LimitSetGranularity(slaveInterp, TCL_LIMIT_TIME, gran); +	} +	return TCL_OK;      } -    return TCL_OK;  } + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ | 
