diff options
Diffstat (limited to 'contrib/tcl/generic/tclInterp.c')
-rw-r--r-- | contrib/tcl/generic/tclInterp.c | 3048 |
1 files changed, 2248 insertions, 800 deletions
diff --git a/contrib/tcl/generic/tclInterp.c b/contrib/tcl/generic/tclInterp.c index d2b7f1ae33575..e9ad76a294f3d 100644 --- a/contrib/tcl/generic/tclInterp.c +++ b/contrib/tcl/generic/tclInterp.c @@ -4,12 +4,12 @@ * This file implements the "interp" command which allows creation * and manipulation of Tcl interpreters from within Tcl scripts. * - * Copyright (c) 1995 Sun Microsystems, Inc. + * Copyright (c) 1995-1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * SCCS: @(#) tclInterp.c 1.73 96/06/11 18:14:22 + * SCCS: @(#) tclInterp.c 1.115 97/06/19 18:06:39 */ #include <stdio.h> @@ -17,6 +17,20 @@ #include "tclPort.h" /* + * Tcl script to make an interpreter safe. + */ + +static char makeSafeScript[] = +"if {[info exists env(DISPLAY)]} {\n\ + set ___x___ $env(DISPLAY)\n\ +}\n\ +unset env\n\ +if {[info exists ___x___]} {\n\ + set env(DISPLAY) $___x___\n\ + unset ___x___\n\ +}"; + +/* * Counter for how many aliases were created (global) */ @@ -57,8 +71,8 @@ typedef struct { char *aliasName; /* Name of alias command. */ char *targetName; /* Name of target command in master interp. */ Tcl_Interp *targetInterp; /* Master interpreter. */ - int argc; /* Count of additional args to pass. */ - char **argv; /* Actual additional args to pass. */ + 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 @@ -123,71 +137,112 @@ typedef struct { */ static int AliasCmd _ANSI_ARGS_((ClientData dummy, - Tcl_Interp *currentInterp, int argc, char **argv)); + Tcl_Interp *currentInterp, int objc, + Tcl_Obj *CONST objv[])); static void AliasCmdDeleteProc _ANSI_ARGS_(( ClientData clientData)); -static int AliasHelper _ANSI_ARGS_((Tcl_Interp *curInterp, +static int AliasCreationHelper _ANSI_ARGS_((Tcl_Interp *curInterp, Tcl_Interp *slaveInterp, Tcl_Interp *masterInterp, Master *masterPtr, char *aliasName, - char *targetName, int argc, char **argv)); + char *targetName, int objc, + Tcl_Obj *CONST objv[])); static int CreateInterpObject _ANSI_ARGS_((Tcl_Interp *interp, - int argc, char **argv)); + Master *masterPtr, int objc, + Tcl_Obj *CONST objv[])); static Tcl_Interp *CreateSlave _ANSI_ARGS_((Tcl_Interp *interp, - char *slavePath, int safe)); + 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, - int argc, char **argv)); + Master *masterPtr, int objc, + Tcl_Obj *CONST objv[])); static int DeleteOneInterpObject _ANSI_ARGS_((Tcl_Interp *interp, - char *path)); + 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 MarkTrusted _ANSI_ARGS_((Tcl_Interp *interp)); static void MasterRecordDeleteProc _ANSI_ARGS_(( ClientData clientData, Tcl_Interp *interp)); -static int MakeSafe _ANSI_ARGS_((Tcl_Interp *interp)); static int SlaveAliasHelper _ANSI_ARGS_((Tcl_Interp *interp, - int argc, char **argv)); + 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 argc, char **argv)); + 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)); /* - * These are all the Tcl core commands which are available in a safe - * interpeter: - */ - -static char *TclCommandsToKeep[] = { - "after", "append", "array", - "break", - "case", "catch", "clock", "close", "concat", "continue", - "eof", "error", "eval", "expr", - "fblocked", "fileevent", "flush", "for", "foreach", "format", - "gets", "global", - "history", - "if", "incr", "info", "interp", - "join", - "lappend", "lindex", "linsert", "list", "llength", - "lower", "lrange", "lreplace", "lsearch", "lsort", - "package", "pid", "proc", "puts", - "read", "regexp", "regsub", "rename", "return", - "scan", "seek", "set", "split", "string", "subst", "switch", - "tell", "time", "trace", - "unset", "unsupported0", "update", "uplevel", "upvar", - "vwait", - "while", - NULL}; -static int TclCommandsToKeepCt = - (sizeof (TclCommandsToKeep) / sizeof (char *)) -1 ; - -/* *---------------------------------------------------------------------- * * TclPreventAliasLoop -- @@ -196,40 +251,38 @@ static int TclCommandsToKeepCt = * loop from being formed. * * Results: - * A standard Tcl result. + * A standard Tcl object result. * * Side effects: - * If TCL_ERROR is returned, the function also sets interp->result - * to an error message. + * 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 Tcl_RenameCmd. + * this file) because it is also used from TclRenameCommand. * *---------------------------------------------------------------------- */ int -TclPreventAliasLoop(interp, cmdInterp, cmdName, proc, clientData) +TclPreventAliasLoop(interp, cmdInterp, cmd) Tcl_Interp *interp; /* Interp in which to report errors. */ Tcl_Interp *cmdInterp; /* Interp in which the command is * being defined. */ - char *cmdName; /* Name of Tcl command we are - * attempting to define. */ - Tcl_CmdProc *proc; /* The command procedure for the - * command being created. */ - ClientData clientData; /* The client data associated with the - * command to be created. */ + Tcl_Command cmd; /* Tcl command we are attempting + * to define. */ { + Command *cmdPtr = (Command *) cmd; Alias *aliasPtr, *nextAliasPtr; - Tcl_CmdInfo cmdInfo; + 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 (proc != AliasCmd) { + if (cmdPtr->objProc != AliasCmd) { return TCL_OK; } @@ -239,42 +292,40 @@ TclPreventAliasLoop(interp, cmdInterp, cmdName, proc, clientData) * the chain then we have a loop. */ - aliasPtr = (Alias *) clientData; + 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. - */ - - if ((strcmp(nextAliasPtr->targetName, cmdName) == 0) && - (nextAliasPtr->targetInterp == cmdInterp)) { - Tcl_AppendResult(interp, "cannot define or rename alias \"", - aliasPtr->aliasName, "\": would create a loop", - (char *) NULL); - return TCL_ERROR; - } + * If the target of the next alias in the chain is the same as + * the source alias, we have a loop. + */ - /* - * Otherwise, follow the chain one step further. If the target - * command is undefined then there is no loop. - */ - - if (Tcl_GetCommandInfo(nextAliasPtr->targetInterp, - nextAliasPtr->targetName, &cmdInfo) == 0) { + 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; + } /* - * See if the target command is an alias - if so, follow the - * loop to its target command. Otherwise we do not have a loop. - */ + * 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 (cmdInfo.proc != AliasCmd) { + if (aliasCmdPtr->objProc != AliasCmd) { return TCL_OK; } - nextAliasPtr = (Alias *) cmdInfo.clientData; + nextAliasPtr = (Alias *) aliasCmdPtr->objClientData; } /* NOTREACHED */ @@ -283,67 +334,90 @@ TclPreventAliasLoop(interp, cmdInterp, cmdName, proc, clientData) /* *---------------------------------------------------------------------- * - * MakeSafe -- + * MarkTrusted -- + * + * Mark an interpreter as unsafe (i.e. remove the "safe" mark). + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Removes the "safe" mark from an interpreter. + * + *---------------------------------------------------------------------- + */ + +static int +MarkTrusted(interp) + Tcl_Interp *interp; /* Interpreter to be marked unsafe. */ +{ + Master *masterPtr; /* Master record for interpreter to + * be marked unsafe. */ + + masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", + NULL); + if (masterPtr == (Master *) NULL) { + panic("MarkTrusted: could not find master record"); + } + masterPtr->isSafe = 0; + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_MakeSafe -- * * Makes its argument interpreter contain only functionality that is - * defined to be part of Safe Tcl. + * defined to be part of Safe Tcl. Unsafe commands are hidden, the + * env array is unset, and the standard channels are removed. * * Results: * None. * * Side effects: - * Removes commands from its argument interpreter. + * Hides commands in its argument interpreter, and removes settings + * and channels. * *---------------------------------------------------------------------- */ -static int -MakeSafe(interp) +int +Tcl_MakeSafe(interp) Tcl_Interp *interp; /* Interpreter to be made safe. */ { - char **argv; /* Args for Tcl_Eval. */ - int argc, keep, i, j; /* Loop indices. */ - char *cmdGetGlobalCmds = "info commands"; /* What command to run. */ - char *cmdNoEnv = "unset env"; /* How to get rid of env. */ Master *masterPtr; /* Master record of interp * to be made safe. */ Tcl_Channel chan; /* Channel to remove from * safe interpreter. */ + Tcl_Obj *objPtr; - /* - * Below, Tcl_Eval sets interp->result, so we do not. - */ - - Tcl_ResetResult(interp); - if ((Tcl_Eval(interp, cmdGetGlobalCmds) == TCL_ERROR) || - (Tcl_SplitList(interp, interp->result, &argc, &argv) != TCL_OK)) { - return TCL_ERROR; - } - for (i = 0; i < argc; i++) { - for (keep = 0, j = 0; j < TclCommandsToKeepCt; j++) { - if (strcmp(TclCommandsToKeep[j], argv[i]) == 0) { - keep = 1; - break; - } - } - if (keep == 0) { - (void) Tcl_DeleteCommand(interp, argv[i]); - } - } - ckfree((char *) argv); + TclHideUnsafeCommands(interp); masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL); if (masterPtr == (Master *) NULL) { panic("MakeSafe: could not find master record"); } masterPtr->isSafe = 1; - if (Tcl_Eval(interp, cmdNoEnv) == TCL_ERROR) { + objPtr = Tcl_NewStringObj(makeSafeScript, -1); + Tcl_IncrRefCount(objPtr); + + if (Tcl_EvalObj(interp, objPtr) == TCL_ERROR) { + Tcl_DecrRefCount(objPtr); return TCL_ERROR; } + Tcl_DecrRefCount(objPtr); + /* * 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); @@ -392,7 +466,9 @@ GetInterp(interp, masterPtr, path, masterPtrPtr) int argc, i; /* Loop indices. */ Tcl_Interp *searchInterp; /* Interim storage for interp. to find. */ - if (masterPtrPtr != (Master **) NULL) *masterPtrPtr = masterPtr; + if (masterPtrPtr != (Master **) NULL) { + *masterPtrPtr = masterPtr; + } if (Tcl_SplitList(interp, path, &argc, &argv) != TCL_OK) { return (Tcl_Interp *) NULL; @@ -443,12 +519,12 @@ GetInterp(interp, masterPtr, path, masterPtrPtr) */ static Tcl_Interp * -CreateSlave(interp, slavePath, safe) +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"? */ { - Master *masterPtr; /* Master record. */ Tcl_Interp *slaveInterp; /* Ptr to slave interpreter. */ Tcl_Interp *masterInterp; /* Ptr to master interp for slave. */ Slave *slavePtr; /* Slave record. */ @@ -458,12 +534,6 @@ CreateSlave(interp, slavePath, safe) char **argv; /* Elements in slavePath. */ char *masterPath; /* Path to its master. */ - masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", - NULL); - if (masterPtr == (Master *) NULL) { - panic("CreatSlave: could not find master record"); - } - if (Tcl_SplitList(interp, slavePath, &argc, &argv) != TCL_OK) { return (Tcl_Interp *) NULL; } @@ -477,7 +547,8 @@ CreateSlave(interp, slavePath, safe) masterPath = Tcl_Merge(argc-1, argv); masterInterp = GetInterp(interp, masterPtr, masterPath, &masterPtr); if (masterInterp == (Tcl_Interp *) NULL) { - Tcl_AppendResult(interp, "interpreter named \"", masterPath, + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "interpreter named \"", masterPath, "\" not found", (char *) NULL); ckfree((char *) argv); ckfree((char *) masterPath); @@ -491,7 +562,8 @@ CreateSlave(interp, slavePath, safe) } hPtr = Tcl_CreateHashEntry(&(masterPtr->slaveTable), slavePath, &new); if (new == 0) { - Tcl_AppendResult(interp, "interpreter named \"", slavePath, + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "interpreter named \"", slavePath, "\" already exists, cannot create", (char *) NULL); ckfree((char *) argv); return (Tcl_Interp *) NULL; @@ -504,7 +576,7 @@ CreateSlave(interp, slavePath, safe) slavePtr->masterInterp = masterInterp; slavePtr->slaveEntry = hPtr; slavePtr->slaveInterp = slaveInterp; - slavePtr->interpCmd = Tcl_CreateCommand(masterInterp, slavePath, + slavePtr->interpCmd = Tcl_CreateObjCommand(masterInterp, slavePath, SlaveObjectCmd, (ClientData) slaveInterp, SlaveObjectDeleteProc); Tcl_InitHashTable(&(slavePtr->aliasTable), TCL_STRING_KEYS); (void) Tcl_SetAssocData(slaveInterp, "tclSlaveRecord", @@ -512,28 +584,33 @@ CreateSlave(interp, slavePath, safe) Tcl_SetHashValue(hPtr, (ClientData) slavePtr); Tcl_SetVar(slaveInterp, "tcl_interactive", "0", TCL_GLOBAL_ONLY); - if (((safe) && (MakeSafe(slaveInterp) == TCL_ERROR)) || - ((!safe) && (Tcl_Init(slaveInterp) == TCL_ERROR))) { - Tcl_ResetResult(interp); - 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); - if (slaveInterp->freeProc != NULL) { - interp->result = slaveInterp->result; - interp->freeProc = slaveInterp->freeProc; - slaveInterp->freeProc = 0; - } else { - Tcl_SetResult(interp, slaveInterp->result, TCL_VOLATILE); + if (safe) { + if (Tcl_MakeSafe(slaveInterp) == TCL_ERROR) { + goto error; + } + } else { + if (Tcl_Init(slaveInterp) == TCL_ERROR) { + goto error; } - Tcl_ResetResult(slaveInterp); - (void) Tcl_DeleteCommand(masterInterp, slavePath); - slaveInterp = (Tcl_Interp *) NULL; } + ckfree((char *) argv); return slaveInterp; + +error: + + 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); + + Tcl_SetObjResult(interp, Tcl_GetObjResult(slaveInterp)); + Tcl_ResetResult(slaveInterp); + + (void) Tcl_DeleteCommand(masterInterp, slavePath); + return (Tcl_Interp *) NULL; } /* @@ -554,48 +631,47 @@ CreateSlave(interp, slavePath, safe) */ static int -CreateInterpObject(interp, argc, argv) +CreateInterpObject(interp, masterPtr, objc, objv) Tcl_Interp *interp; /* Invoking interpreter. */ - int argc; /* Number of arguments. */ - char **argv; /* Argument strings. */ + Master *masterPtr; /* Master record for same. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* with alias. */ { int safe; /* Create a safe interpreter? */ - Master *masterPtr; /* Master record. */ 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. */ - size_t len; /* Length of option argument. */ + int len; /* Length of option argument. */ static int interpCounter = 0; /* Unique id for created names. */ - masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL); - if (masterPtr == (Master *) NULL) { - panic("CreateInterpObject: could not find master record"); - } moreFlags = 1; slavePath = NULL; safe = masterPtr->isSafe; - if (argc < 2 || argc > 5) { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " create ?-safe? ?--? ?path?\"", (char *) NULL); + if ((objc < 2) || (objc > 5)) { + Tcl_WrongNumArgs(interp, 1, objv, "create ?-safe? ?--? ?path?"); return TCL_ERROR; } - for (i = 2; i < argc; i++) { - len = strlen(argv[i]); - if ((argv[i][0] == '-') && (moreFlags != 0)) { - if ((argv[i][1] == 's') && (strncmp(argv[i], "-safe", len) == 0) - && (len > 1)){ + 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(argv[i], "--", len) == 0) && (len > 1)) { + } else if ((strncmp(string, "--", (size_t) len) == 0) && + (len > 1)) { moreFlags = 0; } else { - Tcl_AppendResult(interp, "bad option \"", argv[i], - "\": should be -safe", (char *) NULL); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "bad option \"", string, "\": should be -safe", + (char *) NULL); return TCL_ERROR; } } else { - slavePath = argv[i]; + slavePath = string; } } if (slavePath == (char *) NULL) { @@ -603,12 +679,12 @@ CreateInterpObject(interp, argc, argv) interpCounter++; slavePath = localSlaveName; } - if (CreateSlave(interp, slavePath, safe) != NULL) { - Tcl_AppendResult(interp, slavePath, (char *) NULL); + if (CreateSlave(interp, masterPtr, slavePath, safe) != NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(slavePath, -1)); return TCL_OK; } else { /* - * CreateSlave already set interp->result if there was an error, + * CreateSlave already set the result if there was an error, * so we do not do it here. */ return TCL_ERROR; @@ -633,11 +709,11 @@ CreateInterpObject(interp, argc, argv) */ static int -DeleteOneInterpObject(interp, path) +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. */ { - Master *masterPtr; /* Interim storage for master record.*/ Slave *slavePtr; /* Interim storage for slave record. */ Tcl_Interp *masterInterp; /* Master of interp. to delete. */ Tcl_HashEntry *hPtr; /* Search element. */ @@ -647,13 +723,9 @@ DeleteOneInterpObject(interp, path) char *slaveName; /* Last component in path. */ char *masterPath; /* One-before-last component in path.*/ - masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL); - if (masterPtr == (Master *) NULL) { - panic("DeleteInterpObject: could not find master record"); - } if (Tcl_SplitList(interp, path, &localArgc, &localArgv) != TCL_OK) { - Tcl_AppendResult(interp, "bad interpreter path \"", path, - "\"", (char *) NULL); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "bad interpreter path \"", path, "\"", (char *) NULL); return TCL_ERROR; } if (localArgc < 2) { @@ -667,8 +739,9 @@ DeleteOneInterpObject(interp, path) masterPath = Tcl_Merge(localArgc-1, localArgv); masterInterp = GetInterp(interp, masterPtr, masterPath, &masterPtr); if (masterInterp == (Tcl_Interp *) NULL) { - Tcl_AppendResult(interp, "interpreter named \"", masterPath, - "\" not found", (char *) NULL); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "interpreter named \"", masterPath, "\" not found", + (char *) NULL); ckfree((char *) localArgv); ckfree((char *) masterPath); return TCL_ERROR; @@ -679,19 +752,19 @@ DeleteOneInterpObject(interp, path) hPtr = Tcl_FindHashEntry(&(masterPtr->slaveTable), slaveName); if (hPtr == (Tcl_HashEntry *) NULL) { ckfree((char *) localArgv); - Tcl_AppendResult(interp, "interpreter named \"", path, - "\" not found", (char *) NULL); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "interpreter named \"", path, "\" not found", (char *) NULL); return TCL_ERROR; } slavePtr = (Slave *) Tcl_GetHashValue(hPtr); - slaveName = Tcl_GetCommandName(masterInterp, slavePtr->interpCmd); - if (Tcl_DeleteCommand(masterInterp, slaveName) != 0) { + if (Tcl_DeleteCommandFromToken(masterInterp, slavePtr->interpCmd) != 0) { ckfree((char *) localArgv); - Tcl_AppendResult(interp, "interpreter named \"", path, - "\" not found", (char *) NULL); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "interpreter named \"", path, "\" not found", (char *) NULL); return TCL_ERROR; } ckfree((char *) localArgv); + return TCL_OK; } @@ -713,16 +786,19 @@ DeleteOneInterpObject(interp, path) */ static int -DeleteInterpObject(interp, argc, argv) +DeleteInterpObject(interp, masterPtr, objc, objv) Tcl_Interp *interp; /* Interpreter start search from. */ - int argc; /* Number of arguments in vector. */ - char **argv; /* Contains path to interps to - * delete. */ + Master *masterPtr; /* Interim storage for master record.*/ + int objc; /* Number of arguments in vector. */ + Tcl_Obj *CONST objv[]; /* with alias. */ { int i; + int len; - for (i = 2; i < argc; i++) { - if (DeleteOneInterpObject(interp, argv[i]) != TCL_OK) { + for (i = 2; i < objc; i++) { + if (DeleteOneInterpObject(interp, masterPtr, + Tcl_GetStringFromObj(objv[i], &len)) + != TCL_OK) { return TCL_ERROR; } } @@ -732,7 +808,7 @@ DeleteInterpObject(interp, argc, argv) /* *---------------------------------------------------------------------- * - * AliasHelper -- + * AliasCreationHelper -- * * Helper function to do the work to actually create an alias or * delete an alias. @@ -748,8 +824,8 @@ DeleteInterpObject(interp, argc, argv) */ static int -AliasHelper(curInterp, slaveInterp, masterInterp, masterPtr, - aliasName, targetName, argc, argv) +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 @@ -758,8 +834,8 @@ AliasHelper(curInterp, slaveInterp, masterInterp, masterPtr, Master *masterPtr; /* Master record for target interp. */ char *aliasName; /* Name of alias cmd. */ char *targetName; /* Name of target cmd. */ - int argc; /* Additional arguments to store */ - char **argv; /* with alias. */ + 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. */ @@ -790,9 +866,10 @@ AliasHelper(curInterp, slaveInterp, masterInterp, masterPtr, } if ((targetName == (char *) NULL) || (targetName[0] == '\0')) { - if (argc != 0) { - Tcl_AppendResult(curInterp, "malformed command: should be", - " \"alias ", aliasName, " {}\"", (char *) NULL); + if (objc != 0) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(curInterp), + "malformed command: should be", + " \"alias ", aliasName, " {}\"", (char *) NULL); return TCL_ERROR; } @@ -806,35 +883,55 @@ AliasHelper(curInterp, slaveInterp, masterInterp, masterPtr, strcpy(aliasPtr->targetName, targetName); aliasPtr->targetInterp = masterInterp; - aliasPtr->argv = (char **) NULL; - aliasPtr->argc = argc; - if (aliasPtr->argc > 0) { - aliasPtr->argv = (char **) ckalloc((unsigned) sizeof(char *) * - aliasPtr->argc); - for (i = 0; i < argc; i++) { - aliasPtr->argv[i] = (char *) ckalloc((unsigned) strlen(argv[i])+1); - strcpy(aliasPtr->argv[i], argv[i]); + aliasPtr->objv = NULL; + aliasPtr->objc = objc; + + 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]); } } - if (TclPreventAliasLoop(curInterp, slaveInterp, aliasName, AliasCmd, - (ClientData) aliasPtr) != TCL_OK) { - for (i = 0; i < argc; i++) { - ckfree(aliasPtr->argv[i]); + aliasPtr->slaveCmd = Tcl_CreateObjCommand(slaveInterp, aliasName, + AliasCmd, (ClientData) aliasPtr, AliasCmdDeleteProc); + + if (TclPreventAliasLoop(curInterp, 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->argv != (char **) NULL) { - ckfree((char *) aliasPtr->argv); + 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; } - aliasPtr->slaveCmd = Tcl_CreateCommand(slaveInterp, aliasName, AliasCmd, - (ClientData) aliasPtr, AliasCmdDeleteProc); - /* * Make an entry in the alias table. If it already exists delete * the alias command. Then retry. @@ -842,14 +939,29 @@ AliasHelper(curInterp, slaveInterp, masterInterp, masterPtr, do { hPtr = Tcl_CreateHashEntry(&(slavePtr->aliasTable), aliasName, &new); - if (new == 0) { + if (!new) { tmpAliasPtr = (Alias *) Tcl_GetHashValue(hPtr); - (void) Tcl_DeleteCommand(slaveInterp, tmpAliasPtr->aliasName); - Tcl_DeleteHashEntry(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); + + /* + * 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: + * + * interp alias {} foo {} bar # Create an alias "foo" + * rename foo zop # Now rename the alias + * interp alias {} foo {} zop # Now recreate "foo"... + */ targetPtr = (Target *) ckalloc((unsigned) sizeof(Target)); targetPtr->slaveCmd = aliasPtr->slaveCmd; @@ -865,15 +977,90 @@ AliasHelper(curInterp, slaveInterp, masterInterp, masterPtr, aliasPtr->targetEntry = hPtr; - curInterp->result = aliasPtr->aliasName; + /* + * Make sure we clear out the object result when setting the string + * result. + */ + + Tcl_SetObjResult(curInterp, Tcl_NewStringObj(aliasPtr->aliasName, -1)); + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * InterpAliasesHelper -- + * + * Computes a list of aliases defined in an interpreter. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +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, 1, objv, " aliases ?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; + } + + /* + * Build a list to return the aliases: + */ + 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); + } + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; } /* *---------------------------------------------------------------------- * - * SlaveAliasHelper - + * InterpAliasHelper - * * Handles the different forms of the "interp alias" command: * - interp alias slavePath aliasName @@ -893,52 +1080,835 @@ AliasHelper(curInterp, slaveInterp, masterInterp, masterPtr, */ static int -SlaveAliasHelper(interp, argc, argv) +InterpAliasHelper(interp, masterPtr, objc, objv) Tcl_Interp *interp; /* Current interpreter. */ - int argc; /* Number of arguments. */ - char **argv; /* Argument strings. */ -{ Master *masterPtr; /* Master record for current interp. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ Tcl_Interp *slaveInterp, /* Interpreters used when */ *masterInterp; /* creating an alias btn siblings. */ Master *masterMasterPtr; /* Master record for master interp. */ + int len; - masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL); - if (masterPtr == (Master *) NULL) { - panic("SlaveAliasHelper: could not find master record"); + if (objc < 4) { + Tcl_WrongNumArgs(interp, 1, objv, + "alias slavePath slaveCmd masterPath masterCmd ?args ..?"); + return TCL_ERROR; } - if (argc < 4) { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " alias slavePath slaveCmd masterPath masterCmd ?args ..?\"", + 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; } - slaveInterp = GetInterp(interp, masterPtr, argv[2], NULL); + 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, 1, objv, + "alias 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; + } + return AliasCreationHelper(interp, slaveInterp, masterInterp, + masterMasterPtr, Tcl_GetStringFromObj(objv[3], &len), + Tcl_GetStringFromObj(objv[5], &len), + objc-6, objv+6); +} + +/* + *---------------------------------------------------------------------- + * + * InterpExistsHelper -- + * + * Computes whether a named interpreter exists or not. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +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, 1, objv, "exists ?path?"); + return TCL_ERROR; + } + if (objc == 3) { + if (GetInterp(interp, masterPtr, + Tcl_GetStringFromObj(objv[2], &len), NULL) == + (Tcl_Interp *) NULL) { + objPtr = Tcl_NewStringObj("0", 1); + } else { + objPtr = Tcl_NewStringObj("1", 1); + } + } else { + objPtr = Tcl_NewStringObj("1", 1); + } + Tcl_SetObjResult(interp, objPtr); + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * InterpEvalHelper -- + * + * Helper function to handle all the details of evaluating a + * command in another interpreter. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Whatever the command itself does. + * + *---------------------------------------------------------------------- + */ + +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; + + if (objc < 4) { + Tcl_WrongNumArgs(interp, 1, objv, " eval path arg ?arg ...?"); + return TCL_ERROR; + } + slaveInterp = GetInterp(interp, masterPtr, + Tcl_GetStringFromObj(objv[2], &len), NULL); if (slaveInterp == (Tcl_Interp *) NULL) { - Tcl_AppendResult(interp, "could not find interpreter \"", - argv[2], "\"", (char *) NULL); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "interpreter named \"", Tcl_GetStringFromObj(objv[2], &len), + "\" not found", (char *) NULL); return TCL_ERROR; } - if (argc == 4) { - return DescribeAlias(interp, slaveInterp, argv[3]); + objPtr = Tcl_ConcatObj(objc-3, objv+3); + Tcl_IncrRefCount(objPtr); + + Tcl_Preserve((ClientData) slaveInterp); + result = Tcl_EvalObj(slaveInterp, objPtr); + + Tcl_DecrRefCount(objPtr); + + /* + * 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); + } + + /* + * Move the result object from one interpreter to the + * other. + */ + + Tcl_SetObjResult(interp, Tcl_GetObjResult(slaveInterp)); + Tcl_ResetResult(slaveInterp); + } - if (argc == 5 && strcmp(argv[4], "") == 0) { - return DeleteAlias(interp, slaveInterp, argv[3]); + Tcl_Release((ClientData) slaveInterp); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * InterpExposeHelper -- + * + * Helper function to handle the details of exposing a command in + * another interpreter. + * + * Results: + * Standard Tcl result. + * + * Side effects: + * Exposes a command. From now on the command can be called by scripts + * in the interpreter in which it was exposed. + * + *---------------------------------------------------------------------- + */ + +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. */ +{ + Tcl_Interp *slaveInterp; /* A slave. */ + int len; /* Dummy length variable. */ + + if ((objc != 4) && (objc != 5)) { + Tcl_WrongNumArgs(interp, 1, objv, + "expose path hiddenCmdName ?cmdName?"); + return TCL_ERROR; } - if (argc < 6) { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " alias slavePath slaveCmd masterPath masterCmd ?args ..?\"", + if (Tcl_IsSafe(interp)) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "permission denied: safe interpreter cannot expose commands", (char *) NULL); return TCL_ERROR; } - masterInterp = GetInterp(interp, masterPtr, argv[4], &masterMasterPtr); + 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; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * InterpHideHelper -- + * + * Helper function that handles the details of hiding a command in + * another interpreter. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Hides a command. From now on the command cannot be called by + * scripts in that interpreter. + * + *---------------------------------------------------------------------- + */ + +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 *slaveInterp; /* A slave. */ + int len; /* Dummy length variable. */ + + if ((objc != 4) && (objc != 5)) { + Tcl_WrongNumArgs(interp, 1, objv, + " hide 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; +} + +/* + *---------------------------------------------------------------------- + * + * InterpHiddenHelper -- + * + * Computes the list of hidden commands in a named interpreter. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +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, 1, objv, "hidden ?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_ListObjAppendElement(interp, listObjPtr, + Tcl_NewStringObj(Tcl_GetHashKey(hTblPtr, hPtr), -1)); + } + } + Tcl_SetObjResult(interp, listObjPtr); + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * InterpInvokeHiddenHelper -- + * + * Helper routine to handle the details of invoking a hidden + * command in another interpreter. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Whatever the hidden command does. + * + *---------------------------------------------------------------------- + */ + +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, 1, objv, + "invokehidden 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, 1, objv, + "invokehidden 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); + } + + /* + * Move the result object from the slave to the master. + */ + + Tcl_SetObjResult(interp, Tcl_GetObjResult(slaveInterp)); + Tcl_ResetResult(slaveInterp); + } + Tcl_Release((ClientData) slaveInterp); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * InterpMarkTrustedHelper -- + * + * Helper function to handle the details of marking another + * interpreter as trusted (unsafe). + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Henceforth the hard-wired checks for safety will not prevent + * this interpreter from performing certain operations. + * + *---------------------------------------------------------------------- + */ + +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. */ +{ + Tcl_Interp *slaveInterp; /* A slave. */ + int len; /* Dummy length variable. */ + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 1, objv, "marktrusted 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; + } + + 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; + } + return MarkTrusted(slaveInterp); +} + +/* + *---------------------------------------------------------------------- + * + * InterpIsSafeHelper -- + * + * Computes whether a named interpreter is safe. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +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, 1, objv, "issafe ?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; + } + } + if (masterPtr->isSafe == 0) { + objPtr = Tcl_NewStringObj("0", 1); + } else { + objPtr = Tcl_NewStringObj("1", 1); + } + Tcl_SetObjResult(interp, objPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * InterpSlavesHelper -- + * + * Computes a list of slave interpreters of a named interpreter. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +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, 1, objv, "slaves ?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)); + } + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * InterpShareHelper -- + * + * Helper function to handle the details of sharing a channel between + * interpreters. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * After this call the named channel will be shared between the + * interpreters named in the arguments. + * + *---------------------------------------------------------------------- + */ + +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, 1, objv, "share 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); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * InterpTargetHelper -- + * + * Helper function to compute the target of an alias. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +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. */ +{ + int len; + + if (objc != 4) { + Tcl_WrongNumArgs(interp, 1, objv, "target path alias"); + return TCL_ERROR; + } + return GetTarget(interp, + Tcl_GetStringFromObj(objv[2], &len), + Tcl_GetStringFromObj(objv[3], &len)); +} + +/* + *---------------------------------------------------------------------- + * + * InterpTransferHelper -- + * + * Helper function to handle the details of transferring ownership + * of a channel between interpreters. + * + * 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. + * + *---------------------------------------------------------------------- + */ + +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, 1, objv, + "transfer srcPath channelId destPath"); + return TCL_ERROR; + } + masterInterp = GetInterp(interp, masterPtr, + Tcl_GetStringFromObj(objv[2], &len), NULL); if (masterInterp == (Tcl_Interp *) NULL) { - Tcl_AppendResult(interp, "could not find interpreter \"", - argv[4], "\"", (char *) 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; } - return AliasHelper(interp, slaveInterp, masterInterp, masterMasterPtr, - argv[3], argv[5], argc-6, argv+6); + return TCL_OK; } /* @@ -946,9 +1916,10 @@ SlaveAliasHelper(interp, argc, argv) * * DescribeAlias -- * - * Sets interp->result 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. + * 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 standard Tcl result. @@ -961,30 +1932,50 @@ SlaveAliasHelper(interp, argc, argv) static int DescribeAlias(interp, slaveInterp, aliasName) - Tcl_Interp *interp; /* Interpreter for result and errors. */ - Tcl_Interp *slaveInterp; /* Interpreter defining alias. */ - char *aliasName; /* Name of alias to describe. */ + Tcl_Interp *interp; /* Interpreter for result & errors. */ + Tcl_Interp *slaveInterp; /* Interpreter defining alias. */ + char *aliasName; /* Name of alias to describe. */ { - Slave *slavePtr; /* Slave record for slave interpreter. */ - Tcl_HashEntry *hPtr; /* Search variable. */ - Alias *aliasPtr; /* Structure describing alias. */ - int i; /* Loop variable. */ + 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. */ slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord", NULL); if (slavePtr == (Slave *) NULL) { - panic("DescribeAlias: could not find slave record"); + + /* + * It's possible that the interpreter still does not have a slave + * record. If so, create such a record now. This is only possible + * for interpreters that were created with Tcl_CreateInterp, not + * those created with Tcl_CreateSlave, so this interpreter does + * not have a master. + */ + + slavePtr = (Slave *) ckalloc((unsigned) sizeof(Slave)); + slavePtr->masterInterp = (Tcl_Interp *) NULL; + slavePtr->slaveEntry = (Tcl_HashEntry *) NULL; + slavePtr->slaveInterp = slaveInterp; + slavePtr->interpCmd = (Tcl_Command) NULL; + Tcl_InitHashTable(&(slavePtr->aliasTable), TCL_STRING_KEYS); + (void) Tcl_SetAssocData(slaveInterp, "tclSlaveRecord", + SlaveRecordDeleteProc, (ClientData) slavePtr); } hPtr = Tcl_FindHashEntry(&(slavePtr->aliasTable), aliasName); if (hPtr == (Tcl_HashEntry *) NULL) { return TCL_OK; } aliasPtr = (Alias *) Tcl_GetHashValue(hPtr); - Tcl_AppendResult(interp, aliasPtr->targetName, (char *) NULL); - for (i = 0; i < aliasPtr->argc; i++) { - Tcl_AppendElement(interp, aliasPtr->argv[i]); + + 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_SetObjResult(interp, listObjPtr); return TCL_OK; } @@ -1011,31 +2002,44 @@ DeleteAlias(interp, slaveInterp, aliasName) 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. */ - Alias *aliasPtr; /* Structure describing alias to delete. */ + char *tmpPtr, *namePtr; /* Local pointers to name of command to + * be deleted. */ slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord", NULL); if (slavePtr == (Slave *) NULL) { - panic("DeleteAlias: could not find slave record"); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "alias \"", aliasName, "\" not found", (char *) NULL); + return TCL_ERROR; } /* - * Get the alias from the alias table, determine the current - * true name of the alias (it may have been renamed!) and then - * delete the true command name. The deleteProc on the alias - * command will take care of removing the entry from the alias - * table. + * 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_AppendResult(interp, "alias \"", aliasName, "\" not found", - (char *) NULL); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "alias \"", aliasName, "\" not found", (char *) NULL); return TCL_ERROR; } aliasPtr = (Alias *) Tcl_GetHashValue(hPtr); - aliasName = Tcl_GetCommandName(slaveInterp, aliasPtr->slaveCmd); + + /* + * 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 @@ -1044,9 +2048,15 @@ DeleteAlias(interp, slaveInterp, aliasName) * target table. */ - if (Tcl_DeleteCommand(slaveInterp, aliasName) != 0) { - panic("DeleteAlias: did not find alias to be deleted"); + 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"); + } } + ckfree(namePtr); return TCL_OK; } @@ -1097,9 +2107,11 @@ Tcl_GetInterpPath(askingInterp, targetInterp) return TCL_ERROR; } if (Tcl_GetInterpPath(askingInterp, slavePtr->masterInterp) == TCL_ERROR) { + /* - * AskingInterp->result was set by recursive call. + * The result of askingInterp was set by recursive call. */ + return TCL_ERROR; } masterPtr = (Master *) Tcl_GetAssocData(slavePtr->masterInterp, @@ -1143,7 +2155,6 @@ GetTarget(askingInterp, path, aliasName) Alias *aliasPtr; /* Data describing the alias. */ Tcl_ResetResult(askingInterp); - masterPtr = (Master *) Tcl_GetAssocData(askingInterp, "tclMasterRecord", NULL); if (masterPtr == (Master *) NULL) { @@ -1151,8 +2162,8 @@ GetTarget(askingInterp, path, aliasName) } slaveInterp = GetInterp(askingInterp, masterPtr, path, NULL); if (slaveInterp == (Tcl_Interp *) NULL) { - Tcl_AppendResult(askingInterp, "could not find interpreter \"", - path, "\"", (char *) NULL); + Tcl_AppendStringsToObj(Tcl_GetObjResult(askingInterp), + "could not find interpreter \"", path, "\"", (char *) NULL); return TCL_ERROR; } slaveSlavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord", @@ -1162,21 +2173,25 @@ GetTarget(askingInterp, path, aliasName) } hPtr = Tcl_FindHashEntry(&(slaveSlavePtr->aliasTable), aliasName); if (hPtr == (Tcl_HashEntry *) NULL) { - Tcl_AppendResult(askingInterp, "alias \"", aliasName, "\" in path \"", - path, "\" not found", (char *) 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_AppendResult(askingInterp, "target interpreter for alias \"", + Tcl_AppendStringsToObj(Tcl_GetObjResult(askingInterp), + "target interpreter for alias \"", aliasName, "\" in path \"", path, "\" is not my descendant", (char *) NULL); return TCL_ERROR; } + return TCL_OK; } @@ -1198,289 +2213,625 @@ GetTarget(askingInterp, path, aliasName) */ /* ARGSUSED */ int -Tcl_InterpCmd(clientData, interp, argc, argv) +Tcl_InterpObjCmd(clientData, interp, objc, objv) ClientData clientData; /* Unused. */ Tcl_Interp *interp; /* Current interpreter. */ - int argc; /* Number of arguments. */ - char **argv; /* Argument strings. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ { - Tcl_Interp *slaveInterp; /* A slave. */ - Tcl_Interp *masterInterp; /* A master. */ Master *masterPtr; /* Master record for current interp. */ - Slave *slavePtr; /* Record for slave interp. */ - Tcl_HashEntry *hPtr; - Tcl_HashSearch hSearch; - size_t len; /* Length of command name. */ - int result; /* Result of eval. */ - char *cmdName; /* Name of sub command to do. */ - char *cmd; /* Command to eval. */ - Tcl_Channel chan; /* Channel to share or transfer. */ + int result; /* Local result variable. */ - if (argc < 2) { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " cmd ?arg ...?\"", (char *) NULL); + /* + * 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; } - cmdName = argv[1]; masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL); if (masterPtr == (Master *) NULL) { panic("Tcl_InterpCmd: could not find master record"); } - len = strlen(cmdName); + result = Tcl_GetIndexFromObj(interp, objv[1], subCmds, "option", + 0, (int *) &index); + if (result != TCL_OK) { + return result; + } - if (cmdName[0] == 'a') { - if ((strncmp(cmdName, "alias", len) == 0) && (len <= 5)) { - return SlaveAliasHelper(interp, argc, argv); - } + 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); + } - if (strcmp(cmdName, "aliases") == 0) { - if (argc != 2 && argc != 3) { - Tcl_AppendResult(interp, "wrong # args: should be \"", - argv[0], " aliases ?path?\"", (char *) NULL); - return TCL_ERROR; - } - if (argc == 3) { - slaveInterp = GetInterp(interp, masterPtr, argv[2], NULL); - if (slaveInterp == (Tcl_Interp *) NULL) { - Tcl_AppendResult(interp, "interpreter \"", - argv[2], "\" not found", (char *) NULL); - return TCL_ERROR; - } - } else { - slaveInterp = interp; - } - slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, - "tclSlaveRecord", NULL); - if (slavePtr == (Slave *) NULL) { - return TCL_OK; - } - for (hPtr = Tcl_FirstHashEntry(&(slavePtr->aliasTable), &hSearch); - hPtr != NULL; - hPtr = Tcl_NextHashEntry(&hSearch)) { - Tcl_AppendElement(interp, - Tcl_GetHashKey(&(slavePtr->aliasTable), hPtr)); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * SlaveAliasHelper -- + * + * Helper function to construct or query an alias for a slave + * interpreter. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Potentially creates a new alias. + * + *---------------------------------------------------------------------- + */ + +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. */ +{ + Master *masterPtr; + int len; + + switch (objc-2) { + case 0: + Tcl_WrongNumArgs(interp, 1, objv, + "alias 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 + */ + + return DescribeAlias(interp, slaveInterp, + Tcl_GetStringFromObj(objv[2], &len)); + + default: + masterPtr = (Master *) Tcl_GetAssocData(interp, + "tclMasterRecord", NULL); + if (masterPtr == (Master *) NULL) { + panic("SlaveObjectCmd: could not find master record"); } - return TCL_OK; - } + return AliasCreationHelper(interp, slaveInterp, interp, + masterPtr, + Tcl_GetStringFromObj(objv[2], &len), + Tcl_GetStringFromObj(objv[3], &len), + objc-4, objv+4); } +} + +/* + *---------------------------------------------------------------------- + * + * SlaveAliasesHelper -- + * + * Computes a list of aliases defined in a slave interpreter. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +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. */ +{ + 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. + */ - if ((cmdName[0] == 'c') && (strncmp(cmdName, "create", len) == 0)) { - return CreateInterpObject(interp, argc, argv); + 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)); } + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * SlaveEvalHelper -- + * + * Helper function to evaluate a command in a slave interpreter. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Whatever the command does. + * + *---------------------------------------------------------------------- + */ - if ((cmdName[0] == 'd') && (strncmp(cmdName, "delete", len) == 0)) { - return DeleteInterpObject(interp, argc, argv); +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; + int result; + + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "eval arg ?arg ...?"); + return TCL_ERROR; } - if (cmdName[0] == 'e') { - if ((strncmp(cmdName, "exists", len) == 0) && (len > 1)) { - if (argc > 3) { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " exists ?path?\"", (char *) NULL); - return TCL_ERROR; - } - if (argc == 3) { - if (GetInterp(interp, masterPtr, argv[2], NULL) == - (Tcl_Interp *) NULL) { - Tcl_AppendResult(interp, "0", (char *) NULL); - } else { - Tcl_AppendResult(interp, "1", (char *) NULL); - } - } else { - Tcl_AppendResult(interp, "1", (char *) NULL); - } - return TCL_OK; - } - if ((strncmp(cmdName, "eval", len) == 0) && (len > 1)) { - if (argc < 4) { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " eval path arg ?arg ...?\"", (char *) NULL); - return TCL_ERROR; - } - slaveInterp = GetInterp(interp, masterPtr, argv[2], NULL); - if (slaveInterp == (Tcl_Interp *) NULL) { - Tcl_AppendResult(interp, "interpreter named \"", argv[2], - "\" not found", (char *) NULL); - return TCL_ERROR; - } - cmd = Tcl_Concat(argc-3, argv+3); - Tcl_Preserve((ClientData) slaveInterp); - result = Tcl_Eval(slaveInterp, cmd); - ckfree((char *) cmd); + objPtr = Tcl_ConcatObj(objc-2, objv+2); + Tcl_IncrRefCount(objPtr); + + Tcl_Preserve((ClientData) slaveInterp); + result = Tcl_EvalObj(slaveInterp, objPtr); + + Tcl_DecrRefCount(objPtr); + + /* + * 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) { /* - * 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. + * An error occurred, so transfer error information from the + * destination interpreter back to our interpreter. */ - if (interp != slaveInterp) { - if (result == TCL_ERROR) { - - /* - * An error occurred, so transfer error information from - * the target interpreter back to our interpreter. Must - * clear interp's result before calling Tcl_AddErrorInfo, - * since Tcl_AddErrorInfo will store the interp's result in - * errorInfo before appending slaveInterp's $errorInfo; - * we've already got everything we need in the slave - * interpreter's $errorInfo. - */ - - Tcl_ResetResult(interp); - 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); - } - if (slaveInterp->freeProc != NULL) { - interp->result = slaveInterp->result; - interp->freeProc = slaveInterp->freeProc; - slaveInterp->freeProc = 0; - } else { - Tcl_SetResult(interp, slaveInterp->result, TCL_VOLATILE); - } - Tcl_ResetResult(slaveInterp); + iPtr = (Interp *) slaveInterp; + if (!(iPtr->flags & ERR_ALREADY_LOGGED)) { + Tcl_AddErrorInfo(slaveInterp, ""); } - Tcl_Release((ClientData) slaveInterp); - return result; + 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); } + + /* + * Move the result object from one interpreter to the + * other. + */ + + Tcl_SetObjResult(interp, Tcl_GetObjResult(slaveInterp)); + Tcl_ResetResult(slaveInterp); } + Tcl_Release((ClientData) slaveInterp); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * SlaveExposeHelper -- + * + * Helper function to expose a command in a slave interpreter. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * After this call scripts in the slave will be able to invoke + * the newly exposed command. + * + *---------------------------------------------------------------------- + */ - if ((cmdName[0] == 'i') && (strncmp(cmdName, "issafe", len) == 0)) { - if (argc > 3) { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " issafe ?path?\"", (char *) NULL); - return TCL_ERROR; - } - if (argc == 3) { - slaveInterp = GetInterp(interp, masterPtr, argv[2], &masterPtr); - if (slaveInterp == (Tcl_Interp *) NULL) { - Tcl_AppendResult(interp, "interpreter \"", argv[2], - "\" not found", (char *) NULL); - return TCL_ERROR; - } - } - if (masterPtr->isSafe == 0) { - Tcl_AppendResult(interp, "0", (char *) NULL); - } else { - Tcl_AppendResult(interp, "1", (char *) NULL); - } - return TCL_OK; +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, 1, objv, "expose hiddenCmdName ?cmdName?"); + return TCL_ERROR; + } + 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; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * SlaveHideHelper -- + * + * Helper function to hide a command in a slave interpreter. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * After this call scripts in the slave will no longer be able + * to invoke the named command. + * + *---------------------------------------------------------------------- + */ + +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 len; + + if ((objc != 3) && (objc != 4)) { + Tcl_WrongNumArgs(interp, 1, objv, "hide 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; + } + 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; +} + +/* + *---------------------------------------------------------------------- + * + * SlaveHiddenHelper -- + * + * Helper function to compute list of hidden commands in a slave + * interpreter. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +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 (cmdName[0] == 's') { - if ((strncmp(cmdName, "slaves", len) == 0) && (len > 1)) { - if (argc != 2 && argc != 3) { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " slaves ?path?\"", (char *) NULL); - return TCL_ERROR; - } - if (argc == 3) { - if (GetInterp(interp, masterPtr, argv[2], &masterPtr) == - (Tcl_Interp *) NULL) { - Tcl_AppendResult(interp, "interpreter \"", argv[2], - "\" not found", (char *) NULL); - return TCL_ERROR; - } - } - for (hPtr = Tcl_FirstHashEntry(&(masterPtr->slaveTable), &hSearch); - hPtr != NULL; - hPtr = Tcl_NextHashEntry(&hSearch)) { - Tcl_AppendElement(interp, - Tcl_GetHashKey(&(masterPtr->slaveTable), hPtr)); - } - return TCL_OK; - } - if ((strncmp(cmdName, "share", len) == 0) && (len > 1)) { - if (argc != 5) { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " share srcPath channelId destPath\"", (char *) NULL); - return TCL_ERROR; - } - masterInterp = GetInterp(interp, masterPtr, argv[2], NULL); - if (masterInterp == (Tcl_Interp *) NULL) { - Tcl_AppendResult(interp, "interpreter \"", argv[2], - "\" not found", (char *) NULL); - return TCL_ERROR; - } - slaveInterp = GetInterp(interp, masterPtr, argv[4], NULL); - if (slaveInterp == (Tcl_Interp *) NULL) { - Tcl_AppendResult(interp, "interpreter \"", argv[4], - "\" not found", (char *) NULL); - return TCL_ERROR; - } - chan = Tcl_GetChannel(masterInterp, argv[3], NULL); - if (chan == (Tcl_Channel) NULL) { - if (interp != masterInterp) { - Tcl_AppendResult(interp, masterInterp->result, - (char *) NULL); - Tcl_ResetResult(masterInterp); - } - return TCL_ERROR; - } - Tcl_RegisterChannel(slaveInterp, chan); - return TCL_OK; - } + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "hidden"); + return TCL_ERROR; } - if ((cmdName[0] == 't') && (strncmp(cmdName, "target", len) == 0)) { - if (argc != 4) { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " target path alias\"", (char *) 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)); } - return GetTarget(interp, argv[2], argv[3]); } + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * SlaveIsSafeHelper -- + * + * Helper function to compute whether a slave interpreter is safe. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ - if ((cmdName[0] == 't') && (strncmp(cmdName, "transfer", len) == 0)) { - if (argc != 5) { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " transfer srcPath channelId destPath\"", (char *) NULL); - return TCL_ERROR; - } - masterInterp = GetInterp(interp, masterPtr, argv[2], NULL); - if (masterInterp == (Tcl_Interp *) NULL) { - Tcl_AppendResult(interp, "interpreter \"", argv[2], - "\" not found", (char *) NULL); - return TCL_ERROR; - } - slaveInterp = GetInterp(interp, masterPtr, argv[4], NULL); - if (slaveInterp == (Tcl_Interp *) NULL) { - Tcl_AppendResult(interp, "interpreter \"", argv[4], - "\" not found", (char *) NULL); - return TCL_ERROR; - } - chan = Tcl_GetChannel(masterInterp, argv[3], NULL); - if (chan == (Tcl_Channel) NULL) { - if (interp != masterInterp) { - Tcl_AppendResult(interp, masterInterp->result, (char *) NULL); - Tcl_ResetResult(masterInterp); - } +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. */ +{ + Master *masterPtr; /* Master record for slave interp. */ + Tcl_Obj *namePtr; /* Local object pointer. */ + + if (objc > 2) { + Tcl_WrongNumArgs(interp, 1, objv, "issafe"); + return TCL_ERROR; + } + masterPtr = (Master *) Tcl_GetAssocData(slaveInterp, + "tclMasterRecord", NULL); + if (masterPtr == (Master *) NULL) { + panic("SlaveObjectCmd: could not find master record"); + } + if (masterPtr->isSafe == 1) { + namePtr = Tcl_NewStringObj("1", 1); + } else { + namePtr = Tcl_NewStringObj("0", 1); + } + Tcl_SetObjResult(interp, namePtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * SlaveInvokeHiddenHelper -- + * + * Helper function to invoke a hidden command in a slave interpreter. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Whatever the hidden command does. + * + *---------------------------------------------------------------------- + */ + +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. */ +{ + Interp *iPtr; + Master *masterPtr; + int doGlobal = 0; + int result; + int len; + char *string; + Tcl_Obj *namePtr, *objPtr; + + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, + "invokehidden ?-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, 1, objv, + "invokehidden path ?-global? cmd ?arg ..?"); return TCL_ERROR; } - Tcl_RegisterChannel(slaveInterp, chan); - if (Tcl_UnregisterChannel(masterInterp, chan) != TCL_OK) { - if (interp != masterInterp) { - Tcl_AppendResult(interp, masterInterp->result, (char *) NULL); - Tcl_ResetResult(masterInterp); + } + 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); + } + + /* + * 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, ""); } - return TCL_ERROR; + 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); } - return TCL_OK; + /* + * Move the result object from the slave to the master. + */ + + Tcl_SetObjResult(interp, Tcl_GetObjResult(slaveInterp)); + Tcl_ResetResult(slaveInterp); } - - Tcl_AppendResult(interp, "bad option \"", argv[1], - "\": should be alias, aliases, create, delete, exists, eval, ", - "issafe, share, slaves, target or transfer", (char *) NULL); - return TCL_ERROR; + Tcl_Release((ClientData) slaveInterp); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * SlaveMarkTrustedHelper -- + * + * Helper function to mark a slave interpreter as trusted (unsafe). + * + * Results: + * A standard Tcl result. + * + * Side effects: + * After this call the hard-wired security checks in the core no + * longer prevent the slave from performing certain operations. + * + *---------------------------------------------------------------------- + */ + +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, 1, objv, "marktrusted"); + 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; + } + return MarkTrusted(slaveInterp); } /* @@ -1501,34 +2852,46 @@ Tcl_InterpCmd(clientData, interp, argc, argv) */ static int -SlaveObjectCmd(clientData, interp, argc, argv) +SlaveObjectCmd(clientData, interp, objc, objv) ClientData clientData; /* Slave interpreter. */ Tcl_Interp *interp; /* Current interpreter. */ - int argc; /* Number of arguments. */ - char **argv; /* Argument strings. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* The argument vector. */ { - Master *masterPtr; /* Master record for slave interp. */ Slave *slavePtr; /* Slave record. */ Tcl_Interp *slaveInterp; /* Slave interpreter. */ - char *cmdName; /* Name of command to do. */ - char *cmd; /* Command to evaluate in slave - * interpreter. */ - Alias *aliasPtr; /* Alias information. */ - Tcl_HashEntry *hPtr; /* For local searches. */ - Tcl_HashSearch hSearch; /* For local searches. */ int result; /* Loop counter, status return. */ - size_t len; /* Length of command name. */ + int len; /* Length of command name. */ + + /* + * These are all the different subcommands for this command: + */ - if (argc < 2) { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " cmd ?arg ...?\"", (char *) NULL); + 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; } slaveInterp = (Tcl_Interp *) clientData; if (slaveInterp == (Tcl_Interp *) NULL) { - Tcl_AppendResult(interp, "interpreter ", argv[0], " has been deleted", - (char *) NULL); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "interpreter ", Tcl_GetStringFromObj(objv[0], &len), + " has been deleted", (char *) NULL); return TCL_ERROR; } @@ -1538,132 +2901,40 @@ SlaveObjectCmd(clientData, interp, argc, argv) panic("SlaveObjectCmd: could not find slave record"); } - cmdName = argv[1]; - len = strlen(cmdName); - - if (cmdName[0] == 'a') { - if (strncmp(cmdName, "alias", len) == 0) { - switch (argc-2) { - case 0: - Tcl_AppendResult(interp, "wrong # args: should be \"", - argv[0], " alias aliasName ?targetName? ?args..?", - (char *) NULL); - 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 - */ - - return DescribeAlias(interp, slaveInterp, argv[2]); - - default: - masterPtr = (Master *) Tcl_GetAssocData(interp, - "tclMasterRecord", NULL); - if (masterPtr == (Master *) NULL) { - panic("SlaveObjectCmd: could not find master record"); - } - return AliasHelper(interp, slaveInterp, interp, masterPtr, - argv[2], argv[3], argc-4, argv+4); - } - } - - if (strncmp(cmdName, "aliases", len) == 0) { - - /* - * Return the names of all the aliases created in the - * slave interpreter. - */ - - for (hPtr = Tcl_FirstHashEntry(&(slavePtr->aliasTable), - &hSearch); - hPtr != (Tcl_HashEntry *) NULL; - hPtr = Tcl_NextHashEntry(&hSearch)) { - aliasPtr = (Alias *) Tcl_GetHashValue(hPtr); - Tcl_AppendElement(interp, aliasPtr->aliasName); - } - return TCL_OK; - } - } - - - if ((cmdName[0] == 'e') && (strncmp(cmdName, "eval", len) == 0)) { - if (argc < 3) { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " eval arg ?arg ...?\"", (char *) NULL); - return TCL_ERROR; - } - - cmd = Tcl_Concat(argc-2, argv+2); - Tcl_Preserve((ClientData) slaveInterp); - result = Tcl_Eval(slaveInterp, cmd); - ckfree((char *) cmd); - - /* - * 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 - * destination interpreter back to our interpreter. Must clear - * interp's result before calling Tcl_AddErrorInfo, since - * Tcl_AddErrorInfo will store the interp's result in errorInfo - * before appending slaveInterp's $errorInfo; - * we've already got everything we need in the slave - * interpreter's $errorInfo. - */ - - Tcl_ResetResult(interp); - 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); - } - if (slaveInterp->freeProc != NULL) { - interp->result = slaveInterp->result; - interp->freeProc = slaveInterp->freeProc; - slaveInterp->freeProc = 0; - } else { - Tcl_SetResult(interp, slaveInterp->result, TCL_VOLATILE); - } - Tcl_ResetResult(slaveInterp); - } - Tcl_Release((ClientData) slaveInterp); + result = Tcl_GetIndexFromObj(interp, objv[1], subCmds, "option", + 0, (int *) &index); + if (result != TCL_OK) { return result; } - if ((cmdName[0] == 'i') && (strncmp(cmdName, "issafe", len) == 0)) { - if (argc > 2) { - Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], - " issafe\"", (char *) NULL); - return TCL_ERROR; - } - masterPtr = (Master *) Tcl_GetAssocData(slaveInterp, - "tclMasterRecord", NULL); - if (masterPtr == (Master *) NULL) { - panic("SlaveObjectCmd: could not find master record"); - } - if (masterPtr->isSafe == 1) { - Tcl_AppendResult(interp, "1", (char *) NULL); - } else { - Tcl_AppendResult(interp, "0", (char *) NULL); - } - return TCL_OK; + 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); } - Tcl_AppendResult(interp, "bad option \"", argv[1], - "\": should be alias, aliases, eval or issafe", (char *) NULL); return TCL_ERROR; } @@ -1750,76 +3021,172 @@ SlaveObjectDeleteProc(clientData) */ static int -AliasCmd(clientData, interp, argc, argv) +AliasCmd(clientData, interp, objc, objv) ClientData clientData; /* Alias record. */ Tcl_Interp *interp; /* Current interpreter. */ - int argc; /* Number of arguments. */ - char **argv; /* Argument strings. */ + 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_CmdInfo cmdInfo; /* Info about target command. */ + 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, addArgc; /* Loop counters. */ - int localArgc; /* Local argument count. */ - char **localArgv; /* Local argument vector. */ - Interp *iPtr; /* The target interpreter. */ + 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; - result = Tcl_GetCommandInfo(aliasPtr->targetInterp, aliasPtr->targetName, - &cmdInfo); - if (result == 0) { - Tcl_AppendResult(interp, "aliased target \"", aliasPtr->targetName, - "\" for \"", argv[0], "\" not found", (char *) NULL); - return TCL_ERROR; + /* + * Look for the target command in the global namespace of the target + * interpreter. + */ + + 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 (aliasPtr->argc <= 0) { - localArgv = argv; - localArgc = argc; - } else { - addArgc = aliasPtr->argc; - localArgc = argc + addArgc; - localArgv = (char **) ckalloc((unsigned) sizeof(char *) * localArgc); - localArgv[0] = argv[0]; - for (i = 0, j = 1; i < addArgc; i++, j++) { - localArgv[j] = aliasPtr->argv[i]; + + iPtr = (Interp *) targetInterp; + + /* + * 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 < argc; i++, j++) { - localArgv[j] = argv[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; } /* - * Invoke the redirected command in the target interpreter. Note - * that we are not calling eval because of possible security holes with - * $ substitution and bracketed command evaluation. - * - * We duplicate some code here from Tcl_Eval to implement recursion - * level counting and correct deletion of the target interpreter if - * that was requested but delayed because of in-progress evaluations. + * 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 = (Interp *) aliasPtr->targetInterp; iPtr->numLevels++; - Tcl_Preserve((ClientData) iPtr); - Tcl_ResetResult((Tcl_Interp *) iPtr); - result = (cmdInfo.proc)(cmdInfo.clientData, (Tcl_Interp *) iPtr, - localArgc, localArgv); + Tcl_Preserve((ClientData) targetInterp); + + /* + * Reset the interpreter to its clean state; we do not know what state + * it is in now.. + */ + + 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((Tcl_Interp *) iPtr); + Tcl_ResetResult(targetInterp); if (result == TCL_BREAK) { - iPtr->result = "invoked \"break\" outside of a loop"; + Tcl_SetObjResult(targetInterp, + Tcl_NewStringObj("invoked \"break\" outside of a loop", + -1)); } else if (result == TCL_CONTINUE) { - iPtr->result = "invoked \"continue\" outside of a loop"; + Tcl_SetObjResult(targetInterp, + Tcl_NewStringObj( + "invoked \"continue\" outside of a loop", + -1)); } else { - iPtr->result = iPtr->resultSpace; - sprintf(iPtr->resultSpace, "command returned bad code: %d", - result); + char buf[128]; + + sprintf(buf, "command returned bad code: %d", result); + Tcl_SetObjResult(targetInterp, Tcl_NewStringObj(buf, -1)); } result = TCL_ERROR; } @@ -1829,58 +3196,53 @@ AliasCmd(clientData, interp, argc, argv) * Clean up any locally allocated argument vector structure. */ - if (localArgv != argv) { - ckfree((char *) localArgv); + if (localObjv != objv) { + ckfree((char *) localObjv); } /* + * Move the result from the target interpreter to the invoking + * interpreter if they are different. * - * NOTE: Need to be careful if the target interpreter and the current - * interpreter are the same - must not destroy result. This may happen - * if an alias is created which redirects to a command in the same - * interpreter as the one in which the source command will be defined. - * Also: We cannot use aliasPtr any more because the alias may have + * Note: We cannot use aliasPtr any more because the alias may have * been deleted. */ - if (interp != (Tcl_Interp *) iPtr) { + if (interp != targetInterp) { if (result == TCL_ERROR) { - /* - * An error occurred, so transfer error information from the - * destination interpreter back to our interpreter. Some tricky - * points: - * 1. Must call Tcl_AddErrorInfo in destination interpreter to - * make sure that the errorInfo variable has been initialized - * (it's initialized lazily and might not have been initialized - * yet). - * 2. Must clear interp's result before calling Tcl_AddErrorInfo, - * since Tcl_AddErrorInfo will store the interp's result in - * errorInfo before appending aliasPtr->interp's $errorInfo; - * we've already got everything we need in the redirected - * interpreter's $errorInfo. - */ - - if (!(iPtr->flags & ERR_ALREADY_LOGGED)) { - Tcl_AddErrorInfo((Tcl_Interp *) iPtr, ""); - } - iPtr->flags &= ~ERR_ALREADY_LOGGED; + + /* + * 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); - Tcl_AddErrorInfo(interp, Tcl_GetVar2((Tcl_Interp *) iPtr, - "errorInfo", (char *) NULL, TCL_GLOBAL_ONLY)); + 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((Tcl_Interp *) iPtr, "errorCode", - (char *) NULL, TCL_GLOBAL_ONLY), TCL_GLOBAL_ONLY); - } - if (iPtr->freeProc != NULL) { - interp->result = iPtr->result; - interp->freeProc = iPtr->freeProc; - iPtr->freeProc = 0; - } else { - Tcl_SetResult(interp, iPtr->result, TCL_VOLATILE); + Tcl_GetVar2(targetInterp, "errorCode", (char *) NULL, + TCL_GLOBAL_ONLY), + TCL_GLOBAL_ONLY); + Tcl_DecrRefCount(namePtr); } - Tcl_ResetResult((Tcl_Interp *) iPtr); + + /* + * Move the result object from one interpreter to the + * other. + */ + + Tcl_SetObjResult(interp, Tcl_GetObjResult(targetInterp)); + Tcl_ResetResult(targetInterp); } - Tcl_Release((ClientData) iPtr); + Tcl_Release((ClientData) targetInterp); return result; } @@ -1918,11 +3280,11 @@ AliasCmdDeleteProc(clientData) ckfree((char *) aliasPtr->targetName); ckfree((char *) aliasPtr->aliasName); - for (i = 0; i < aliasPtr->argc; i++) { - ckfree((char *) aliasPtr->argv[i]); + for (i = 0; i < aliasPtr->objc; i++) { + Tcl_DecrRefCount(aliasPtr->objv[i]); } - if (aliasPtr->argv != (char **) NULL) { - ckfree((char *) aliasPtr->argv); + if (aliasPtr->objv != (Tcl_Obj **) NULL) { + ckfree((char *) aliasPtr->objv); } Tcl_DeleteHashEntry(aliasPtr->aliasEntry); @@ -1957,7 +3319,6 @@ MasterRecordDeleteProc(clientData, interp) Tcl_HashEntry *hPtr; /* Search element. */ Tcl_HashSearch hSearch; /* Search record (internal). */ Slave *slavePtr; /* Loop variable. */ - char *cmdName; /* Name of command to delete. */ Master *masterPtr; /* Interim storage. */ masterPtr = (Master *) clientData; @@ -1965,8 +3326,7 @@ MasterRecordDeleteProc(clientData, interp) hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) { slavePtr = (Slave *) Tcl_GetHashValue(hPtr); - cmdName = Tcl_GetCommandName(interp, slavePtr->interpCmd); - (void) Tcl_DeleteCommand(interp, cmdName); + (void) Tcl_DeleteCommandFromToken(interp, slavePtr->interpCmd); } Tcl_DeleteHashTable(&(masterPtr->slaveTable)); @@ -1974,9 +3334,8 @@ MasterRecordDeleteProc(clientData, interp) hPtr != NULL; hPtr = Tcl_FirstHashEntry(&(masterPtr->targetTable), &hSearch)) { targetPtr = (Target *) Tcl_GetHashValue(hPtr); - cmdName = Tcl_GetCommandName(targetPtr->slaveInterp, - targetPtr->slaveCmd); - (void) Tcl_DeleteCommand(targetPtr->slaveInterp, cmdName); + (void) Tcl_DeleteCommandFromToken(targetPtr->slaveInterp, + targetPtr->slaveCmd); } Tcl_DeleteHashTable(&(masterPtr->targetTable)); @@ -2045,14 +3404,8 @@ SlaveRecordDeleteProc(clientData, interp) cmdPtr->deleteProc = NULL; cmdPtr->deleteData = NULL; - /* - * Get the command name from the master interpreter instead of - * relying on the stored name; the command may have been renamed. - */ - - Tcl_DeleteCommand(slavePtr->masterInterp, - Tcl_GetCommandName(slavePtr->masterInterp, - slavePtr->interpCmd)); + Tcl_DeleteCommandFromToken(slavePtr->masterInterp, + slavePtr->interpCmd); } /* @@ -2069,20 +3422,17 @@ SlaveRecordDeleteProc(clientData, interp) /* * The call to Tcl_DeleteCommand will release the storage - * occuppied by the hash entry and the alias record. - * NOTE that we cannot use the alias name directly because its - * storage will be deleted in the command deletion callback. Hence - * we must use the name for the command as stored in the hash table. + * occupied by the hash entry and the alias record. */ - Tcl_DeleteCommand(interp, - Tcl_GetCommandName(interp, aliasPtr->slaveCmd)); + Tcl_DeleteCommandFromToken(interp, aliasPtr->slaveCmd); } /* - * Finally dispose of the slave record itself. + * Finally dispose of the hash table and the slave record. */ - + + Tcl_DeleteHashTable(hTblPtr); ckfree((char *) slavePtr); } @@ -2156,32 +3506,6 @@ Tcl_IsSafe(interp) /* *---------------------------------------------------------------------- * - * Tcl_MakeSafe -- - * - * Makes an interpreter safe. - * - * Results: - * TCL_OK if it succeeds, TCL_ERROR else. - * - * Side effects: - * Removes functionality from an interpreter. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_MakeSafe(interp) - Tcl_Interp *interp; /* Make this interpreter "safe". */ -{ - if (interp == (Tcl_Interp *) NULL) { - return TCL_ERROR; - } - return MakeSafe(interp); -} - -/* - *---------------------------------------------------------------------- - * * Tcl_CreateSlave -- * * Creates a slave interpreter. The slavePath argument denotes the @@ -2208,10 +3532,17 @@ Tcl_CreateSlave(interp, slavePath, isSafe) char *slavePath; /* Name of slave to create. */ int isSafe; /* Should new slave be "safe" ? */ { + Master *masterPtr; /* Master record for same. */ + if ((interp == (Tcl_Interp *) NULL) || (slavePath == (char *) NULL)) { return NULL; } - return CreateSlave(interp, slavePath, isSafe); + masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", + NULL); + if (masterPtr == (Master *) NULL) { + panic("CreatSlave: could not find master record"); + } + return CreateSlave(interp, masterPtr, slavePath, isSafe); } /* @@ -2288,8 +3619,7 @@ Tcl_GetMaster(interp) * Creates an alias between two interpreters. * * Results: - * TCL_OK if successful, TCL_ERROR if failed. If TCL_ERROR is returned - * the result of slaveInterp will contain an error message. + * A standard Tcl result. * * Side effects: * Creates a new alias, manipulates the result field of slaveInterp. @@ -2307,6 +3637,61 @@ Tcl_CreateAlias(slaveInterp, slaveCmd, targetInterp, targetCmd, argc, argv) 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); + + ckfree((char *) objv); + + return result; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_CreateAliasObj -- + * + * Object version: Creates an alias between two interpreters. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Creates a new alias. + * + *---------------------------------------------------------------------- + */ + +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. */ +{ + Master *masterPtr; /* Master record for target interp. */ if ((slaveInterp == (Tcl_Interp *) NULL) || (targetInterp == (Tcl_Interp *) NULL) || @@ -2319,8 +3704,8 @@ Tcl_CreateAlias(slaveInterp, slaveCmd, targetInterp, targetCmd, argc, argv) if (masterPtr == (Master *) NULL) { panic("Tcl_CreateAlias: could not find master record"); } - return AliasHelper(slaveInterp, slaveInterp, targetInterp, masterPtr, - slaveCmd, targetCmd, argc, argv); + return AliasCreationHelper(slaveInterp, slaveInterp, targetInterp, + masterPtr, slaveCmd, targetCmd, objc, objv); } /* @@ -2331,12 +3716,10 @@ Tcl_CreateAlias(slaveInterp, slaveCmd, targetInterp, targetCmd, argc, argv) * Gets information about an alias. * * Results: - * TCL_OK if successful, TCL_ERROR else. If TCL_ERROR is returned, the - * result field of the interpreter given as argument will contain an - * error message. + * A standard Tcl result. * * Side effects: - * Manipulates the result field of the interpreter given as argument. + * None. * *---------------------------------------------------------------------- */ @@ -2354,6 +3737,8 @@ Tcl_GetAlias(interp, aliasName, targetInterpPtr, targetNamePtr, argcPtr, Slave *slavePtr; /* Slave record for slave interp. */ Tcl_HashEntry *hPtr; /* Search element. */ Alias *aliasPtr; /* Storage for alias found. */ + int len; + int i; if ((interp == (Tcl_Interp *) NULL) || (aliasName == (char *) NULL)) { return TCL_ERROR; @@ -2376,10 +3761,73 @@ Tcl_GetAlias(interp, aliasName, targetInterpPtr, targetNamePtr, argcPtr, *targetNamePtr = aliasPtr->targetName; } if (argcPtr != (int *) NULL) { - *argcPtr = aliasPtr->argc; + *argcPtr = aliasPtr->objc; } if (argvPtr != (char ***) NULL) { - *argvPtr = aliasPtr->argv; + *argvPtr = (char **) ckalloc((unsigned) sizeof(char *) * + aliasPtr->objc); + for (i = 0; i < aliasPtr->objc; i++) { + *argvPtr[i] = Tcl_GetStringFromObj(aliasPtr->objv[i], &len); + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_ObjGetAlias -- + * + * Object version: Gets information about an alias. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +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. */ +{ + Slave *slavePtr; /* Slave record for slave interp. */ + Tcl_HashEntry *hPtr; /* Search element. */ + Alias *aliasPtr; /* Storage for alias found. */ + + 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; + } + if (objvPtr != (Tcl_Obj ***) NULL) { + *objvPtr = aliasPtr->objv; } return TCL_OK; } |