diff options
Diffstat (limited to 'generic/tclNamesp.c')
-rw-r--r-- | generic/tclNamesp.c | 3765 |
1 files changed, 3765 insertions, 0 deletions
diff --git a/generic/tclNamesp.c b/generic/tclNamesp.c new file mode 100644 index 0000000..d4ace43 --- /dev/null +++ b/generic/tclNamesp.c @@ -0,0 +1,3765 @@ +/* + * tclNamesp.c -- + * + * Contains support for namespaces, which provide a separate context of + * commands and global variables. The global :: namespace is the + * traditional Tcl "global" scope. Other namespaces are created as + * children of the global namespace. These other namespaces contain + * special-purpose commands and variables for packages. + * + * Copyright (c) 1993-1997 Lucent Technologies. + * Copyright (c) 1997 Sun Microsystems, Inc. + * + * Originally implemented by + * Michael J. McLennan + * Bell Labs Innovations for Lucent Technologies + * mmclennan@lucent.com + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * SCCS: @(#) tclNamesp.c 1.29 97/08/04 09:32:38 + */ + +#include "tclInt.h" + +/* + * Flag passed to TclGetNamespaceForQualName to indicate that it should + * search for a namespace rather than a command or variable inside a + * namespace. Note that this flag's value must not conflict with the values + * of TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY, or CREATE_NS_IF_UNKNOWN. + */ + +#define FIND_ONLY_NS 0x1000 + +/* + * Count of the number of namespaces created. This value is used as a + * unique id for each namespace. + */ + +static long numNsCreated = 0; + +/* + * Data structure used as the ClientData of imported commands: commands + * created in an namespace when it imports a "real" command from another + * namespace. + */ + +typedef struct ImportedCmdData { + Command *realCmdPtr; /* "Real" command that this imported command + * refers to. */ + Command *selfPtr; /* Pointer to this imported command. Needed + * only when deleting it in order to remove + * it from the real command's linked list of + * imported commands that refer to it. */ +} ImportedCmdData; + +/* + * This structure contains a cached pointer to a namespace that is the + * result of resolving the namespace's name in some other namespace. It is + * the internal representation for a nsName object. It contains the + * pointer along with some information that is used to check the cached + * pointer's validity. + */ + +typedef struct ResolvedNsName { + Namespace *nsPtr; /* A cached namespace pointer. */ + long nsId; /* nsPtr's unique namespace id. Used to + * verify that nsPtr is still valid + * (e.g., it's possible that the namespace + * was deleted and a new one created at + * the same address). */ + Namespace *refNsPtr; /* Points to the namespace containing the + * reference (not the namespace that + * contains the referenced namespace). */ + int refCount; /* Reference count: 1 for each nsName + * object that has a pointer to this + * ResolvedNsName structure as its internal + * rep. This structure can be freed when + * refCount becomes zero. */ +} ResolvedNsName; + +/* + * Declarations for procedures local to this file: + */ + +static void DeleteImportedCmd _ANSI_ARGS_(( + ClientData clientData)); +static void DupNsNameInternalRep _ANSI_ARGS_((Tcl_Obj *objPtr, + Tcl_Obj *copyPtr)); +static void FreeNsNameInternalRep _ANSI_ARGS_(( + Tcl_Obj *objPtr)); +static int GetNamespaceFromObj _ANSI_ARGS_(( + Tcl_Interp *interp, Tcl_Obj *objPtr, + Tcl_Namespace **nsPtrPtr)); +static int InvokeImportedCmd _ANSI_ARGS_(( + ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[])); +static int NamespaceChildrenCmd _ANSI_ARGS_(( + ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[])); +static int NamespaceCodeCmd _ANSI_ARGS_(( + ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[])); +static int NamespaceCurrentCmd _ANSI_ARGS_(( + ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[])); +static int NamespaceDeleteCmd _ANSI_ARGS_(( + ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[])); +static int NamespaceEvalCmd _ANSI_ARGS_(( + ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[])); +static int NamespaceExportCmd _ANSI_ARGS_(( + ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[])); +static int NamespaceForgetCmd _ANSI_ARGS_(( + ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[])); +static void NamespaceFree _ANSI_ARGS_((Namespace *nsPtr)); +static int NamespaceImportCmd _ANSI_ARGS_(( + ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[])); +static int NamespaceInscopeCmd _ANSI_ARGS_(( + ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[])); +static int NamespaceOriginCmd _ANSI_ARGS_(( + ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[])); +static int NamespaceParentCmd _ANSI_ARGS_(( + ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[])); +static int NamespaceQualifiersCmd _ANSI_ARGS_(( + ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[])); +static int NamespaceTailCmd _ANSI_ARGS_(( + ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[])); +static int NamespaceWhichCmd _ANSI_ARGS_(( + ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[])); +static int SetNsNameFromAny _ANSI_ARGS_(( + Tcl_Interp *interp, Tcl_Obj *objPtr)); +static void UpdateStringOfNsName _ANSI_ARGS_((Tcl_Obj *objPtr)); + +/* + * This structure defines a Tcl object type that contains a + * namespace reference. It is used in commands that take the + * name of a namespace as an argument. The namespace reference + * is resolved, and the result in cached in the object. + */ + +Tcl_ObjType tclNsNameType = { + "nsName", /* the type's name */ + FreeNsNameInternalRep, /* freeIntRepProc */ + DupNsNameInternalRep, /* dupIntRepProc */ + UpdateStringOfNsName, /* updateStringProc */ + SetNsNameFromAny /* setFromAnyProc */ +}; + +/* + * Boolean flag indicating whether or not the namespName object + * type has been registered with the Tcl compiler. + */ + +static int nsInitialized = 0; + +/* + *---------------------------------------------------------------------- + * + * TclInitNamespaces -- + * + * Called when any interpreter is created to make sure that + * things are properly set up for namespaces. + * + * Results: + * None. + * + * Side effects: + * On the first call, the namespName object type is registered + * with the Tcl compiler. + * + *---------------------------------------------------------------------- + */ + +void +TclInitNamespaces() +{ + if (!nsInitialized) { + Tcl_RegisterObjType(&tclNsNameType); + nsInitialized = 1; + } +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_GetCurrentNamespace -- + * + * Returns a pointer to an interpreter's currently active namespace. + * + * Results: + * Returns a pointer to the interpreter's current namespace. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_Namespace * +Tcl_GetCurrentNamespace(interp) + register Tcl_Interp *interp; /* Interpreter whose current namespace is + * being queried. */ +{ + register Interp *iPtr = (Interp *) interp; + register Namespace *nsPtr; + + if (iPtr->varFramePtr != NULL) { + nsPtr = iPtr->varFramePtr->nsPtr; + } else { + nsPtr = iPtr->globalNsPtr; + } + return (Tcl_Namespace *) nsPtr; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_GetGlobalNamespace -- + * + * Returns a pointer to an interpreter's global :: namespace. + * + * Results: + * Returns a pointer to the specified interpreter's global namespace. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_Namespace * +Tcl_GetGlobalNamespace(interp) + register Tcl_Interp *interp; /* Interpreter whose global namespace + * should be returned. */ +{ + register Interp *iPtr = (Interp *) interp; + + return (Tcl_Namespace *) iPtr->globalNsPtr; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_PushCallFrame -- + * + * Pushes a new call frame onto the interpreter's Tcl call stack. + * Called when executing a Tcl procedure or a "namespace eval" or + * "namespace inscope" command. + * + * Results: + * Returns TCL_OK if successful, or TCL_ERROR (along with an error + * message in the interpreter's result object) if something goes wrong. + * + * Side effects: + * Modifies the interpreter's Tcl call stack. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_PushCallFrame(interp, callFramePtr, namespacePtr, isProcCallFrame) + Tcl_Interp *interp; /* Interpreter in which the new call frame + * is to be pushed. */ + Tcl_CallFrame *callFramePtr; /* Points to a call frame structure to + * push. Storage for this have already been + * allocated by the caller; typically this + * is the address of a CallFrame structure + * allocated on the caller's C stack. The + * call frame will be initialized by this + * procedure. The caller can pop the frame + * later with Tcl_PopCallFrame, and it is + * responsible for freeing the frame's + * storage. */ + Tcl_Namespace *namespacePtr; /* Points to the namespace in which the + * frame will execute. If NULL, the + * interpreter's current namespace will + * be used. */ + int isProcCallFrame; /* If nonzero, the frame represents a + * called Tcl procedure and may have local + * vars. Vars will ordinarily be looked up + * in the frame. If new variables are + * created, they will be created in the + * frame. If 0, the frame is for a + * "namespace eval" or "namespace inscope" + * command and var references are treated + * as references to namespace variables. */ +{ + Interp *iPtr = (Interp *) interp; + register CallFrame *framePtr = (CallFrame *) callFramePtr; + register Namespace *nsPtr; + + if (namespacePtr == NULL) { + nsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp); + } else { + nsPtr = (Namespace *) namespacePtr; + if (nsPtr->flags & NS_DEAD) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "namespace \"", + nsPtr->fullName, "\" not found in context \"", + Tcl_GetCurrentNamespace(interp)->fullName, "\"", + (char *) NULL); + return TCL_ERROR; + } + } + + nsPtr->activationCount++; + framePtr->nsPtr = nsPtr; + framePtr->isProcCallFrame = isProcCallFrame; + framePtr->objc = 0; + framePtr->objv = NULL; + framePtr->callerPtr = iPtr->framePtr; + framePtr->callerVarPtr = iPtr->varFramePtr; + if (iPtr->varFramePtr != NULL) { + framePtr->level = (iPtr->varFramePtr->level + 1); + } else { + framePtr->level = 1; + } + framePtr->procPtr = NULL; /* no called procedure */ + framePtr->varTablePtr = NULL; /* and no local variables */ + framePtr->numCompiledLocals = 0; + framePtr->compiledLocals = NULL; + + /* + * Push the new call frame onto the interpreter's stack of procedure + * call frames making it the current frame. + */ + + iPtr->framePtr = framePtr; + iPtr->varFramePtr = framePtr; + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_PopCallFrame -- + * + * Removes a call frame from the Tcl call stack for the interpreter. + * Called to remove a frame previously pushed by Tcl_PushCallFrame. + * + * Results: + * None. + * + * Side effects: + * Modifies the call stack of the interpreter. Resets various fields of + * the popped call frame. If a namespace has been deleted and + * has no more activations on the call stack, the namespace is + * destroyed. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_PopCallFrame(interp) + Tcl_Interp* interp; /* Interpreter with call frame to pop. */ +{ + register Interp *iPtr = (Interp *) interp; + register CallFrame *framePtr = iPtr->framePtr; + int saveErrFlag; + Namespace *nsPtr; + + /* + * It's important to remove the call frame from the interpreter's stack + * of call frames before deleting local variables, so that traces + * invoked by the variable deletion don't see the partially-deleted + * frame. + */ + + iPtr->framePtr = framePtr->callerPtr; + iPtr->varFramePtr = framePtr->callerVarPtr; + + /* + * Delete the local variables. As a hack, we save then restore the + * ERR_IN_PROGRESS flag in the interpreter. The problem is that there + * could be unset traces on the variables, which cause scripts to be + * evaluated. This will clear the ERR_IN_PROGRESS flag, losing stack + * trace information if the procedure was exiting with an error. The + * code below preserves the flag. Unfortunately, that isn't really + * enough: we really should preserve the errorInfo variable too + * (otherwise a nested error in the trace script will trash errorInfo). + * What's really needed is a general-purpose mechanism for saving and + * restoring interpreter state. + */ + + saveErrFlag = (iPtr->flags & ERR_IN_PROGRESS); + + if (framePtr->varTablePtr != NULL) { + TclDeleteVars(iPtr, framePtr->varTablePtr); + ckfree((char *) framePtr->varTablePtr); + framePtr->varTablePtr = NULL; + } + if (framePtr->numCompiledLocals > 0) { + TclDeleteCompiledLocalVars(iPtr, framePtr); + } + + iPtr->flags |= saveErrFlag; + + /* + * Decrement the namespace's count of active call frames. If the + * namespace is "dying" and there are no more active call frames, + * call Tcl_DeleteNamespace to destroy it. + */ + + nsPtr = framePtr->nsPtr; + nsPtr->activationCount--; + if ((nsPtr->flags & NS_DYING) + && (nsPtr->activationCount == 0)) { + Tcl_DeleteNamespace((Tcl_Namespace *) nsPtr); + } + framePtr->nsPtr = NULL; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_CreateNamespace -- + * + * Creates a new namespace with the given name. If there is no + * active namespace (i.e., the interpreter is being initialized), + * the global :: namespace is created and returned. + * + * Results: + * Returns a pointer to the new namespace if successful. If the + * namespace already exists or if another error occurs, this routine + * returns NULL, along with an error message in the interpreter's + * result object. + * + * Side effects: + * If the name contains "::" qualifiers and a parent namespace does + * not already exist, it is automatically created. + * + *---------------------------------------------------------------------- + */ + +Tcl_Namespace * +Tcl_CreateNamespace(interp, name, clientData, deleteProc) + Tcl_Interp *interp; /* Interpreter in which a new namespace + * is being created. Also used for + * error reporting. */ + char *name; /* Name for the new namespace. May be a + * qualified name with names of ancestor + * namespaces separated by "::"s. */ + ClientData clientData; /* One-word value to store with + * namespace. */ + Tcl_NamespaceDeleteProc *deleteProc; + /* Procedure called to delete client + * data when the namespace is deleted. + * NULL if no procedure should be + * called. */ +{ + Interp *iPtr = (Interp *) interp; + register Namespace *nsPtr, *ancestorPtr; + Namespace *parentPtr, *dummy1Ptr, *dummy2Ptr; + Namespace *globalNsPtr = iPtr->globalNsPtr; + char *simpleName; + Tcl_HashEntry *entryPtr; + Tcl_DString buffer1, buffer2; + int newEntry, result; + + /* + * If there is no active namespace, the interpreter is being + * initialized. + */ + + if ((globalNsPtr == NULL) && (iPtr->varFramePtr == NULL)) { + /* + * Treat this namespace as the global namespace, and avoid + * looking for a parent. + */ + + parentPtr = NULL; + simpleName = ""; + } else if (*name == '\0') { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "can't create namespace \"\": only global namespace can have empty name", (char *) NULL); + return NULL; + } else { + /* + * Find the parent for the new namespace. + */ + + result = TclGetNamespaceForQualName(interp, name, + (Namespace *) NULL, + /*flags*/ (CREATE_NS_IF_UNKNOWN | TCL_LEAVE_ERR_MSG), + &parentPtr, &dummy1Ptr, &dummy2Ptr, &simpleName); + if (result != TCL_OK) { + return NULL; + } + + /* + * If the unqualified name at the end is empty, there were trailing + * "::"s after the namespace's name which we ignore. The new + * namespace was already (recursively) created and is pointed to + * by parentPtr. + */ + + if (*simpleName == '\0') { + return (Tcl_Namespace *) parentPtr; + } + + /* + * Check for a bad namespace name and make sure that the name + * does not already exist in the parent namespace. + */ + + if (Tcl_FindHashEntry(&parentPtr->childTable, simpleName) != NULL) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "can't create namespace \"", name, + "\": already exists", (char *) NULL); + return NULL; + } + } + + /* + * Create the new namespace and root it in its parent. Increment the + * count of namespaces created. + */ + + numNsCreated++; + + nsPtr = (Namespace *) ckalloc(sizeof(Namespace)); + nsPtr->name = (char *) ckalloc((unsigned) (strlen(simpleName)+1)); + strcpy(nsPtr->name, simpleName); + nsPtr->fullName = NULL; /* set below */ + nsPtr->clientData = clientData; + nsPtr->deleteProc = deleteProc; + nsPtr->parentPtr = parentPtr; + Tcl_InitHashTable(&nsPtr->childTable, TCL_STRING_KEYS); + nsPtr->nsId = numNsCreated; + nsPtr->interp = interp; + nsPtr->flags = 0; + nsPtr->activationCount = 0; + nsPtr->refCount = 0; + Tcl_InitHashTable(&nsPtr->cmdTable, TCL_STRING_KEYS); + Tcl_InitHashTable(&nsPtr->varTable, TCL_STRING_KEYS); + nsPtr->exportArrayPtr = NULL; + nsPtr->numExportPatterns = 0; + nsPtr->maxExportPatterns = 0; + nsPtr->cmdRefEpoch = 0; + + if (parentPtr != NULL) { + entryPtr = Tcl_CreateHashEntry(&parentPtr->childTable, simpleName, + &newEntry); + Tcl_SetHashValue(entryPtr, (ClientData) nsPtr); + } + + /* + * Build the fully qualified name for this namespace. + */ + + Tcl_DStringInit(&buffer1); + Tcl_DStringInit(&buffer2); + for (ancestorPtr = nsPtr; ancestorPtr != NULL; + ancestorPtr = ancestorPtr->parentPtr) { + if (ancestorPtr != globalNsPtr) { + Tcl_DStringAppend(&buffer1, "::", 2); + Tcl_DStringAppend(&buffer1, ancestorPtr->name, -1); + } + Tcl_DStringAppend(&buffer1, Tcl_DStringValue(&buffer2), -1); + + Tcl_DStringSetLength(&buffer2, 0); + Tcl_DStringAppend(&buffer2, Tcl_DStringValue(&buffer1), -1); + Tcl_DStringSetLength(&buffer1, 0); + } + + name = Tcl_DStringValue(&buffer2); + nsPtr->fullName = (char *) ckalloc((unsigned) (strlen(name)+1)); + strcpy(nsPtr->fullName, name); + + Tcl_DStringFree(&buffer1); + Tcl_DStringFree(&buffer2); + + /* + * Return a pointer to the new namespace. + */ + + return (Tcl_Namespace *) nsPtr; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_DeleteNamespace -- + * + * Deletes a namespace and all of the commands, variables, and other + * namespaces within it. + * + * Results: + * None. + * + * Side effects: + * When a namespace is deleted, it is automatically removed as a + * child of its parent namespace. Also, all its commands, variables + * and child namespaces are deleted. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_DeleteNamespace(namespacePtr) + Tcl_Namespace *namespacePtr; /* Points to the namespace to delete. */ +{ + register Namespace *nsPtr = (Namespace *) namespacePtr; + Interp *iPtr = (Interp *) nsPtr->interp; + Namespace *globalNsPtr = + (Namespace *) Tcl_GetGlobalNamespace((Tcl_Interp *) iPtr); + Tcl_HashEntry *entryPtr; + + /* + * If the namespace is on the call frame stack, it is marked as "dying" + * (NS_DYING is OR'd into its flags): the namespace can't be looked up + * by name but its commands and variables are still usable by those + * active call frames. When all active call frames referring to the + * namespace have been popped from the Tcl stack, Tcl_PopCallFrame will + * call this procedure again to delete everything in the namespace. + * If no nsName objects refer to the namespace (i.e., if its refCount + * is zero), its commands and variables are deleted and the storage for + * its namespace structure is freed. Otherwise, if its refCount is + * nonzero, the namespace's commands and variables are deleted but the + * structure isn't freed. Instead, NS_DEAD is OR'd into the structure's + * flags to allow the namespace resolution code to recognize that the + * namespace is "deleted". The structure's storage is freed by + * FreeNsNameInternalRep when its refCount reaches 0. + */ + + if (nsPtr->activationCount > 0) { + nsPtr->flags |= NS_DYING; + if (nsPtr->parentPtr != NULL) { + entryPtr = Tcl_FindHashEntry(&nsPtr->parentPtr->childTable, + nsPtr->name); + if (entryPtr != NULL) { + Tcl_DeleteHashEntry(entryPtr); + } + } + nsPtr->parentPtr = NULL; + } else { + /* + * Delete the namespace and everything in it. If this is the global + * namespace, then clear it but don't free its storage unless the + * interpreter is being torn down. + */ + + TclTeardownNamespace(nsPtr); + + if ((nsPtr != globalNsPtr) || (iPtr->flags & DELETED)) { + /* + * If this is the global namespace, then it may have residual + * "errorInfo" and "errorCode" variables for errors that + * occurred while it was being torn down. Try to clear the + * variable list one last time. + */ + + TclDeleteVars((Interp *) nsPtr->interp, &nsPtr->varTable); + + Tcl_DeleteHashTable(&nsPtr->childTable); + Tcl_DeleteHashTable(&nsPtr->cmdTable); + + /* + * If the reference count is 0, then discard the namespace. + * Otherwise, mark it as "dead" so that it can't be used. + */ + + if (nsPtr->refCount == 0) { + NamespaceFree(nsPtr); + } else { + nsPtr->flags |= NS_DEAD; + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TclTeardownNamespace -- + * + * Used internally to dismantle and unlink a namespace when it is + * deleted. Divorces the namespace from its parent, and deletes all + * commands, variables, and child namespaces. + * + * This is kept separate from Tcl_DeleteNamespace so that the global + * namespace can be handled specially. Global variables like + * "errorInfo" and "errorCode" need to remain intact while other + * namespaces and commands are torn down, in case any errors occur. + * + * Results: + * None. + * + * Side effects: + * Removes this namespace from its parent's child namespace hashtable. + * Deletes all commands, variables and namespaces in this namespace. + * If this is the global namespace, the "errorInfo" and "errorCode" + * variables are left alone and deleted later. + * + *---------------------------------------------------------------------- + */ + +void +TclTeardownNamespace(nsPtr) + register Namespace *nsPtr; /* Points to the namespace to be dismantled + * and unlinked from its parent. */ +{ + Interp *iPtr = (Interp *) nsPtr->interp; + register Tcl_HashEntry *entryPtr; + Tcl_HashSearch search; + Tcl_Namespace *childNsPtr; + Tcl_Command cmd; + Namespace *globalNsPtr = + (Namespace *) Tcl_GetGlobalNamespace((Tcl_Interp *) iPtr); + int i; + + /* + * Start by destroying the namespace's variable table, + * since variables might trigger traces. + */ + + if (nsPtr == globalNsPtr) { + /* + * This is the global namespace, so be careful to preserve the + * "errorInfo" and "errorCode" variables. These might be needed + * later on if errors occur while deleting commands. We are careful + * to destroy and recreate the "errorInfo" and "errorCode" + * variables, in case they had any traces on them. + */ + + char *str, *errorInfoStr, *errorCodeStr; + + str = Tcl_GetVar((Tcl_Interp *) iPtr, "errorInfo", TCL_GLOBAL_ONLY); + if (str != NULL) { + errorInfoStr = ckalloc((unsigned) (strlen(str)+1)); + strcpy(errorInfoStr, str); + } else { + errorInfoStr = NULL; + } + + str = Tcl_GetVar((Tcl_Interp *) iPtr, "errorCode", TCL_GLOBAL_ONLY); + if (str != NULL) { + errorCodeStr = ckalloc((unsigned) (strlen(str)+1)); + strcpy(errorCodeStr, str); + } else { + errorCodeStr = NULL; + } + + TclDeleteVars(iPtr, &nsPtr->varTable); + Tcl_InitHashTable(&nsPtr->varTable, TCL_STRING_KEYS); + + if (errorInfoStr != NULL) { + Tcl_SetVar((Tcl_Interp *) iPtr, "errorInfo", errorInfoStr, + TCL_GLOBAL_ONLY); + ckfree(errorInfoStr); + } + if (errorCodeStr != NULL) { + Tcl_SetVar((Tcl_Interp *) iPtr, "errorCode", errorCodeStr, + TCL_GLOBAL_ONLY); + ckfree(errorCodeStr); + } + } else { + /* + * Variable table should be cleared but not freed! TclDeleteVars + * frees it, so we reinitialize it afterwards. + */ + + TclDeleteVars(iPtr, &nsPtr->varTable); + Tcl_InitHashTable(&nsPtr->varTable, TCL_STRING_KEYS); + } + + /* + * Remove the namespace from its parent's child hashtable. + */ + + if (nsPtr->parentPtr != NULL) { + entryPtr = Tcl_FindHashEntry(&nsPtr->parentPtr->childTable, + nsPtr->name); + if (entryPtr != NULL) { + Tcl_DeleteHashEntry(entryPtr); + } + } + nsPtr->parentPtr = NULL; + + /* + * Delete all the child namespaces. + * + * BE CAREFUL: When each child is deleted, it will divorce + * itself from its parent. You can't traverse a hash table + * properly if its elements are being deleted. We use only + * the Tcl_FirstHashEntry function to be safe. + */ + + for (entryPtr = Tcl_FirstHashEntry(&nsPtr->childTable, &search); + entryPtr != NULL; + entryPtr = Tcl_FirstHashEntry(&nsPtr->childTable, &search)) { + childNsPtr = (Tcl_Namespace *) Tcl_GetHashValue(entryPtr); + Tcl_DeleteNamespace(childNsPtr); + } + + /* + * Delete all commands in this namespace. Be careful when traversing the + * hash table: when each command is deleted, it removes itself from the + * command table. + */ + + for (entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search); + entryPtr != NULL; + entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search)) { + cmd = (Tcl_Command) Tcl_GetHashValue(entryPtr); + Tcl_DeleteCommandFromToken((Tcl_Interp *) iPtr, cmd); + } + Tcl_DeleteHashTable(&nsPtr->cmdTable); + Tcl_InitHashTable(&nsPtr->cmdTable, TCL_STRING_KEYS); + + /* + * Free the namespace's export pattern array. + */ + + if (nsPtr->exportArrayPtr != NULL) { + for (i = 0; i < nsPtr->numExportPatterns; i++) { + ckfree(nsPtr->exportArrayPtr[i]); + } + ckfree((char *) nsPtr->exportArrayPtr); + nsPtr->exportArrayPtr = NULL; + nsPtr->numExportPatterns = 0; + nsPtr->maxExportPatterns = 0; + } + + /* + * Free any client data associated with the namespace. + */ + + if (nsPtr->deleteProc != NULL) { + (*nsPtr->deleteProc)(nsPtr->clientData); + } + nsPtr->deleteProc = NULL; + nsPtr->clientData = NULL; + + /* + * Reset the namespace's id field to ensure that this namespace won't + * be interpreted as valid by, e.g., the cache validation code for + * cached command references in Tcl_GetCommandFromObj. + */ + + nsPtr->nsId = 0; +} + +/* + *---------------------------------------------------------------------- + * + * NamespaceFree -- + * + * Called after a namespace has been deleted, when its + * reference count reaches 0. Frees the data structure + * representing the namespace. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +NamespaceFree(nsPtr) + register Namespace *nsPtr; /* Points to the namespace to free. */ +{ + /* + * Most of the namespace's contents are freed when the namespace is + * deleted by Tcl_DeleteNamespace. All that remains is to free its names + * (for error messages), and the structure itself. + */ + + ckfree(nsPtr->name); + ckfree(nsPtr->fullName); + + ckfree((char *) nsPtr); +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_Export -- + * + * Makes all the commands matching a pattern available to later be + * imported from the namespace specified by contextNsPtr (or the + * current namespace if contextNsPtr is NULL). The specified pattern is + * appended onto the namespace's export pattern list, which is + * optionally cleared beforehand. + * + * Results: + * Returns TCL_OK if successful, or TCL_ERROR (along with an error + * message in the interpreter's result) if something goes wrong. + * + * Side effects: + * Appends the export pattern onto the namespace's export list. + * Optionally reset the namespace's export pattern list. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_Export(interp, namespacePtr, pattern, resetListFirst) + Tcl_Interp *interp; /* Current interpreter. */ + Tcl_Namespace *namespacePtr; /* Points to the namespace from which + * commands are to be exported. NULL for + * the current namespace. */ + char *pattern; /* String pattern indicating which commands + * to export. This pattern may not include + * any namespace qualifiers; only commands + * in the specified namespace may be + * exported. */ + int resetListFirst; /* If nonzero, resets the namespace's + * export list before appending + * be overwritten by imported commands. + * If 0, return an error if an imported + * cmd conflicts with an existing one. */ +{ +#define INIT_EXPORT_PATTERNS 5 + Namespace *nsPtr, *exportNsPtr, *dummyPtr; + Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp); + char *simplePattern, *patternCpy; + int neededElems, len, i, result; + + /* + * If the specified namespace is NULL, use the current namespace. + */ + + if (namespacePtr == NULL) { + nsPtr = (Namespace *) currNsPtr; + } else { + nsPtr = (Namespace *) namespacePtr; + } + + /* + * If resetListFirst is true (nonzero), clear the namespace's export + * pattern list. + */ + + if (resetListFirst) { + if (nsPtr->exportArrayPtr != NULL) { + for (i = 0; i < nsPtr->numExportPatterns; i++) { + ckfree(nsPtr->exportArrayPtr[i]); + } + ckfree((char *) nsPtr->exportArrayPtr); + nsPtr->exportArrayPtr = NULL; + nsPtr->numExportPatterns = 0; + nsPtr->maxExportPatterns = 0; + } + } + + /* + * Check that the pattern doesn't have namespace qualifiers. + */ + + result = TclGetNamespaceForQualName(interp, pattern, nsPtr, + /*flags*/ TCL_LEAVE_ERR_MSG, &exportNsPtr, &dummyPtr, + &dummyPtr, &simplePattern); + if (result != TCL_OK) { + return result; + } + if ((exportNsPtr != nsPtr) || (strcmp(pattern, simplePattern) != 0)) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "invalid export pattern \"", pattern, + "\": pattern can't specify a namespace", + (char *) NULL); + return TCL_ERROR; + } + + /* + * Make sure there is room in the namespace's pattern array for the + * new pattern. + */ + + neededElems = nsPtr->numExportPatterns + 1; + if (nsPtr->exportArrayPtr == NULL) { + nsPtr->exportArrayPtr = (char **) + ckalloc((unsigned) (INIT_EXPORT_PATTERNS * sizeof(char *))); + nsPtr->numExportPatterns = 0; + nsPtr->maxExportPatterns = INIT_EXPORT_PATTERNS; + } else if (neededElems > nsPtr->maxExportPatterns) { + int numNewElems = 2 * nsPtr->maxExportPatterns; + size_t currBytes = nsPtr->numExportPatterns * sizeof(char *); + size_t newBytes = numNewElems * sizeof(char *); + char **newPtr = (char **) ckalloc((unsigned) newBytes); + + memcpy((VOID *) newPtr, (VOID *) nsPtr->exportArrayPtr, + currBytes); + ckfree((char *) nsPtr->exportArrayPtr); + nsPtr->exportArrayPtr = (char **) newPtr; + nsPtr->maxExportPatterns = numNewElems; + } + + /* + * Add the pattern to the namespace's array of export patterns. + */ + + len = strlen(pattern); + patternCpy = (char *) ckalloc((unsigned) (len + 1)); + strcpy(patternCpy, pattern); + + nsPtr->exportArrayPtr[nsPtr->numExportPatterns] = patternCpy; + nsPtr->numExportPatterns++; + return TCL_OK; +#undef INIT_EXPORT_PATTERNS +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_AppendExportList -- + * + * Appends onto the argument object the list of export patterns for the + * specified namespace. + * + * Results: + * The return value is normally TCL_OK; in this case the object + * referenced by objPtr has each export pattern appended to it. If an + * error occurs, TCL_ERROR is returned and the interpreter's result + * holds an error message. + * + * Side effects: + * If necessary, the object referenced by objPtr is converted into + * a list object. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_AppendExportList(interp, namespacePtr, objPtr) + Tcl_Interp *interp; /* Interpreter used for error reporting. */ + Tcl_Namespace *namespacePtr; /* Points to the namespace whose export + * pattern list is appended onto objPtr. + * NULL for the current namespace. */ + Tcl_Obj *objPtr; /* Points to the Tcl object onto which the + * export pattern list is appended. */ +{ + Namespace *nsPtr; + int i, result; + + /* + * If the specified namespace is NULL, use the current namespace. + */ + + if (namespacePtr == NULL) { + nsPtr = (Namespace *) (Namespace *) Tcl_GetCurrentNamespace(interp); + } else { + nsPtr = (Namespace *) namespacePtr; + } + + /* + * Append the export pattern list onto objPtr. + */ + + for (i = 0; i < nsPtr->numExportPatterns; i++) { + result = Tcl_ListObjAppendElement(interp, objPtr, + Tcl_NewStringObj(nsPtr->exportArrayPtr[i], -1)); + if (result != TCL_OK) { + return result; + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_Import -- + * + * Imports all of the commands matching a pattern into the namespace + * specified by contextNsPtr (or the current namespace if contextNsPtr + * is NULL). This is done by creating a new command (the "imported + * command") that points to the real command in its original namespace. + * + * Results: + * Returns TCL_OK if successful, or TCL_ERROR (along with an error + * message in the interpreter's result) if something goes wrong. + * + * Side effects: + * Creates new commands in the importing namespace. These indirect + * calls back to the real command and are deleted if the real commands + * are deleted. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_Import(interp, namespacePtr, pattern, allowOverwrite) + Tcl_Interp *interp; /* Current interpreter. */ + Tcl_Namespace *namespacePtr; /* Points to the namespace into which the + * commands are to be imported. NULL for + * the current namespace. */ + char *pattern; /* String pattern indicating which commands + * to import. This pattern should be + * qualified by the name of the namespace + * from which to import the command(s). */ + int allowOverwrite; /* If nonzero, allow existing commands to + * be overwritten by imported commands. + * If 0, return an error if an imported + * cmd conflicts with an existing one. */ +{ + Interp *iPtr = (Interp *) interp; + Namespace *nsPtr, *importNsPtr, *dummyPtr; + Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp); + char *simplePattern, *cmdName; + register Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + Command *cmdPtr; + ImportRef *refPtr; + Tcl_Command importedCmd; + ImportedCmdData *dataPtr; + int wasExported, i, result; + + /* + * If the specified namespace is NULL, use the current namespace. + */ + + if (namespacePtr == NULL) { + nsPtr = (Namespace *) currNsPtr; + } else { + nsPtr = (Namespace *) namespacePtr; + } + + /* + * From the pattern, find the namespace from which we are importing + * and get the simple pattern (no namespace qualifiers or ::'s) at + * the end. + */ + + if (strlen(pattern) == 0) { + Tcl_SetStringObj(Tcl_GetObjResult(interp), + "empty import pattern", -1); + return TCL_ERROR; + } + result = TclGetNamespaceForQualName(interp, pattern, nsPtr, + /*flags*/ TCL_LEAVE_ERR_MSG, &importNsPtr, &dummyPtr, + &dummyPtr, &simplePattern); + if (result != TCL_OK) { + return TCL_ERROR; + } + if (importNsPtr == NULL) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "unknown namespace in import pattern \"", + pattern, "\"", (char *) NULL); + return TCL_ERROR; + } + if (importNsPtr == nsPtr) { + if (pattern == simplePattern) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "no namespace specified in import pattern \"", pattern, + "\"", (char *) NULL); + } else { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "import pattern \"", pattern, + "\" tries to import from namespace \"", + importNsPtr->name, "\" into itself", (char *) NULL); + } + return TCL_ERROR; + } + + /* + * Scan through the command table in the source namespace and look for + * exported commands that match the string pattern. Create an "imported + * command" in the current namespace for each imported command; these + * commands redirect their invocations to the "real" command. + */ + + for (hPtr = Tcl_FirstHashEntry(&importNsPtr->cmdTable, &search); + (hPtr != NULL); + hPtr = Tcl_NextHashEntry(&search)) { + cmdName = Tcl_GetHashKey(&importNsPtr->cmdTable, hPtr); + if (Tcl_StringMatch(cmdName, simplePattern)) { + /* + * The command cmdName in the source namespace matches the + * pattern. Check whether it was exported. If it wasn't, + * we ignore it. + */ + + wasExported = 0; + for (i = 0; i < importNsPtr->numExportPatterns; i++) { + if (Tcl_StringMatch(cmdName, + importNsPtr->exportArrayPtr[i])) { + wasExported = 1; + break; + } + } + if (!wasExported) { + continue; + } + + /* + * Unless there is a name clash, create an imported command + * in the current namespace that refers to cmdPtr. + */ + + if ((Tcl_FindHashEntry(&nsPtr->cmdTable, cmdName) == NULL) + || allowOverwrite) { + /* + * Create the imported command and its client data. + * To create the new command in the current namespace, + * generate a fully qualified name for it. + */ + + Tcl_DString ds; + + Tcl_DStringInit(&ds); + Tcl_DStringAppend(&ds, currNsPtr->fullName, -1); + if (currNsPtr != iPtr->globalNsPtr) { + Tcl_DStringAppend(&ds, "::", 2); + } + Tcl_DStringAppend(&ds, cmdName, -1); + + cmdPtr = (Command *) Tcl_GetHashValue(hPtr); + dataPtr = (ImportedCmdData *) + ckalloc(sizeof(ImportedCmdData)); + importedCmd = Tcl_CreateObjCommand(interp, + Tcl_DStringValue(&ds), InvokeImportedCmd, + (ClientData) dataPtr, DeleteImportedCmd); + dataPtr->realCmdPtr = cmdPtr; + dataPtr->selfPtr = (Command *) importedCmd; + + /* + * Create an ImportRef structure describing this new import + * command and add it to the import ref list in the "real" + * command. + */ + + refPtr = (ImportRef *) ckalloc(sizeof(ImportRef)); + refPtr->importedCmdPtr = (Command *) importedCmd; + refPtr->nextPtr = cmdPtr->importRefPtr; + cmdPtr->importRefPtr = refPtr; + } else { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "can't import command \"", cmdName, + "\": already exists", (char *) NULL); + return TCL_ERROR; + } + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_ForgetImport -- + * + * Deletes previously imported commands. Given a pattern that may + * include the name of an exporting namespace, this procedure first + * finds all matching exported commands. It then looks in the namespace + * specified by namespacePtr for any corresponding previously imported + * commands, which it deletes. If namespacePtr is NULL, commands are + * deleted from the current namespace. + * + * Results: + * Returns TCL_OK if successful. If there is an error, returns + * TCL_ERROR and puts an error message in the interpreter's result + * object. + * + * Side effects: + * May delete commands. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_ForgetImport(interp, namespacePtr, pattern) + Tcl_Interp *interp; /* Current interpreter. */ + Tcl_Namespace *namespacePtr; /* Points to the namespace from which + * previously imported commands should be + * removed. NULL for current namespace. */ + char *pattern; /* String pattern indicating which imported + * commands to remove. This pattern should + * be qualified by the name of the + * namespace from which the command(s) were + * imported. */ +{ + Namespace *nsPtr, *importNsPtr, *dummyPtr, *actualCtxPtr; + char *simplePattern, *cmdName; + register Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + Command *cmdPtr; + int result; + + /* + * If the specified namespace is NULL, use the current namespace. + */ + + if (namespacePtr == NULL) { + nsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp); + } else { + nsPtr = (Namespace *) namespacePtr; + } + + /* + * From the pattern, find the namespace from which we are importing + * and get the simple pattern (no namespace qualifiers or ::'s) at + * the end. + */ + + result = TclGetNamespaceForQualName(interp, pattern, nsPtr, + /*flags*/ TCL_LEAVE_ERR_MSG, &importNsPtr, &dummyPtr, + &actualCtxPtr, &simplePattern); + if (result != TCL_OK) { + return result; + } + if (importNsPtr == NULL) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "unknown namespace in namespace forget pattern \"", + pattern, "\"", (char *) NULL); + return TCL_ERROR; + } + + /* + * Scan through the command table in the source namespace and look for + * exported commands that match the string pattern. If the current + * namespace has an imported command that refers to one of those real + * commands, delete it. + */ + + for (hPtr = Tcl_FirstHashEntry(&importNsPtr->cmdTable, &search); + (hPtr != NULL); + hPtr = Tcl_NextHashEntry(&search)) { + cmdName = Tcl_GetHashKey(&importNsPtr->cmdTable, hPtr); + if (Tcl_StringMatch(cmdName, simplePattern)) { + hPtr = Tcl_FindHashEntry(&nsPtr->cmdTable, cmdName); + if (hPtr != NULL) { /* cmd of same name in current namespace */ + cmdPtr = (Command *) Tcl_GetHashValue(hPtr); + if (cmdPtr->deleteProc == DeleteImportedCmd) { + Tcl_DeleteCommandFromToken(interp, (Tcl_Command) cmdPtr); + } + } + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TclGetOriginalCommand -- + * + * An imported command is created in an namespace when it imports a + * "real" command from another namespace. If the specified command is a + * imported command, this procedure returns the original command it + * refers to. + * + * Results: + * If the command was imported into a sequence of namespaces a, b,...,n + * where each successive namespace just imports the command from the + * previous namespace, this procedure returns the Tcl_Command token in + * the first namespace, a. Otherwise, if the specified command is not + * an imported command, the procedure returns NULL. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_Command +TclGetOriginalCommand(command) + Tcl_Command command; /* The command for which the original + * command should be returned. */ +{ + register Command *cmdPtr = (Command *) command; + ImportedCmdData *dataPtr; + + if (cmdPtr->deleteProc != DeleteImportedCmd) { + return (Tcl_Command) NULL; + } + + while (cmdPtr->deleteProc == DeleteImportedCmd) { + dataPtr = (ImportedCmdData *) cmdPtr->objClientData; + cmdPtr = dataPtr->realCmdPtr; + } + return (Tcl_Command) cmdPtr; +} + +/* + *---------------------------------------------------------------------- + * + * InvokeImportedCmd -- + * + * Invoked by Tcl whenever the user calls an imported command that + * was created by Tcl_Import. Finds the "real" command (in another + * namespace), and passes control to it. + * + * Results: + * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. + * + * Side effects: + * Returns a result in the interpreter's result object. If anything + * goes wrong, the result object is set to an error message. + * + *---------------------------------------------------------------------- + */ + +static int +InvokeImportedCmd(clientData, interp, objc, objv) + ClientData clientData; /* Points to the imported command's + * ImportedCmdData structure. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* The argument objects. */ +{ + register ImportedCmdData *dataPtr = (ImportedCmdData *) clientData; + register Command *realCmdPtr = dataPtr->realCmdPtr; + + return (*realCmdPtr->objProc)(realCmdPtr->objClientData, interp, + objc, objv); +} + +/* + *---------------------------------------------------------------------- + * + * DeleteImportedCmd -- + * + * Invoked by Tcl whenever an imported command is deleted. The "real" + * command keeps a list of all the imported commands that refer to it, + * so those imported commands can be deleted when the real command is + * deleted. This procedure removes the imported command reference from + * the real command's list, and frees up the memory associated with + * the imported command. + * + * Results: + * None. + * + * Side effects: + * Removes the imported command from the real command's import list. + * + *---------------------------------------------------------------------- + */ + +static void +DeleteImportedCmd(clientData) + ClientData clientData; /* Points to the imported command's + * ImportedCmdData structure. */ +{ + ImportedCmdData *dataPtr = (ImportedCmdData *) clientData; + Command *realCmdPtr = dataPtr->realCmdPtr; + Command *selfPtr = dataPtr->selfPtr; + register ImportRef *refPtr, *prevPtr; + + prevPtr = NULL; + for (refPtr = realCmdPtr->importRefPtr; refPtr != NULL; + refPtr = refPtr->nextPtr) { + if (refPtr->importedCmdPtr == selfPtr) { + /* + * Remove *refPtr from real command's list of imported commands + * that refer to it. + */ + + if (prevPtr == NULL) { /* refPtr is first in list */ + realCmdPtr->importRefPtr = refPtr->nextPtr; + } else { + prevPtr->nextPtr = refPtr->nextPtr; + } + ckfree((char *) refPtr); + ckfree((char *) dataPtr); + return; + } + prevPtr = refPtr; + } + + panic("DeleteImportedCmd: did not find cmd in real cmd's list of import references"); +} + +/* + *---------------------------------------------------------------------- + * + * TclGetNamespaceForQualName -- + * + * Given a qualified name specifying a command, variable, or namespace, + * and a namespace in which to resolve the name, this procedure returns + * a pointer to the namespace that contains the item. A qualified name + * consists of the "simple" name of an item qualified by the names of + * an arbitrary number of containing namespace separated by "::"s. If + * the qualified name starts with "::", it is interpreted absolutely + * from the global namespace. Otherwise, it is interpreted relative to + * the namespace specified by cxtNsPtr if it is non-NULL. If cxtNsPtr + * is NULL, the name is interpreted relative to the current namespace. + * + * A relative name like "foo::bar::x" can be found starting in either + * the current namespace or in the global namespace. So each search + * usually follows two tracks, and two possible namespaces are + * returned. If the procedure sets either *nsPtrPtr or *altNsPtrPtr to + * NULL, then that path failed. + * + * If "flags" contains TCL_GLOBAL_ONLY, the relative qualified name is + * sought only in the global :: namespace. The alternate search + * (also) starting from the global namespace is ignored and + * *altNsPtrPtr is set NULL. + * + * If "flags" contains TCL_NAMESPACE_ONLY, the relative qualified + * name is sought only in the namespace specified by cxtNsPtr. The + * alternate search starting from the global namespace is ignored and + * *altNsPtrPtr is set NULL. If both TCL_GLOBAL_ONLY and + * TCL_NAMESPACE_ONLY are specified, TCL_GLOBAL_ONLY is ignored and + * the search starts from the namespace specified by cxtNsPtr. + * + * If "flags" contains CREATE_NS_IF_UNKNOWN, all namespace + * components of the qualified name that cannot be found are + * automatically created within their specified parent. This makes sure + * that functions like Tcl_CreateCommand always succeed. There is no + * alternate search path, so *altNsPtrPtr is set NULL. + * + * If "flags" contains FIND_ONLY_NS, the qualified name is treated as a + * reference to a namespace, and the entire qualified name is + * followed. If the name is relative, the namespace is looked up only + * in the current namespace. A pointer to the namespace is stored in + * *nsPtrPtr and NULL is stored in *simpleNamePtr. Otherwise, if + * FIND_ONLY_NS is not specified, only the leading components are + * treated as namespace names, and a pointer to the simple name of the + * final component is stored in *simpleNamePtr. + * + * Results: + * Ordinarily this procedure returns TCL_OK. It sets *nsPtrPtr and + * *altNsPtrPtr to point to the two possible namespaces which represent + * the last (containing) namespace in the qualified name. If the + * procedure sets either *nsPtrPtr or *altNsPtrPtr to NULL, then the + * search along that path failed. The procedure also stores a pointer + * to the simple name of the final component in *simpleNamePtr. If the + * qualified name is "::" or was treated as a namespace reference + * (FIND_ONLY_NS), the procedure stores a pointer to the + * namespace in *nsPtrPtr, NULL in *altNsPtrPtr, and sets + * *simpleNamePtr to point to an empty string. + * + * If there is an error, this procedure returns TCL_ERROR. If "flags" + * contains TCL_LEAVE_ERR_MSG, an error message is returned in the + * interpreter's result object. Otherwise, the interpreter's result + * object is left unchanged. + * + * *actualCxtPtrPtr is set to the actual context namespace. It is + * set to the input context namespace pointer in cxtNsPtr. If cxtNsPtr + * is NULL, it is set to the current namespace context. + * + * Side effects: + * If flags contains TCL_LEAVE_ERR_MSG and an error is encountered, + * the interpreter's result object will contain an error message. + * + *---------------------------------------------------------------------- + */ + +int +TclGetNamespaceForQualName(interp, qualName, cxtNsPtr, flags, + nsPtrPtr, altNsPtrPtr, actualCxtPtrPtr, simpleNamePtr) + Tcl_Interp *interp; /* Interpreter in which to find the + * namespace containing qualName. */ + register char *qualName; /* A namespace-qualified name of an + * command, variable, or namespace. */ + Namespace *cxtNsPtr; /* The namespace in which to start the + * search for qualName's namespace. If NULL + * start from the current namespace. + * Ignored if TCL_GLOBAL_ONLY or + * TCL_NAMESPACE_ONLY are set. */ + int flags; /* Flags controlling the search: an OR'd + * combination of TCL_GLOBAL_ONLY, + * TCL_NAMESPACE_ONLY, + * CREATE_NS_IF_UNKNOWN, and + * FIND_ONLY_NS. */ + Namespace **nsPtrPtr; /* Address where procedure stores a pointer + * to containing namespace if qualName is + * found starting from *cxtNsPtr or, if + * TCL_GLOBAL_ONLY is set, if qualName is + * found in the global :: namespace. NULL + * is stored otherwise. */ + Namespace **altNsPtrPtr; /* Address where procedure stores a pointer + * to containing namespace if qualName is + * found starting from the global :: + * namespace. NULL is stored if qualName + * isn't found starting from :: or if the + * TCL_GLOBAL_ONLY, TCL_NAMESPACE_ONLY, + * CREATE_NS_IF_UNKNOWN, FIND_ONLY_NS flag + * is set. */ + Namespace **actualCxtPtrPtr; /* Address where procedure stores a pointer + * to the actual namespace from which the + * search started. This is either cxtNsPtr, + * the :: namespace if TCL_GLOBAL_ONLY was + * specified, or the current namespace if + * cxtNsPtr was NULL. */ + char **simpleNamePtr; /* Address where procedure stores the + * simple name at end of the qualName, or + * NULL if qualName is "::" or the flag + * FIND_ONLY_NS was specified. */ +{ + Interp *iPtr = (Interp *) interp; + Namespace *nsPtr = cxtNsPtr; + Namespace *altNsPtr; + Namespace *globalNsPtr = iPtr->globalNsPtr; + register char *start, *end; + char *nsName; + Tcl_HashEntry *entryPtr; + Tcl_DString buffer; + int len, result; + + /* + * Determine the context namespace nsPtr in which to start the primary + * search. If TCL_NAMESPACE_ONLY or FIND_ONLY_NS was specified, search + * from the current namespace. If the qualName name starts with a "::" + * or TCL_GLOBAL_ONLY was specified, search from the global + * namespace. Otherwise, use the given namespace given in cxtNsPtr, or + * if that is NULL, use the current namespace context. Note that we + * always treat two or more adjacent ":"s as a namespace separator. + */ + + if (flags & (TCL_NAMESPACE_ONLY | FIND_ONLY_NS)) { + nsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp); + } else if (flags & TCL_GLOBAL_ONLY) { + nsPtr = globalNsPtr; + } else if (nsPtr == NULL) { + if (iPtr->varFramePtr != NULL) { + nsPtr = iPtr->varFramePtr->nsPtr; + } else { + nsPtr = iPtr->globalNsPtr; + } + } + + start = qualName; /* pts to start of qualifying namespace */ + if ((*qualName == ':') && (*(qualName+1) == ':')) { + start = qualName+2; /* skip over the initial :: */ + while (*start == ':') { + start++; /* skip over a subsequent : */ + } + nsPtr = globalNsPtr; + if (*start == '\0') { /* qualName is just two or more ":"s */ + *nsPtrPtr = globalNsPtr; + *altNsPtrPtr = NULL; + *actualCxtPtrPtr = globalNsPtr; + *simpleNamePtr = start; /* points to empty string */ + return TCL_OK; + } + } + *actualCxtPtrPtr = nsPtr; + + /* + * Start an alternate search path starting with the global namespace. + * However, if the starting context is the global namespace, or if the + * flag is set to search only the namespace *cxtNsPtr, ignore the + * alternate search path. + */ + + altNsPtr = globalNsPtr; + if ((nsPtr == globalNsPtr) + || (flags & (TCL_NAMESPACE_ONLY | FIND_ONLY_NS))) { + altNsPtr = NULL; + } + + /* + * Loop to resolve each namespace qualifier in qualName. + */ + + Tcl_DStringInit(&buffer); + end = start; + while (*start != '\0') { + /* + * Find the next namespace qualifier (i.e., a name ending in "::") + * or the end of the qualified name (i.e., a name ending in "\0"). + * Set len to the number of characters, starting from start, + * in the name; set end to point after the "::"s or at the "\0". + */ + + len = 0; + for (end = start; *end != '\0'; end++) { + if ((*end == ':') && (*(end+1) == ':')) { + end += 2; /* skip over the initial :: */ + while (*end == ':') { + end++; /* skip over the subsequent : */ + } + break; /* exit for loop; end is after ::'s */ + } + len++; + } + + if ((*end == '\0') + && !((end-start >= 2) && (*(end-1) == ':') && (*(end-2) == ':'))) { + /* + * qualName ended with a simple name at start. If FIND_ONLY_NS + * was specified, look this up as a namespace. Otherwise, + * start is the name of a cmd or var and we are done. + */ + + if (flags & FIND_ONLY_NS) { + nsName = start; + } else { + *nsPtrPtr = nsPtr; + *altNsPtrPtr = altNsPtr; + *simpleNamePtr = start; + Tcl_DStringFree(&buffer); + return TCL_OK; + } + } else { + /* + * start points to the beginning of a namespace qualifier ending + * in "::". end points to the start of a name in that namespace + * that might be empty. Copy the namespace qualifier to a + * buffer so it can be null terminated. We can't modify the + * incoming qualName since it may be a string constant. + */ + + Tcl_DStringSetLength(&buffer, 0); + Tcl_DStringAppend(&buffer, start, len); + nsName = Tcl_DStringValue(&buffer); + } + + /* + * Look up the namespace qualifier nsName in the current namespace + * context. If it isn't found but CREATE_NS_IF_UNKNOWN is set, + * create that qualifying namespace. This is needed for procedures + * like Tcl_CreateCommand that cannot fail. + */ + + if (nsPtr != NULL) { + entryPtr = Tcl_FindHashEntry(&nsPtr->childTable, nsName); + if (entryPtr != NULL) { + nsPtr = (Namespace *) Tcl_GetHashValue(entryPtr); + } else if (flags & CREATE_NS_IF_UNKNOWN) { + Tcl_CallFrame frame; + + result = Tcl_PushCallFrame(interp, &frame, + (Tcl_Namespace *) nsPtr, /*isProcCallFrame*/ 0); + if (result != TCL_OK) { + Tcl_DStringFree(&buffer); + return result; + } + nsPtr = (Namespace *) Tcl_CreateNamespace(interp, nsName, + (ClientData) NULL, (Tcl_NamespaceDeleteProc *) NULL); + Tcl_PopCallFrame(interp); + if (nsPtr == NULL) { + Tcl_DStringFree(&buffer); + return TCL_ERROR; + } + } else { /* namespace not found and wasn't created */ + nsPtr = NULL; + } + } + + /* + * Look up the namespace qualifier in the alternate search path too. + */ + + if (altNsPtr != NULL) { + entryPtr = Tcl_FindHashEntry(&altNsPtr->childTable, nsName); + if (entryPtr != NULL) { + altNsPtr = (Namespace *) Tcl_GetHashValue(entryPtr); + } else { + altNsPtr = NULL; + } + } + + /* + * If both search paths have failed, return NULL results. + */ + + if ((nsPtr == NULL) && (altNsPtr == NULL)) { + *nsPtrPtr = NULL; + *altNsPtrPtr = NULL; + *simpleNamePtr = NULL; + Tcl_DStringFree(&buffer); + return TCL_OK; + } + + start = end; + } + + /* + * We ignore trailing "::"s in a namespace name, but in a command or + * variable name, trailing "::"s refer to the cmd or var named {}. + */ + + if ((flags & FIND_ONLY_NS) + || ((end > start ) && (*(end-1) != ':'))) { + *simpleNamePtr = NULL; /* found namespace name */ + } else { + *simpleNamePtr = end; /* found cmd/var: points to empty string */ + } + + /* + * As a special case, if we are looking for a namespace and qualName + * is "" and the current active namespace (nsPtr) is not the global + * namespace, return NULL (no namespace was found). This is because + * namespaces can not have empty names except for the global namespace. + */ + + if ((flags & FIND_ONLY_NS) && (*qualName == '\0') + && (nsPtr != globalNsPtr)) { + nsPtr = NULL; + } + + *nsPtrPtr = nsPtr; + *altNsPtrPtr = altNsPtr; + Tcl_DStringFree(&buffer); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_FindNamespace -- + * + * Searches for a namespace. + * + * Results: + * Returns a pointer to the namespace if it is found. Otherwise, + * returns NULL and leaves an error message in the interpreter's + * result object if "flags" contains TCL_LEAVE_ERR_MSG. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_Namespace * +Tcl_FindNamespace(interp, name, contextNsPtr, flags) + Tcl_Interp *interp; /* The interpreter in which to find the + * namespace. */ + char *name; /* Namespace name. If it starts with "::", + * will be looked up in global namespace. + * Else, looked up first in contextNsPtr + * (current namespace if contextNsPtr is + * NULL), then in global namespace. */ + Tcl_Namespace *contextNsPtr; /* Ignored if TCL_GLOBAL_ONLY flag is set + * or if the name starts with "::". + * Otherwise, points to namespace in which + * to resolve name; if NULL, look up name + * in the current namespace. */ + register int flags; /* Flags controlling namespace lookup: an + * OR'd combination of TCL_GLOBAL_ONLY and + * TCL_LEAVE_ERR_MSG flags. */ +{ + Namespace *nsPtr, *dummy1Ptr, *dummy2Ptr; + char *dummy; + int result; + + /* + * Find the namespace(s) that contain the specified namespace name. + * Add the FIND_ONLY_NS flag to resolve the name all the way down + * to its last component, a namespace. + */ + + result = TclGetNamespaceForQualName(interp, name, + (Namespace *) contextNsPtr, /*flags*/ (flags | FIND_ONLY_NS), + &nsPtr, &dummy1Ptr, &dummy2Ptr, &dummy); + if (result != TCL_OK) { + return NULL; + } + if (nsPtr != NULL) { + return (Tcl_Namespace *) nsPtr; + } else if (flags & TCL_LEAVE_ERR_MSG) { + Tcl_ResetResult(interp); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "unknown namespace \"", name, "\"", (char *) NULL); + } + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_FindCommand -- + * + * Searches for a command. + * + * Results: + * Returns a token for the command if it is found. Otherwise, if it + * can't be found or there is an error, returns NULL and leaves an + * error message in the interpreter's result object if "flags" + * contains TCL_LEAVE_ERR_MSG. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_Command +Tcl_FindCommand(interp, name, contextNsPtr, flags) + Tcl_Interp *interp; /* The interpreter in which to find the + * command and to report errors. */ + char *name; /* Command's name. If it starts with "::", + * will be looked up in global namespace. + * Else, looked up first in contextNsPtr + * (current namespace if contextNsPtr is + * NULL), then in global namespace. */ + Tcl_Namespace *contextNsPtr; /* Ignored if TCL_GLOBAL_ONLY flag set. + * Otherwise, points to namespace in which + * to resolve name. If NULL, look up name + * in the current namespace. */ + int flags; /* An OR'd combination of flags: + * TCL_GLOBAL_ONLY (look up name only in + * global namespace), TCL_NAMESPACE_ONLY + * (look up only in contextNsPtr, or the + * current namespace if contextNsPtr is + * NULL), and TCL_LEAVE_ERR_MSG. If both + * TCL_GLOBAL_ONLY and TCL_NAMESPACE_ONLY + * are given, TCL_GLOBAL_ONLY is + * ignored. */ +{ + Namespace *nsPtr[2], *cxtNsPtr; + char *simpleName; + register Tcl_HashEntry *entryPtr; + register Command *cmdPtr; + register int search; + int result; + + /* + * Find the namespace(s) that contain the command. + */ + + result = TclGetNamespaceForQualName(interp, name, + (Namespace *) contextNsPtr, flags, &nsPtr[0], &nsPtr[1], + &cxtNsPtr, &simpleName); + if (result != TCL_OK) { + return (Tcl_Command) NULL; + } + + /* + * Look for the command in the command table of its namespace. + * Be sure to check both possible search paths: from the specified + * namespace context and from the global namespace. + */ + + cmdPtr = NULL; + for (search = 0; (search < 2) && (cmdPtr == NULL); search++) { + if ((nsPtr[search] != NULL) && (simpleName != NULL)) { + entryPtr = Tcl_FindHashEntry(&nsPtr[search]->cmdTable, + simpleName); + if (entryPtr != NULL) { + cmdPtr = (Command *) Tcl_GetHashValue(entryPtr); + } + } + } + if (cmdPtr != NULL) { + return (Tcl_Command) cmdPtr; + } else if (flags & TCL_LEAVE_ERR_MSG) { + Tcl_ResetResult(interp); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "unknown command \"", name, "\"", (char *) NULL); + } + return (Tcl_Command) NULL; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_FindNamespaceVar -- + * + * Searches for a namespace variable, a variable not local to a + * procedure. The variable can be either a scalar or an array, but + * may not be an element of an array. + * + * Results: + * Returns a token for the variable if it is found. Otherwise, if it + * can't be found or there is an error, returns NULL and leaves an + * error message in the interpreter's result object if "flags" + * contains TCL_LEAVE_ERR_MSG. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_Var +Tcl_FindNamespaceVar(interp, name, contextNsPtr, flags) + Tcl_Interp *interp; /* The interpreter in which to find the + * variable. */ + char *name; /* Variable's name. If it starts with "::", + * will be looked up in global namespace. + * Else, looked up first in contextNsPtr + * (current namespace if contextNsPtr is + * NULL), then in global namespace. */ + Tcl_Namespace *contextNsPtr; /* Ignored if TCL_GLOBAL_ONLY flag set. + * Otherwise, points to namespace in which + * to resolve name. If NULL, look up name + * in the current namespace. */ + int flags; /* An OR'd combination of flags: + * TCL_GLOBAL_ONLY (look up name only in + * global namespace), TCL_NAMESPACE_ONLY + * (look up only in contextNsPtr, or the + * current namespace if contextNsPtr is + * NULL), and TCL_LEAVE_ERR_MSG. If both + * TCL_GLOBAL_ONLY and TCL_NAMESPACE_ONLY + * are given, TCL_GLOBAL_ONLY is + * ignored. */ +{ + Namespace *nsPtr[2], *cxtNsPtr; + char *simpleName; + Tcl_HashEntry *entryPtr; + Var *varPtr; + register int search; + int result; + + /* + * Find the namespace(s) that contain the variable. + */ + + result = TclGetNamespaceForQualName(interp, name, + (Namespace *) contextNsPtr, flags, &nsPtr[0], &nsPtr[1], + &cxtNsPtr, &simpleName); + if (result != TCL_OK) { + return (Tcl_Var) NULL; + } + + /* + * Look for the variable in the variable table of its namespace. + * Be sure to check both possible search paths: from the specified + * namespace context and from the global namespace. + */ + + varPtr = NULL; + for (search = 0; (search < 2) && (varPtr == NULL); search++) { + if ((nsPtr[search] != NULL) && (simpleName != NULL)) { + entryPtr = Tcl_FindHashEntry(&nsPtr[search]->varTable, + simpleName); + if (entryPtr != NULL) { + varPtr = (Var *) Tcl_GetHashValue(entryPtr); + } + } + } + if (varPtr != NULL) { + return (Tcl_Var) varPtr; + } else if (flags & TCL_LEAVE_ERR_MSG) { + Tcl_ResetResult(interp); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "unknown variable \"", name, "\"", (char *) NULL); + } + return (Tcl_Var) NULL; +} + +/* + *---------------------------------------------------------------------- + * + * TclResetShadowedCmdRefs -- + * + * Called when a command is added to a namespace to check for existing + * command references that the new command may invalidate. Consider the + * following cases that could happen when you add a command "foo" to a + * namespace "b": + * 1. It could shadow a command named "foo" at the global scope. + * If it does, all command references in the namespace "b" are + * suspect. + * 2. Suppose the namespace "b" resides in a namespace "a". + * Then to "a" the new command "b::foo" could shadow another + * command "b::foo" in the global namespace. If so, then all + * command references in "a" are suspect. + * The same checks are applied to all parent namespaces, until we + * reach the global :: namespace. + * + * Results: + * None. + * + * Side effects: + * If the new command shadows an existing command, the cmdRefEpoch + * counter is incremented in each namespace that sees the shadow. + * This invalidates all command references that were previously cached + * in that namespace. The next time the commands are used, they are + * resolved from scratch. + * + *---------------------------------------------------------------------- + */ + +void +TclResetShadowedCmdRefs(interp, newCmdPtr) + Tcl_Interp *interp; /* Interpreter containing the new command. */ + Command *newCmdPtr; /* Points to the new command. */ +{ + char *cmdName; + Tcl_HashEntry *hPtr; + register Namespace *nsPtr; + Namespace *trailNsPtr, *shadowNsPtr; + Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp); + int found, i; + + /* + * This procedure generates an array used to hold the trail list. This + * starts out with stack-allocated space but uses dynamically-allocated + * storage if needed. + */ + +#define NUM_TRAIL_ELEMS 5 + Namespace *(trailStorage[NUM_TRAIL_ELEMS]); + Namespace **trailPtr = trailStorage; + int trailFront = -1; + int trailSize = NUM_TRAIL_ELEMS; + + /* + * Start at the namespace containing the new command, and work up + * through the list of parents. Stop just before the global namespace, + * since the global namespace can't "shadow" its own entries. + * + * The namespace "trail" list we build consists of the names of each + * namespace that encloses the new command, in order from outermost to + * innermost: for example, "a" then "b". Each iteration of this loop + * eventually extends the trail upwards by one namespace, nsPtr. We use + * this trail list to see if nsPtr (e.g. "a" in 2. above) could have + * now-invalid cached command references. This will happen if nsPtr + * (e.g. "a") contains a sequence of child namespaces (e.g. "b") + * such that there is a identically-named sequence of child namespaces + * starting from :: (e.g. "::b") whose tail namespace contains a command + * also named cmdName. + */ + + cmdName = Tcl_GetHashKey(newCmdPtr->hPtr->tablePtr, newCmdPtr->hPtr); + for (nsPtr = newCmdPtr->nsPtr; + (nsPtr != NULL) && (nsPtr != globalNsPtr); + nsPtr = nsPtr->parentPtr) { + /* + * Find the maximal sequence of child namespaces contained in nsPtr + * such that there is a identically-named sequence of child + * namespaces starting from ::. shadowNsPtr will be the tail of this + * sequence, or the deepest namespace under :: that might contain a + * command now shadowed by cmdName. We check below if shadowNsPtr + * actually contains a command cmdName. + */ + + found = 1; + shadowNsPtr = globalNsPtr; + + for (i = trailFront; i >= 0; i--) { + trailNsPtr = trailPtr[i]; + hPtr = Tcl_FindHashEntry(&shadowNsPtr->childTable, + trailNsPtr->name); + if (hPtr != NULL) { + shadowNsPtr = (Namespace *) Tcl_GetHashValue(hPtr); + } else { + found = 0; + break; + } + } + + /* + * If shadowNsPtr contains a command named cmdName, we invalidate + * all of the command refs cached in nsPtr. As a boundary case, + * shadowNsPtr is initially :: and we check for case 1. above. + */ + + if (found) { + hPtr = Tcl_FindHashEntry(&shadowNsPtr->cmdTable, cmdName); + if (hPtr != NULL) { + nsPtr->cmdRefEpoch++; + } + } + + /* + * Insert nsPtr at the front of the trail list: i.e., at the end + * of the trailPtr array. + */ + + trailFront++; + if (trailFront == trailSize) { + size_t currBytes = trailSize * sizeof(Namespace *); + int newSize = 2*trailSize; + size_t newBytes = newSize * sizeof(Namespace *); + Namespace **newPtr = + (Namespace **) ckalloc((unsigned) newBytes); + + memcpy((VOID *) newPtr, (VOID *) trailPtr, currBytes); + if (trailPtr != trailStorage) { + ckfree((char *) trailPtr); + } + trailPtr = newPtr; + trailSize = newSize; + } + trailPtr[trailFront] = nsPtr; + } + + /* + * Free any allocated storage. + */ + + if (trailPtr != trailStorage) { + ckfree((char *) trailPtr); + } +#undef NUM_TRAIL_ELEMS +} + +/* + *---------------------------------------------------------------------- + * + * GetNamespaceFromObj -- + * + * Returns the namespace specified by the name in a Tcl_Obj. + * + * Results: + * Returns TCL_OK if the namespace was resolved successfully, and + * stores a pointer to the namespace in the location specified by + * nsPtrPtr. If the namespace can't be found, the procedure stores + * NULL in *nsPtrPtr and returns TCL_OK. If anything else goes wrong, + * this procedure returns TCL_ERROR. + * + * Side effects: + * May update the internal representation for the object, caching the + * namespace reference. The next time this procedure is called, the + * namespace value can be found quickly. + * + * If anything goes wrong, an error message is left in the + * interpreter's result object. + * + *---------------------------------------------------------------------- + */ + +static int +GetNamespaceFromObj(interp, objPtr, nsPtrPtr) + Tcl_Interp *interp; /* The current interpreter. */ + Tcl_Obj *objPtr; /* The object to be resolved as the name + * of a namespace. */ + Tcl_Namespace **nsPtrPtr; /* Result namespace pointer goes here. */ +{ + register ResolvedNsName *resNamePtr; + register Namespace *nsPtr; + Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp); + int result; + + /* + * Get the internal representation, converting to a namespace type if + * needed. The internal representation is a ResolvedNsName that points + * to the actual namespace. + */ + + if (objPtr->typePtr != &tclNsNameType) { + result = tclNsNameType.setFromAnyProc(interp, objPtr); + if (result != TCL_OK) { + return TCL_ERROR; + } + } + resNamePtr = (ResolvedNsName *) objPtr->internalRep.otherValuePtr; + + /* + * Check the context namespace of the resolved symbol to make sure that + * it is fresh. If not, then force another conversion to the namespace + * type, to discard the old rep and create a new one. Note that we + * verify that the namespace id of the cached namespace is the same as + * the id when we cached it; this insures that the namespace wasn't + * deleted and a new one created at the same address. + */ + + nsPtr = NULL; + if ((resNamePtr != NULL) + && (resNamePtr->refNsPtr == currNsPtr) + && (resNamePtr->nsId == resNamePtr->nsPtr->nsId)) { + nsPtr = resNamePtr->nsPtr; + if (nsPtr->flags & NS_DEAD) { + nsPtr = NULL; + } + } + if (nsPtr == NULL) { /* try again */ + result = tclNsNameType.setFromAnyProc(interp, objPtr); + if (result != TCL_OK) { + return TCL_ERROR; + } + resNamePtr = (ResolvedNsName *) objPtr->internalRep.otherValuePtr; + if (resNamePtr != NULL) { + nsPtr = resNamePtr->nsPtr; + if (nsPtr->flags & NS_DEAD) { + nsPtr = NULL; + } + } + } + *nsPtrPtr = (Tcl_Namespace *) nsPtr; + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_NamespaceObjCmd -- + * + * Invoked to implement the "namespace" command that creates, deletes, + * or manipulates Tcl namespaces. Handles the following syntax: + * + * namespace children ?name? ?pattern? + * namespace code arg + * namespace current + * namespace delete ?name name...? + * namespace eval name arg ?arg...? + * namespace export ?-clear? ?pattern pattern...? + * namespace forget ?pattern pattern...? + * namespace import ?-force? ?pattern pattern...? + * namespace inscope name arg ?arg...? + * namespace origin name + * namespace parent ?name? + * namespace qualifiers string + * namespace tail string + * namespace which ?-command? ?-variable? name + * + * Results: + * Returns TCL_OK if the command is successful. Returns TCL_ERROR if + * anything goes wrong. + * + * Side effects: + * Based on the subcommand name (e.g., "import"), this procedure + * dispatches to a corresponding procedure NamespaceXXXCmd defined + * statically in this file. This procedure's side effects depend on + * whatever that subcommand procedure does. If there is an error, this + * procedure returns an error message in the interpreter's result + * object. Otherwise it may return a result in the interpreter's result + * object. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_NamespaceObjCmd(clientData, interp, objc, objv) + ClientData clientData; /* Arbitrary value passed to cmd. */ + Tcl_Interp *interp; /* Current interpreter. */ + register int objc; /* Number of arguments. */ + register Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + static char *subCmds[] = { + "children", "code", "current", "delete", + "eval", "export", "forget", "import", + "inscope", "origin", "parent", "qualifiers", + "tail", "which", (char *) NULL}; + enum NSSubCmdIdx { + NSChildrenIdx, NSCodeIdx, NSCurrentIdx, NSDeleteIdx, + NSEvalIdx, NSExportIdx, NSForgetIdx, NSImportIdx, + NSInscopeIdx, NSOriginIdx, NSParentIdx, NSQualifiersIdx, + NSTailIdx, NSWhichIdx + } index; + int result; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "subcommand ?arg ...?"); + return TCL_ERROR; + } + + /* + * Return an index reflecting the particular subcommand. + */ + + result = Tcl_GetIndexFromObj((Tcl_Interp *) interp, objv[1], subCmds, + "option", /*flags*/ 0, (int *) &index); + if (result != TCL_OK) { + return result; + } + + switch (index) { + case NSChildrenIdx: + result = NamespaceChildrenCmd(clientData, interp, objc, objv); + break; + case NSCodeIdx: + result = NamespaceCodeCmd(clientData, interp, objc, objv); + break; + case NSCurrentIdx: + result = NamespaceCurrentCmd(clientData, interp, objc, objv); + break; + case NSDeleteIdx: + result = NamespaceDeleteCmd(clientData, interp, objc, objv); + break; + case NSEvalIdx: + result = NamespaceEvalCmd(clientData, interp, objc, objv); + break; + case NSExportIdx: + result = NamespaceExportCmd(clientData, interp, objc, objv); + break; + case NSForgetIdx: + result = NamespaceForgetCmd(clientData, interp, objc, objv); + break; + case NSImportIdx: + result = NamespaceImportCmd(clientData, interp, objc, objv); + break; + case NSInscopeIdx: + result = NamespaceInscopeCmd(clientData, interp, objc, objv); + break; + case NSOriginIdx: + result = NamespaceOriginCmd(clientData, interp, objc, objv); + break; + case NSParentIdx: + result = NamespaceParentCmd(clientData, interp, objc, objv); + break; + case NSQualifiersIdx: + result = NamespaceQualifiersCmd(clientData, interp, objc, objv); + break; + case NSTailIdx: + result = NamespaceTailCmd(clientData, interp, objc, objv); + break; + case NSWhichIdx: + result = NamespaceWhichCmd(clientData, interp, objc, objv); + break; + } + return result; +} + +/* + *---------------------------------------------------------------------- + * + * NamespaceChildrenCmd -- + * + * Invoked to implement the "namespace children" command that returns a + * list containing the fully-qualified names of the child namespaces of + * a given namespace. Handles the following syntax: + * + * namespace children ?name? ?pattern? + * + * Results: + * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. + * + * Side effects: + * Returns a result in the interpreter's result object. If anything + * goes wrong, the result is an error message. + * + *---------------------------------------------------------------------- + */ + +static int +NamespaceChildrenCmd(dummy, interp, objc, objv) + ClientData dummy; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + Tcl_Namespace *namespacePtr; + Namespace *nsPtr, *childNsPtr; + Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp); + char *pattern = NULL; + Tcl_DString buffer; + register Tcl_HashEntry *entryPtr; + Tcl_HashSearch search; + Tcl_Obj *listPtr, *elemPtr; + + /* + * Get a pointer to the specified namespace, or the current namespace. + */ + + if (objc == 2) { + nsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp); + } else if ((objc == 3) || (objc == 4)) { + if (GetNamespaceFromObj(interp, objv[2], &namespacePtr) != TCL_OK) { + return TCL_ERROR; + } + if (namespacePtr == NULL) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "unknown namespace \"", + Tcl_GetStringFromObj(objv[2], (int *) NULL), + "\" in namespace children command", (char *) NULL); + return TCL_ERROR; + } + nsPtr = (Namespace *) namespacePtr; + } else { + Tcl_WrongNumArgs(interp, 2, objv, "?name? ?pattern?"); + return TCL_ERROR; + } + + /* + * Get the glob-style pattern, if any, used to narrow the search. + */ + + Tcl_DStringInit(&buffer); + if (objc == 4) { + char *name = Tcl_GetStringFromObj(objv[3], (int *) NULL); + + if ((*name == ':') && (*(name+1) == ':')) { + pattern = name; + } else { + Tcl_DStringAppend(&buffer, nsPtr->fullName, -1); + if (nsPtr != globalNsPtr) { + Tcl_DStringAppend(&buffer, "::", 2); + } + Tcl_DStringAppend(&buffer, name, -1); + pattern = Tcl_DStringValue(&buffer); + } + } + + /* + * Create a list containing the full names of all child namespaces + * whose names match the specified pattern, if any. + */ + + listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + entryPtr = Tcl_FirstHashEntry(&nsPtr->childTable, &search); + while (entryPtr != NULL) { + childNsPtr = (Namespace *) Tcl_GetHashValue(entryPtr); + if ((pattern == NULL) + || Tcl_StringMatch(childNsPtr->fullName, pattern)) { + elemPtr = Tcl_NewStringObj(childNsPtr->fullName, -1); + Tcl_ListObjAppendElement(interp, listPtr, elemPtr); + } + entryPtr = Tcl_NextHashEntry(&search); + } + + Tcl_SetObjResult(interp, listPtr); + Tcl_DStringFree(&buffer); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NamespaceCodeCmd -- + * + * Invoked to implement the "namespace code" command to capture the + * namespace context of a command. Handles the following syntax: + * + * namespace code arg + * + * Here "arg" can be a list. "namespace code arg" produces a result + * equivalent to that produced by the command + * + * list namespace inscope [namespace current] $arg + * + * However, if "arg" is itself a scoped value starting with + * "namespace inscope", then the result is just "arg". + * + * Results: + * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. + * + * Side effects: + * If anything goes wrong, this procedure returns an error + * message as the result in the interpreter's result object. + * + *---------------------------------------------------------------------- + */ + +static int +NamespaceCodeCmd(dummy, interp, objc, objv) + ClientData dummy; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + Namespace *currNsPtr; + Tcl_Obj *listPtr, *objPtr; + register char *arg, *p; + int length; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "arg"); + return TCL_ERROR; + } + + /* + * If "arg" is already a scoped value, then return it directly. + */ + + arg = Tcl_GetStringFromObj(objv[2], &length); + if ((*arg == 'n') && (length > 17) + && (strncmp(arg, "namespace", 9) == 0)) { + for (p = (arg + 9); (*p == ' '); p++) { + /* empty body: skip over spaces */ + } + if ((*p == 'i') && ((p + 7) <= (arg + length)) + && (strncmp(p, "inscope", 7) == 0)) { + Tcl_SetObjResult(interp, objv[2]); + return TCL_OK; + } + } + + /* + * Otherwise, construct a scoped command by building a list with + * "namespace inscope", the full name of the current namespace, and + * the argument "arg". By constructing a list, we ensure that scoped + * commands are interpreted properly when they are executed later, + * by the "namespace inscope" command. + */ + + listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + Tcl_ListObjAppendElement(interp, listPtr, + Tcl_NewStringObj("namespace", -1)); + Tcl_ListObjAppendElement(interp, listPtr, + Tcl_NewStringObj("inscope", -1)); + + currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp); + if (currNsPtr == (Namespace *) Tcl_GetGlobalNamespace(interp)) { + objPtr = Tcl_NewStringObj("::", -1); + } else { + objPtr = Tcl_NewStringObj(currNsPtr->fullName, -1); + } + Tcl_ListObjAppendElement(interp, listPtr, objPtr); + + Tcl_ListObjAppendElement(interp, listPtr, objv[2]); + + Tcl_SetObjResult(interp, listPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NamespaceCurrentCmd -- + * + * Invoked to implement the "namespace current" command which returns + * the fully-qualified name of the current namespace. Handles the + * following syntax: + * + * namespace current + * + * Results: + * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. + * + * Side effects: + * Returns a result in the interpreter's result object. If anything + * goes wrong, the result is an error message. + * + *---------------------------------------------------------------------- + */ + +static int +NamespaceCurrentCmd(dummy, interp, objc, objv) + ClientData dummy; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + register Namespace *currNsPtr; + + if (objc != 2) { + Tcl_WrongNumArgs(interp, 2, objv, NULL); + return TCL_ERROR; + } + + /* + * The "real" name of the global namespace ("::") is the null string, + * but we return "::" for it as a convenience to programmers. Note that + * "" and "::" are treated as synonyms by the namespace code so that it + * is still easy to do things like: + * + * namespace [namespace current]::bar { ... } + */ + + currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp); + if (currNsPtr == (Namespace *) Tcl_GetGlobalNamespace(interp)) { + Tcl_AppendToObj(Tcl_GetObjResult(interp), "::", -1); + } else { + Tcl_AppendToObj(Tcl_GetObjResult(interp), currNsPtr->fullName, -1); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NamespaceDeleteCmd -- + * + * Invoked to implement the "namespace delete" command to delete + * namespace(s). Handles the following syntax: + * + * namespace delete ?name name...? + * + * Each name identifies a namespace. It may include a sequence of + * namespace qualifiers separated by "::"s. If a namespace is found, it + * is deleted: all variables and procedures contained in that namespace + * are deleted. If that namespace is being used on the call stack, it + * is kept alive (but logically deleted) until it is removed from the + * call stack: that is, it can no longer be referenced by name but any + * currently executing procedure that refers to it is allowed to do so + * until the procedure returns. If the namespace can't be found, this + * procedure returns an error. If no namespaces are specified, this + * command does nothing. + * + * Results: + * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. + * + * Side effects: + * Deletes the specified namespaces. If anything goes wrong, this + * procedure returns an error message in the interpreter's + * result object. + * + *---------------------------------------------------------------------- + */ + +static int +NamespaceDeleteCmd(dummy, interp, objc, objv) + ClientData dummy; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + Tcl_Namespace *namespacePtr; + char *name; + register int i; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 2, objv, "?name name...?"); + return TCL_ERROR; + } + + /* + * Destroying one namespace may cause another to be destroyed. Break + * this into two passes: first check to make sure that all namespaces on + * the command line are valid, and report any errors. + */ + + for (i = 2; i < objc; i++) { + name = Tcl_GetStringFromObj(objv[i], (int *) NULL); + namespacePtr = Tcl_FindNamespace(interp, name, + (Tcl_Namespace *) NULL, /*flags*/ 0); + if (namespacePtr == NULL) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "unknown namespace \"", + Tcl_GetStringFromObj(objv[i], (int *) NULL), + "\" in namespace delete command", (char *) NULL); + return TCL_ERROR; + } + } + + /* + * Okay, now delete each namespace. + */ + + for (i = 2; i < objc; i++) { + name = Tcl_GetStringFromObj(objv[i], (int *) NULL); + namespacePtr = Tcl_FindNamespace(interp, name, + (Tcl_Namespace *) NULL, TCL_LEAVE_ERR_MSG); + if (namespacePtr == NULL) { + return TCL_ERROR; + } + Tcl_DeleteNamespace(namespacePtr); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NamespaceEvalCmd -- + * + * Invoked to implement the "namespace eval" command. Executes + * commands in a namespace. If the namespace does not already exist, + * it is created. Handles the following syntax: + * + * namespace eval name arg ?arg...? + * + * If more than one arg argument is specified, the command that is + * executed is the result of concatenating the arguments together with + * a space between each argument. + * + * Results: + * Returns TCL_OK if the namespace is found and the commands are + * executed successfully. Returns TCL_ERROR if anything goes wrong. + * + * Side effects: + * Returns the result of the command in the interpreter's result + * object. If anything goes wrong, this procedure returns an error + * message as the result. + * + *---------------------------------------------------------------------- + */ + +static int +NamespaceEvalCmd(dummy, interp, objc, objv) + ClientData dummy; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + Tcl_Namespace *namespacePtr; + Tcl_CallFrame frame; + Tcl_Obj *objPtr; + char *name; + int length, result; + + if (objc < 4) { + Tcl_WrongNumArgs(interp, 2, objv, "name arg ?arg...?"); + return TCL_ERROR; + } + + /* + * Try to resolve the namespace reference, caching the result in the + * namespace object along the way. + */ + + result = GetNamespaceFromObj(interp, objv[2], &namespacePtr); + if (result != TCL_OK) { + return result; + } + + /* + * If the namespace wasn't found, try to create it. + */ + + if (namespacePtr == NULL) { + name = Tcl_GetStringFromObj(objv[2], &length); + namespacePtr = Tcl_CreateNamespace(interp, name, (ClientData) NULL, + (Tcl_NamespaceDeleteProc *) NULL); + if (namespacePtr == NULL) { + return TCL_ERROR; + } + } + + /* + * Make the specified namespace the current namespace and evaluate + * the command(s). + */ + + result = Tcl_PushCallFrame(interp, &frame, namespacePtr, + /*isProcCallFrame*/ 0); + if (result != TCL_OK) { + return TCL_ERROR; + } + + if (objc == 4) { + result = Tcl_EvalObj(interp, objv[3]); + } else { + objPtr = Tcl_ConcatObj(objc-3, objv+3); + result = Tcl_EvalObj(interp, objPtr); + Tcl_DecrRefCount(objPtr); /* we're done with the object */ + } + if (result == TCL_ERROR) { + char msg[256]; + + sprintf(msg, "\n (in namespace eval \"%.200s\" script line %d)", + namespacePtr->fullName, interp->errorLine); + Tcl_AddObjErrorInfo(interp, msg, -1); + } + + /* + * Restore the previous "current" namespace. + */ + + Tcl_PopCallFrame(interp); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * NamespaceExportCmd -- + * + * Invoked to implement the "namespace export" command that specifies + * which commands are exported from a namespace. The exported commands + * are those that can be imported into another namespace using + * "namespace import". Both commands defined in a namespace and + * commands the namespace has imported can be exported by a + * namespace. This command has the following syntax: + * + * namespace export ?-clear? ?pattern pattern...? + * + * Each pattern may contain "string match"-style pattern matching + * special characters, but the pattern may not include any namespace + * qualifiers: that is, the pattern must specify commands in the + * current (exporting) namespace. The specified patterns are appended + * onto the namespace's list of export patterns. + * + * To reset the namespace's export pattern list, specify the "-clear" + * flag. + * + * If there are no export patterns and the "-clear" flag isn't given, + * this command returns the namespace's current export list. + * + * Results: + * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. + * + * Side effects: + * Returns a result in the interpreter's result object. If anything + * goes wrong, the result is an error message. + * + *---------------------------------------------------------------------- + */ + +static int +NamespaceExportCmd(dummy, interp, objc, objv) + ClientData dummy; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + Namespace *currNsPtr = (Namespace*) Tcl_GetCurrentNamespace(interp); + char *pattern, *string; + int resetListFirst = 0; + int firstArg, patternCt, i, result; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 2, objv, + "?-clear? ?pattern pattern...?"); + return TCL_ERROR; + } + + /* + * Process the optional "-clear" argument. + */ + + firstArg = 2; + if (firstArg < objc) { + string = Tcl_GetStringFromObj(objv[firstArg], (int *) NULL); + if (strcmp(string, "-clear") == 0) { + resetListFirst = 1; + firstArg++; + } + } + + /* + * If no pattern arguments are given, and "-clear" isn't specified, + * return the namespace's current export pattern list. + */ + + patternCt = (objc - firstArg); + if (patternCt == 0) { + if (firstArg > 2) { + return TCL_OK; + } else { /* create list with export patterns */ + Tcl_Obj *listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + result = Tcl_AppendExportList(interp, + (Tcl_Namespace *) currNsPtr, listPtr); + if (result != TCL_OK) { + return result; + } + Tcl_SetObjResult(interp, listPtr); + return TCL_OK; + } + } + + /* + * Add each pattern to the namespace's export pattern list. + */ + + for (i = firstArg; i < objc; i++) { + pattern = Tcl_GetStringFromObj(objv[i], (int *) NULL); + result = Tcl_Export(interp, (Tcl_Namespace *) currNsPtr, pattern, + ((i == firstArg)? resetListFirst : 0)); + if (result != TCL_OK) { + return result; + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NamespaceForgetCmd -- + * + * Invoked to implement the "namespace forget" command to remove + * imported commands from a namespace. Handles the following syntax: + * + * namespace forget ?pattern pattern...? + * + * Each pattern is a name like "foo::*" or "a::b::x*". That is, the + * pattern may include the special pattern matching characters + * recognized by the "string match" command, but only in the command + * name at the end of the qualified name; the special pattern + * characters may not appear in a namespace name. All of the commands + * that match that pattern are checked to see if they have an imported + * command in the current namespace that refers to the matched + * command. If there is an alias, it is removed. + * + * Results: + * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. + * + * Side effects: + * Imported commands are removed from the current namespace. If + * anything goes wrong, this procedure returns an error message in the + * interpreter's result object. + * + *---------------------------------------------------------------------- + */ + +static int +NamespaceForgetCmd(dummy, interp, objc, objv) + ClientData dummy; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + char *pattern; + register int i, result; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 2, objv, "?pattern pattern...?"); + return TCL_ERROR; + } + + for (i = 2; i < objc; i++) { + pattern = Tcl_GetStringFromObj(objv[i], (int *) NULL); + result = Tcl_ForgetImport(interp, (Tcl_Namespace *) NULL, pattern); + if (result != TCL_OK) { + return result; + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NamespaceImportCmd -- + * + * Invoked to implement the "namespace import" command that imports + * commands into a namespace. Handles the following syntax: + * + * namespace import ?-force? ?pattern pattern...? + * + * Each pattern is a namespace-qualified name like "foo::*", + * "a::b::x*", or "bar::p". That is, the pattern may include the + * special pattern matching characters recognized by the "string match" + * command, but only in the command name at the end of the qualified + * name; the special pattern characters may not appear in a namespace + * name. All of the commands that match the pattern and which are + * exported from their namespace are made accessible from the current + * namespace context. This is done by creating a new "imported command" + * in the current namespace that points to the real command in its + * original namespace; when the imported command is called, it invokes + * the real command. + * + * If an imported command conflicts with an existing command, it is + * treated as an error. But if the "-force" option is included, then + * existing commands are overwritten by the imported commands. + * + * Results: + * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. + * + * Side effects: + * Adds imported commands to the current namespace. If anything goes + * wrong, this procedure returns an error message in the interpreter's + * result object. + * + *---------------------------------------------------------------------- + */ + +static int +NamespaceImportCmd(dummy, interp, objc, objv) + ClientData dummy; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + int allowOverwrite = 0; + char *string, *pattern; + register int i, result; + int firstArg; + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 2, objv, + "?-force? ?pattern pattern...?"); + return TCL_ERROR; + } + + /* + * Skip over the optional "-force" as the first argument. + */ + + firstArg = 2; + if (firstArg < objc) { + string = Tcl_GetStringFromObj(objv[firstArg], (int *) NULL); + if ((*string == '-') && (strcmp(string, "-force") == 0)) { + allowOverwrite = 1; + firstArg++; + } + } + + /* + * Handle the imports for each of the patterns. + */ + + for (i = firstArg; i < objc; i++) { + pattern = Tcl_GetStringFromObj(objv[i], (int *) NULL); + result = Tcl_Import(interp, (Tcl_Namespace *) NULL, pattern, + allowOverwrite); + if (result != TCL_OK) { + return result; + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NamespaceInscopeCmd -- + * + * Invoked to implement the "namespace inscope" command that executes a + * script in the context of a particular namespace. This command is not + * expected to be used directly by programmers; calls to it are + * generated implicitly when programs use "namespace code" commands + * to register callback scripts. Handles the following syntax: + * + * namespace inscope name arg ?arg...? + * + * The "namespace inscope" command is much like the "namespace eval" + * command except that it has lappend semantics and the namespace must + * already exist. It treats the first argument as a list, and appends + * any arguments after the first onto the end as proper list elements. + * For example, + * + * namespace inscope ::foo a b c d + * + * is equivalent to + * + * namespace eval ::foo [concat a [list b c d]] + * + * This lappend semantics is important because many callback scripts + * are actually prefixes. + * + * Results: + * Returns TCL_OK to indicate success, or TCL_ERROR to indicate + * failure. + * + * Side effects: + * Returns a result in the Tcl interpreter's result object. + * + *---------------------------------------------------------------------- + */ + +static int +NamespaceInscopeCmd(dummy, interp, objc, objv) + ClientData dummy; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + Tcl_Namespace *namespacePtr; + Tcl_CallFrame frame; + int i, result; + + if (objc < 4) { + Tcl_WrongNumArgs(interp, 2, objv, "name arg ?arg...?"); + return TCL_ERROR; + } + + /* + * Resolve the namespace reference. + */ + + result = GetNamespaceFromObj(interp, objv[2], &namespacePtr); + if (result != TCL_OK) { + return result; + } + if (namespacePtr == NULL) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "unknown namespace \"", + Tcl_GetStringFromObj(objv[2], (int *) NULL), + "\" in inscope namespace command", (char *) NULL); + return TCL_ERROR; + } + + /* + * Make the specified namespace the current namespace. + */ + + result = Tcl_PushCallFrame(interp, &frame, namespacePtr, + /*isProcCallFrame*/ 0); + if (result != TCL_OK) { + return result; + } + + /* + * Execute the command. If there is just one argument, just treat it as + * a script and evaluate it. Otherwise, create a list from the arguments + * after the first one, then concatenate the first argument and the list + * of extra arguments to form the command to evaluate. + */ + + if (objc == 4) { + result = Tcl_EvalObj(interp, objv[3]); + } else { + Tcl_Obj *concatObjv[2]; + register Tcl_Obj *listPtr, *cmdObjPtr; + + listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + for (i = 4; i < objc; i++) { + result = Tcl_ListObjAppendElement(interp, listPtr, objv[i]); + if (result != TCL_OK) { + Tcl_DecrRefCount(listPtr); /* free unneeded obj */ + return result; + } + } + + concatObjv[0] = objv[3]; + concatObjv[1] = listPtr; + cmdObjPtr = Tcl_ConcatObj(2, concatObjv); + result = Tcl_EvalObj(interp, cmdObjPtr); + + Tcl_DecrRefCount(cmdObjPtr); /* we're done with the cmd object */ + Tcl_DecrRefCount(listPtr); /* we're done with the list object */ + } + if (result == TCL_ERROR) { + char msg[256]; + + sprintf(msg, + "\n (in namespace inscope \"%.200s\" script line %d)", + namespacePtr->fullName, interp->errorLine); + Tcl_AddObjErrorInfo(interp, msg, -1); + } + + /* + * Restore the previous "current" namespace. + */ + + Tcl_PopCallFrame(interp); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * NamespaceOriginCmd -- + * + * Invoked to implement the "namespace origin" command to return the + * fully-qualified name of the "real" command to which the specified + * "imported command" refers. Handles the following syntax: + * + * namespace origin name + * + * Results: + * An imported command is created in an namespace when that namespace + * imports a command from another namespace. If a command is imported + * into a sequence of namespaces a, b,...,n where each successive + * namespace just imports the command from the previous namespace, this + * command returns the fully-qualified name of the original command in + * the first namespace, a. If "name" does not refer to an alias, its + * fully-qualified name is returned. The returned name is stored in the + * interpreter's result object. This procedure returns TCL_OK if + * successful, and TCL_ERROR if anything goes wrong. + * + * Side effects: + * If anything goes wrong, this procedure returns an error message in + * the interpreter's result object. + * + *---------------------------------------------------------------------- + */ + +static int +NamespaceOriginCmd(dummy, interp, objc, objv) + ClientData dummy; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + Tcl_Command command, origCommand; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "name"); + return TCL_ERROR; + } + + command = Tcl_GetCommandFromObj(interp, objv[2]); + if (command == (Tcl_Command) NULL) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "invalid command name \"", + Tcl_GetStringFromObj(objv[2], (int *) NULL), + "\"", (char *) NULL); + return TCL_ERROR; + } + origCommand = TclGetOriginalCommand(command); + if (origCommand == (Tcl_Command) NULL) { + /* + * The specified command isn't an imported command. Return the + * command's name qualified by the full name of the namespace it + * was defined in. + */ + + Tcl_GetCommandFullName(interp, command, Tcl_GetObjResult(interp)); + } else { + Tcl_GetCommandFullName(interp, origCommand, Tcl_GetObjResult(interp)); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NamespaceParentCmd -- + * + * Invoked to implement the "namespace parent" command that returns the + * fully-qualified name of the parent namespace for a specified + * namespace. Handles the following syntax: + * + * namespace parent ?name? + * + * Results: + * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. + * + * Side effects: + * Returns a result in the interpreter's result object. If anything + * goes wrong, the result is an error message. + * + *---------------------------------------------------------------------- + */ + +static int +NamespaceParentCmd(dummy, interp, objc, objv) + ClientData dummy; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + Tcl_Namespace *nsPtr; + int result; + + if (objc == 2) { + nsPtr = Tcl_GetCurrentNamespace(interp); + } else if (objc == 3) { + result = GetNamespaceFromObj(interp, objv[2], &nsPtr); + if (result != TCL_OK) { + return result; + } + if (nsPtr == NULL) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "unknown namespace \"", + Tcl_GetStringFromObj(objv[2], (int *) NULL), + "\" in namespace parent command", (char *) NULL); + return TCL_ERROR; + } + } else { + Tcl_WrongNumArgs(interp, 2, objv, "?name?"); + return TCL_ERROR; + } + + /* + * Report the parent of the specified namespace. + */ + + if (nsPtr->parentPtr != NULL) { + Tcl_SetStringObj(Tcl_GetObjResult(interp), + nsPtr->parentPtr->fullName, -1); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NamespaceQualifiersCmd -- + * + * Invoked to implement the "namespace qualifiers" command that returns + * any leading namespace qualifiers in a string. These qualifiers are + * namespace names separated by "::"s. For example, for "::foo::p" this + * command returns "::foo", and for "::" it returns "". This command + * is the complement of the "namespace tail" command. Note that this + * command does not check whether the "namespace" names are, in fact, + * the names of currently defined namespaces. Handles the following + * syntax: + * + * namespace qualifiers string + * + * Results: + * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. + * + * Side effects: + * Returns a result in the interpreter's result object. If anything + * goes wrong, the result is an error message. + * + *---------------------------------------------------------------------- + */ + +static int +NamespaceQualifiersCmd(dummy, interp, objc, objv) + ClientData dummy; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + register char *name, *p; + int length; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "string"); + return TCL_ERROR; + } + + /* + * Find the end of the string, then work backward and find + * the start of the last "::" qualifier. + */ + + name = Tcl_GetStringFromObj(objv[2], (int *) NULL); + for (p = name; *p != '\0'; p++) { + /* empty body */ + } + while (--p >= name) { + if ((*p == ':') && (p > name) && (*(p-1) == ':')) { + p -= 2; /* back up over the :: */ + while ((p >= name) && (*p == ':')) { + p--; /* back up over the preceeding : */ + } + break; + } + } + + if (p >= name) { + length = p-name+1; + Tcl_AppendToObj(Tcl_GetObjResult(interp), name, length); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NamespaceTailCmd -- + * + * Invoked to implement the "namespace tail" command that returns the + * trailing name at the end of a string with "::" namespace + * qualifiers. These qualifiers are namespace names separated by + * "::"s. For example, for "::foo::p" this command returns "p", and for + * "::" it returns "". This command is the complement of the "namespace + * qualifiers" command. Note that this command does not check whether + * the "namespace" names are, in fact, the names of currently defined + * namespaces. Handles the following syntax: + * + * namespace tail string + * + * Results: + * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. + * + * Side effects: + * Returns a result in the interpreter's result object. If anything + * goes wrong, the result is an error message. + * + *---------------------------------------------------------------------- + */ + +static int +NamespaceTailCmd(dummy, interp, objc, objv) + ClientData dummy; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + register char *name, *p; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "string"); + return TCL_ERROR; + } + + /* + * Find the end of the string, then work backward and find the + * last "::" qualifier. + */ + + name = Tcl_GetStringFromObj(objv[2], (int *) NULL); + for (p = name; *p != '\0'; p++) { + /* empty body */ + } + while (--p > name) { + if ((*p == ':') && (*(p-1) == ':')) { + p++; /* just after the last "::" */ + break; + } + } + + if (p >= name) { + Tcl_AppendToObj(Tcl_GetObjResult(interp), p, -1); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NamespaceWhichCmd -- + * + * Invoked to implement the "namespace which" command that returns the + * fully-qualified name of a command or variable. If the specified + * command or variable does not exist, it returns "". Handles the + * following syntax: + * + * namespace which ?-command? ?-variable? name + * + * Results: + * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. + * + * Side effects: + * Returns a result in the interpreter's result object. If anything + * goes wrong, the result is an error message. + * + *---------------------------------------------------------------------- + */ + +static int +NamespaceWhichCmd(dummy, interp, objc, objv) + ClientData dummy; /* Not used. */ + Tcl_Interp *interp; /* Current interpreter. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST objv[]; /* Argument objects. */ +{ + register char *arg; + Tcl_Command cmd; + Tcl_Var variable; + int argIndex, lookup; + + if (objc < 3) { + badArgs: + Tcl_WrongNumArgs(interp, 2, objv, + "?-command? ?-variable? name"); + return TCL_ERROR; + } + + /* + * Look for a flag controlling the lookup. + */ + + argIndex = 2; + lookup = 0; /* assume command lookup by default */ + arg = Tcl_GetStringFromObj(objv[2], (int *) NULL); + if (*arg == '-') { + if (strncmp(arg, "-command", 8) == 0) { + lookup = 0; + } else if (strncmp(arg, "-variable", 9) == 0) { + lookup = 1; + } else { + goto badArgs; + } + argIndex = 3; + } + if (objc != (argIndex + 1)) { + goto badArgs; + } + + switch (lookup) { + case 0: /* -command */ + cmd = Tcl_GetCommandFromObj(interp, objv[argIndex]); + if (cmd == (Tcl_Command) NULL) { + return TCL_OK; /* cmd not found, just return (no error) */ + } + Tcl_GetCommandFullName(interp, cmd, Tcl_GetObjResult(interp)); + break; + + case 1: /* -variable */ + arg = Tcl_GetStringFromObj(objv[argIndex], (int *) NULL); + variable = Tcl_FindNamespaceVar(interp, arg, (Tcl_Namespace *) NULL, + /*flags*/ 0); + if (variable != (Tcl_Var) NULL) { + Tcl_GetVariableFullName(interp, variable, Tcl_GetObjResult(interp)); + } + break; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * FreeNsNameInternalRep -- + * + * Frees the resources associated with a nsName object's internal + * representation. + * + * Results: + * None. + * + * Side effects: + * Decrements the ref count of any Namespace structure pointed + * to by the nsName's internal representation. If there are no more + * references to the namespace, it's structure will be freed. + * + *---------------------------------------------------------------------- + */ + +static void +FreeNsNameInternalRep(objPtr) + register Tcl_Obj *objPtr; /* nsName object with internal + * representation to free */ +{ + register ResolvedNsName *resNamePtr = + (ResolvedNsName *) objPtr->internalRep.otherValuePtr; + Namespace *nsPtr; + + /* + * Decrement the reference count of the namespace. If there are no + * more references, free it up. + */ + + if (resNamePtr != NULL) { + resNamePtr->refCount--; + if (resNamePtr->refCount == 0) { + + /* + * Decrement the reference count for the cached namespace. If + * the namespace is dead, and there are no more references to + * it, free it. + */ + + nsPtr = resNamePtr->nsPtr; + nsPtr->refCount--; + if ((nsPtr->refCount == 0) && (nsPtr->flags & NS_DEAD)) { + NamespaceFree(nsPtr); + } + ckfree((char *) resNamePtr); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * DupNsNameInternalRep -- + * + * Initializes the internal representation of a nsName object to a copy + * of the internal representation of another nsName object. + * + * Results: + * None. + * + * Side effects: + * copyPtr's internal rep is set to refer to the same namespace + * referenced by srcPtr's internal rep. Increments the ref count of + * the ResolvedNsName structure used to hold the namespace reference. + * + *---------------------------------------------------------------------- + */ + +static void +DupNsNameInternalRep(srcPtr, copyPtr) + Tcl_Obj *srcPtr; /* Object with internal rep to copy. */ + register Tcl_Obj *copyPtr; /* Object with internal rep to set. */ +{ + register ResolvedNsName *resNamePtr = + (ResolvedNsName *) srcPtr->internalRep.otherValuePtr; + + copyPtr->internalRep.otherValuePtr = (VOID *) resNamePtr; + if (resNamePtr != NULL) { + resNamePtr->refCount++; + } + copyPtr->typePtr = &tclNsNameType; +} + +/* + *---------------------------------------------------------------------- + * + * SetNsNameFromAny -- + * + * Attempt to generate a nsName internal representation for a + * Tcl object. + * + * Results: + * Returns TCL_OK if the value could be converted to a proper + * namespace reference. Otherwise, it returns TCL_ERROR, along + * with an error message in the interpreter's result object. + * + * Side effects: + * If successful, the object is made a nsName object. Its internal rep + * is set to point to a ResolvedNsName, which contains a cached pointer + * to the Namespace. Reference counts are kept on both the + * ResolvedNsName and the Namespace, so we can keep track of their + * usage and free them when appropriate. + * + *---------------------------------------------------------------------- + */ + +static int +SetNsNameFromAny(interp, objPtr) + Tcl_Interp *interp; /* Points to the namespace in which to + * resolve name. Also used for error + * reporting if not NULL. */ + register Tcl_Obj *objPtr; /* The object to convert. */ +{ + register Tcl_ObjType *oldTypePtr = objPtr->typePtr; + char *name, *dummy; + Namespace *nsPtr, *dummy1Ptr, *dummy2Ptr; + register ResolvedNsName *resNamePtr; + int flags, result; + + /* + * Get the string representation. Make it up-to-date if necessary. + */ + + name = objPtr->bytes; + if (name == NULL) { + name = Tcl_GetStringFromObj(objPtr, (int *) NULL); + } + + /* + * Look for the namespace "name" in the current namespace. If there is + * an error parsing the (possibly qualified) name, return an error. + * If the namespace isn't found, we convert the object to an nsName + * object with a NULL ResolvedNsName* internal rep. + */ + + flags = ((interp != NULL)? TCL_LEAVE_ERR_MSG : 0) | FIND_ONLY_NS; + result = TclGetNamespaceForQualName(interp, name, (Namespace *) NULL, + flags, &nsPtr, &dummy1Ptr, &dummy2Ptr, &dummy); + if (result != TCL_OK) { + return result; + } + + /* + * If we found a namespace, then create a new ResolvedNsName structure + * that holds a reference to it. + */ + + if (nsPtr != NULL) { + Namespace *currNsPtr = + (Namespace *) Tcl_GetCurrentNamespace(interp); + + nsPtr->refCount++; + resNamePtr = (ResolvedNsName *) ckalloc(sizeof(ResolvedNsName)); + resNamePtr->nsPtr = nsPtr; + resNamePtr->nsId = nsPtr->nsId; + resNamePtr->refNsPtr = currNsPtr; + resNamePtr->refCount = 1; + } else { + resNamePtr = NULL; + } + + /* + * Free the old internalRep before setting the new one. + * We do this as late as possible to allow the conversion code + * (in particular, Tcl_GetStringFromObj) to use that old internalRep. + */ + + if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) { + oldTypePtr->freeIntRepProc(objPtr); + } + + objPtr->internalRep.otherValuePtr = (VOID *) resNamePtr; + objPtr->typePtr = &tclNsNameType; + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * UpdateStringOfNsName -- + * + * Updates the string representation for a nsName object. + * Note: This procedure does not free an existing old string rep + * so storage will be lost if this has not already been done. + * + * Results: + * None. + * + * Side effects: + * The object's string is set to a copy of the fully qualified + * namespace name. + * + *---------------------------------------------------------------------- + */ + +static void +UpdateStringOfNsName(objPtr) + register Tcl_Obj *objPtr; /* nsName object with string rep to update. */ +{ + ResolvedNsName *resNamePtr = + (ResolvedNsName *) objPtr->internalRep.otherValuePtr; + register Namespace *nsPtr; + char *name = ""; + int length; + + if ((resNamePtr != NULL) + && (resNamePtr->nsId == resNamePtr->nsPtr->nsId)) { + nsPtr = resNamePtr->nsPtr; + if (nsPtr->flags & NS_DEAD) { + nsPtr = NULL; + } + if (nsPtr != NULL) { + name = nsPtr->fullName; + } + } + + /* + * The following sets the string rep to an empty string on the heap + * if the internal rep is NULL. + */ + + length = strlen(name); + if (length == 0) { + objPtr->bytes = tclEmptyStringRep; + } else { + objPtr->bytes = (char *) ckalloc((unsigned) (length + 1)); + memcpy((VOID *) objPtr->bytes, (VOID *) name, (unsigned) length); + objPtr->bytes[length] = '\0'; + } + objPtr->length = length; +} |