/*1* tclInterp.c --2*3* This file implements the "interp" command which allows creation4* and manipulation of Tcl interpreters from within Tcl scripts.5*6* Copyright (c) 1995 Sun Microsystems, Inc.7*8* See the file "license.terms" for information on usage and redistribution9* of this file, and for a DISCLAIMER OF ALL WARRANTIES.10*11* SCCS: @(#) tclInterp.c 1.79 96/09/20 17:20:1612*/1314#include <ast.h>15#include <stdio.h>16#include "tclInt.h"17#include "tclPort.h"1819/*20* Counter for how many aliases were created (global)21*/2223static int aliasCounter = 0;2425/*26*27* struct Slave:28*29* Used by the "interp" command to record and find information about slave30* interpreters. Maps from a command name in the master to information about31* a slave interpreter, e.g. what aliases are defined in it.32*/3334typedef struct {35Tcl_Interp *masterInterp; /* Master interpreter for this slave. */36Tcl_HashEntry *slaveEntry; /* Hash entry in masters slave table for37* this slave interpreter. Used to find38* this record, and used when deleting the39* slave interpreter to delete it from the40* masters table. */41Tcl_Interp *slaveInterp; /* The slave interpreter. */42Tcl_Command interpCmd; /* Interpreter object command. */43Tcl_HashTable aliasTable; /* Table which maps from names of commands44* in slave interpreter to struct Alias45* defined below. */46} Slave;4748/*49* struct Alias:50*51* Stores information about an alias. Is stored in the slave interpreter52* and used by the source command to find the target command in the master53* when the source command is invoked.54*/5556typedef struct {57char *aliasName; /* Name of alias command. */58char *targetName; /* Name of target command in master interp. */59Tcl_Interp *targetInterp; /* Master interpreter. */60int argc; /* Count of additional args to pass. */61char **argv; /* Actual additional args to pass. */62Tcl_HashEntry *aliasEntry; /* Entry for the alias hash table in slave.63* This is used by alias deletion to remove64* the alias from the slave interpreter65* alias table. */66Tcl_HashEntry *targetEntry; /* Entry for target command in master.67* This is used in the master interpreter to68* map back from the target command to aliases69* redirecting to it. Random access to this70* hash table is never required - we are using71* a hash table only for convenience. */72Tcl_Command slaveCmd; /* Source command in slave interpreter. */73} Alias;7475/*76* struct Target:77*78* Maps from master interpreter commands back to the source commands in slave79* interpreters. This is needed because aliases can be created between sibling80* interpreters and must be deleted when the target interpreter is deleted. In81* case they would not be deleted the source interpreter would be left with a82* "dangling pointer". One such record is stored in the Master record of the83* master interpreter (in the targetTable hashtable, see below) with the84* master for each alias which directs to a command in the master. These85* records are used to remove the source command for an from a slave if/when86* the master is deleted.87*/8889typedef struct {90Tcl_Command slaveCmd; /* Command for alias in slave interp. */91Tcl_Interp *slaveInterp; /* Slave Interpreter. */92} Target;9394/*95* struct Master:96*97* This record is used for three purposes: First, slaveTable (a hashtable)98* maps from names of commands to slave interpreters. This hashtable is99* used to store information about slave interpreters of this interpreter,100* to map over all slaves, etc. The second purpose is to store information101* about all aliases in slaves (or siblings) which direct to target commands102* in this interpreter (using the targetTable hashtable). The third field in103* the record, isSafe, denotes whether the interpreter is safe or not. Safe104* interpreters have restricted functionality, can only create safe slave105* interpreters and can only load safe extensions.106*/107108typedef struct {109Tcl_HashTable slaveTable; /* Hash table for slave interpreters.110* Maps from command names to Slave records. */111int isSafe; /* Am I a "safe" interpreter? */112Tcl_HashTable targetTable; /* Hash table for Target Records. Contains113* all Target records which denote aliases114* from slaves or sibling interpreters that115* direct to commands in this interpreter. This116* table is used to remove dangling pointers117* from the slave (or sibling) interpreters118* when this interpreter is deleted. */119} Master;120121/*122* Prototypes for local static procedures:123*/124125static int AliasCmd _ANSI_ARGS_((ClientData dummy,126Tcl_Interp *currentInterp, int argc, char **argv));127static void AliasCmdDeleteProc _ANSI_ARGS_((128ClientData clientData));129static int AliasHelper _ANSI_ARGS_((Tcl_Interp *curInterp,130Tcl_Interp *slaveInterp, Tcl_Interp *masterInterp,131Master *masterPtr, char *aliasName,132char *targetName, int argc, char **argv));133static int CreateInterpObject _ANSI_ARGS_((Tcl_Interp *interp,134int argc, char **argv));135static Tcl_Interp *CreateSlave _ANSI_ARGS_((Tcl_Interp *interp,136char *slavePath, int safe));137static int DeleteAlias _ANSI_ARGS_((Tcl_Interp *interp,138Tcl_Interp *slaveInterp, char *aliasName));139static int DescribeAlias _ANSI_ARGS_((Tcl_Interp *interp,140Tcl_Interp *slaveInterp, char *aliasName));141static int DeleteInterpObject _ANSI_ARGS_((Tcl_Interp *interp,142int argc, char **argv));143static int DeleteOneInterpObject _ANSI_ARGS_((Tcl_Interp *interp,144char *path));145static Tcl_Interp *GetInterp _ANSI_ARGS_((Tcl_Interp *interp,146Master *masterPtr, char *path,147Master **masterPtrPtr));148static int GetTarget _ANSI_ARGS_((Tcl_Interp *interp, char *path,149char *aliasName));150static void MasterRecordDeleteProc _ANSI_ARGS_((151ClientData clientData, Tcl_Interp *interp));152static int MakeSafe _ANSI_ARGS_((Tcl_Interp *interp));153static int SlaveAliasHelper _ANSI_ARGS_((Tcl_Interp *interp,154int argc, char **argv));155static int SlaveObjectCmd _ANSI_ARGS_((ClientData dummy,156Tcl_Interp *interp, int argc, char **argv));157static void SlaveObjectDeleteProc _ANSI_ARGS_((158ClientData clientData));159static void SlaveRecordDeleteProc _ANSI_ARGS_((160ClientData clientData, Tcl_Interp *interp));161162/*163* These are all the Tcl core commands which are available in a safe164* interpeter:165*/166167static char *TclCommandsToKeep[] = {168"after", "append", "array",169"break",170"case", "catch", "clock", "close", "concat", "continue",171"eof", "error", "eval", "expr",172"fblocked", "fileevent", "flush", "for", "foreach", "format",173"gets", "global",174"history",175"if", "incr", "info", "interp",176"join",177"lappend", "lindex", "linsert", "list", "llength",178"lower", "lrange", "lreplace", "lsearch", "lsort",179"package", "pid", "proc", "puts",180"read", "regexp", "regsub", "rename", "return",181"scan", "seek", "set", "split", "string", "subst", "switch",182"tell", "time", "trace",183"unset", "unsupported0", "update", "uplevel", "upvar",184"vwait",185"while",186NULL};187static int TclCommandsToKeepCt =188(sizeof (TclCommandsToKeep) / sizeof (char *)) -1 ;189190/*191*----------------------------------------------------------------------192*193* TclPreventAliasLoop --194*195* When defining an alias or renaming a command, prevent an alias196* loop from being formed.197*198* Results:199* A standard Tcl result.200*201* Side effects:202* If TCL_ERROR is returned, the function also sets interp->result203* to an error message.204*205* NOTE:206* This function is public internal (instead of being static to207* this file) because it is also used from Tcl_RenameCmd.208*209*----------------------------------------------------------------------210*/211212int213TclPreventAliasLoop(interp, cmdInterp, cmdName, proc, clientData)214Tcl_Interp *interp; /* Interp in which to report errors. */215Tcl_Interp *cmdInterp; /* Interp in which the command is216* being defined. */217char *cmdName; /* Name of Tcl command we are218* attempting to define. */219Tcl_CmdProc *proc; /* The command procedure for the220* command being created. */221ClientData clientData; /* The client data associated with the222* command to be created. */223{224Alias *aliasPtr, *nextAliasPtr;225Tcl_CmdInfo cmdInfo;226227/*228* If we are not creating or renaming an alias, then it is229* always OK to create or rename the command.230*/231232if (proc != AliasCmd) {233return TCL_OK;234}235236/*237* OK, we are dealing with an alias, so traverse the chain of aliases.238* If we encounter the alias we are defining (or renaming to) any in239* the chain then we have a loop.240*/241242aliasPtr = (Alias *) clientData;243nextAliasPtr = aliasPtr;244while (1) {245246/*247* If the target of the next alias in the chain is the same as the248* source alias, we have a loop.249*/250251if ((strcmp(nextAliasPtr->targetName, cmdName) == 0) &&252(nextAliasPtr->targetInterp == cmdInterp)) {253Tcl_AppendResult(interp, "cannot define or rename alias \"",254aliasPtr->aliasName, "\": would create a loop",255(char *) NULL);256return TCL_ERROR;257}258259/*260* Otherwise, follow the chain one step further. If the target261* command is undefined then there is no loop.262*/263264if (Tcl_GetCommandInfo(nextAliasPtr->targetInterp,265nextAliasPtr->targetName, &cmdInfo) == 0) {266return TCL_OK;267}268269/*270* See if the target command is an alias - if so, follow the271* loop to its target command. Otherwise we do not have a loop.272*/273274if (cmdInfo.proc != AliasCmd) {275return TCL_OK;276}277nextAliasPtr = (Alias *) cmdInfo.clientData;278}279280/* NOTREACHED */281}282283/*284*----------------------------------------------------------------------285*286* MakeSafe --287*288* Makes its argument interpreter contain only functionality that is289* defined to be part of Safe Tcl.290*291* Results:292* None.293*294* Side effects:295* Removes commands from its argument interpreter.296*297*----------------------------------------------------------------------298*/299300static int301MakeSafe(interp)302Tcl_Interp *interp; /* Interpreter to be made safe. */303{304char **argv; /* Args for Tcl_Eval. */305int argc, keep, i, j; /* Loop indices. */306char *cmdGetGlobalCmds = "info commands"; /* What command to run. */307char *cmdNoEnv = "unset env"; /* How to get rid of env. */308Master *masterPtr; /* Master record of interp309* to be made safe. */310Tcl_Channel chan; /* Channel to remove from311* safe interpreter. */312313/*314* Below, Tcl_Eval sets interp->result, so we do not.315*/316317Tcl_ResetResult(interp);318if ((Tcl_Eval(interp, cmdGetGlobalCmds) == TCL_ERROR) ||319(Tcl_SplitList(interp, interp->result, &argc, &argv) != TCL_OK)) {320return TCL_ERROR;321}322for (i = 0; i < argc; i++) {323for (keep = 0, j = 0; j < TclCommandsToKeepCt; j++) {324if (strcmp(TclCommandsToKeep[j], argv[i]) == 0) {325keep = 1;326break;327}328}329if (keep == 0) {330(void) Tcl_DeleteCommand(interp, argv[i]);331}332}333ckfree((char *) argv);334masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord",335NULL);336if (masterPtr == (Master *) NULL) {337panic("MakeSafe: could not find master record");338}339masterPtr->isSafe = 1;340if (Tcl_Eval(interp, cmdNoEnv) == TCL_ERROR) {341return TCL_ERROR;342}343344/*345* Remove the standard channels from the interpreter; safe interpreters346* do not ordinarily have access to stdin, stdout and stderr.347*348* NOTE: These channels are not added to the interpreter by the349* Tcl_CreateInterp call, but may be added later, by another I/O350* operation. We want to ensure that the interpreter does not have351* these channels even if it is being made safe after being used for352* some time..353*/354355chan = Tcl_GetStdChannel(TCL_STDIN);356if (chan != (Tcl_Channel) NULL) {357Tcl_UnregisterChannel(interp, chan);358}359chan = Tcl_GetStdChannel(TCL_STDOUT);360if (chan != (Tcl_Channel) NULL) {361Tcl_UnregisterChannel(interp, chan);362}363chan = Tcl_GetStdChannel(TCL_STDERR);364if (chan != (Tcl_Channel) NULL) {365Tcl_UnregisterChannel(interp, chan);366}367368return TCL_OK;369}370371/*372*----------------------------------------------------------------------373*374* GetInterp --375*376* Helper function to find a slave interpreter given a pathname.377*378* Results:379* Returns the slave interpreter known by that name in the calling380* interpreter, or NULL if no interpreter known by that name exists.381*382* Side effects:383* Assigns to the pointer variable passed in, if not NULL.384*385*----------------------------------------------------------------------386*/387388static Tcl_Interp *389GetInterp(interp, masterPtr, path, masterPtrPtr)390Tcl_Interp *interp; /* Interp. to start search from. */391Master *masterPtr; /* Its master record. */392char *path; /* The path (name) of interp. to be found. */393Master **masterPtrPtr; /* (Return) its master record. */394{395Tcl_HashEntry *hPtr; /* Search element. */396Slave *slavePtr; /* Interim slave record. */397char **argv; /* Split-up path (name) for interp to find. */398int argc, i; /* Loop indices. */399Tcl_Interp *searchInterp; /* Interim storage for interp. to find. */400401if (masterPtrPtr != (Master **) NULL) *masterPtrPtr = masterPtr;402403if (Tcl_SplitList(interp, path, &argc, &argv) != TCL_OK) {404return (Tcl_Interp *) NULL;405}406407for (searchInterp = interp, i = 0; i < argc; i++) {408409hPtr = Tcl_FindHashEntry(&(masterPtr->slaveTable), argv[i]);410if (hPtr == (Tcl_HashEntry *) NULL) {411ckfree((char *) argv);412return (Tcl_Interp *) NULL;413}414slavePtr = (Slave *) Tcl_GetHashValue(hPtr);415searchInterp = slavePtr->slaveInterp;416if (searchInterp == (Tcl_Interp *) NULL) {417ckfree((char *) argv);418return (Tcl_Interp *) NULL;419}420masterPtr = (Master *) Tcl_GetAssocData(searchInterp,421"tclMasterRecord", NULL);422if (masterPtrPtr != (Master **) NULL) *masterPtrPtr = masterPtr;423if (masterPtr == (Master *) NULL) {424ckfree((char *) argv);425return (Tcl_Interp *) NULL;426}427}428ckfree((char *) argv);429return searchInterp;430}431432/*433*----------------------------------------------------------------------434*435* CreateSlave --436*437* Helper function to do the actual work of creating a slave interp438* and new object command. Also optionally makes the new slave439* interpreter "safe".440*441* Results:442* Returns the new Tcl_Interp * if successful or NULL if not. If failed,443* the result of the invoking interpreter contains an error message.444*445* Side effects:446* Creates a new slave interpreter and a new object command.447*448*----------------------------------------------------------------------449*/450451static Tcl_Interp *452CreateSlave(interp, slavePath, safe)453Tcl_Interp *interp; /* Interp. to start search from. */454char *slavePath; /* Path (name) of slave to create. */455int safe; /* Should we make it "safe"? */456{457Master *masterPtr; /* Master record. */458Tcl_Interp *slaveInterp; /* Ptr to slave interpreter. */459Tcl_Interp *masterInterp; /* Ptr to master interp for slave. */460Slave *slavePtr; /* Slave record. */461Tcl_HashEntry *hPtr; /* Entry into interp hashtable. */462int new; /* Indicates whether new entry. */463int argc; /* Count of elements in slavePath. */464char **argv; /* Elements in slavePath. */465char *masterPath; /* Path to its master. */466467masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord",468NULL);469if (masterPtr == (Master *) NULL) {470panic("CreatSlave: could not find master record");471}472473if (Tcl_SplitList(interp, slavePath, &argc, &argv) != TCL_OK) {474return (Tcl_Interp *) NULL;475}476477if (argc < 2) {478masterInterp = interp;479if (argc == 1) {480slavePath = argv[0];481}482} else {483masterPath = Tcl_Merge(argc-1, argv);484masterInterp = GetInterp(interp, masterPtr, masterPath, &masterPtr);485if (masterInterp == (Tcl_Interp *) NULL) {486Tcl_AppendResult(interp, "interpreter named \"", masterPath,487"\" not found", (char *) NULL);488ckfree((char *) argv);489ckfree((char *) masterPath);490return (Tcl_Interp *) NULL;491}492ckfree((char *) masterPath);493slavePath = argv[argc-1];494if (!safe) {495safe = masterPtr->isSafe;496}497}498hPtr = Tcl_CreateHashEntry(&(masterPtr->slaveTable), slavePath, &new);499if (new == 0) {500Tcl_AppendResult(interp, "interpreter named \"", slavePath,501"\" already exists, cannot create", (char *) NULL);502ckfree((char *) argv);503return (Tcl_Interp *) NULL;504}505slaveInterp = Tcl_CreateInterp();506if (slaveInterp == (Tcl_Interp *) NULL) {507panic("CreateSlave: out of memory while creating a new interpreter");508}509slavePtr = (Slave *) ckalloc((unsigned) sizeof(Slave));510slavePtr->masterInterp = masterInterp;511slavePtr->slaveEntry = hPtr;512slavePtr->slaveInterp = slaveInterp;513slavePtr->interpCmd = Tcl_CreateCommand(masterInterp, slavePath,514SlaveObjectCmd, (ClientData) slaveInterp, SlaveObjectDeleteProc);515Tcl_InitHashTable(&(slavePtr->aliasTable), TCL_STRING_KEYS);516(void) Tcl_SetAssocData(slaveInterp, "tclSlaveRecord",517SlaveRecordDeleteProc, (ClientData) slavePtr);518Tcl_SetHashValue(hPtr, (ClientData) slavePtr);519Tcl_SetVar(slaveInterp, "tcl_interactive", "0", TCL_GLOBAL_ONLY);520521if (((safe) && (MakeSafe(slaveInterp) == TCL_ERROR)) ||522((!safe) && (Tcl_Init(slaveInterp) == TCL_ERROR))) {523Tcl_ResetResult(interp);524Tcl_AddErrorInfo(interp, Tcl_GetVar2(slaveInterp, "errorInfo", (char *)525NULL, TCL_GLOBAL_ONLY));526Tcl_SetVar2(interp, "errorCode", (char *) NULL,527Tcl_GetVar2(slaveInterp, "errorCode", (char *) NULL,528TCL_GLOBAL_ONLY),529TCL_GLOBAL_ONLY);530if (slaveInterp->freeProc != NULL) {531interp->result = slaveInterp->result;532interp->freeProc = slaveInterp->freeProc;533slaveInterp->freeProc = 0;534} else {535Tcl_SetResult(interp, slaveInterp->result, TCL_VOLATILE);536}537Tcl_ResetResult(slaveInterp);538(void) Tcl_DeleteCommand(masterInterp, slavePath);539slaveInterp = (Tcl_Interp *) NULL;540}541ckfree((char *) argv);542return slaveInterp;543}544545/*546*----------------------------------------------------------------------547*548* CreateInterpObject -549*550* Helper function to do the actual work of creating a new interpreter551* and an object command.552*553* Results:554* A Tcl result.555*556* Side effects:557* See user documentation for details.558*559*----------------------------------------------------------------------560*/561562static int563CreateInterpObject(interp, argc, argv)564Tcl_Interp *interp; /* Invoking interpreter. */565int argc; /* Number of arguments. */566char **argv; /* Argument strings. */567{568int safe; /* Create a safe interpreter? */569Master *masterPtr; /* Master record. */570int moreFlags; /* Expecting more flag args? */571char *slavePath; /* Name of slave. */572char localSlaveName[200]; /* Local area for creating names. */573int i; /* Loop counter. */574size_t len; /* Length of option argument. */575static int interpCounter = 0; /* Unique id for created names. */576577masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL);578if (masterPtr == (Master *) NULL) {579panic("CreateInterpObject: could not find master record");580}581moreFlags = 1;582slavePath = NULL;583safe = masterPtr->isSafe;584585if (argc < 2 || argc > 5) {586Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],587" create ?-safe? ?--? ?path?\"", (char *) NULL);588return TCL_ERROR;589}590for (i = 2; i < argc; i++) {591len = strlen(argv[i]);592if ((argv[i][0] == '-') && (moreFlags != 0)) {593if ((argv[i][1] == 's') && (strncmp(argv[i], "-safe", len) == 0)594&& (len > 1)){595safe = 1;596} else if ((strncmp(argv[i], "--", len) == 0) && (len > 1)) {597moreFlags = 0;598} else {599Tcl_AppendResult(interp, "bad option \"", argv[i],600"\": should be -safe", (char *) NULL);601return TCL_ERROR;602}603} else {604slavePath = argv[i];605}606}607if (slavePath == (char *) NULL) {608sprintf(localSlaveName, "interp%d", interpCounter);609interpCounter++;610slavePath = localSlaveName;611}612if (CreateSlave(interp, slavePath, safe) != NULL) {613Tcl_AppendResult(interp, slavePath, (char *) NULL);614return TCL_OK;615} else {616/*617* CreateSlave already set interp->result if there was an error,618* so we do not do it here.619*/620return TCL_ERROR;621}622}623624/*625*----------------------------------------------------------------------626*627* DeleteOneInterpObject --628*629* Helper function for DeleteInterpObject. It deals with deleting one630* interpreter at a time.631*632* Results:633* A standard Tcl result.634*635* Side effects:636* Deletes an interpreter and its interpreter object command.637*638*----------------------------------------------------------------------639*/640641static int642DeleteOneInterpObject(interp, path)643Tcl_Interp *interp; /* Interpreter for reporting errors. */644char *path; /* Path of interpreter to delete. */645{646Master *masterPtr; /* Interim storage for master record.*/647Slave *slavePtr; /* Interim storage for slave record. */648Tcl_Interp *masterInterp; /* Master of interp. to delete. */649Tcl_HashEntry *hPtr; /* Search element. */650int localArgc; /* Local copy of count of elements in651* path (name) of interp. to delete. */652char **localArgv; /* Local copy of path. */653char *slaveName; /* Last component in path. */654char *masterPath; /* One-before-last component in path.*/655656masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL);657if (masterPtr == (Master *) NULL) {658panic("DeleteInterpObject: could not find master record");659}660if (Tcl_SplitList(interp, path, &localArgc, &localArgv) != TCL_OK) {661Tcl_AppendResult(interp, "bad interpreter path \"", path,662"\"", (char *) NULL);663return TCL_ERROR;664}665if (localArgc < 2) {666masterInterp = interp;667if (localArgc == 0) {668slaveName = "";669} else {670slaveName = localArgv[0];671}672} else {673masterPath = Tcl_Merge(localArgc-1, localArgv);674masterInterp = GetInterp(interp, masterPtr, masterPath, &masterPtr);675if (masterInterp == (Tcl_Interp *) NULL) {676Tcl_AppendResult(interp, "interpreter named \"", masterPath,677"\" not found", (char *) NULL);678ckfree((char *) localArgv);679ckfree((char *) masterPath);680return TCL_ERROR;681}682ckfree((char *) masterPath);683slaveName = localArgv[localArgc-1];684}685hPtr = Tcl_FindHashEntry(&(masterPtr->slaveTable), slaveName);686if (hPtr == (Tcl_HashEntry *) NULL) {687ckfree((char *) localArgv);688Tcl_AppendResult(interp, "interpreter named \"", path,689"\" not found", (char *) NULL);690return TCL_ERROR;691}692slavePtr = (Slave *) Tcl_GetHashValue(hPtr);693slaveName = Tcl_GetCommandName(masterInterp, slavePtr->interpCmd);694if (Tcl_DeleteCommand(masterInterp, slaveName) != 0) {695ckfree((char *) localArgv);696Tcl_AppendResult(interp, "interpreter named \"", path,697"\" not found", (char *) NULL);698return TCL_ERROR;699}700ckfree((char *) localArgv);701return TCL_OK;702}703704/*705*----------------------------------------------------------------------706*707* DeleteInterpObject --708*709* Helper function to do the work of deleting zero or more710* interpreters and their interpreter object commands.711*712* Results:713* A standard Tcl result.714*715* Side effects:716* Deletes interpreters and their interpreter object command.717*718*----------------------------------------------------------------------719*/720721static int722DeleteInterpObject(interp, argc, argv)723Tcl_Interp *interp; /* Interpreter start search from. */724int argc; /* Number of arguments in vector. */725char **argv; /* Contains path to interps to726* delete. */727{728int i;729730for (i = 2; i < argc; i++) {731if (DeleteOneInterpObject(interp, argv[i]) != TCL_OK) {732return TCL_ERROR;733}734}735return TCL_OK;736}737738/*739*----------------------------------------------------------------------740*741* AliasHelper --742*743* Helper function to do the work to actually create an alias or744* delete an alias.745*746* Results:747* A standard Tcl result.748*749* Side effects:750* An alias command is created and entered into the alias table751* for the slave interpreter.752*753*----------------------------------------------------------------------754*/755756static int757AliasHelper(curInterp, slaveInterp, masterInterp, masterPtr,758aliasName, targetName, argc, argv)759Tcl_Interp *curInterp; /* Interp that invoked this proc. */760Tcl_Interp *slaveInterp; /* Interp where alias cmd will live761* or from which alias will be762* deleted. */763Tcl_Interp *masterInterp; /* Interp where target cmd will be. */764Master *masterPtr; /* Master record for target interp. */765char *aliasName; /* Name of alias cmd. */766char *targetName; /* Name of target cmd. */767int argc; /* Additional arguments to store */768char **argv; /* with alias. */769{770Alias *aliasPtr; /* Storage for alias data. */771Alias *tmpAliasPtr; /* Temp storage for alias to delete. */772char *tmpAliasName; /* Temp storage for name of alias773* to delete. */774Tcl_HashEntry *hPtr; /* Entry into interp hashtable. */775int i; /* Loop index. */776int new; /* Is it a new hash entry? */777Target *targetPtr; /* Maps from target command in master778* to source command in slave. */779Slave *slavePtr; /* Maps from source command in slave780* to target command in master. */781782slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord", NULL);783784/*785* Fix it up if there is no slave record. This can happen if someone786* uses "" as the source for an alias.787*/788789if (slavePtr == (Slave *) NULL) {790slavePtr = (Slave *) ckalloc((unsigned) sizeof(Slave));791slavePtr->masterInterp = (Tcl_Interp *) NULL;792slavePtr->slaveEntry = (Tcl_HashEntry *) NULL;793slavePtr->slaveInterp = slaveInterp;794slavePtr->interpCmd = (Tcl_Command) NULL;795Tcl_InitHashTable(&(slavePtr->aliasTable), TCL_STRING_KEYS);796(void) Tcl_SetAssocData(slaveInterp, "tclSlaveRecord",797SlaveRecordDeleteProc, (ClientData) slavePtr);798}799800if ((targetName == (char *) NULL) || (targetName[0] == '\0')) {801if (argc != 0) {802Tcl_AppendResult(curInterp, "malformed command: should be",803" \"alias ", aliasName, " {}\"", (char *) NULL);804return TCL_ERROR;805}806807return DeleteAlias(curInterp, slaveInterp, aliasName);808}809810aliasPtr = (Alias *) ckalloc((unsigned) sizeof(Alias));811aliasPtr->aliasName = (char *) ckalloc((unsigned) strlen(aliasName)+1);812aliasPtr->targetName = (char *) ckalloc((unsigned) strlen(targetName)+1);813strcpy(aliasPtr->aliasName, aliasName);814strcpy(aliasPtr->targetName, targetName);815aliasPtr->targetInterp = masterInterp;816817aliasPtr->argv = (char **) NULL;818aliasPtr->argc = argc;819if (aliasPtr->argc > 0) {820aliasPtr->argv = (char **) ckalloc((unsigned) sizeof(char *) *821aliasPtr->argc);822for (i = 0; i < argc; i++) {823aliasPtr->argv[i] = (char *) ckalloc((unsigned) strlen(argv[i])+1);824strcpy(aliasPtr->argv[i], argv[i]);825}826}827828if (TclPreventAliasLoop(curInterp, slaveInterp, aliasName, AliasCmd,829(ClientData) aliasPtr) != TCL_OK) {830for (i = 0; i < argc; i++) {831ckfree(aliasPtr->argv[i]);832}833if (aliasPtr->argv != (char **) NULL) {834ckfree((char *) aliasPtr->argv);835}836ckfree(aliasPtr->aliasName);837ckfree(aliasPtr->targetName);838ckfree((char *) aliasPtr);839840return TCL_ERROR;841}842843aliasPtr->slaveCmd = Tcl_CreateCommand(slaveInterp, aliasName, AliasCmd,844(ClientData) aliasPtr, AliasCmdDeleteProc);845846/*847* Make an entry in the alias table. If it already exists delete848* the alias command. Then retry.849*/850851do {852hPtr = Tcl_CreateHashEntry(&(slavePtr->aliasTable), aliasName, &new);853if (new == 0) {854tmpAliasPtr = (Alias *) Tcl_GetHashValue(hPtr);855tmpAliasName = Tcl_GetCommandName(slaveInterp,856tmpAliasPtr->slaveCmd);857(void) Tcl_DeleteCommand(slaveInterp, tmpAliasName);858859/*860* The hash entry should be deleted by the Tcl_DeleteCommand861* above, in its command deletion callback (most likely this862* will be AliasCmdDeleteProc, which does the deletion).863*/864}865} while (new == 0);866aliasPtr->aliasEntry = hPtr;867Tcl_SetHashValue(hPtr, (ClientData) aliasPtr);868869/*870* Create the new command. We must do it after deleting any old command,871* because the alias may be pointing at a renamed alias, as in:872*873* interp alias {} foo {} bar # Create an alias "foo"874* rename foo zop # Now rename the alias875* interp alias {} foo {} zop # Now recreate "foo"...876*/877878targetPtr = (Target *) ckalloc((unsigned) sizeof(Target));879targetPtr->slaveCmd = aliasPtr->slaveCmd;880targetPtr->slaveInterp = slaveInterp;881882do {883hPtr = Tcl_CreateHashEntry(&(masterPtr->targetTable),884(char *) aliasCounter, &new);885aliasCounter++;886} while (new == 0);887888Tcl_SetHashValue(hPtr, (ClientData) targetPtr);889890aliasPtr->targetEntry = hPtr;891892curInterp->result = aliasPtr->aliasName;893894return TCL_OK;895}896897/*898*----------------------------------------------------------------------899*900* SlaveAliasHelper -901*902* Handles the different forms of the "interp alias" command:903* - interp alias slavePath aliasName904* Describes an alias.905* - interp alias slavePath aliasName {}906* Deletes an alias.907* - interp alias slavePath srcCmd masterPath targetCmd args...908* Creates an alias.909*910* Results:911* A Tcl result.912*913* Side effects:914* See user documentation for details.915*916*----------------------------------------------------------------------917*/918919static int920SlaveAliasHelper(interp, argc, argv)921Tcl_Interp *interp; /* Current interpreter. */922int argc; /* Number of arguments. */923char **argv; /* Argument strings. */924{925Master *masterPtr; /* Master record for current interp. */926Tcl_Interp *slaveInterp, /* Interpreters used when */927*masterInterp; /* creating an alias btn siblings. */928Master *masterMasterPtr; /* Master record for master interp. */929930masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL);931if (masterPtr == (Master *) NULL) {932panic("SlaveAliasHelper: could not find master record");933}934if (argc < 4) {935Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],936" alias slavePath slaveCmd masterPath masterCmd ?args ..?\"",937(char *) NULL);938return TCL_ERROR;939}940slaveInterp = GetInterp(interp, masterPtr, argv[2], NULL);941if (slaveInterp == (Tcl_Interp *) NULL) {942Tcl_AppendResult(interp, "could not find interpreter \"",943argv[2], "\"", (char *) NULL);944return TCL_ERROR;945}946if (argc == 4) {947return DescribeAlias(interp, slaveInterp, argv[3]);948}949if (argc == 5 && strcmp(argv[4], "") == 0) {950return DeleteAlias(interp, slaveInterp, argv[3]);951}952if (argc < 6) {953Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],954" alias slavePath slaveCmd masterPath masterCmd ?args ..?\"",955(char *) NULL);956return TCL_ERROR;957}958masterInterp = GetInterp(interp, masterPtr, argv[4], &masterMasterPtr);959if (masterInterp == (Tcl_Interp *) NULL) {960Tcl_AppendResult(interp, "could not find interpreter \"",961argv[4], "\"", (char *) NULL);962return TCL_ERROR;963}964return AliasHelper(interp, slaveInterp, masterInterp, masterMasterPtr,965argv[3], argv[5], argc-6, argv+6);966}967968/*969*----------------------------------------------------------------------970*971* DescribeAlias --972*973* Sets interp->result to a Tcl list describing the given alias in the974* given interpreter: its target command and the additional arguments975* to prepend to any invocation of the alias.976*977* Results:978* A standard Tcl result.979*980* Side effects:981* None.982*983*----------------------------------------------------------------------984*/985986static int987DescribeAlias(interp, slaveInterp, aliasName)988Tcl_Interp *interp; /* Interpreter for result and errors. */989Tcl_Interp *slaveInterp; /* Interpreter defining alias. */990char *aliasName; /* Name of alias to describe. */991{992Slave *slavePtr; /* Slave record for slave interpreter. */993Tcl_HashEntry *hPtr; /* Search variable. */994Alias *aliasPtr; /* Structure describing alias. */995int i; /* Loop variable. */996997slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord",998NULL);999if (slavePtr == (Slave *) NULL) {10001001/*1002* It's possible that the interpreter still does not have a slave1003* record. If so, create such a record now. This is only possible1004* for interpreters that were created with Tcl_CreateInterp, not1005* those created with Tcl_CreateSlave, so this interpreter does1006* not have a master.1007*/10081009slavePtr = (Slave *) ckalloc((unsigned) sizeof(Slave));1010slavePtr->masterInterp = (Tcl_Interp *) NULL;1011slavePtr->slaveEntry = (Tcl_HashEntry *) NULL;1012slavePtr->slaveInterp = slaveInterp;1013slavePtr->interpCmd = (Tcl_Command) NULL;1014Tcl_InitHashTable(&(slavePtr->aliasTable), TCL_STRING_KEYS);1015(void) Tcl_SetAssocData(slaveInterp, "tclSlaveRecord",1016SlaveRecordDeleteProc, (ClientData) slavePtr);1017}1018hPtr = Tcl_FindHashEntry(&(slavePtr->aliasTable), aliasName);1019if (hPtr == (Tcl_HashEntry *) NULL) {1020return TCL_OK;1021}1022aliasPtr = (Alias *) Tcl_GetHashValue(hPtr);1023Tcl_AppendResult(interp, aliasPtr->targetName, (char *) NULL);1024for (i = 0; i < aliasPtr->argc; i++) {1025Tcl_AppendElement(interp, aliasPtr->argv[i]);1026}10271028return TCL_OK;1029}10301031/*1032*----------------------------------------------------------------------1033*1034* DeleteAlias --1035*1036* Deletes the given alias from the slave interpreter given.1037*1038* Results:1039* A standard Tcl result.1040*1041* Side effects:1042* Deletes the alias from the slave interpreter.1043*1044*----------------------------------------------------------------------1045*/10461047static int1048DeleteAlias(interp, slaveInterp, aliasName)1049Tcl_Interp *interp; /* Interpreter for result and errors. */1050Tcl_Interp *slaveInterp; /* Interpreter defining alias. */1051char *aliasName; /* Name of alias to delete. */1052{1053Slave *slavePtr; /* Slave record for slave interpreter. */1054Tcl_HashEntry *hPtr; /* Search variable. */1055Alias *aliasPtr; /* Structure describing alias to delete. */10561057slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord",1058NULL);1059if (slavePtr == (Slave *) NULL) {1060Tcl_AppendResult(interp, "alias \"", aliasName, "\" not found",1061(char *) NULL);1062return TCL_ERROR;1063}10641065/*1066* Get the alias from the alias table, determine the current1067* true name of the alias (it may have been renamed!) and then1068* delete the true command name. The deleteProc on the alias1069* command will take care of removing the entry from the alias1070* table.1071*/10721073hPtr = Tcl_FindHashEntry(&(slavePtr->aliasTable), aliasName);1074if (hPtr == (Tcl_HashEntry *) NULL) {1075Tcl_AppendResult(interp, "alias \"", aliasName, "\" not found",1076(char *) NULL);1077return TCL_ERROR;1078}1079aliasPtr = (Alias *) Tcl_GetHashValue(hPtr);1080aliasName = Tcl_GetCommandName(slaveInterp, aliasPtr->slaveCmd);10811082/*1083* NOTE: The deleteProc for this command will delete the1084* alias from the hash table. The deleteProc will also1085* delete the target information from the master interpreter1086* target table.1087*/10881089if (Tcl_DeleteCommand(slaveInterp, aliasName) != 0) {1090panic("DeleteAlias: did not find alias to be deleted");1091}10921093return TCL_OK;1094}10951096/*1097*----------------------------------------------------------------------1098*1099* Tcl_GetInterpPath --1100*1101* Sets the result of the asking interpreter to a proper Tcl list1102* containing the names of interpreters between the asking and1103* target interpreters. The target interpreter must be either the1104* same as the asking interpreter or one of its slaves (including1105* recursively).1106*1107* Results:1108* TCL_OK if the target interpreter is the same as, or a descendant1109* of, the asking interpreter; TCL_ERROR else. This way one can1110* distinguish between the case where the asking and target interps1111* are the same (an empty list is the result, and TCL_OK is returned)1112* and when the target is not a descendant of the asking interpreter1113* (in which case the Tcl result is an error message and the function1114* returns TCL_ERROR).1115*1116* Side effects:1117* None.1118*1119*----------------------------------------------------------------------1120*/11211122int1123Tcl_GetInterpPath(askingInterp, targetInterp)1124Tcl_Interp *askingInterp; /* Interpreter to start search from. */1125Tcl_Interp *targetInterp; /* Interpreter to find. */1126{1127Master *masterPtr; /* Interim storage for Master record. */1128Slave *slavePtr; /* Interim storage for Slave record. */11291130if (targetInterp == askingInterp) {1131return TCL_OK;1132}1133if (targetInterp == (Tcl_Interp *) NULL) {1134return TCL_ERROR;1135}1136slavePtr = (Slave *) Tcl_GetAssocData(targetInterp, "tclSlaveRecord",1137NULL);1138if (slavePtr == (Slave *) NULL) {1139return TCL_ERROR;1140}1141if (Tcl_GetInterpPath(askingInterp, slavePtr->masterInterp) == TCL_ERROR) {1142/*1143* AskingInterp->result was set by recursive call.1144*/1145return TCL_ERROR;1146}1147masterPtr = (Master *) Tcl_GetAssocData(slavePtr->masterInterp,1148"tclMasterRecord", NULL);1149if (masterPtr == (Master *) NULL) {1150panic("Tcl_GetInterpPath: could not find master record");1151}1152Tcl_AppendElement(askingInterp, Tcl_GetHashKey(&(masterPtr->slaveTable),1153slavePtr->slaveEntry));1154return TCL_OK;1155}11561157/*1158*----------------------------------------------------------------------1159*1160* GetTarget --1161*1162* Sets the result of the invoking interpreter to a path name for1163* the target interpreter of an alias in one of the slaves.1164*1165* Results:1166* TCL_OK if the target interpreter of the alias is a slave of the1167* invoking interpreter, TCL_ERROR else.1168*1169* Side effects:1170* Sets the result of the invoking interpreter.1171*1172*----------------------------------------------------------------------1173*/11741175static int1176GetTarget(askingInterp, path, aliasName)1177Tcl_Interp *askingInterp; /* Interpreter to start search from. */1178char *path; /* The path of the interp to find. */1179char *aliasName; /* The target of this allias. */1180{1181Tcl_Interp *slaveInterp; /* Interim storage for slave. */1182Slave *slaveSlavePtr; /* Its Slave record. */1183Master *masterPtr; /* Interim storage for Master record. */1184Tcl_HashEntry *hPtr; /* Search element. */1185Alias *aliasPtr; /* Data describing the alias. */11861187Tcl_ResetResult(askingInterp);11881189masterPtr = (Master *) Tcl_GetAssocData(askingInterp, "tclMasterRecord",1190NULL);1191if (masterPtr == (Master *) NULL) {1192panic("GetTarget: could not find master record");1193}1194slaveInterp = GetInterp(askingInterp, masterPtr, path, NULL);1195if (slaveInterp == (Tcl_Interp *) NULL) {1196Tcl_AppendResult(askingInterp, "could not find interpreter \"",1197path, "\"", (char *) NULL);1198return TCL_ERROR;1199}1200slaveSlavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord",1201NULL);1202if (slaveSlavePtr == (Slave *) NULL) {1203panic("GetTarget: could not find slave record");1204}1205hPtr = Tcl_FindHashEntry(&(slaveSlavePtr->aliasTable), aliasName);1206if (hPtr == (Tcl_HashEntry *) NULL) {1207Tcl_AppendResult(askingInterp, "alias \"", aliasName, "\" in path \"",1208path, "\" not found", (char *) NULL);1209return TCL_ERROR;1210}1211aliasPtr = (Alias *) Tcl_GetHashValue(hPtr);1212if (aliasPtr == (Alias *) NULL) {1213panic("GetTarget: could not find alias record");1214}1215if (Tcl_GetInterpPath(askingInterp, aliasPtr->targetInterp) == TCL_ERROR) {1216Tcl_ResetResult(askingInterp);1217Tcl_AppendResult(askingInterp, "target interpreter for alias \"",1218aliasName, "\" in path \"", path, "\" is not my descendant",1219(char *) NULL);1220return TCL_ERROR;1221}1222return TCL_OK;1223}12241225/*1226*----------------------------------------------------------------------1227*1228* Tcl_InterpCmd --1229*1230* This procedure is invoked to process the "interp" Tcl command.1231* See the user documentation for details on what it does.1232*1233* Results:1234* A standard Tcl result.1235*1236* Side effects:1237* See the user documentation.1238*1239*----------------------------------------------------------------------1240*/1241/* ARGSUSED */1242int1243Tcl_InterpCmd(clientData, interp, argc, argv)1244ClientData clientData; /* Unused. */1245Tcl_Interp *interp; /* Current interpreter. */1246int argc; /* Number of arguments. */1247char **argv; /* Argument strings. */1248{1249Tcl_Interp *slaveInterp; /* A slave. */1250Tcl_Interp *masterInterp; /* A master. */1251Master *masterPtr; /* Master record for current interp. */1252Slave *slavePtr; /* Record for slave interp. */1253Tcl_HashEntry *hPtr; /* Search variable. */1254Tcl_HashSearch hSearch; /* Iteration variable. */1255size_t len; /* Length of command name. */1256int result; /* Result of eval. */1257char *cmdName; /* Name of sub command to do. */1258char *cmd; /* Command to eval. */1259Tcl_Channel chan; /* Channel to share or transfer. */12601261if (argc < 2) {1262Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],1263" cmd ?arg ...?\"", (char *) NULL);1264return TCL_ERROR;1265}1266cmdName = argv[1];12671268masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL);1269if (masterPtr == (Master *) NULL) {1270panic("Tcl_InterpCmd: could not find master record");1271}12721273len = strlen(cmdName);12741275if (cmdName[0] == 'a') {1276if ((strncmp(cmdName, "alias", len) == 0) && (len <= 5)) {1277return SlaveAliasHelper(interp, argc, argv);1278}12791280if (strcmp(cmdName, "aliases") == 0) {1281if (argc != 2 && argc != 3) {1282Tcl_AppendResult(interp, "wrong # args: should be \"",1283argv[0], " aliases ?path?\"", (char *) NULL);1284return TCL_ERROR;1285}1286if (argc == 3) {1287slaveInterp = GetInterp(interp, masterPtr, argv[2], NULL);1288if (slaveInterp == (Tcl_Interp *) NULL) {1289Tcl_AppendResult(interp, "interpreter \"",1290argv[2], "\" not found", (char *) NULL);1291return TCL_ERROR;1292}1293} else {1294slaveInterp = interp;1295}1296slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp,1297"tclSlaveRecord", NULL);1298if (slavePtr == (Slave *) NULL) {1299return TCL_OK;1300}1301for (hPtr = Tcl_FirstHashEntry(&(slavePtr->aliasTable), &hSearch);1302hPtr != NULL;1303hPtr = Tcl_NextHashEntry(&hSearch)) {1304Tcl_AppendElement(interp,1305Tcl_GetHashKey(&(slavePtr->aliasTable), hPtr));1306}1307return TCL_OK;1308}1309}13101311if ((cmdName[0] == 'c') && (strncmp(cmdName, "create", len) == 0)) {1312return CreateInterpObject(interp, argc, argv);1313}13141315if ((cmdName[0] == 'd') && (strncmp(cmdName, "delete", len) == 0)) {1316return DeleteInterpObject(interp, argc, argv);1317}13181319if (cmdName[0] == 'e') {1320if ((strncmp(cmdName, "eval", len) == 0) && (len > 1)) {1321if (argc < 4) {1322Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],1323" eval path arg ?arg ...?\"", (char *) NULL);1324return TCL_ERROR;1325}1326slaveInterp = GetInterp(interp, masterPtr, argv[2], NULL);1327if (slaveInterp == (Tcl_Interp *) NULL) {1328Tcl_AppendResult(interp, "interpreter named \"", argv[2],1329"\" not found", (char *) NULL);1330return TCL_ERROR;1331}1332cmd = Tcl_Concat(argc-3, argv+3);1333Tcl_Preserve((ClientData) slaveInterp);1334result = Tcl_Eval(slaveInterp, cmd);1335ckfree((char *) cmd);13361337/*1338* Now make the result and any error information accessible. We1339* have to be careful because the slave interpreter and the current1340* interpreter can be the same - do not destroy the result.. This1341* can happen if an interpreter contains an alias which is directed1342* at a target command in the same interpreter.1343*/13441345if (interp != slaveInterp) {1346if (result == TCL_ERROR) {13471348/*1349* An error occurred, so transfer error information from1350* the target interpreter back to our interpreter. Must1351* clear interp's result before calling Tcl_AddErrorInfo,1352* since Tcl_AddErrorInfo will store the interp's result in1353* errorInfo before appending slaveInterp's $errorInfo;1354* we've already got everything we need in the slave1355* interpreter's $errorInfo.1356*/13571358Tcl_ResetResult(interp);1359Tcl_AddErrorInfo(interp, Tcl_GetVar2(slaveInterp,1360"errorInfo", (char *) NULL, TCL_GLOBAL_ONLY));1361Tcl_SetVar2(interp, "errorCode", (char *) NULL,1362Tcl_GetVar2(slaveInterp, "errorCode", (char *)1363NULL, TCL_GLOBAL_ONLY),1364TCL_GLOBAL_ONLY);1365}1366if (slaveInterp->freeProc != NULL) {1367interp->result = slaveInterp->result;1368interp->freeProc = slaveInterp->freeProc;1369slaveInterp->freeProc = 0;1370} else {1371Tcl_SetResult(interp, slaveInterp->result, TCL_VOLATILE);1372}1373Tcl_ResetResult(slaveInterp);1374}1375Tcl_Release((ClientData) slaveInterp);1376return result;1377}1378if ((strncmp(cmdName, "exists", len) == 0) && (len > 2)) {1379if (argc > 3) {1380Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],1381" exists ?path?\"", (char *) NULL);1382return TCL_ERROR;1383}1384if (argc == 3) {1385if (GetInterp(interp, masterPtr, argv[2], NULL) ==1386(Tcl_Interp *) NULL) {1387Tcl_AppendResult(interp, "0", (char *) NULL);1388} else {1389Tcl_AppendResult(interp, "1", (char *) NULL);1390}1391} else {1392Tcl_AppendResult(interp, "1", (char *) NULL);1393}1394return TCL_OK;1395}1396}13971398if (cmdName[0] == 'i') {1399if ((len > 1) && (strncmp(cmdName, "issafe", len) == 0)) {1400if (argc > 3) {1401Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],1402" issafe ?path?\"", (char *) NULL);1403return TCL_ERROR;1404}1405if (argc == 3) {1406slaveInterp = GetInterp(interp, masterPtr, argv[2],1407&masterPtr);1408if (slaveInterp == (Tcl_Interp *) NULL) {1409Tcl_AppendResult(interp, "interpreter \"", argv[2],1410"\" not found", (char *) NULL);1411return TCL_ERROR;1412}1413}1414if (masterPtr->isSafe == 0) {1415Tcl_AppendResult(interp, "0", (char *) NULL);1416} else {1417Tcl_AppendResult(interp, "1", (char *) NULL);1418}1419return TCL_OK;1420}1421}14221423if (cmdName[0] == 's') {1424if ((strncmp(cmdName, "slaves", len) == 0) && (len > 1)) {1425if (argc != 2 && argc != 3) {1426Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],1427" slaves ?path?\"", (char *) NULL);1428return TCL_ERROR;1429}1430if (argc == 3) {1431if (GetInterp(interp, masterPtr, argv[2], &masterPtr) ==1432(Tcl_Interp *) NULL) {1433Tcl_AppendResult(interp, "interpreter \"", argv[2],1434"\" not found", (char *) NULL);1435return TCL_ERROR;1436}1437}1438for (hPtr = Tcl_FirstHashEntry(&(masterPtr->slaveTable), &hSearch);1439hPtr != NULL;1440hPtr = Tcl_NextHashEntry(&hSearch)) {1441Tcl_AppendElement(interp,1442Tcl_GetHashKey(&(masterPtr->slaveTable), hPtr));1443}1444return TCL_OK;1445}1446if ((strncmp(cmdName, "share", len) == 0) && (len > 1)) {1447if (argc != 5) {1448Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],1449" share srcPath channelId destPath\"", (char *) NULL);1450return TCL_ERROR;1451}1452masterInterp = GetInterp(interp, masterPtr, argv[2], NULL);1453if (masterInterp == (Tcl_Interp *) NULL) {1454Tcl_AppendResult(interp, "interpreter \"", argv[2],1455"\" not found", (char *) NULL);1456return TCL_ERROR;1457}1458slaveInterp = GetInterp(interp, masterPtr, argv[4], NULL);1459if (slaveInterp == (Tcl_Interp *) NULL) {1460Tcl_AppendResult(interp, "interpreter \"", argv[4],1461"\" not found", (char *) NULL);1462return TCL_ERROR;1463}1464chan = Tcl_GetChannel(masterInterp, argv[3], NULL);1465if (chan == (Tcl_Channel) NULL) {1466if (interp != masterInterp) {1467Tcl_AppendResult(interp, masterInterp->result,1468(char *) NULL);1469Tcl_ResetResult(masterInterp);1470}1471return TCL_ERROR;1472}1473Tcl_RegisterChannel(slaveInterp, chan);1474return TCL_OK;1475}1476}14771478if ((cmdName[0] == 't') && (strncmp(cmdName, "target", len) == 0)) {1479if (argc != 4) {1480Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],1481" target path alias\"", (char *) NULL);1482return TCL_ERROR;1483}1484return GetTarget(interp, argv[2], argv[3]);1485}14861487if ((cmdName[0] == 't') && (strncmp(cmdName, "transfer", len) == 0)) {1488if (argc != 5) {1489Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],1490" transfer srcPath channelId destPath\"", (char *) NULL);1491return TCL_ERROR;1492}1493masterInterp = GetInterp(interp, masterPtr, argv[2], NULL);1494if (masterInterp == (Tcl_Interp *) NULL) {1495Tcl_AppendResult(interp, "interpreter \"", argv[2],1496"\" not found", (char *) NULL);1497return TCL_ERROR;1498}1499slaveInterp = GetInterp(interp, masterPtr, argv[4], NULL);1500if (slaveInterp == (Tcl_Interp *) NULL) {1501Tcl_AppendResult(interp, "interpreter \"", argv[4],1502"\" not found", (char *) NULL);1503return TCL_ERROR;1504}1505chan = Tcl_GetChannel(masterInterp, argv[3], NULL);1506if (chan == (Tcl_Channel) NULL) {1507if (interp != masterInterp) {1508Tcl_AppendResult(interp, masterInterp->result, (char *) NULL);1509Tcl_ResetResult(masterInterp);1510}1511return TCL_ERROR;1512}1513Tcl_RegisterChannel(slaveInterp, chan);1514if (Tcl_UnregisterChannel(masterInterp, chan) != TCL_OK) {1515if (interp != masterInterp) {1516Tcl_AppendResult(interp, masterInterp->result, (char *) NULL);1517Tcl_ResetResult(masterInterp);1518}1519return TCL_ERROR;1520}15211522return TCL_OK;1523}15241525Tcl_AppendResult(interp, "bad option \"", argv[1],1526"\": should be alias, aliases, create, delete, exists, eval, ",1527"issafe, share, slaves, target or transfer", (char *) NULL);1528return TCL_ERROR;1529}15301531/*1532*----------------------------------------------------------------------1533*1534* SlaveObjectCmd --1535*1536* Command to manipulate an interpreter, e.g. to send commands to it1537* to be evaluated. One such command exists for each slave interpreter.1538*1539* Results:1540* A standard Tcl result.1541*1542* Side effects:1543* See user documentation for details.1544*1545*----------------------------------------------------------------------1546*/15471548static int1549SlaveObjectCmd(clientData, interp, argc, argv)1550ClientData clientData; /* Slave interpreter. */1551Tcl_Interp *interp; /* Current interpreter. */1552int argc; /* Number of arguments. */1553char **argv; /* Argument strings. */1554{1555Master *masterPtr; /* Master record for slave interp. */1556Slave *slavePtr; /* Slave record. */1557Tcl_Interp *slaveInterp; /* Slave interpreter. */1558char *cmdName; /* Name of command to do. */1559char *cmd; /* Command to evaluate in slave1560* interpreter. */1561Alias *aliasPtr; /* Alias information. */1562Tcl_HashEntry *hPtr; /* For local searches. */1563Tcl_HashSearch hSearch; /* For local searches. */1564int result; /* Loop counter, status return. */1565size_t len; /* Length of command name. */15661567if (argc < 2) {1568Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],1569" cmd ?arg ...?\"", (char *) NULL);1570return TCL_ERROR;1571}15721573slaveInterp = (Tcl_Interp *) clientData;1574if (slaveInterp == (Tcl_Interp *) NULL) {1575Tcl_AppendResult(interp, "interpreter ", argv[0], " has been deleted",1576(char *) NULL);1577return TCL_ERROR;1578}15791580slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp,1581"tclSlaveRecord", NULL);1582if (slavePtr == (Slave *) NULL) {1583panic("SlaveObjectCmd: could not find slave record");1584}15851586cmdName = argv[1];1587len = strlen(cmdName);15881589if (cmdName[0] == 'a') {1590if (strncmp(cmdName, "alias", len) == 0) {1591switch (argc-2) {1592case 0:1593Tcl_AppendResult(interp, "wrong # args: should be \"",1594argv[0], " alias aliasName ?targetName? ?args..?",1595(char *) NULL);1596return TCL_ERROR;15971598case 1:15991600/*1601* Return the name of the command in the current1602* interpreter for which the argument is an alias in the1603* slave interpreter, and the list of saved arguments1604*/16051606return DescribeAlias(interp, slaveInterp, argv[2]);16071608default:1609masterPtr = (Master *) Tcl_GetAssocData(interp,1610"tclMasterRecord", NULL);1611if (masterPtr == (Master *) NULL) {1612panic("SlaveObjectCmd: could not find master record");1613}1614return AliasHelper(interp, slaveInterp, interp, masterPtr,1615argv[2], argv[3], argc-4, argv+4);1616}1617}16181619if (strncmp(cmdName, "aliases", len) == 0) {16201621/*1622* Return the names of all the aliases created in the1623* slave interpreter.1624*/16251626for (hPtr = Tcl_FirstHashEntry(&(slavePtr->aliasTable),1627&hSearch);1628hPtr != (Tcl_HashEntry *) NULL;1629hPtr = Tcl_NextHashEntry(&hSearch)) {1630aliasPtr = (Alias *) Tcl_GetHashValue(hPtr);1631Tcl_AppendElement(interp, aliasPtr->aliasName);1632}1633return TCL_OK;1634}1635}16361637if (cmdName[0] == 'e') {1638if ((len > 1) && (strncmp(cmdName, "eval", len) == 0)) {1639if (argc < 3) {1640Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],1641" eval arg ?arg ...?\"", (char *) NULL);1642return TCL_ERROR;1643}16441645cmd = Tcl_Concat(argc-2, argv+2);1646Tcl_Preserve((ClientData) slaveInterp);1647result = Tcl_Eval(slaveInterp, cmd);1648ckfree((char *) cmd);16491650/*1651* Make the result and any error information accessible. We have1652* to be careful because the slave interpreter and the current1653* interpreter can be the same - do not destroy the result.. This1654* can happen if an interpreter contains an alias which is directed1655* at a target command in the same interpreter.1656*/16571658if (interp != slaveInterp) {1659if (result == TCL_ERROR) {16601661/*1662* An error occurred, so transfer error information from the1663* destination interpreter back to our interpreter. Clear1664* interp's result before calling Tcl_AddErrorInfo, since1665* Tcl_AddErrorInfo stores the interp's result in errorInfo1666* before appending slaveInterp's $errorInfo;1667* we've already got everything we need in the slave1668* interpreter's $errorInfo.1669*/16701671Tcl_ResetResult(interp);1672Tcl_AddErrorInfo(interp, Tcl_GetVar2(slaveInterp,1673"errorInfo", (char *) NULL, TCL_GLOBAL_ONLY));1674Tcl_SetVar2(interp, "errorCode", (char *) NULL,1675Tcl_GetVar2(slaveInterp, "errorCode",1676(char *) NULL, TCL_GLOBAL_ONLY),1677TCL_GLOBAL_ONLY);1678}1679if (slaveInterp->freeProc != NULL) {1680interp->result = slaveInterp->result;1681interp->freeProc = slaveInterp->freeProc;1682slaveInterp->freeProc = 0;1683} else {1684Tcl_SetResult(interp, slaveInterp->result, TCL_VOLATILE);1685}1686Tcl_ResetResult(slaveInterp);1687}1688Tcl_Release((ClientData) slaveInterp);1689return result;1690}1691}16921693if (cmdName[0] == 'i') {1694if ((len > 1) && (strncmp(cmdName, "issafe", len) == 0)) {1695if (argc > 2) {1696Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],1697" issafe\"", (char *) NULL);1698return TCL_ERROR;1699}1700masterPtr = (Master *) Tcl_GetAssocData(slaveInterp,1701"tclMasterRecord", NULL);1702if (masterPtr == (Master *) NULL) {1703panic("SlaveObjectCmd: could not find master record");1704}1705if (masterPtr->isSafe == 1) {1706Tcl_AppendResult(interp, "1", (char *) NULL);1707} else {1708Tcl_AppendResult(interp, "0", (char *) NULL);1709}1710return TCL_OK;1711}1712}17131714Tcl_AppendResult(interp, "bad option \"", argv[1],1715"\": should be alias, aliases, eval, or issafe", (char *) NULL);1716return TCL_ERROR;1717}17181719/*1720*----------------------------------------------------------------------1721*1722* SlaveObjectDeleteProc --1723*1724* Invoked when an object command for a slave interpreter is deleted;1725* cleans up all state associated with the slave interpreter and destroys1726* the slave interpreter.1727*1728* Results:1729* None.1730*1731* Side effects:1732* Cleans up all state associated with the slave interpreter and1733* destroys the slave interpreter.1734*1735*----------------------------------------------------------------------1736*/17371738static void1739SlaveObjectDeleteProc(clientData)1740ClientData clientData; /* The SlaveRecord for the command. */1741{1742Slave *slavePtr; /* Interim storage for Slave record. */1743Tcl_Interp *slaveInterp; /* And for a slave interp. */17441745slaveInterp = (Tcl_Interp *) clientData;1746slavePtr = (Slave *) Tcl_GetAssocData(slaveInterp, "tclSlaveRecord",NULL);1747if (slavePtr == (Slave *) NULL) {1748panic("SlaveObjectDeleteProc: could not find slave record");1749}17501751/*1752* Delete the entry in the slave table in the master interpreter now.1753* This is to avoid an infinite loop in the Master hash table cleanup in1754* the master interpreter. This can happen if this slave is being deleted1755* because the master is being deleted and the slave deletion is deferred1756* because it is still active.1757*/17581759Tcl_DeleteHashEntry(slavePtr->slaveEntry);17601761/*1762* Set to NULL so that when the slave record is cleaned up in the slave1763* it does not try to delete the command causing all sorts of grief.1764* See SlaveRecordDeleteProc().1765*/17661767slavePtr->interpCmd = NULL;17681769/*1770* Destroy the interpreter - this will cause all the deleteProcs for1771* all commands (including aliases) to run.1772*1773* NOTE: WE ASSUME THAT THE INTERPRETER HAS NOT BEEN DELETED YET!!1774*/17751776Tcl_DeleteInterp(slavePtr->slaveInterp);1777}17781779/*1780*----------------------------------------------------------------------1781*1782* AliasCmd --1783*1784* This is the procedure that services invocations of aliases in a1785* slave interpreter. One such command exists for each alias. When1786* invoked, this procedure redirects the invocation to the target1787* command in the master interpreter as designated by the Alias1788* record associated with this command.1789*1790* Results:1791* A standard Tcl result.1792*1793* Side effects:1794* Causes forwarding of the invocation; all possible side effects1795* may occur as a result of invoking the command to which the1796* invocation is forwarded.1797*1798*----------------------------------------------------------------------1799*/18001801static int1802AliasCmd(clientData, interp, argc, argv)1803ClientData clientData; /* Alias record. */1804Tcl_Interp *interp; /* Current interpreter. */1805int argc; /* Number of arguments. */1806char **argv; /* Argument strings. */1807{1808Alias *aliasPtr; /* Describes the alias. */1809Tcl_CmdInfo cmdInfo; /* Info about target command. */1810int result; /* Result of execution. */1811int i, j, addArgc; /* Loop counters. */1812int localArgc; /* Local argument count. */1813char **localArgv; /* Local argument vector. */1814Interp *iPtr; /* The target interpreter. */18151816aliasPtr = (Alias *) clientData;18171818result = Tcl_GetCommandInfo(aliasPtr->targetInterp, aliasPtr->targetName,1819&cmdInfo);1820if (result == 0) {1821Tcl_AppendResult(interp, "aliased target \"", aliasPtr->targetName,1822"\" for \"", argv[0], "\" not found", (char *) NULL);1823return TCL_ERROR;1824}1825if (aliasPtr->argc <= 0) {1826localArgv = argv;1827localArgc = argc;1828} else {1829addArgc = aliasPtr->argc;1830localArgc = argc + addArgc;1831localArgv = (char **) ckalloc((unsigned) sizeof(char *) * localArgc);1832localArgv[0] = argv[0];1833for (i = 0, j = 1; i < addArgc; i++, j++) {1834localArgv[j] = aliasPtr->argv[i];1835}1836for (i = 1; i < argc; i++, j++) {1837localArgv[j] = argv[i];1838}1839}18401841/*1842* Invoke the redirected command in the target interpreter. Note1843* that we are not calling eval because of possible security holes with1844* $ substitution and bracketed command evaluation.1845*1846* We duplicate some code here from Tcl_Eval to implement recursion1847* level counting and correct deletion of the target interpreter if1848* that was requested but delayed because of in-progress evaluations.1849*/18501851iPtr = (Interp *) aliasPtr->targetInterp;1852iPtr->numLevels++;1853Tcl_Preserve((ClientData) iPtr);1854Tcl_ResetResult((Tcl_Interp *) iPtr);1855result = (cmdInfo.proc)(cmdInfo.clientData, (Tcl_Interp *) iPtr,1856localArgc, localArgv);1857iPtr->numLevels--;1858if (iPtr->numLevels == 0) {1859if (result == TCL_RETURN) {1860result = TclUpdateReturnInfo(iPtr);1861}1862if ((result != TCL_OK) && (result != TCL_ERROR)) {1863Tcl_ResetResult((Tcl_Interp *) iPtr);1864if (result == TCL_BREAK) {1865iPtr->result = "invoked \"break\" outside of a loop";1866} else if (result == TCL_CONTINUE) {1867iPtr->result = "invoked \"continue\" outside of a loop";1868} else {1869iPtr->result = iPtr->resultSpace;1870sprintf(iPtr->resultSpace, "command returned bad code: %d",1871result);1872}1873result = TCL_ERROR;1874}1875}18761877/*1878* Clean up any locally allocated argument vector structure.1879*/18801881if (localArgv != argv) {1882ckfree((char *) localArgv);1883}18841885/*1886*1887* NOTE: Need to be careful if the target interpreter and the current1888* interpreter are the same - must not destroy result. This may happen1889* if an alias is created which redirects to a command in the same1890* interpreter as the one in which the source command will be defined.1891* Also: We cannot use aliasPtr any more because the alias may have1892* been deleted.1893*/18941895if (interp != (Tcl_Interp *) iPtr) {1896if (result == TCL_ERROR) {1897/*1898* An error occurred, so transfer error information from the1899* destination interpreter back to our interpreter. Some tricky1900* points:1901* 1. Must call Tcl_AddErrorInfo in destination interpreter to1902* make sure that the errorInfo variable has been initialized1903* (it's initialized lazily and might not have been initialized1904* yet).1905* 2. Must clear interp's result before calling Tcl_AddErrorInfo,1906* since Tcl_AddErrorInfo will store the interp's result in1907* errorInfo before appending aliasPtr->interp's $errorInfo;1908* we've already got everything we need in the redirected1909* interpreter's $errorInfo.1910*/19111912if (!(iPtr->flags & ERR_ALREADY_LOGGED)) {1913Tcl_AddErrorInfo((Tcl_Interp *) iPtr, "");1914}1915iPtr->flags &= ~ERR_ALREADY_LOGGED;1916Tcl_ResetResult(interp);1917Tcl_AddErrorInfo(interp, Tcl_GetVar2((Tcl_Interp *) iPtr,1918"errorInfo", (char *) NULL, TCL_GLOBAL_ONLY));1919Tcl_SetVar2(interp, "errorCode", (char *) NULL,1920Tcl_GetVar2((Tcl_Interp *) iPtr, "errorCode",1921(char *) NULL, TCL_GLOBAL_ONLY), TCL_GLOBAL_ONLY);1922}1923if (iPtr->freeProc != NULL) {1924interp->result = iPtr->result;1925interp->freeProc = iPtr->freeProc;1926iPtr->freeProc = 0;1927} else {1928Tcl_SetResult(interp, iPtr->result, TCL_VOLATILE);1929}1930Tcl_ResetResult((Tcl_Interp *) iPtr);1931}1932Tcl_Release((ClientData) iPtr);1933return result;1934}19351936/*1937*----------------------------------------------------------------------1938*1939* AliasCmdDeleteProc --1940*1941* Is invoked when an alias command is deleted in a slave. Cleans up1942* all storage associated with this alias.1943*1944* Results:1945* None.1946*1947* Side effects:1948* Deletes the alias record and its entry in the alias table for1949* the interpreter.1950*1951*----------------------------------------------------------------------1952*/19531954static void1955AliasCmdDeleteProc(clientData)1956ClientData clientData; /* The alias record for this alias. */1957{1958Alias *aliasPtr; /* Alias record for alias to delete. */1959Target *targetPtr; /* Record for target of this alias. */1960int i; /* Loop counter. */19611962aliasPtr = (Alias *) clientData;19631964targetPtr = (Target *) Tcl_GetHashValue(aliasPtr->targetEntry);1965ckfree((char *) targetPtr);1966Tcl_DeleteHashEntry(aliasPtr->targetEntry);19671968ckfree((char *) aliasPtr->targetName);1969ckfree((char *) aliasPtr->aliasName);1970for (i = 0; i < aliasPtr->argc; i++) {1971ckfree((char *) aliasPtr->argv[i]);1972}1973if (aliasPtr->argv != (char **) NULL) {1974ckfree((char *) aliasPtr->argv);1975}19761977Tcl_DeleteHashEntry(aliasPtr->aliasEntry);19781979ckfree((char *) aliasPtr);1980}19811982/*1983*----------------------------------------------------------------------1984*1985* MasterRecordDeleteProc -1986*1987* Is invoked when an interpreter (which is using the "interp" facility)1988* is deleted, and it cleans up the storage associated with the1989* "tclMasterRecord" assoc-data entry.1990*1991* Results:1992* None.1993*1994* Side effects:1995* Cleans up storage.1996*1997*----------------------------------------------------------------------1998*/19992000static void2001MasterRecordDeleteProc(clientData, interp)2002ClientData clientData; /* Master record for deleted interp. */2003Tcl_Interp *interp; /* Interpreter being deleted. */2004{2005Target *targetPtr; /* Loop variable. */2006Tcl_HashEntry *hPtr; /* Search element. */2007Tcl_HashSearch hSearch; /* Search record (internal). */2008Slave *slavePtr; /* Loop variable. */2009char *cmdName; /* Name of command to delete. */2010Master *masterPtr; /* Interim storage. */20112012masterPtr = (Master *) clientData;2013for (hPtr = Tcl_FirstHashEntry(&(masterPtr->slaveTable), &hSearch);2014hPtr != NULL;2015hPtr = Tcl_NextHashEntry(&hSearch)) {2016slavePtr = (Slave *) Tcl_GetHashValue(hPtr);2017cmdName = Tcl_GetCommandName(interp, slavePtr->interpCmd);2018(void) Tcl_DeleteCommand(interp, cmdName);2019}2020Tcl_DeleteHashTable(&(masterPtr->slaveTable));20212022for (hPtr = Tcl_FirstHashEntry(&(masterPtr->targetTable), &hSearch);2023hPtr != NULL;2024hPtr = Tcl_FirstHashEntry(&(masterPtr->targetTable), &hSearch)) {2025targetPtr = (Target *) Tcl_GetHashValue(hPtr);2026cmdName = Tcl_GetCommandName(targetPtr->slaveInterp,2027targetPtr->slaveCmd);2028(void) Tcl_DeleteCommand(targetPtr->slaveInterp, cmdName);2029}2030Tcl_DeleteHashTable(&(masterPtr->targetTable));20312032ckfree((char *) masterPtr);2033}20342035/*2036*----------------------------------------------------------------------2037*2038* SlaveRecordDeleteProc --2039*2040* Is invoked when an interpreter (which is using the interp facility)2041* is deleted, and it cleans up the storage associated with the2042* tclSlaveRecord assoc-data entry.2043*2044* Results:2045* None2046*2047* Side effects:2048* Cleans up storage.2049*2050*----------------------------------------------------------------------2051*/20522053static void2054SlaveRecordDeleteProc(clientData, interp)2055ClientData clientData; /* Slave record for deleted interp. */2056Tcl_Interp *interp; /* Interpreter being deleted. */2057{2058Slave *slavePtr; /* Interim storage. */2059Alias *aliasPtr;2060Tcl_HashTable *hTblPtr;2061Tcl_HashEntry *hPtr;2062Tcl_HashSearch hSearch;20632064slavePtr = (Slave *) clientData;20652066/*2067* In every case that we call SetAssocData on "tclSlaveRecord",2068* slavePtr is not NULL. Otherwise we panic.2069*/20702071if (slavePtr == NULL) {2072panic("SlaveRecordDeleteProc: NULL slavePtr");2073}20742075if (slavePtr->interpCmd != (Tcl_Command) NULL) {2076Command *cmdPtr = (Command *) slavePtr->interpCmd;20772078/*2079* The interpCmd has not been deleted in the master yet, since2080* it's callback sets interpCmd to NULL.2081*2082* Probably Tcl_DeleteInterp() was called on this interpreter directly,2083* rather than via "interp delete", or equivalent (deletion of the2084* command in the master).2085*2086* Perform the cleanup done by SlaveObjectDeleteProc() directly,2087* and turn off the callback now (since we are about to free slavePtr2088* and this interpreter is going away, while the deletion of commands2089* in the master may be deferred).2090*/20912092Tcl_DeleteHashEntry(slavePtr->slaveEntry);2093cmdPtr->clientData = NULL;2094cmdPtr->deleteProc = NULL;2095cmdPtr->deleteData = NULL;20962097/*2098* Get the command name from the master interpreter instead of2099* relying on the stored name; the command may have been renamed.2100*/21012102Tcl_DeleteCommand(slavePtr->masterInterp,2103Tcl_GetCommandName(slavePtr->masterInterp,2104slavePtr->interpCmd));2105}21062107/*2108* If there are any aliases, delete those now. This removes any2109* dependency on the order of deletion between commands and the2110* slave record.2111*/21122113hTblPtr = (Tcl_HashTable *) &(slavePtr->aliasTable);2114for (hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch);2115hPtr != (Tcl_HashEntry *) NULL;2116hPtr = Tcl_FirstHashEntry(hTblPtr, &hSearch)) {2117aliasPtr = (Alias *) Tcl_GetHashValue(hPtr);21182119/*2120* The call to Tcl_DeleteCommand will release the storage2121* occuppied by the hash entry and the alias record.2122* NOTE that we cannot use the alias name directly because its2123* storage will be deleted in the command deletion callback. Hence2124* we must use the name for the command as stored in the hash table.2125*/21262127Tcl_DeleteCommand(interp,2128Tcl_GetCommandName(interp, aliasPtr->slaveCmd));2129}21302131/*2132* Finally dispose of the hash table and the slave record.2133*/21342135Tcl_DeleteHashTable(hTblPtr);2136ckfree((char *) slavePtr);2137}21382139/*2140*----------------------------------------------------------------------2141*2142* TclInterpInit --2143*2144* Initializes the invoking interpreter for using the "interp"2145* facility. This is called from inside Tcl_Init.2146*2147* Results:2148* None.2149*2150* Side effects:2151* Adds the "interp" command to an interpreter and initializes several2152* records in the associated data of the invoking interpreter.2153*2154*----------------------------------------------------------------------2155*/21562157int2158TclInterpInit(interp)2159Tcl_Interp *interp; /* Interpreter to initialize. */2160{2161Master *masterPtr; /* Its Master record. */21622163masterPtr = (Master *) ckalloc((unsigned) sizeof(Master));2164masterPtr->isSafe = 0;2165Tcl_InitHashTable(&(masterPtr->slaveTable), TCL_STRING_KEYS);2166Tcl_InitHashTable(&(masterPtr->targetTable), TCL_ONE_WORD_KEYS);21672168(void) Tcl_SetAssocData(interp, "tclMasterRecord", MasterRecordDeleteProc,2169(ClientData) masterPtr);21702171return TCL_OK;2172}21732174/*2175*----------------------------------------------------------------------2176*2177* Tcl_IsSafe --2178*2179* Determines whether an interpreter is safe2180*2181* Results:2182* 1 if it is safe, 0 if it is not.2183*2184* Side effects:2185* None.2186*2187*----------------------------------------------------------------------2188*/21892190int2191Tcl_IsSafe(interp)2192Tcl_Interp *interp; /* Is this interpreter "safe" ? */2193{2194Master *masterPtr; /* Its master record. */21952196if (interp == (Tcl_Interp *) NULL) {2197return 0;2198}2199masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL);2200if (masterPtr == (Master *) NULL) {2201panic("Tcl_IsSafe: could not find master record");2202}2203return masterPtr->isSafe;2204}22052206/*2207*----------------------------------------------------------------------2208*2209* Tcl_MakeSafe --2210*2211* Makes an interpreter safe.2212*2213* Results:2214* TCL_OK if it succeeds, TCL_ERROR else.2215*2216* Side effects:2217* Removes functionality from an interpreter.2218*2219*----------------------------------------------------------------------2220*/22212222int2223Tcl_MakeSafe(interp)2224Tcl_Interp *interp; /* Make this interpreter "safe". */2225{2226if (interp == (Tcl_Interp *) NULL) {2227return TCL_ERROR;2228}2229return MakeSafe(interp);2230}22312232/*2233*----------------------------------------------------------------------2234*2235* Tcl_CreateSlave --2236*2237* Creates a slave interpreter. The slavePath argument denotes the2238* name of the new slave relative to the current interpreter; the2239* slave is a direct descendant of the one-before-last component of2240* the path, e.g. it is a descendant of the current interpreter if2241* the slavePath argument contains only one component. Optionally makes2242* the slave interpreter safe.2243*2244* Results:2245* Returns the interpreter structure created, or NULL if an error2246* occurred.2247*2248* Side effects:2249* Creates a new interpreter and a new interpreter object command in2250* the interpreter indicated by the slavePath argument.2251*2252*----------------------------------------------------------------------2253*/22542255Tcl_Interp *2256Tcl_CreateSlave(interp, slavePath, isSafe)2257Tcl_Interp *interp; /* Interpreter to start search at. */2258char *slavePath; /* Name of slave to create. */2259int isSafe; /* Should new slave be "safe" ? */2260{2261if ((interp == (Tcl_Interp *) NULL) || (slavePath == (char *) NULL)) {2262return NULL;2263}2264return CreateSlave(interp, slavePath, isSafe);2265}22662267/*2268*----------------------------------------------------------------------2269*2270* Tcl_GetSlave --2271*2272* Finds a slave interpreter by its path name.2273*2274* Results:2275* Returns a Tcl_Interp * for the named interpreter or NULL if not2276* found.2277*2278* Side effects:2279* None.2280*2281*----------------------------------------------------------------------2282*/22832284Tcl_Interp *2285Tcl_GetSlave(interp, slavePath)2286Tcl_Interp *interp; /* Interpreter to start search from. */2287char *slavePath; /* Path of slave to find. */2288{2289Master *masterPtr; /* Interim storage for Master record. */22902291if ((interp == (Tcl_Interp *) NULL) || (slavePath == (char *) NULL)) {2292return NULL;2293}2294masterPtr = (Master *) Tcl_GetAssocData(interp, "tclMasterRecord", NULL);2295if (masterPtr == (Master *) NULL) {2296panic("Tcl_GetSlave: could not find master record");2297}2298return GetInterp(interp, masterPtr, slavePath, NULL);2299}23002301/*2302*----------------------------------------------------------------------2303*2304* Tcl_GetMaster --2305*2306* Finds the master interpreter of a slave interpreter.2307*2308* Results:2309* Returns a Tcl_Interp * for the master interpreter or NULL if none.2310*2311* Side effects:2312* None.2313*2314*----------------------------------------------------------------------2315*/23162317Tcl_Interp *2318Tcl_GetMaster(interp)2319Tcl_Interp *interp; /* Get the master of this interpreter. */2320{2321Slave *slavePtr; /* Slave record of this interpreter. */23222323if (interp == (Tcl_Interp *) NULL) {2324return NULL;2325}2326slavePtr = (Slave *) Tcl_GetAssocData(interp, "tclSlaveRecord", NULL);2327if (slavePtr == (Slave *) NULL) {2328return NULL;2329}2330return slavePtr->masterInterp;2331}23322333/*2334*----------------------------------------------------------------------2335*2336* Tcl_CreateAlias --2337*2338* Creates an alias between two interpreters.2339*2340* Results:2341* TCL_OK if successful, TCL_ERROR if failed. If TCL_ERROR is returned2342* the result of slaveInterp will contain an error message.2343*2344* Side effects:2345* Creates a new alias, manipulates the result field of slaveInterp.2346*2347*----------------------------------------------------------------------2348*/23492350int2351Tcl_CreateAlias(slaveInterp, slaveCmd, targetInterp, targetCmd, argc, argv)2352Tcl_Interp *slaveInterp; /* Interpreter for source command. */2353char *slaveCmd; /* Command to install in slave. */2354Tcl_Interp *targetInterp; /* Interpreter for target command. */2355char *targetCmd; /* Name of target command. */2356int argc; /* How many additional arguments? */2357char **argv; /* These are the additional args. */2358{2359Master *masterPtr; /* Master record for target interp. */23602361if ((slaveInterp == (Tcl_Interp *) NULL) ||2362(targetInterp == (Tcl_Interp *) NULL) ||2363(slaveCmd == (char *) NULL) ||2364(targetCmd == (char *) NULL)) {2365return TCL_ERROR;2366}2367masterPtr = (Master *) Tcl_GetAssocData(targetInterp, "tclMasterRecord",2368NULL);2369if (masterPtr == (Master *) NULL) {2370panic("Tcl_CreateAlias: could not find master record");2371}2372return AliasHelper(slaveInterp, slaveInterp, targetInterp, masterPtr,2373slaveCmd, targetCmd, argc, argv);2374}23752376/*2377*----------------------------------------------------------------------2378*2379* Tcl_GetAlias --2380*2381* Gets information about an alias.2382*2383* Results:2384* TCL_OK if successful, TCL_ERROR else. If TCL_ERROR is returned, the2385* result field of the interpreter given as argument will contain an2386* error message.2387*2388* Side effects:2389* Manipulates the result field of the interpreter given as argument.2390*2391*----------------------------------------------------------------------2392*/23932394int2395Tcl_GetAlias(interp, aliasName, targetInterpPtr, targetNamePtr, argcPtr,2396argvPtr)2397Tcl_Interp *interp; /* Interp to start search from. */2398char *aliasName; /* Name of alias to find. */2399Tcl_Interp **targetInterpPtr; /* (Return) target interpreter. */2400char **targetNamePtr; /* (Return) name of target command. */2401int *argcPtr; /* (Return) count of addnl args. */2402char ***argvPtr; /* (Return) additional arguments. */2403{2404Slave *slavePtr; /* Slave record for slave interp. */2405Tcl_HashEntry *hPtr; /* Search element. */2406Alias *aliasPtr; /* Storage for alias found. */24072408if ((interp == (Tcl_Interp *) NULL) || (aliasName == (char *) NULL)) {2409return TCL_ERROR;2410}2411slavePtr = (Slave *) Tcl_GetAssocData(interp, "tclSlaveRecord", NULL);2412if (slavePtr == (Slave *) NULL) {2413panic("Tcl_GetAlias: could not find slave record");2414}2415hPtr = Tcl_FindHashEntry(&(slavePtr->aliasTable), aliasName);2416if (hPtr == (Tcl_HashEntry *) NULL) {2417Tcl_AppendResult(interp, "alias \"", aliasName, "\" not found",2418(char *) NULL);2419return TCL_ERROR;2420}2421aliasPtr = (Alias *) Tcl_GetHashValue(hPtr);2422if (targetInterpPtr != (Tcl_Interp **) NULL) {2423*targetInterpPtr = aliasPtr->targetInterp;2424}2425if (targetNamePtr != (char **) NULL) {2426*targetNamePtr = aliasPtr->targetName;2427}2428if (argcPtr != (int *) NULL) {2429*argcPtr = aliasPtr->argc;2430}2431if (argvPtr != (char ***) NULL) {2432*argvPtr = aliasPtr->argv;2433}2434return TCL_OK;2435}243624372438