summaryrefslogtreecommitdiffstats
path: root/generic/tclNamesp.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclNamesp.c')
-rw-r--r--generic/tclNamesp.c3765
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;
+}