diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2016-12-21 22:47:21 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2016-12-21 22:47:21 (GMT) |
commit | 5514e37335c012cc70f5b9aee3cedfe3d57f583f (patch) | |
tree | 4ba7d8aad13735e52f59bdce7ca5ba3151ebd7e3 /tcl8.6/generic/tclNamesp.c | |
parent | 768f87f613cc9789fcf8073018fa02178c8c91df (diff) | |
download | blt-5514e37335c012cc70f5b9aee3cedfe3d57f583f.zip blt-5514e37335c012cc70f5b9aee3cedfe3d57f583f.tar.gz blt-5514e37335c012cc70f5b9aee3cedfe3d57f583f.tar.bz2 |
undo subtree
Diffstat (limited to 'tcl8.6/generic/tclNamesp.c')
-rw-r--r-- | tcl8.6/generic/tclNamesp.c | 5101 |
1 files changed, 0 insertions, 5101 deletions
diff --git a/tcl8.6/generic/tclNamesp.c b/tcl8.6/generic/tclNamesp.c deleted file mode 100644 index 5930859..0000000 --- a/tcl8.6/generic/tclNamesp.c +++ /dev/null @@ -1,5101 +0,0 @@ -/* - * 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. - * Copyright (c) 1998-1999 by Scriptics Corporation. - * Copyright (c) 2002-2005 Donal K. Fellows. - * Copyright (c) 2006 Neil Madden. - * Contributions from Don Porter, NIST, 2007. (not subject to US copyright) - * - * 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. - */ - -#include "tclInt.h" -#include "tclCompile.h" /* for TclLogCommandInfo visibility */ - -/* - * Thread-local storage used to avoid having a global lock on data that is not - * limited to a single interpreter. - */ - -typedef struct ThreadSpecificData { - long numNsCreated; /* Count of the number of namespaces created - * within the thread. This value is used as a - * unique id for each namespace. Cannot be - * per-interp because the nsId is used to - * distinguish objects which can be passed - * around between interps in the same thread, - * but does not need to be global because - * object internal reps are always per-thread - * anyway. */ -} ThreadSpecificData; - -static Tcl_ThreadDataKey dataKey; - -/* - * 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 pointer to the Namespace that the - * name resolved to. */ - Namespace *refNsPtr; /* Points to the namespace context in which - * the name was resolved. NULL if the name is - * fully qualified and thus the resolution - * does not depend on the context. */ - 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 functions local to this file: - */ - -static void DeleteImportedCmd(ClientData clientData); -static int DoImport(Tcl_Interp *interp, - Namespace *nsPtr, Tcl_HashEntry *hPtr, - const char *cmdName, const char *pattern, - Namespace *importNsPtr, int allowOverwrite); -static void DupNsNameInternalRep(Tcl_Obj *objPtr,Tcl_Obj *copyPtr); -static char * ErrorCodeRead(ClientData clientData,Tcl_Interp *interp, - const char *name1, const char *name2, int flags); -static char * ErrorInfoRead(ClientData clientData,Tcl_Interp *interp, - const char *name1, const char *name2, int flags); -static char * EstablishErrorCodeTraces(ClientData clientData, - Tcl_Interp *interp, const char *name1, - const char *name2, int flags); -static char * EstablishErrorInfoTraces(ClientData clientData, - Tcl_Interp *interp, const char *name1, - const char *name2, int flags); -static void FreeNsNameInternalRep(Tcl_Obj *objPtr); -static int GetNamespaceFromObj(Tcl_Interp *interp, - Tcl_Obj *objPtr, Tcl_Namespace **nsPtrPtr); -static int InvokeImportedCmd(ClientData clientData, - Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]); -static int InvokeImportedNRCmd(ClientData clientData, - Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]); -static int NamespaceChildrenCmd(ClientData dummy, - Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]); -static int NamespaceCodeCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static int NamespaceCurrentCmd(ClientData dummy, - Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]); -static int NamespaceDeleteCmd(ClientData dummy,Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static int NamespaceEvalCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static int NRNamespaceEvalCmd(ClientData dummy, - Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]); -static int NamespaceExistsCmd(ClientData dummy,Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static int NamespaceExportCmd(ClientData dummy,Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static int NamespaceForgetCmd(ClientData dummy,Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static void NamespaceFree(Namespace *nsPtr); -static int NamespaceImportCmd(ClientData dummy,Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static int NamespaceInscopeCmd(ClientData dummy, - Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]); -static int NRNamespaceInscopeCmd(ClientData dummy, - Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]); -static int NamespaceOriginCmd(ClientData dummy,Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static int NamespaceParentCmd(ClientData dummy,Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static int NamespacePathCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static int NamespaceQualifiersCmd(ClientData dummy, - Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]); -static int NamespaceTailCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static int NamespaceUpvarCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static int NamespaceUnknownCmd(ClientData dummy, - Tcl_Interp *interp,int objc,Tcl_Obj *const objv[]); -static int NamespaceWhichCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *const objv[]); -static int SetNsNameFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); -static void UnlinkNsPath(Namespace *nsPtr); - -static Tcl_NRPostProc NsEval_Callback; - -/* - * 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. - */ - -static const Tcl_ObjType nsNameType = { - "nsName", /* the type's name */ - FreeNsNameInternalRep, /* freeIntRepProc */ - DupNsNameInternalRep, /* dupIntRepProc */ - NULL, /* updateStringProc */ - SetNsNameFromAny /* setFromAnyProc */ -}; - -/* - * Array of values describing how to implement each standard subcommand of the - * "namespace" command. - */ - -static const EnsembleImplMap defaultNamespaceMap[] = { - {"children", NamespaceChildrenCmd, TclCompileBasic0To2ArgCmd, NULL, NULL, 0}, - {"code", NamespaceCodeCmd, TclCompileNamespaceCodeCmd, NULL, NULL, 0}, - {"current", NamespaceCurrentCmd, TclCompileNamespaceCurrentCmd, NULL, NULL, 0}, - {"delete", NamespaceDeleteCmd, TclCompileBasicMin0ArgCmd, NULL, NULL, 0}, - {"ensemble", TclNamespaceEnsembleCmd, NULL, NULL, NULL, 0}, - {"eval", NamespaceEvalCmd, NULL, NRNamespaceEvalCmd, NULL, 0}, - {"exists", NamespaceExistsCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0}, - {"export", NamespaceExportCmd, TclCompileBasicMin0ArgCmd, NULL, NULL, 0}, - {"forget", NamespaceForgetCmd, TclCompileBasicMin0ArgCmd, NULL, NULL, 0}, - {"import", NamespaceImportCmd, TclCompileBasicMin0ArgCmd, NULL, NULL, 0}, - {"inscope", NamespaceInscopeCmd, NULL, NRNamespaceInscopeCmd, NULL, 0}, - {"origin", NamespaceOriginCmd, TclCompileNamespaceOriginCmd, NULL, NULL, 0}, - {"parent", NamespaceParentCmd, TclCompileBasic0Or1ArgCmd, NULL, NULL, 0}, - {"path", NamespacePathCmd, TclCompileBasic0Or1ArgCmd, NULL, NULL, 0}, - {"qualifiers", NamespaceQualifiersCmd, TclCompileNamespaceQualifiersCmd, NULL, NULL, 0}, - {"tail", NamespaceTailCmd, TclCompileNamespaceTailCmd, NULL, NULL, 0}, - {"unknown", NamespaceUnknownCmd, TclCompileBasic0Or1ArgCmd, NULL, NULL, 0}, - {"upvar", NamespaceUpvarCmd, TclCompileNamespaceUpvarCmd, NULL, NULL, 0}, - {"which", NamespaceWhichCmd, TclCompileNamespaceWhichCmd, NULL, NULL, 0}, - {NULL, NULL, NULL, NULL, NULL, 0} -}; - -/* - *---------------------------------------------------------------------- - * - * TclInitNamespaceSubsystem -- - * - * This function is called to initialize all the structures that are used - * by namespaces on a per-process basis. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -void -TclInitNamespaceSubsystem(void) -{ - /* - * Does nothing for now. - */ -} - -/* - *---------------------------------------------------------------------- - * - * 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( - register Tcl_Interp *interp)/* Interpreter whose current namespace is - * being queried. */ -{ - return TclGetCurrentNamespace(interp); -} - -/* - *---------------------------------------------------------------------- - * - * 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( - register Tcl_Interp *interp)/* Interpreter whose global namespace should - * be returned. */ -{ - return TclGetGlobalNamespace(interp); -} - -/* - *---------------------------------------------------------------------- - * - * 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( - 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 has 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 function. 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 *) TclGetCurrentNamespace(interp); - } else { - nsPtr = (Namespace *) namespacePtr; - - /* - * TODO: Examine whether it would be better to guard based on NS_DYING - * or NS_KILLED. It appears that these are not tested because they can - * be set in a global interp that has been [namespace delete]d, but - * which never really completely goes away because of lingering global - * things like ::errorInfo and [::unknown] and hidden commands. - * Review of those designs might permit stricter checking here. - */ - - if (nsPtr->flags & NS_DEAD) { - Tcl_Panic("Trying to push call frame for dead namespace"); - /*NOTREACHED*/ - } - } - - 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 = 0; - } - framePtr->procPtr = NULL; /* no called procedure */ - framePtr->varTablePtr = NULL; /* and no local variables */ - framePtr->numCompiledLocals = 0; - framePtr->compiledLocals = NULL; - framePtr->clientData = NULL; - framePtr->localCachePtr = NULL; - framePtr->tailcallPtr = 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( - Tcl_Interp *interp) /* Interpreter with call frame to pop. */ -{ - register Interp *iPtr = (Interp *) interp; - register CallFrame *framePtr = iPtr->framePtr; - 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. - */ - - if (framePtr->callerPtr) { - iPtr->framePtr = framePtr->callerPtr; - iPtr->varFramePtr = framePtr->callerVarPtr; - } else { - /* Tcl_PopCallFrame: trying to pop rootCallFrame! */ - } - - if (framePtr->varTablePtr != NULL) { - TclDeleteVars(iPtr, framePtr->varTablePtr); - ckfree(framePtr->varTablePtr); - framePtr->varTablePtr = NULL; - } - if (framePtr->numCompiledLocals > 0) { - TclDeleteCompiledLocalVars(iPtr, framePtr); - if (--framePtr->localCachePtr->refCount == 0) { - TclFreeLocalCache(interp, framePtr->localCachePtr); - } - framePtr->localCachePtr = NULL; - } - - /* - * 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 - (nsPtr == iPtr->globalNsPtr) == 0)) { - Tcl_DeleteNamespace((Tcl_Namespace *) nsPtr); - } - framePtr->nsPtr = NULL; - - if (framePtr->tailcallPtr) { - TclSetTailcall(interp, framePtr->tailcallPtr); - } -} - -/* - *---------------------------------------------------------------------- - * - * TclPushStackFrame -- - * - * Allocates a new call frame in the interpreter's execution stack, then - * pushes it 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 -TclPushStackFrame( - Tcl_Interp *interp, /* Interpreter in which the new call frame is - * to be pushed. */ - Tcl_CallFrame **framePtrPtr,/* Place to store a pointer to the stack - * allocated call frame. */ - 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. */ -{ - *framePtrPtr = TclStackAlloc(interp, sizeof(CallFrame)); - return Tcl_PushCallFrame(interp, *framePtrPtr, namespacePtr, - isProcCallFrame); -} - -void -TclPopStackFrame( - Tcl_Interp *interp) /* Interpreter with call frame to pop. */ -{ - CallFrame *freePtr = ((Interp *) interp)->framePtr; - - Tcl_PopCallFrame(interp); - TclStackFree(interp, freePtr); -} - -/* - *---------------------------------------------------------------------- - * - * EstablishErrorCodeTraces -- - * - * Creates traces on the ::errorCode variable to keep its value - * consistent with the expectations of legacy code. - * - * Results: - * None. - * - * Side effects: - * Read and unset traces are established on ::errorCode. - * - *---------------------------------------------------------------------- - */ - -static char * -EstablishErrorCodeTraces( - ClientData clientData, - Tcl_Interp *interp, - const char *name1, - const char *name2, - int flags) -{ - Tcl_TraceVar2(interp, "errorCode", NULL, TCL_GLOBAL_ONLY|TCL_TRACE_READS, - ErrorCodeRead, NULL); - Tcl_TraceVar2(interp, "errorCode", NULL, TCL_GLOBAL_ONLY|TCL_TRACE_UNSETS, - EstablishErrorCodeTraces, NULL); - return NULL; -} - -/* - *---------------------------------------------------------------------- - * - * ErrorCodeRead -- - * - * Called when the ::errorCode variable is read. Copies the current value - * of the interp's errorCode field into ::errorCode. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static char * -ErrorCodeRead( - ClientData clientData, - Tcl_Interp *interp, - const char *name1, - const char *name2, - int flags) -{ - Interp *iPtr = (Interp *) interp; - - if (Tcl_InterpDeleted(interp) || !(iPtr->flags & ERR_LEGACY_COPY)) { - return NULL; - } - if (iPtr->errorCode) { - Tcl_ObjSetVar2(interp, iPtr->ecVar, NULL, - iPtr->errorCode, TCL_GLOBAL_ONLY); - return NULL; - } - if (NULL == Tcl_ObjGetVar2(interp, iPtr->ecVar, NULL, TCL_GLOBAL_ONLY)) { - Tcl_ObjSetVar2(interp, iPtr->ecVar, NULL, - Tcl_NewObj(), TCL_GLOBAL_ONLY); - } - return NULL; -} - -/* - *---------------------------------------------------------------------- - * - * EstablishErrorInfoTraces -- - * - * Creates traces on the ::errorInfo variable to keep its value - * consistent with the expectations of legacy code. - * - * Results: - * None. - * - * Side effects: - * Read and unset traces are established on ::errorInfo. - * - *---------------------------------------------------------------------- - */ - -static char * -EstablishErrorInfoTraces( - ClientData clientData, - Tcl_Interp *interp, - const char *name1, - const char *name2, - int flags) -{ - Tcl_TraceVar2(interp, "errorInfo", NULL, TCL_GLOBAL_ONLY|TCL_TRACE_READS, - ErrorInfoRead, NULL); - Tcl_TraceVar2(interp, "errorInfo", NULL, TCL_GLOBAL_ONLY|TCL_TRACE_UNSETS, - EstablishErrorInfoTraces, NULL); - return NULL; -} - -/* - *---------------------------------------------------------------------- - * - * ErrorInfoRead -- - * - * Called when the ::errorInfo variable is read. Copies the current value - * of the interp's errorInfo field into ::errorInfo. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static char * -ErrorInfoRead( - ClientData clientData, - Tcl_Interp *interp, - const char *name1, - const char *name2, - int flags) -{ - Interp *iPtr = (Interp *) interp; - - if (Tcl_InterpDeleted(interp) || !(iPtr->flags & ERR_LEGACY_COPY)) { - return NULL; - } - if (iPtr->errorInfo) { - Tcl_ObjSetVar2(interp, iPtr->eiVar, NULL, - iPtr->errorInfo, TCL_GLOBAL_ONLY); - return NULL; - } - if (NULL == Tcl_ObjGetVar2(interp, iPtr->eiVar, NULL, TCL_GLOBAL_ONLY)) { - Tcl_ObjSetVar2(interp, iPtr->eiVar, NULL, - Tcl_NewObj(), TCL_GLOBAL_ONLY); - } - return 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( - Tcl_Interp *interp, /* Interpreter in which a new namespace is - * being created. Also used for error - * reporting. */ - const 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) - /* Function called to delete client data when - * the namespace is deleted. NULL if no - * function should be called. */ -{ - Interp *iPtr = (Interp *) interp; - register Namespace *nsPtr, *ancestorPtr; - Namespace *parentPtr, *dummy1Ptr, *dummy2Ptr; - Namespace *globalNsPtr = iPtr->globalNsPtr; - const char *simpleName; - Tcl_HashEntry *entryPtr; - Tcl_DString buffer1, buffer2; - Tcl_DString *namePtr, *buffPtr; - int newEntry, nameLen; - ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - const char *nameStr; - Tcl_DString tmpBuffer; - - Tcl_DStringInit(&tmpBuffer); - - /* - * 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 = ""; - goto doCreate; - } - - /* - * Ensure that there are no trailing colons as that causes chaos when a - * deleteProc is specified. [Bug d614d63989] - */ - - if (deleteProc != NULL) { - nameStr = name + strlen(name) - 2; - if (nameStr >= name && nameStr[1] == ':' && nameStr[0] == ':') { - Tcl_DStringAppend(&tmpBuffer, name, -1); - while ((nameLen = Tcl_DStringLength(&tmpBuffer)) > 0 - && Tcl_DStringValue(&tmpBuffer)[nameLen-1] == ':') { - Tcl_DStringSetLength(&tmpBuffer, nameLen-1); - } - name = Tcl_DStringValue(&tmpBuffer); - } - } - - /* - * If we've ended up with an empty string now, we're attempting to create - * the global namespace despite the global namespace existing. That's - * naughty! - */ - - if (*name == '\0') { - Tcl_SetObjResult(interp, Tcl_NewStringObj("can't create namespace" - " \"\": only global namespace can have empty name", -1)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "NAMESPACE", - "CREATEGLOBAL", NULL); - Tcl_DStringFree(&tmpBuffer); - return NULL; - } - - /* - * Find the parent for the new namespace. - */ - - TclGetNamespaceForQualName(interp, name, NULL, TCL_CREATE_NS_IF_UNKNOWN, - &parentPtr, &dummy1Ptr, &dummy2Ptr, &simpleName); - - /* - * 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') { - Tcl_DStringFree(&tmpBuffer); - 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 ( -#ifndef BREAK_NAMESPACE_COMPAT - Tcl_FindHashEntry(&parentPtr->childTable, simpleName) != NULL -#else - parentPtr->childTablePtr != NULL && - Tcl_FindHashEntry(parentPtr->childTablePtr, simpleName) != NULL -#endif - ) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "can't create namespace \"%s\": already exists", name)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "NAMESPACE", - "CREATEEXISTING", NULL); - Tcl_DStringFree(&tmpBuffer); - return NULL; - } - - /* - * Create the new namespace and root it in its parent. Increment the count - * of namespaces created. - */ - - doCreate: - nsPtr = ckalloc(sizeof(Namespace)); - nameLen = strlen(simpleName) + 1; - nsPtr->name = ckalloc(nameLen); - memcpy(nsPtr->name, simpleName, nameLen); - nsPtr->fullName = NULL; /* Set below. */ - nsPtr->clientData = clientData; - nsPtr->deleteProc = deleteProc; - nsPtr->parentPtr = parentPtr; -#ifndef BREAK_NAMESPACE_COMPAT - Tcl_InitHashTable(&nsPtr->childTable, TCL_STRING_KEYS); -#else - nsPtr->childTablePtr = NULL; -#endif - nsPtr->nsId = ++(tsdPtr->numNsCreated); - nsPtr->interp = interp; - nsPtr->flags = 0; - nsPtr->activationCount = 0; - nsPtr->refCount = 0; - Tcl_InitHashTable(&nsPtr->cmdTable, TCL_STRING_KEYS); - TclInitVarHashTable(&nsPtr->varTable, nsPtr); - nsPtr->exportArrayPtr = NULL; - nsPtr->numExportPatterns = 0; - nsPtr->maxExportPatterns = 0; - nsPtr->cmdRefEpoch = 0; - nsPtr->resolverEpoch = 0; - nsPtr->cmdResProc = NULL; - nsPtr->varResProc = NULL; - nsPtr->compiledVarResProc = NULL; - nsPtr->exportLookupEpoch = 0; - nsPtr->ensembles = NULL; - nsPtr->unknownHandlerPtr = NULL; - nsPtr->commandPathLength = 0; - nsPtr->commandPathArray = NULL; - nsPtr->commandPathSourceList = NULL; - nsPtr->earlyDeleteProc = NULL; - - if (parentPtr != NULL) { - entryPtr = Tcl_CreateHashEntry( - TclGetNamespaceChildTable((Tcl_Namespace *) parentPtr), - simpleName, &newEntry); - Tcl_SetHashValue(entryPtr, nsPtr); - } else { - /* - * In the global namespace create traces to maintain the ::errorInfo - * and ::errorCode variables. - */ - - iPtr->globalNsPtr = nsPtr; - EstablishErrorInfoTraces(NULL, interp, NULL, NULL, 0); - EstablishErrorCodeTraces(NULL, interp, NULL, NULL, 0); - } - - /* - * Build the fully qualified name for this namespace. - */ - - Tcl_DStringInit(&buffer1); - Tcl_DStringInit(&buffer2); - namePtr = &buffer1; - buffPtr = &buffer2; - for (ancestorPtr = nsPtr; ancestorPtr != NULL; - ancestorPtr = ancestorPtr->parentPtr) { - if (ancestorPtr != globalNsPtr) { - register Tcl_DString *tempPtr = namePtr; - - TclDStringAppendLiteral(buffPtr, "::"); - Tcl_DStringAppend(buffPtr, ancestorPtr->name, -1); - TclDStringAppendDString(buffPtr, namePtr); - - /* - * Clear the unwanted buffer or we end up appending to previous - * results, making the namespace fullNames of nested namespaces - * very wrong (and strange). - */ - - TclDStringClear(namePtr); - - /* - * Now swap the buffer pointers so that we build in the other - * buffer. This is faster than repeated copying back and forth - * between buffers. - */ - - namePtr = buffPtr; - buffPtr = tempPtr; - } - } - - name = Tcl_DStringValue(namePtr); - nameLen = Tcl_DStringLength(namePtr); - nsPtr->fullName = ckalloc(nameLen + 1); - memcpy(nsPtr->fullName, name, (unsigned) nameLen + 1); - - Tcl_DStringFree(&buffer1); - Tcl_DStringFree(&buffer2); - Tcl_DStringFree(&tmpBuffer); - - /* - * If compilation of commands originating from the parent NS is - * suppressed, suppress it for commands originating in this one too. - */ - - if (nsPtr->parentPtr != NULL && - nsPtr->parentPtr->flags & NS_SUPPRESS_COMPILATION) { - nsPtr->flags |= NS_SUPPRESS_COMPILATION; - } - - /* - * 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( - Tcl_Namespace *namespacePtr)/* Points to the namespace to delete. */ -{ - register Namespace *nsPtr = (Namespace *) namespacePtr; - Interp *iPtr = (Interp *) nsPtr->interp; - Namespace *globalNsPtr = (Namespace *) - TclGetGlobalNamespace((Tcl_Interp *) iPtr); - Tcl_HashEntry *entryPtr; - Tcl_HashSearch search; - Command *cmdPtr; - - /* - * Give anyone interested - notably TclOO - a chance to use this namespace - * normally despite the fact that the namespace is going to go. Allows the - * calling of destructors. Will only be called once (unless re-established - * by the called function). [Bug 2950259] - * - * Note that setting this field requires access to the internal definition - * of namespaces, so it should only be accessed by code that knows about - * being careful with reentrancy. - */ - - if (nsPtr->earlyDeleteProc != NULL) { - Tcl_NamespaceDeleteProc *earlyDeleteProc = nsPtr->earlyDeleteProc; - - nsPtr->earlyDeleteProc = NULL; - nsPtr->activationCount++; - earlyDeleteProc(nsPtr->clientData); - nsPtr->activationCount--; - } - - /* - * Delete all coroutine commands now: break the circular ref cycle between - * the namespace and the coroutine command [Bug 2724403]. This code is - * essentially duplicated in TclTeardownNamespace() for all other - * commands. Don't optimize to Tcl_NextHashEntry() because of traces. - * - * NOTE: we could avoid traversing the ns's command list by keeping a - * separate list of coros. - */ - - for (entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search); - entryPtr != NULL;) { - cmdPtr = Tcl_GetHashValue(entryPtr); - if (cmdPtr->nreProc == TclNRInterpCoroutine) { - Tcl_DeleteCommandFromToken((Tcl_Interp *) iPtr, - (Tcl_Command) cmdPtr); - entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search); - } else { - entryPtr = Tcl_NextHashEntry(&search); - } - } - - /* - * If the namespace has associated ensemble commands, delete them first. - * This leaves the actual contents of the namespace alone (unless they are - * linked ensemble commands, of course). Note that this code is actually - * reentrant so command delete traces won't purturb things badly. - */ - - while (nsPtr->ensembles != NULL) { - EnsembleConfig *ensemblePtr = (EnsembleConfig *) nsPtr->ensembles; - - /* - * Splice out and link to indicate that we've already been killed. - */ - - nsPtr->ensembles = (Tcl_Ensemble *) ensemblePtr->next; - ensemblePtr->next = ensemblePtr; - Tcl_DeleteCommandFromToken(nsPtr->interp, ensemblePtr->token); - } - - /* - * If the namespace has a registered unknown handler (TIP 181), then free - * it here. - */ - - if (nsPtr->unknownHandlerPtr != NULL) { - Tcl_DecrRefCount(nsPtr->unknownHandlerPtr); - nsPtr->unknownHandlerPtr = NULL; - } - - /* - * 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 - * function 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 - (nsPtr == globalNsPtr) > 0) { - nsPtr->flags |= NS_DYING; - if (nsPtr->parentPtr != NULL) { - entryPtr = Tcl_FindHashEntry( - TclGetNamespaceChildTable((Tcl_Namespace *) - nsPtr->parentPtr), nsPtr->name); - if (entryPtr != NULL) { - Tcl_DeleteHashEntry(entryPtr); - } - } - nsPtr->parentPtr = NULL; - } else if (!(nsPtr->flags & NS_KILLED)) { - /* - * 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. Set the NS_KILLED flag to avoid - * recursive calls here - if the namespace is really in the process of - * being deleted, ignore any second call. - */ - - nsPtr->flags |= (NS_DYING|NS_KILLED); - - 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. - */ - - TclDeleteNamespaceVars(nsPtr); - -#ifndef BREAK_NAMESPACE_COMPAT - Tcl_DeleteHashTable(&nsPtr->childTable); -#else - if (nsPtr->childTablePtr != NULL) { - Tcl_DeleteHashTable(nsPtr->childTablePtr); - ckfree(nsPtr->childTablePtr); - } -#endif - 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; - } - } else { - /* - * Restore the ::errorInfo and ::errorCode traces. - */ - - EstablishErrorInfoTraces(NULL, nsPtr->interp, NULL, NULL, 0); - EstablishErrorCodeTraces(NULL, nsPtr->interp, NULL, NULL, 0); - - /* - * We didn't really kill it, so remove the KILLED marks, so it can - * get killed later, avoiding mem leaks. - */ - - nsPtr->flags &= ~(NS_DYING|NS_KILLED); - } - } -} - -/* - *---------------------------------------------------------------------- - * - * 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. - * - * Results: - * None. - * - * Side effects: - * Removes this namespace from its parent's child namespace hashtable. - * Deletes all commands, variables and namespaces in this namespace. - * - *---------------------------------------------------------------------- - */ - -void -TclTeardownNamespace( - 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; - int i; - - /* - * Start by destroying the namespace's variable table, since variables - * might trigger traces. Variable table should be cleared but not freed! - * TclDeleteNamespaceVars frees it, so we reinitialize it afterwards. - */ - - TclDeleteNamespaceVars(nsPtr); - TclInitVarHashTable(&nsPtr->varTable, nsPtr); - - /* - * 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. Because of traces (and the desire to avoid the quadratic - * problems of just using Tcl_FirstHashEntry over and over, [Bug - * f97d4ee020]) we copy to a temporary array and then delete all those - * commands. - */ - - while (nsPtr->cmdTable.numEntries > 0) { - int length = nsPtr->cmdTable.numEntries; - Command **cmds = TclStackAlloc((Tcl_Interp *) iPtr, - sizeof(Command *) * length); - - i = 0; - for (entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search); - entryPtr != NULL; - entryPtr = Tcl_NextHashEntry(&search)) { - cmds[i] = Tcl_GetHashValue(entryPtr); - cmds[i]->refCount++; - i++; - } - for (i = 0 ; i < length ; i++) { - Tcl_DeleteCommandFromToken((Tcl_Interp *) iPtr, - (Tcl_Command) cmds[i]); - TclCleanupCommandMacro(cmds[i]); - } - TclStackFree((Tcl_Interp *) iPtr, cmds); - } - Tcl_DeleteHashTable(&nsPtr->cmdTable); - Tcl_InitHashTable(&nsPtr->cmdTable, TCL_STRING_KEYS); - - /* - * Remove the namespace from its parent's child hashtable. - */ - - if (nsPtr->parentPtr != NULL) { - entryPtr = Tcl_FindHashEntry( - TclGetNamespaceChildTable((Tcl_Namespace *) - nsPtr->parentPtr), nsPtr->name); - if (entryPtr != NULL) { - Tcl_DeleteHashEntry(entryPtr); - } - } - nsPtr->parentPtr = NULL; - - /* - * Delete the namespace path if one is installed. - */ - - if (nsPtr->commandPathLength != 0) { - UnlinkNsPath(nsPtr); - nsPtr->commandPathLength = 0; - } - if (nsPtr->commandPathSourceList != NULL) { - NamespacePathEntry *nsPathPtr = nsPtr->commandPathSourceList; - - do { - if (nsPathPtr->nsPtr != NULL && nsPathPtr->creatorNsPtr != NULL) { - nsPathPtr->creatorNsPtr->cmdRefEpoch++; - } - nsPathPtr->nsPtr = NULL; - nsPathPtr = nsPathPtr->nextPtr; - } while (nsPathPtr != NULL); - nsPtr->commandPathSourceList = 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. Because of traces (and the desire to avoid the - * quadratic problems of just using Tcl_FirstHashEntry over and over, [Bug - * f97d4ee020]) we copy to a temporary array and then delete all those - * namespaces. - * - * Important: leave the hash table itself still live. - */ - -#ifndef BREAK_NAMESPACE_COMPAT - while (nsPtr->childTable.numEntries > 0) { - int length = nsPtr->childTable.numEntries; - Namespace **children = TclStackAlloc((Tcl_Interp *) iPtr, - sizeof(Namespace *) * length); - - i = 0; - for (entryPtr = Tcl_FirstHashEntry(&nsPtr->childTable, &search); - entryPtr != NULL; - entryPtr = Tcl_NextHashEntry(&search)) { - children[i] = Tcl_GetHashValue(entryPtr); - children[i]->refCount++; - i++; - } - for (i = 0 ; i < length ; i++) { - Tcl_DeleteNamespace((Tcl_Namespace *) children[i]); - TclNsDecrRefCount(children[i]); - } - TclStackFree((Tcl_Interp *) iPtr, children); - } -#else - if (nsPtr->childTablePtr != NULL) { - while (nsPtr->childTablePtr->numEntries > 0) { - int length = nsPtr->childTablePtr->numEntries; - Namespace **children = TclStackAlloc((Tcl_Interp *) iPtr, - sizeof(Namespace *) * length); - - i = 0; - for (entryPtr = Tcl_FirstHashEntry(nsPtr->childTablePtr, &search); - entryPtr != NULL; - entryPtr = Tcl_NextHashEntry(&search)) { - children[i] = Tcl_GetHashValue(entryPtr); - children[i]->refCount++; - i++; - } - for (i = 0 ; i < length ; i++) { - Tcl_DeleteNamespace((Tcl_Namespace *) children[i]); - TclNsDecrRefCount(children[i]); - } - TclStackFree((Tcl_Interp *) iPtr, children); - } - } -#endif - - /* - * Free the namespace's export pattern array. - */ - - if (nsPtr->exportArrayPtr != NULL) { - for (i = 0; i < nsPtr->numExportPatterns; i++) { - ckfree(nsPtr->exportArrayPtr[i]); - } - ckfree(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( - 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(nsPtr); -} - -/* - *---------------------------------------------------------------------- - * - * TclNsDecrRefCount -- - * - * Drops a reference to a namespace and frees it if the namespace has - * been deleted and the last reference has just been dropped. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -void -TclNsDecrRefCount( - Namespace *nsPtr) -{ - nsPtr->refCount--; - if ((nsPtr->refCount == 0) && (nsPtr->flags & NS_DEAD)) { - NamespaceFree(nsPtr); - } -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_Export -- - * - * Makes all the commands matching a pattern available to later be - * imported from the namespace specified by namespacePtr (or the current - * namespace if namespacePtr 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( - Tcl_Interp *interp, /* Current interpreter. */ - Tcl_Namespace *namespacePtr,/* Points to the namespace from which commands - * are to be exported. NULL for the current - * namespace. */ - const 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. */ -{ -#define INIT_EXPORT_PATTERNS 5 - Namespace *nsPtr, *exportNsPtr, *dummyPtr; - Namespace *currNsPtr = (Namespace *) TclGetCurrentNamespace(interp); - const char *simplePattern; - char *patternCpy; - int neededElems, len, i; - - /* - * 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(nsPtr->exportArrayPtr); - nsPtr->exportArrayPtr = NULL; - TclInvalidateNsCmdLookup(nsPtr); - nsPtr->numExportPatterns = 0; - nsPtr->maxExportPatterns = 0; - } - } - - /* - * Check that the pattern doesn't have namespace qualifiers. - */ - - TclGetNamespaceForQualName(interp, pattern, nsPtr, TCL_NAMESPACE_ONLY, - &exportNsPtr, &dummyPtr, &dummyPtr, &simplePattern); - - if ((exportNsPtr != nsPtr) || (strcmp(pattern, simplePattern) != 0)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf("invalid export pattern" - " \"%s\": pattern can't specify a namespace", pattern)); - Tcl_SetErrorCode(interp, "TCL", "EXPORT", "INVALID", NULL); - return TCL_ERROR; - } - - /* - * Make sure that we don't already have the pattern in the array - */ - - if (nsPtr->exportArrayPtr != NULL) { - for (i = 0; i < nsPtr->numExportPatterns; i++) { - if (strcmp(pattern, nsPtr->exportArrayPtr[i]) == 0) { - /* - * The pattern already exists in the list. - */ - - return TCL_OK; - } - } - } - - /* - * Make sure there is room in the namespace's pattern array for the new - * pattern. - */ - - neededElems = nsPtr->numExportPatterns + 1; - if (neededElems > nsPtr->maxExportPatterns) { - nsPtr->maxExportPatterns = nsPtr->maxExportPatterns ? - 2 * nsPtr->maxExportPatterns : INIT_EXPORT_PATTERNS; - nsPtr->exportArrayPtr = ckrealloc(nsPtr->exportArrayPtr, - sizeof(char *) * nsPtr->maxExportPatterns); - } - - /* - * Add the pattern to the namespace's array of export patterns. - */ - - len = strlen(pattern); - patternCpy = ckalloc(len + 1); - memcpy(patternCpy, pattern, (unsigned) len + 1); - - nsPtr->exportArrayPtr[nsPtr->numExportPatterns] = patternCpy; - nsPtr->numExportPatterns++; - - /* - * The list of commands actually exported from the namespace might have - * changed (probably will have!) However, we do not need to recompute this - * just yet; next time we need the info will be soon enough. - */ - - TclInvalidateNsCmdLookup(nsPtr); - - 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( - 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 *) TclGetCurrentNamespace(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 namespacePtr (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. - * - * If matching commands are on the autoload path but haven't been loaded - * yet, this command forces them to be loaded, then creates the links to - * them. - * - * 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( - 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. */ - const 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. */ -{ - Namespace *nsPtr, *importNsPtr, *dummyPtr; - const char *simplePattern; - register Tcl_HashEntry *hPtr; - Tcl_HashSearch search; - - /* - * If the specified namespace is NULL, use the current namespace. - */ - - if (namespacePtr == NULL) { - nsPtr = (Namespace *) TclGetCurrentNamespace(interp); - } else { - nsPtr = (Namespace *) namespacePtr; - } - - /* - * First, invoke the "auto_import" command with the pattern being - * imported. This command is part of the Tcl library. It looks for - * imported commands in autoloaded libraries and loads them in. That way, - * they will be found when we try to create links below. - * - * Note that we don't just call Tcl_EvalObjv() directly because we do not - * want absence of the command to be a failure case. - */ - - if (Tcl_FindCommand(interp,"auto_import",NULL,TCL_GLOBAL_ONLY) != NULL) { - Tcl_Obj *objv[2]; - int result; - - TclNewLiteralStringObj(objv[0], "auto_import"); - objv[1] = Tcl_NewStringObj(pattern, -1); - - Tcl_IncrRefCount(objv[0]); - Tcl_IncrRefCount(objv[1]); - result = Tcl_EvalObjv(interp, 2, objv, TCL_GLOBAL_ONLY); - Tcl_DecrRefCount(objv[0]); - Tcl_DecrRefCount(objv[1]); - - if (result != TCL_OK) { - return TCL_ERROR; - } - Tcl_ResetResult(interp); - } - - /* - * 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_SetObjResult(interp, Tcl_NewStringObj("empty import pattern",-1)); - Tcl_SetErrorCode(interp, "TCL", "IMPORT", "EMPTY", NULL); - return TCL_ERROR; - } - TclGetNamespaceForQualName(interp, pattern, nsPtr, TCL_NAMESPACE_ONLY, - &importNsPtr, &dummyPtr, &dummyPtr, &simplePattern); - - if (importNsPtr == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "unknown namespace in import pattern \"%s\"", pattern)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "NAMESPACE", pattern, NULL); - return TCL_ERROR; - } - if (importNsPtr == nsPtr) { - if (pattern == simplePattern) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "no namespace specified in import pattern \"%s\"", - pattern)); - Tcl_SetErrorCode(interp, "TCL", "IMPORT", "ORIGIN", NULL); - } else { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "import pattern \"%s\" tries to import from namespace" - " \"%s\" into itself", pattern, importNsPtr->name)); - Tcl_SetErrorCode(interp, "TCL", "IMPORT", "SELF", 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. - */ - - if ((simplePattern != NULL) && TclMatchIsTrivial(simplePattern)) { - hPtr = Tcl_FindHashEntry(&importNsPtr->cmdTable, simplePattern); - if (hPtr == NULL) { - return TCL_OK; - } - return DoImport(interp, nsPtr, hPtr, simplePattern, pattern, - importNsPtr, allowOverwrite); - } - for (hPtr = Tcl_FirstHashEntry(&importNsPtr->cmdTable, &search); - (hPtr != NULL); hPtr = Tcl_NextHashEntry(&search)) { - char *cmdName = Tcl_GetHashKey(&importNsPtr->cmdTable, hPtr); - - if (Tcl_StringMatch(cmdName, simplePattern) && - DoImport(interp, nsPtr, hPtr, cmdName, pattern, importNsPtr, - allowOverwrite) == TCL_ERROR) { - return TCL_ERROR; - } - } - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * DoImport -- - * - * Import a particular command from one namespace into another. Helper - * for Tcl_Import(). - * - * Results: - * Standard Tcl result code. If TCL_ERROR, appends an error message to - * the interpreter result. - * - * Side effects: - * A new command is created in the target namespace unless this is a - * reimport of exactly the same command as before. - * - *---------------------------------------------------------------------- - */ - -static int -DoImport( - Tcl_Interp *interp, - Namespace *nsPtr, - Tcl_HashEntry *hPtr, - const char *cmdName, - const char *pattern, - Namespace *importNsPtr, - int allowOverwrite) -{ - int i = 0, exported = 0; - Tcl_HashEntry *found; - - /* - * The command cmdName in the source namespace matches the pattern. Check - * whether it was exported. If it wasn't, we ignore it. - */ - - while (!exported && (i < importNsPtr->numExportPatterns)) { - exported |= Tcl_StringMatch(cmdName, - importNsPtr->exportArrayPtr[i++]); - } - if (!exported) { - return TCL_OK; - } - - /* - * Unless there is a name clash, create an imported command in the current - * namespace that refers to cmdPtr. - */ - - found = Tcl_FindHashEntry(&nsPtr->cmdTable, cmdName); - if ((found == 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_Command importedCmd; - ImportedCmdData *dataPtr; - Command *cmdPtr; - ImportRef *refPtr; - - Tcl_DStringInit(&ds); - Tcl_DStringAppend(&ds, nsPtr->fullName, -1); - if (nsPtr != ((Interp *) interp)->globalNsPtr) { - TclDStringAppendLiteral(&ds, "::"); - } - Tcl_DStringAppend(&ds, cmdName, -1); - - /* - * Check whether creating the new imported command in the current - * namespace would create a cycle of imported command references. - */ - - cmdPtr = Tcl_GetHashValue(hPtr); - if (found != NULL && cmdPtr->deleteProc == DeleteImportedCmd) { - Command *overwrite = Tcl_GetHashValue(found); - Command *linkCmd = cmdPtr; - - while (linkCmd->deleteProc == DeleteImportedCmd) { - dataPtr = linkCmd->objClientData; - linkCmd = dataPtr->realCmdPtr; - if (overwrite == linkCmd) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "import pattern \"%s\" would create a loop" - " containing command \"%s\"", - pattern, Tcl_DStringValue(&ds))); - Tcl_DStringFree(&ds); - Tcl_SetErrorCode(interp, "TCL", "IMPORT", "LOOP", NULL); - return TCL_ERROR; - } - } - } - - dataPtr = ckalloc(sizeof(ImportedCmdData)); - importedCmd = Tcl_NRCreateCommand(interp, Tcl_DStringValue(&ds), - InvokeImportedCmd, InvokeImportedNRCmd, dataPtr, - DeleteImportedCmd); - dataPtr->realCmdPtr = cmdPtr; - dataPtr->selfPtr = (Command *) importedCmd; - dataPtr->selfPtr->compileProc = cmdPtr->compileProc; - Tcl_DStringFree(&ds); - - /* - * Create an ImportRef structure describing this new import command - * and add it to the import ref list in the "real" command. - */ - - refPtr = ckalloc(sizeof(ImportRef)); - refPtr->importedCmdPtr = (Command *) importedCmd; - refPtr->nextPtr = cmdPtr->importRefPtr; - cmdPtr->importRefPtr = refPtr; - } else { - Command *overwrite = Tcl_GetHashValue(found); - - if (overwrite->deleteProc == DeleteImportedCmd) { - ImportedCmdData *dataPtr = overwrite->objClientData; - - if (dataPtr->realCmdPtr == Tcl_GetHashValue(hPtr)) { - /* - * Repeated import of same command is acceptable. - */ - - return TCL_OK; - } - } - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "can't import command \"%s\": already exists", cmdName)); - Tcl_SetErrorCode(interp, "TCL", "IMPORT", "OVERWRITE", NULL); - return TCL_ERROR; - } - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_ForgetImport -- - * - * Deletes commands previously imported into the namespace indicated. - * The by namespacePtr, or the current namespace of interp, when - * namespacePtr is NULL. The pattern controls which imported commands are - * deleted. A simple pattern, one without namespace separators, matches - * the current command names of imported commands in the namespace. - * Matching imported commands are deleted. A qualified pattern is - * interpreted as deletion selection on the basis of where the command is - * imported from. The original command and "first link" command for each - * imported command are determined, and they are matched against the - * pattern. A match leads to deletion of the imported command. - * - * Results: - * Returns TCL_ERROR and records an error message in the interp result if - * a namespace qualified pattern refers to a namespace that does not - * exist. Otherwise, returns TCL_OK. - * - * Side effects: - * May delete commands. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_ForgetImport( - Tcl_Interp *interp, /* Current interpreter. */ - Tcl_Namespace *namespacePtr,/* Points to the namespace from which - * previously imported commands should be - * removed. NULL for current namespace. */ - const char *pattern) /* String pattern indicating which imported - * commands to remove. */ -{ - Namespace *nsPtr, *sourceNsPtr, *dummyPtr; - const char *simplePattern; - char *cmdName; - register Tcl_HashEntry *hPtr; - Tcl_HashSearch search; - - /* - * If the specified namespace is NULL, use the current namespace. - */ - - if (namespacePtr == NULL) { - nsPtr = (Namespace *) TclGetCurrentNamespace(interp); - } else { - nsPtr = (Namespace *) namespacePtr; - } - - /* - * Parse the pattern into its namespace-qualification (if any) and the - * simple pattern. - */ - - TclGetNamespaceForQualName(interp, pattern, nsPtr, TCL_NAMESPACE_ONLY, - &sourceNsPtr, &dummyPtr, &dummyPtr, &simplePattern); - - if (sourceNsPtr == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "unknown namespace in namespace forget pattern \"%s\"", - pattern)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "NAMESPACE", pattern, NULL); - return TCL_ERROR; - } - - if (strcmp(pattern, simplePattern) == 0) { - /* - * The pattern is simple. Delete any imported commands that match it. - */ - - if (TclMatchIsTrivial(simplePattern)) { - hPtr = Tcl_FindHashEntry(&nsPtr->cmdTable, simplePattern); - if (hPtr != NULL) { - Command *cmdPtr = Tcl_GetHashValue(hPtr); - - if (cmdPtr && (cmdPtr->deleteProc == DeleteImportedCmd)) { - Tcl_DeleteCommandFromToken(interp, (Tcl_Command) cmdPtr); - } - } - return TCL_OK; - } - for (hPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search); - (hPtr != NULL); hPtr = Tcl_NextHashEntry(&search)) { - Command *cmdPtr = Tcl_GetHashValue(hPtr); - - if (cmdPtr->deleteProc != DeleteImportedCmd) { - continue; - } - cmdName = Tcl_GetHashKey(&nsPtr->cmdTable, hPtr); - if (Tcl_StringMatch(cmdName, simplePattern)) { - Tcl_DeleteCommandFromToken(interp, (Tcl_Command) cmdPtr); - } - } - return TCL_OK; - } - - /* - * The pattern was namespace-qualified. - */ - - for (hPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search); (hPtr != NULL); - hPtr = Tcl_NextHashEntry(&search)) { - Tcl_CmdInfo info; - Tcl_Command token = Tcl_GetHashValue(hPtr); - Tcl_Command origin = TclGetOriginalCommand(token); - - if (Tcl_GetCommandInfoFromToken(origin, &info) == 0) { - continue; /* Not an imported command. */ - } - if (info.namespacePtr != (Tcl_Namespace *) sourceNsPtr) { - /* - * Original not in namespace we're matching. Check the first link - * in the import chain. - */ - - Command *cmdPtr = (Command *) token; - ImportedCmdData *dataPtr = cmdPtr->objClientData; - Tcl_Command firstToken = (Tcl_Command) dataPtr->realCmdPtr; - - if (firstToken == origin) { - continue; - } - Tcl_GetCommandInfoFromToken(firstToken, &info); - if (info.namespacePtr != (Tcl_Namespace *) sourceNsPtr) { - continue; - } - origin = firstToken; - } - if (Tcl_StringMatch(Tcl_GetCommandName(NULL, origin), simplePattern)){ - Tcl_DeleteCommandFromToken(interp, token); - } - } - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * TclGetOriginalCommand -- - * - * An imported command is created in an namespace when a "real" command - * is imported from another namespace. If the specified command is an - * imported command, this function 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 function returns the Tcl_Command token in the - * first namespace, a. Otherwise, if the specified command is not an - * imported command, the function returns NULL. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -Tcl_Command -TclGetOriginalCommand( - Tcl_Command command) /* The imported command for which the original - * command should be returned. */ -{ - register Command *cmdPtr = (Command *) command; - ImportedCmdData *dataPtr; - - if (cmdPtr->deleteProc != DeleteImportedCmd) { - return NULL; - } - - while (cmdPtr->deleteProc == DeleteImportedCmd) { - dataPtr = 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 -InvokeImportedNRCmd( - 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. */ -{ - ImportedCmdData *dataPtr = clientData; - Command *realCmdPtr = dataPtr->realCmdPtr; - - TclSkipTailcall(interp); - return TclNREvalObjv(interp, objc, objv, TCL_EVAL_NOERR, realCmdPtr); -} - -static int -InvokeImportedCmd( - 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. */ -{ - return Tcl_NRCallObjProc(interp, InvokeImportedNRCmd, clientData, - 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 function 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) /* Points to the imported command's - * ImportedCmdData structure. */ -{ - ImportedCmdData *dataPtr = 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(refPtr); - ckfree(dataPtr); - return; - } - prevPtr = refPtr; - } - - Tcl_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 function 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 - * function 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 TCL_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 TCL_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 TCL_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: - * It sets *nsPtrPtr and *altNsPtrPtr to point to the two possible - * namespaces which represent the last (containing) namespace in the - * qualified name. If the function sets either *nsPtrPtr or *altNsPtrPtr - * to NULL, then the search along that path failed. The function 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 (TCL_FIND_ONLY_NS), the function 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 function 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. - * - * For backwards compatibility with the TclPro byte code loader, this - * function always returns TCL_OK. - * - * Side effects: - * If "flags" contains TCL_CREATE_NS_IF_UNKNOWN, new namespaces may be - * created. - * - *---------------------------------------------------------------------- - */ - -int -TclGetNamespaceForQualName( - Tcl_Interp *interp, /* Interpreter in which to find the namespace - * containing qualName. */ - const 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 is set. */ - int flags, /* Flags controlling the search: an OR'd - * combination of TCL_GLOBAL_ONLY, - * TCL_NAMESPACE_ONLY, TCL_FIND_ONLY_NS, and - * TCL_CREATE_NS_IF_UNKNOWN. */ - Namespace **nsPtrPtr, /* Address where function 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 function 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, TCL_FIND_ONLY_NS, - * TCL_CREATE_NS_IF_UNKNOWN flag is set. */ - Namespace **actualCxtPtrPtr,/* Address where function 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. */ - const char **simpleNamePtr) /* Address where function stores the simple - * name at end of the qualName, or NULL if - * qualName is "::" or the flag - * TCL_FIND_ONLY_NS was specified. */ -{ - Interp *iPtr = (Interp *) interp; - Namespace *nsPtr = cxtNsPtr; - Namespace *altNsPtr; - Namespace *globalNsPtr = iPtr->globalNsPtr; - const char *start, *end; - const char *nsName; - Tcl_HashEntry *entryPtr; - Tcl_DString buffer; - int len; - - /* - * Determine the context namespace nsPtr in which to start the primary - * search. If the qualName name starts with a "::" or TCL_GLOBAL_ONLY was - * specified, search from the global namespace. Otherwise, use the - * 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_GLOBAL_ONLY) { - nsPtr = globalNsPtr; - } else if (nsPtr == NULL) { - nsPtr = iPtr->varFramePtr->nsPtr; - } - - start = qualName; /* Points 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 | TCL_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 TCL_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 & TCL_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. - */ - - TclDStringClear(&buffer); - 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 TCL_CREATE_NS_IF_UNKNOWN is set, - * create that qualifying namespace. This is needed for functions like - * Tcl_CreateCommand that cannot fail. - */ - - if (nsPtr != NULL) { -#ifndef BREAK_NAMESPACE_COMPAT - entryPtr = Tcl_FindHashEntry(&nsPtr->childTable, nsName); -#else - if (nsPtr->childTablePtr == NULL) { - entryPtr = NULL; - } else { - entryPtr = Tcl_FindHashEntry(nsPtr->childTablePtr, nsName); - } -#endif - if (entryPtr != NULL) { - nsPtr = Tcl_GetHashValue(entryPtr); - } else if (flags & TCL_CREATE_NS_IF_UNKNOWN) { - Tcl_CallFrame *framePtr; - - (void) TclPushStackFrame(interp, &framePtr, - (Tcl_Namespace *) nsPtr, /*isProcCallFrame*/ 0); - - nsPtr = (Namespace *) - Tcl_CreateNamespace(interp, nsName, NULL, NULL); - TclPopStackFrame(interp); - - if (nsPtr == NULL) { - Tcl_Panic("Could not create namespace '%s'", nsName); - } - } else { /* Namespace not found and was not - * created. */ - nsPtr = NULL; - } - } - - /* - * Look up the namespace qualifier in the alternate search path too. - */ - - if (altNsPtr != NULL) { -#ifndef BREAK_NAMESPACE_COMPAT - entryPtr = Tcl_FindHashEntry(&altNsPtr->childTable, nsName); -#else - if (altNsPtr->childTablePtr != NULL) { - entryPtr = Tcl_FindHashEntry(altNsPtr->childTablePtr, nsName); - } else { - entryPtr = NULL; - } -#endif - if (entryPtr != NULL) { - altNsPtr = 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 & TCL_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 & TCL_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( - Tcl_Interp *interp, /* The interpreter in which to find the - * namespace. */ - const 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; - const char *dummy; - - /* - * Find the namespace(s) that contain the specified namespace name. Add - * the TCL_FIND_ONLY_NS flag to resolve the name all the way down to its - * last component, a namespace. - */ - - TclGetNamespaceForQualName(interp, name, (Namespace *) contextNsPtr, - flags|TCL_FIND_ONLY_NS, &nsPtr, &dummy1Ptr, &dummy2Ptr, &dummy); - - if (nsPtr != NULL) { - return (Tcl_Namespace *) nsPtr; - } - - if (flags & TCL_LEAVE_ERR_MSG) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "unknown namespace \"%s\"", name)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "NAMESPACE", name, 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( - Tcl_Interp *interp, /* The interpreter in which to find the - * command and to report errors. */ - const 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. */ -{ - Interp *iPtr = (Interp *) interp; - Namespace *cxtNsPtr; - register Tcl_HashEntry *entryPtr; - register Command *cmdPtr; - const char *simpleName; - int result; - - /* - * If this namespace has a command resolver, then give it first crack at - * the command resolution. If the interpreter has any command resolvers, - * consult them next. The command resolver functions may return a - * Tcl_Command value, they may signal to continue onward, or they may - * signal an error. - */ - - if ((flags & TCL_GLOBAL_ONLY) || !strncmp(name, "::", 2)) { - cxtNsPtr = (Namespace *) TclGetGlobalNamespace(interp); - } else if (contextNsPtr != NULL) { - cxtNsPtr = (Namespace *) contextNsPtr; - } else { - cxtNsPtr = (Namespace *) TclGetCurrentNamespace(interp); - } - - if (cxtNsPtr->cmdResProc != NULL || iPtr->resolverPtr != NULL) { - ResolverScheme *resPtr = iPtr->resolverPtr; - Tcl_Command cmd; - - if (cxtNsPtr->cmdResProc) { - result = cxtNsPtr->cmdResProc(interp, name, - (Tcl_Namespace *) cxtNsPtr, flags, &cmd); - } else { - result = TCL_CONTINUE; - } - - while (result == TCL_CONTINUE && resPtr) { - if (resPtr->cmdResProc) { - result = resPtr->cmdResProc(interp, name, - (Tcl_Namespace *) cxtNsPtr, flags, &cmd); - } - resPtr = resPtr->nextPtr; - } - - if (result == TCL_OK) { - return cmd; - } else if (result != TCL_CONTINUE) { - return NULL; - } - } - - /* - * Find the namespace(s) that contain the command. - */ - - cmdPtr = NULL; - if (cxtNsPtr->commandPathLength!=0 && strncmp(name, "::", 2) - && !(flags & TCL_NAMESPACE_ONLY)) { - int i; - Namespace *pathNsPtr, *realNsPtr, *dummyNsPtr; - - (void) TclGetNamespaceForQualName(interp, name, cxtNsPtr, - TCL_NAMESPACE_ONLY, &realNsPtr, &dummyNsPtr, &dummyNsPtr, - &simpleName); - if ((realNsPtr != NULL) && (simpleName != NULL)) { - if ((cxtNsPtr == realNsPtr) - || !(realNsPtr->flags & NS_DYING)) { - entryPtr = Tcl_FindHashEntry(&realNsPtr->cmdTable, simpleName); - if (entryPtr != NULL) { - cmdPtr = Tcl_GetHashValue(entryPtr); - } - } - } - - /* - * Next, check along the path. - */ - - for (i=0 ; i<cxtNsPtr->commandPathLength && cmdPtr==NULL ; i++) { - pathNsPtr = cxtNsPtr->commandPathArray[i].nsPtr; - if (pathNsPtr == NULL) { - continue; - } - (void) TclGetNamespaceForQualName(interp, name, pathNsPtr, - TCL_NAMESPACE_ONLY, &realNsPtr, &dummyNsPtr, &dummyNsPtr, - &simpleName); - if ((realNsPtr != NULL) && (simpleName != NULL) - && !(realNsPtr->flags & NS_DYING)) { - entryPtr = Tcl_FindHashEntry(&realNsPtr->cmdTable, simpleName); - if (entryPtr != NULL) { - cmdPtr = Tcl_GetHashValue(entryPtr); - } - } - } - - /* - * If we've still not found the command, look in the global namespace - * as a last resort. - */ - - if (cmdPtr == NULL) { - (void) TclGetNamespaceForQualName(interp, name, NULL, - TCL_GLOBAL_ONLY, &realNsPtr, &dummyNsPtr, &dummyNsPtr, - &simpleName); - if ((realNsPtr != NULL) && (simpleName != NULL) - && !(realNsPtr->flags & NS_DYING)) { - entryPtr = Tcl_FindHashEntry(&realNsPtr->cmdTable, simpleName); - if (entryPtr != NULL) { - cmdPtr = Tcl_GetHashValue(entryPtr); - } - } - } - } else { - Namespace *nsPtr[2]; - register int search; - - TclGetNamespaceForQualName(interp, name, (Namespace *) contextNsPtr, - flags, &nsPtr[0], &nsPtr[1], &cxtNsPtr, &simpleName); - - /* - * 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. - */ - - 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 = Tcl_GetHashValue(entryPtr); - } - } - } - } - - if (cmdPtr != NULL) { - return (Tcl_Command) cmdPtr; - } - - if (flags & TCL_LEAVE_ERR_MSG) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "unknown command \"%s\"", name)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "COMMAND", name, NULL); - } - return 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( - 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 *) TclGetGlobalNamespace(interp); - int found, i; - int trailFront = -1; - int trailSize = 5; /* Formerly NUM_TRAIL_ELEMS. */ - Namespace **trailPtr = TclStackAlloc(interp, - trailSize * sizeof(Namespace *)); - - /* - * 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]; -#ifndef BREAK_NAMESPACE_COMPAT - hPtr = Tcl_FindHashEntry(&shadowNsPtr->childTable, - trailNsPtr->name); -#else - if (shadowNsPtr->childTablePtr != NULL) { - hPtr = Tcl_FindHashEntry(shadowNsPtr->childTablePtr, - trailNsPtr->name); - } else { - hPtr = NULL; - } -#endif - if (hPtr != NULL) { - shadowNsPtr = 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++; - TclInvalidateNsPath(nsPtr); - - /* - * If the shadowed command was compiled to bytecodes, we - * invalidate all the bytecodes in nsPtr, to force a new - * compilation. We use the resolverEpoch to signal the need - * for a fresh compilation of every bytecode. - */ - - if (((Command *)Tcl_GetHashValue(hPtr))->compileProc != NULL){ - nsPtr->resolverEpoch++; - } - } - } - - /* - * Insert nsPtr at the front of the trail list: i.e., at the end of - * the trailPtr array. - */ - - trailFront++; - if (trailFront == trailSize) { - int newSize = 2 * trailSize; - - trailPtr = TclStackRealloc(interp, trailPtr, - newSize * sizeof(Namespace *)); - trailSize = newSize; - } - trailPtr[trailFront] = nsPtr; - } - TclStackFree(interp, trailPtr); -} - -/* - *---------------------------------------------------------------------- - * - * TclGetNamespaceFromObj, GetNamespaceFromObj -- - * - * Gets 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, or anything else goes wrong, this - * function returns TCL_ERROR and writes an error message to interp, - * if non-NULL. - * - * Side effects: - * May update the internal representation for the object, caching the - * namespace reference. The next time this function is called, the - * namespace value can be found quickly. - * - *---------------------------------------------------------------------- - */ - -int -TclGetNamespaceFromObj( - 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. */ -{ - if (GetNamespaceFromObj(interp, objPtr, nsPtrPtr) == TCL_ERROR) { - const char *name = TclGetString(objPtr); - - if ((name[0] == ':') && (name[1] == ':')) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "namespace \"%s\" not found", name)); - } else { - /* - * Get the current namespace name. - */ - - NamespaceCurrentCmd(NULL, interp, 1, NULL); - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "namespace \"%s\" not found in \"%s\"", name, - Tcl_GetStringResult(interp))); - } - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "NAMESPACE", name, NULL); - return TCL_ERROR; - } - return TCL_OK; -} - -static int -GetNamespaceFromObj( - 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. */ -{ - ResolvedNsName *resNamePtr; - Namespace *nsPtr, *refNsPtr; - - if (objPtr->typePtr == &nsNameType) { - /* - * Check that the ResolvedNsName is still valid; avoid letting the ref - * cross interps. - */ - - resNamePtr = objPtr->internalRep.twoPtrValue.ptr1; - nsPtr = resNamePtr->nsPtr; - refNsPtr = resNamePtr->refNsPtr; - if (!(nsPtr->flags & NS_DYING) && (interp == nsPtr->interp) && - (!refNsPtr || ((interp == refNsPtr->interp) && - (refNsPtr== (Namespace *) Tcl_GetCurrentNamespace(interp))))){ - *nsPtrPtr = (Tcl_Namespace *) nsPtr; - return TCL_OK; - } - } - if (SetNsNameFromAny(interp, objPtr) == TCL_OK) { - resNamePtr = objPtr->internalRep.twoPtrValue.ptr1; - *nsPtrPtr = (Tcl_Namespace *) resNamePtr->nsPtr; - return TCL_OK; - } - return TCL_ERROR; -} - -/* - *---------------------------------------------------------------------- - * - * TclInitNamespaceCmd -- - * - * This function is called to create the "namespace" Tcl command. See the - * user documentation for details on what it does. - * - * Results: - * Handle for the namespace command, or NULL on failure. - * - * Side effects: - * none - * - *---------------------------------------------------------------------- - */ - -Tcl_Command -TclInitNamespaceCmd( - Tcl_Interp *interp) /* Current interpreter. */ -{ - return TclMakeEnsemble(interp, "namespace", defaultNamespaceMap); -} - -/* - *---------------------------------------------------------------------- - * - * 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( - 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 *) TclGetGlobalNamespace(interp); - const 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 == 1) { - nsPtr = (Namespace *) TclGetCurrentNamespace(interp); - } else if ((objc == 2) || (objc == 3)) { - if (TclGetNamespaceFromObj(interp, objv[1], &namespacePtr) != TCL_OK){ - return TCL_ERROR; - } - nsPtr = (Namespace *) namespacePtr; - } else { - Tcl_WrongNumArgs(interp, 1, objv, "?name? ?pattern?"); - return TCL_ERROR; - } - - /* - * Get the glob-style pattern, if any, used to narrow the search. - */ - - Tcl_DStringInit(&buffer); - if (objc == 3) { - const char *name = TclGetString(objv[2]); - - if ((*name == ':') && (*(name+1) == ':')) { - pattern = name; - } else { - Tcl_DStringAppend(&buffer, nsPtr->fullName, -1); - if (nsPtr != globalNsPtr) { - TclDStringAppendLiteral(&buffer, "::"); - } - 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, NULL); - if ((pattern != NULL) && TclMatchIsTrivial(pattern)) { - unsigned int length = strlen(nsPtr->fullName); - - if (strncmp(pattern, nsPtr->fullName, length) != 0) { - goto searchDone; - } - if ( -#ifndef BREAK_NAMESPACE_COMPAT - Tcl_FindHashEntry(&nsPtr->childTable, pattern+length) != NULL -#else - nsPtr->childTablePtr != NULL && - Tcl_FindHashEntry(nsPtr->childTablePtr, pattern+length) != NULL -#endif - ) { - Tcl_ListObjAppendElement(interp, listPtr, - Tcl_NewStringObj(pattern, -1)); - } - goto searchDone; - } -#ifndef BREAK_NAMESPACE_COMPAT - entryPtr = Tcl_FirstHashEntry(&nsPtr->childTable, &search); -#else - if (nsPtr->childTablePtr == NULL) { - goto searchDone; - } - entryPtr = Tcl_FirstHashEntry(nsPtr->childTablePtr, &search); -#endif - while (entryPtr != NULL) { - childNsPtr = 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); - } - - searchDone: - 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 function returns an error message as the - * result in the interpreter's result object. - * - *---------------------------------------------------------------------- - */ - -static int -NamespaceCodeCmd( - 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 const char *arg; - int length; - - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "arg"); - return TCL_ERROR; - } - - /* - * If "arg" is already a scoped value, then return it directly. - * Take care to only check for scoping in precisely the style that - * [::namespace code] generates it. Anything more forgiving can have - * the effect of failing in namespaces that contain their own custom - " "namespace" command. [Bug 3202171]. - */ - - arg = TclGetStringFromObj(objv[1], &length); - if (*arg==':' && length > 20 - && strncmp(arg, "::namespace inscope ", 20) == 0) { - Tcl_SetObjResult(interp, objv[1]); - 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. - */ - - TclNewObj(listPtr); - TclNewLiteralStringObj(objPtr, "::namespace"); - Tcl_ListObjAppendElement(interp, listPtr, objPtr); - TclNewLiteralStringObj(objPtr, "inscope"); - Tcl_ListObjAppendElement(interp, listPtr, objPtr); - - currNsPtr = (Namespace *) TclGetCurrentNamespace(interp); - if (currNsPtr == (Namespace *) TclGetGlobalNamespace(interp)) { - TclNewLiteralStringObj(objPtr, "::"); - } else { - objPtr = Tcl_NewStringObj(currNsPtr->fullName, -1); - } - Tcl_ListObjAppendElement(interp, listPtr, objPtr); - - Tcl_ListObjAppendElement(interp, listPtr, objv[1]); - - 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( - 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 != 1) { - Tcl_WrongNumArgs(interp, 1, 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 *) TclGetCurrentNamespace(interp); - if (currNsPtr == (Namespace *) TclGetGlobalNamespace(interp)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("::", 2)); - } else { - Tcl_SetObjResult(interp, Tcl_NewStringObj(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 - * function 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 - * function returns an error message in the interpreter's result object. - * - *---------------------------------------------------------------------- - */ - -static int -NamespaceDeleteCmd( - ClientData dummy, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - Tcl_Namespace *namespacePtr; - const char *name; - register int i; - - if (objc < 1) { - Tcl_WrongNumArgs(interp, 1, 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 = 1; i < objc; i++) { - name = TclGetString(objv[i]); - namespacePtr = Tcl_FindNamespace(interp, name, NULL, /*flags*/ 0); - if ((namespacePtr == NULL) - || (((Namespace *) namespacePtr)->flags & NS_KILLED)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "unknown namespace \"%s\" in namespace delete command", - TclGetString(objv[i]))); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "NAMESPACE", - TclGetString(objv[i]), NULL); - return TCL_ERROR; - } - } - - /* - * Okay, now delete each namespace. - */ - - for (i = 1; i < objc; i++) { - name = TclGetString(objv[i]); - namespacePtr = Tcl_FindNamespace(interp, name, NULL, /* flags */ 0); - if (namespacePtr) { - 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 function returns an error message as the - * result. - * - *---------------------------------------------------------------------- - */ - -static int -NamespaceEvalCmd( - ClientData clientData, /* Arbitrary value passed to cmd. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - return Tcl_NRCallObjProc(interp, NRNamespaceEvalCmd, clientData, objc, - objv); -} - -static int -NRNamespaceEvalCmd( - ClientData dummy, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - Interp *iPtr = (Interp *) interp; - CmdFrame *invoker; - int word; - Tcl_Namespace *namespacePtr; - CallFrame *framePtr, **framePtrPtr; - Tcl_Obj *objPtr; - int result; - - if (objc < 3) { - Tcl_WrongNumArgs(interp, 1, 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[1], &namespacePtr); - - /* - * If the namespace wasn't found, try to create it. - */ - - if (result == TCL_ERROR) { - const char *name = TclGetString(objv[1]); - - namespacePtr = Tcl_CreateNamespace(interp, name, NULL, NULL); - if (namespacePtr == NULL) { - return TCL_ERROR; - } - } - - /* - * Make the specified namespace the current namespace and evaluate the - * command(s). - */ - - /* This is needed to satisfy GCC 3.3's strict aliasing rules */ - framePtrPtr = &framePtr; - (void) TclPushStackFrame(interp, (Tcl_CallFrame **) framePtrPtr, - namespacePtr, /*isProcCallFrame*/ 0); - - framePtr->objv = TclFetchEnsembleRoot(interp, objv, objc, &framePtr->objc); - - if (objc == 3) { - /* - * TIP #280: Make actual argument location available to eval'd script. - */ - - objPtr = objv[2]; - invoker = iPtr->cmdFramePtr; - word = 3; - TclArgumentGet(interp, objPtr, &invoker, &word); - } else { - /* - * More than one argument: concatenate them together with spaces - * between, then evaluate the result. Tcl_EvalObjEx will delete the - * object when it decrements its refcount after eval'ing it. - */ - - objPtr = Tcl_ConcatObj(objc-2, objv+2); - invoker = NULL; - word = 0; - } - - /* - * TIP #280: Make invoking context available to eval'd script. - */ - - TclNRAddCallback(interp, NsEval_Callback, namespacePtr, "eval", - NULL, NULL); - return TclNREvalObjEx(interp, objPtr, 0, invoker, word); -} - -static int -NsEval_Callback( - ClientData data[], - Tcl_Interp *interp, - int result) -{ - Tcl_Namespace *namespacePtr = data[0]; - - if (result == TCL_ERROR) { - int length = strlen(namespacePtr->fullName); - int limit = 200; - int overflow = (length > limit); - char *cmd = data[1]; - - Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( - "\n (in namespace %s \"%.*s%s\" script line %d)", - cmd, - (overflow ? limit : length), namespacePtr->fullName, - (overflow ? "..." : ""), Tcl_GetErrorLine(interp))); - } - - /* - * Restore the previous "current" namespace. - */ - - TclPopStackFrame(interp); - return result; -} - -/* - *---------------------------------------------------------------------- - * - * NamespaceExistsCmd -- - * - * Invoked to implement the "namespace exists" command that returns true - * if the given namespace currently exists, and false otherwise. Handles - * the following syntax: - * - * namespace exists 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 -NamespaceExistsCmd( - ClientData dummy, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - Tcl_Namespace *namespacePtr; - - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "name"); - return TCL_ERROR; - } - - Tcl_SetObjResult(interp, Tcl_NewBooleanObj( - GetNamespaceFromObj(interp, objv[1], &namespacePtr) == TCL_OK)); - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * 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( - ClientData dummy, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - int firstArg, i; - - if (objc < 1) { - Tcl_WrongNumArgs(interp, 1, objv, "?-clear? ?pattern pattern...?"); - return TCL_ERROR; - } - - /* - * If no pattern arguments are given, and "-clear" isn't specified, return - * the namespace's current export pattern list. - */ - - if (objc == 1) { - Tcl_Obj *listPtr = Tcl_NewObj(); - - (void) Tcl_AppendExportList(interp, NULL, listPtr); - Tcl_SetObjResult(interp, listPtr); - return TCL_OK; - } - - /* - * Process the optional "-clear" argument. - */ - - firstArg = 1; - if (strcmp("-clear", Tcl_GetString(objv[firstArg])) == 0) { - Tcl_Export(interp, NULL, "::", 1); - Tcl_ResetResult(interp); - firstArg++; - } - - /* - * Add each pattern to the namespace's export pattern list. - */ - - for (i = firstArg; i < objc; i++) { - int result = Tcl_Export(interp, NULL, Tcl_GetString(objv[i]), 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 function returns an error message in the - * interpreter's result object. - * - *---------------------------------------------------------------------- - */ - -static int -NamespaceForgetCmd( - ClientData dummy, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - const char *pattern; - register int i, result; - - if (objc < 1) { - Tcl_WrongNumArgs(interp, 1, objv, "?pattern pattern...?"); - return TCL_ERROR; - } - - for (i = 1; i < objc; i++) { - pattern = TclGetString(objv[i]); - result = Tcl_ForgetImport(interp, 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. - * - * If there are no pattern arguments and the "-force" flag isn't given, - * this command returns the list of commands currently imported in - * the current namespace. - * - * 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 function returns an error message in the interpreter's - * result object. - * - *---------------------------------------------------------------------- - */ - -static int -NamespaceImportCmd( - ClientData dummy, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - int allowOverwrite = 0; - const char *string, *pattern; - register int i, result; - int firstArg; - - if (objc < 1) { - Tcl_WrongNumArgs(interp, 1, objv, "?-force? ?pattern pattern...?"); - return TCL_ERROR; - } - - /* - * Skip over the optional "-force" as the first argument. - */ - - firstArg = 1; - if (firstArg < objc) { - string = TclGetString(objv[firstArg]); - if ((*string == '-') && (strcmp(string, "-force") == 0)) { - allowOverwrite = 1; - firstArg++; - } - } else { - /* - * When objc == 1, command is just [namespace import]. Introspection - * form to return list of imported commands. - */ - - Tcl_HashEntry *hPtr; - Tcl_HashSearch search; - Namespace *nsPtr = (Namespace *) TclGetCurrentNamespace(interp); - Tcl_Obj *listPtr; - - TclNewObj(listPtr); - for (hPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search); - hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { - Command *cmdPtr = Tcl_GetHashValue(hPtr); - - if (cmdPtr->deleteProc == DeleteImportedCmd) { - Tcl_ListObjAppendElement(NULL, listPtr, Tcl_NewStringObj( - Tcl_GetHashKey(&nsPtr->cmdTable, hPtr) ,-1)); - } - } - Tcl_SetObjResult(interp, listPtr); - return TCL_OK; - } - - /* - * Handle the imports for each of the patterns. - */ - - for (i = firstArg; i < objc; i++) { - pattern = TclGetString(objv[i]); - result = Tcl_Import(interp, 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 e - * - * is equivalent to - * - * namespace eval ::foo [concat {a b} [list c d e]] - * - * 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( - ClientData clientData, /* Arbitrary value passed to cmd. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - return Tcl_NRCallObjProc(interp, NRNamespaceInscopeCmd, clientData, objc, - objv); -} - -static int -NRNamespaceInscopeCmd( - ClientData dummy, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - Tcl_Namespace *namespacePtr; - CallFrame *framePtr, **framePtrPtr; - int i; - Tcl_Obj *cmdObjPtr; - - if (objc < 3) { - Tcl_WrongNumArgs(interp, 1, objv, "name arg ?arg...?"); - return TCL_ERROR; - } - - /* - * Resolve the namespace reference. - */ - - if (TclGetNamespaceFromObj(interp, objv[1], &namespacePtr) != TCL_OK) { - return TCL_ERROR; - } - - /* - * Make the specified namespace the current namespace. - */ - - framePtrPtr = &framePtr; /* This is needed to satisfy GCC's - * strict aliasing rules. */ - (void) TclPushStackFrame(interp, (Tcl_CallFrame **) framePtrPtr, - namespacePtr, /*isProcCallFrame*/ 0); - - framePtr->objv = TclFetchEnsembleRoot(interp, objv, objc, &framePtr->objc); - - /* - * 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 == 3) { - cmdObjPtr = objv[2]; - } else { - Tcl_Obj *concatObjv[2]; - register Tcl_Obj *listPtr; - - listPtr = Tcl_NewListObj(0, NULL); - for (i = 3; i < objc; i++) { - if (Tcl_ListObjAppendElement(interp, listPtr, objv[i]) != TCL_OK){ - Tcl_DecrRefCount(listPtr); /* Free unneeded obj. */ - return TCL_ERROR; - } - } - - concatObjv[0] = objv[2]; - concatObjv[1] = listPtr; - cmdObjPtr = Tcl_ConcatObj(2, concatObjv); - Tcl_DecrRefCount(listPtr); /* We're done with the list object. */ - } - - TclNRAddCallback(interp, NsEval_Callback, namespacePtr, "inscope", - NULL, NULL); - return TclNREvalObjEx(interp, cmdObjPtr, 0, NULL, 0); -} - -/* - *---------------------------------------------------------------------- - * - * 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 function returns TCL_OK if - * successful, and TCL_ERROR if anything goes wrong. - * - * Side effects: - * If anything goes wrong, this function returns an error message in the - * interpreter's result object. - * - *---------------------------------------------------------------------- - */ - -static int -NamespaceOriginCmd( - ClientData dummy, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - Tcl_Command command, origCommand; - Tcl_Obj *resultPtr; - - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "name"); - return TCL_ERROR; - } - - command = Tcl_GetCommandFromObj(interp, objv[1]); - if (command == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "invalid command name \"%s\"", TclGetString(objv[1]))); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "COMMAND", - TclGetString(objv[1]), NULL); - return TCL_ERROR; - } - origCommand = TclGetOriginalCommand(command); - TclNewObj(resultPtr); - if (origCommand == 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, resultPtr); - } else { - Tcl_GetCommandFullName(interp, origCommand, resultPtr); - } - Tcl_SetObjResult(interp, resultPtr); - 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( - ClientData dummy, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - Tcl_Namespace *nsPtr; - - if (objc == 1) { - nsPtr = TclGetCurrentNamespace(interp); - } else if (objc == 2) { - if (TclGetNamespaceFromObj(interp, objv[1], &nsPtr) != TCL_OK) { - return TCL_ERROR; - } - } else { - Tcl_WrongNumArgs(interp, 1, objv, "?name?"); - return TCL_ERROR; - } - - /* - * Report the parent of the specified namespace. - */ - - if (nsPtr->parentPtr != NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - nsPtr->parentPtr->fullName, -1)); - } - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * NamespacePathCmd -- - * - * Invoked to implement the "namespace path" command that reads and - * writes the current namespace's command resolution path. Has one - * optional argument: if present, it is a list of named namespaces to set - * the path to, and if absent, the current path should be returned. - * Handles the following syntax: - * - * namespace path ?nsList? - * - * Results: - * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong - * (most notably if the namespace list contains the name of something - * other than a namespace). In the successful-exit case, may set the - * interpreter result to the list of names of the namespaces on the - * current namespace's path. - * - * Side effects: - * May update the namespace path (triggering a recomputing of all command - * names that depend on the namespace for resolution). - * - *---------------------------------------------------------------------- - */ - -static int -NamespacePathCmd( - ClientData dummy, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - Namespace *nsPtr = (Namespace *) TclGetCurrentNamespace(interp); - int i, nsObjc, result = TCL_ERROR; - Tcl_Obj **nsObjv; - Tcl_Namespace **namespaceList = NULL; - - if (objc > 2) { - Tcl_WrongNumArgs(interp, 1, objv, "?pathList?"); - return TCL_ERROR; - } - - /* - * If no path is given, return the current path. - */ - - if (objc == 1) { - Tcl_Obj *resultObj = Tcl_NewObj(); - - for (i=0 ; i<nsPtr->commandPathLength ; i++) { - if (nsPtr->commandPathArray[i].nsPtr != NULL) { - Tcl_ListObjAppendElement(NULL, resultObj, Tcl_NewStringObj( - nsPtr->commandPathArray[i].nsPtr->fullName, -1)); - } - } - Tcl_SetObjResult(interp, resultObj); - return TCL_OK; - } - - /* - * There is a path given, so parse it into an array of namespace pointers. - */ - - if (TclListObjGetElements(interp, objv[1], &nsObjc, &nsObjv) != TCL_OK) { - goto badNamespace; - } - if (nsObjc != 0) { - namespaceList = TclStackAlloc(interp, - sizeof(Tcl_Namespace *) * nsObjc); - - for (i=0 ; i<nsObjc ; i++) { - if (TclGetNamespaceFromObj(interp, nsObjv[i], - &namespaceList[i]) != TCL_OK) { - goto badNamespace; - } - } - } - - /* - * Now we have the list of valid namespaces, install it as the path. - */ - - TclSetNsPath(nsPtr, nsObjc, namespaceList); - - result = TCL_OK; - badNamespace: - if (namespaceList != NULL) { - TclStackFree(interp, namespaceList); - } - return result; -} - -/* - *---------------------------------------------------------------------- - * - * TclSetNsPath -- - * - * Sets the namespace command name resolution path to the given list of - * namespaces. If the list is empty (of zero length) the path is set to - * empty and the default old-style behaviour of command name resolution - * is used. - * - * Results: - * nothing - * - * Side effects: - * Invalidates the command name resolution caches for any command - * resolved in the given namespace. - * - *---------------------------------------------------------------------- - */ - -void -TclSetNsPath( - Namespace *nsPtr, /* Namespace whose path is to be set. */ - int pathLength, /* Length of pathAry. */ - Tcl_Namespace *pathAry[]) /* Array of namespaces that are the path. */ -{ - if (pathLength != 0) { - NamespacePathEntry *tmpPathArray = - ckalloc(sizeof(NamespacePathEntry) * pathLength); - int i; - - for (i=0 ; i<pathLength ; i++) { - tmpPathArray[i].nsPtr = (Namespace *) pathAry[i]; - tmpPathArray[i].creatorNsPtr = nsPtr; - tmpPathArray[i].prevPtr = NULL; - tmpPathArray[i].nextPtr = - tmpPathArray[i].nsPtr->commandPathSourceList; - if (tmpPathArray[i].nextPtr != NULL) { - tmpPathArray[i].nextPtr->prevPtr = &tmpPathArray[i]; - } - tmpPathArray[i].nsPtr->commandPathSourceList = &tmpPathArray[i]; - } - if (nsPtr->commandPathLength != 0) { - UnlinkNsPath(nsPtr); - } - nsPtr->commandPathArray = tmpPathArray; - } else { - if (nsPtr->commandPathLength != 0) { - UnlinkNsPath(nsPtr); - } - } - - nsPtr->commandPathLength = pathLength; - nsPtr->cmdRefEpoch++; - nsPtr->resolverEpoch++; -} - -/* - *---------------------------------------------------------------------- - * - * UnlinkNsPath -- - * - * Delete the given namespace's command name resolution path. Only call - * if the path is non-empty. Caller must reset the counter containing the - * path size. - * - * Results: - * nothing - * - * Side effects: - * Deletes the array of path entries and unlinks those path entries from - * the target namespace's list of interested namespaces. - * - *---------------------------------------------------------------------- - */ - -static void -UnlinkNsPath( - Namespace *nsPtr) -{ - int i; - for (i=0 ; i<nsPtr->commandPathLength ; i++) { - NamespacePathEntry *nsPathPtr = &nsPtr->commandPathArray[i]; - - if (nsPathPtr->prevPtr != NULL) { - nsPathPtr->prevPtr->nextPtr = nsPathPtr->nextPtr; - } - if (nsPathPtr->nextPtr != NULL) { - nsPathPtr->nextPtr->prevPtr = nsPathPtr->prevPtr; - } - if (nsPathPtr->nsPtr != NULL) { - if (nsPathPtr->nsPtr->commandPathSourceList == nsPathPtr) { - nsPathPtr->nsPtr->commandPathSourceList = nsPathPtr->nextPtr; - } - } - } - ckfree(nsPtr->commandPathArray); -} - -/* - *---------------------------------------------------------------------- - * - * TclInvalidateNsPath -- - * - * Invalidate the name resolution caches for all names looked up in - * namespaces whose name path includes the given namespace. - * - * Results: - * nothing - * - * Side effects: - * Increments the command reference epoch in each namespace whose path - * includes the given namespace. This causes any cached resolved names - * whose root cacheing context starts at that namespace to be recomputed - * the next time they are used. - * - *---------------------------------------------------------------------- - */ - -void -TclInvalidateNsPath( - Namespace *nsPtr) -{ - NamespacePathEntry *nsPathPtr = nsPtr->commandPathSourceList; - - while (nsPathPtr != NULL) { - if (nsPathPtr->nsPtr != NULL) { - nsPathPtr->creatorNsPtr->cmdRefEpoch++; - } - nsPathPtr = nsPathPtr->nextPtr; - } -} - -/* - *---------------------------------------------------------------------- - * - * 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( - ClientData dummy, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - register const char *name, *p; - int length; - - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "string"); - return TCL_ERROR; - } - - /* - * Find the end of the string, then work backward and find the start of - * the last "::" qualifier. - */ - - name = TclGetString(objv[1]); - 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_SetObjResult(interp, Tcl_NewStringObj(name, length)); - } - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * NamespaceUnknownCmd -- - * - * Invoked to implement the "namespace unknown" command (TIP 181) that - * sets or queries a per-namespace unknown command handler. This handler - * is called when command lookup fails (current and global ns). The - * default handler for the global namespace is ::unknown. The default - * handler for other namespaces is to call the global namespace unknown - * handler. Passing an empty list results in resetting the handler to its - * default. - * - * namespace unknown ?handler? - * - * Results: - * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. - * - * Side effects: - * If no handler is specified, returns a result in the interpreter's - * result object, otherwise it sets the unknown handler pointer in the - * current namespace to the script fragment provided. If anything goes - * wrong, the result is an error message. - * - *---------------------------------------------------------------------- - */ - -static int -NamespaceUnknownCmd( - ClientData dummy, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - Tcl_Namespace *currNsPtr; - Tcl_Obj *resultPtr; - int rc; - - if (objc > 2) { - Tcl_WrongNumArgs(interp, 1, objv, "?script?"); - return TCL_ERROR; - } - - currNsPtr = TclGetCurrentNamespace(interp); - - if (objc == 1) { - /* - * Introspection - return the current namespace handler. - */ - - resultPtr = Tcl_GetNamespaceUnknownHandler(interp, currNsPtr); - if (resultPtr == NULL) { - TclNewObj(resultPtr); - } - Tcl_SetObjResult(interp, resultPtr); - } else { - rc = Tcl_SetNamespaceUnknownHandler(interp, currNsPtr, objv[1]); - if (rc == TCL_OK) { - Tcl_SetObjResult(interp, objv[1]); - } - return rc; - } - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_GetNamespaceUnknownHandler -- - * - * Returns the unknown command handler registered for the given - * namespace. - * - * Results: - * Returns the current unknown command handler, or NULL if none exists - * for the namespace. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -Tcl_Obj * -Tcl_GetNamespaceUnknownHandler( - Tcl_Interp *interp, /* The interpreter in which the namespace - * exists. */ - Tcl_Namespace *nsPtr) /* The namespace. */ -{ - Namespace *currNsPtr = (Namespace *) nsPtr; - - if (currNsPtr->unknownHandlerPtr == NULL && - currNsPtr == ((Interp *) interp)->globalNsPtr) { - /* - * Default handler for global namespace is "::unknown". For all other - * namespaces, it is NULL (which falls back on the global unknown - * handler). - */ - - TclNewLiteralStringObj(currNsPtr->unknownHandlerPtr, "::unknown"); - Tcl_IncrRefCount(currNsPtr->unknownHandlerPtr); - } - return currNsPtr->unknownHandlerPtr; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_SetNamespaceUnknownHandler -- - * - * Sets the unknown command handler for the given namespace to the - * command prefix passed. - * - * Results: - * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. - * - * Side effects: - * Sets the namespace unknown command handler. If the passed in handler - * is NULL or an empty list, then the handler is reset to its default. If - * an error occurs, then an error message is left in the interpreter - * result. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_SetNamespaceUnknownHandler( - Tcl_Interp *interp, /* Interpreter in which the namespace - * exists. */ - Tcl_Namespace *nsPtr, /* Namespace which is being updated. */ - Tcl_Obj *handlerPtr) /* The new handler, or NULL to reset. */ -{ - int lstlen = 0; - Namespace *currNsPtr = (Namespace *) nsPtr; - - /* - * Ensure that we check for errors *first* before we change anything. - */ - - if (handlerPtr != NULL) { - if (TclListObjLength(interp, handlerPtr, &lstlen) != TCL_OK) { - /* - * Not a list. - */ - - return TCL_ERROR; - } - if (lstlen > 0) { - /* - * We are going to be saving this handler. Increment the reference - * count before decrementing the refcount on the previous handler, - * so that nothing strange can happen if we are told to set the - * handler to the previous value. - */ - - Tcl_IncrRefCount(handlerPtr); - } - } - - /* - * Remove old handler next. - */ - - if (currNsPtr->unknownHandlerPtr != NULL) { - Tcl_DecrRefCount(currNsPtr->unknownHandlerPtr); - } - - /* - * Install the new handler. - */ - - if (lstlen > 0) { - /* - * Just store the handler. It already has the correct reference count. - */ - - currNsPtr->unknownHandlerPtr = handlerPtr; - } else { - /* - * If NULL or an empty list is passed, this resets to the default - * handler. - */ - - currNsPtr->unknownHandlerPtr = NULL; - } - 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( - ClientData dummy, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - register const char *name, *p; - - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "string"); - return TCL_ERROR; - } - - /* - * Find the end of the string, then work backward and find the last "::" - * qualifier. - */ - - name = TclGetString(objv[1]); - 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_SetObjResult(interp, Tcl_NewStringObj(p, -1)); - } - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * NamespaceUpvarCmd -- - * - * Invoked to implement the "namespace upvar" command, that creates - * variables in the current scope linked to variables in another - * namespace. Handles the following syntax: - * - * namespace upvar ns otherVar myVar ?otherVar myVar ...? - * - * Results: - * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. - * - * Side effects: - * Creates new variables in the current scope, linked to the - * corresponding variables in the stipulated nmamespace. If anything goes - * wrong, the result is an error message. - * - *---------------------------------------------------------------------- - */ - -static int -NamespaceUpvarCmd( - ClientData dummy, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - Interp *iPtr = (Interp *) interp; - Tcl_Namespace *nsPtr, *savedNsPtr; - Var *otherPtr, *arrayPtr; - const char *myName; - - if (objc < 2 || (objc & 1)) { - Tcl_WrongNumArgs(interp, 1, objv, "ns ?otherVar myVar ...?"); - return TCL_ERROR; - } - - if (TclGetNamespaceFromObj(interp, objv[1], &nsPtr) != TCL_OK) { - return TCL_ERROR; - } - - objc -= 2; - objv += 2; - - for (; objc>0 ; objc-=2, objv+=2) { - /* - * Locate the other variable. - */ - - savedNsPtr = (Tcl_Namespace *) iPtr->varFramePtr->nsPtr; - iPtr->varFramePtr->nsPtr = (Namespace *) nsPtr; - otherPtr = TclObjLookupVarEx(interp, objv[0], NULL, - (TCL_NAMESPACE_ONLY|TCL_LEAVE_ERR_MSG|TCL_AVOID_RESOLVERS), - "access", /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr); - iPtr->varFramePtr->nsPtr = (Namespace *) savedNsPtr; - if (otherPtr == NULL) { - return TCL_ERROR; - } - - /* - * Create the new variable and link it to otherPtr. - */ - - myName = TclGetString(objv[1]); - if (TclPtrMakeUpvar(interp, otherPtr, myName, 0, -1) != TCL_OK) { - return TCL_ERROR; - } - } - - 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( - ClientData dummy, /* Not used. */ - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - static const char *const opts[] = { - "-command", "-variable", NULL - }; - int lookupType = 0; - Tcl_Obj *resultPtr; - - if (objc < 2 || objc > 3) { - badArgs: - Tcl_WrongNumArgs(interp, 1, objv, "?-command? ?-variable? name"); - return TCL_ERROR; - } else if (objc == 3) { - /* - * Look for a flag controlling the lookup. - */ - - if (Tcl_GetIndexFromObj(interp, objv[1], opts, "option", 0, - &lookupType) != TCL_OK) { - /* - * Preserve old style of error message! - */ - - Tcl_ResetResult(interp); - goto badArgs; - } - } - - TclNewObj(resultPtr); - switch (lookupType) { - case 0: { /* -command */ - Tcl_Command cmd = Tcl_GetCommandFromObj(interp, objv[objc-1]); - - if (cmd != NULL) { - Tcl_GetCommandFullName(interp, cmd, resultPtr); - } - break; - } - case 1: { /* -variable */ - Tcl_Var var = Tcl_FindNamespaceVar(interp, - TclGetString(objv[objc-1]), NULL, /*flags*/ 0); - - if (var != NULL) { - Tcl_GetVariableFullName(interp, var, resultPtr); - } - break; - } - } - Tcl_SetObjResult(interp, resultPtr); - 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( - register Tcl_Obj *objPtr) /* nsName object with internal representation - * to free. */ -{ - ResolvedNsName *resNamePtr = objPtr->internalRep.twoPtrValue.ptr1; - - /* - * Decrement the reference count of the namespace. If there are no more - * references, free it up. - */ - - 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. - */ - - TclNsDecrRefCount(resNamePtr->nsPtr); - ckfree(resNamePtr); - } - objPtr->typePtr = NULL; -} - -/* - *---------------------------------------------------------------------- - * - * 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( - Tcl_Obj *srcPtr, /* Object with internal rep to copy. */ - register Tcl_Obj *copyPtr) /* Object with internal rep to set. */ -{ - ResolvedNsName *resNamePtr = srcPtr->internalRep.twoPtrValue.ptr1; - - copyPtr->internalRep.twoPtrValue.ptr1 = resNamePtr; - resNamePtr->refCount++; - copyPtr->typePtr = &nsNameType; -} - -/* - *---------------------------------------------------------------------- - * - * 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( - 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. */ -{ - const char *dummy; - Namespace *nsPtr, *dummy1Ptr, *dummy2Ptr; - register ResolvedNsName *resNamePtr; - const char *name; - - if (interp == NULL) { - return TCL_ERROR; - } - - name = TclGetString(objPtr); - TclGetNamespaceForQualName(interp, name, NULL, TCL_FIND_ONLY_NS, - &nsPtr, &dummy1Ptr, &dummy2Ptr, &dummy); - - /* - * If we found a namespace, then create a new ResolvedNsName structure - * that holds a reference to it. - */ - - if ((nsPtr == NULL) || (nsPtr->flags & NS_DYING)) { - /* - * Our failed lookup proves any previously cached nsName intrep is no - * longer valid. Get rid of it so we no longer waste memory storing - * it, nor time determining its invalidity again and again. - */ - - if (objPtr->typePtr == &nsNameType) { - TclFreeIntRep(objPtr); - } - return TCL_ERROR; - } - - nsPtr->refCount++; - resNamePtr = ckalloc(sizeof(ResolvedNsName)); - resNamePtr->nsPtr = nsPtr; - if ((name[0] == ':') && (name[1] == ':')) { - resNamePtr->refNsPtr = NULL; - } else { - resNamePtr->refNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp); - } - resNamePtr->refCount = 1; - TclFreeIntRep(objPtr); - objPtr->internalRep.twoPtrValue.ptr1 = resNamePtr; - objPtr->typePtr = &nsNameType; - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * TclGetNamespaceCommandTable -- - * - * Returns the hash table of commands. - * - * Results: - * Pointer to the hash table. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -Tcl_HashTable * -TclGetNamespaceCommandTable( - Tcl_Namespace *nsPtr) -{ - return &((Namespace *) nsPtr)->cmdTable; -} - -/* - *---------------------------------------------------------------------- - * - * TclGetNamespaceChildTable -- - * - * Returns the hash table of child namespaces. - * - * Results: - * Pointer to the hash table. - * - * Side effects: - * Might allocate memory. - * - *---------------------------------------------------------------------- - */ - -Tcl_HashTable * -TclGetNamespaceChildTable( - Tcl_Namespace *nsPtr) -{ - Namespace *nPtr = (Namespace *) nsPtr; -#ifndef BREAK_NAMESPACE_COMPAT - return &nPtr->childTable; -#else - if (nPtr->childTablePtr == NULL) { - nPtr->childTablePtr = ckalloc(sizeof(Tcl_HashTable)); - Tcl_InitHashTable(nPtr->childTablePtr, TCL_STRING_KEYS); - } - return nPtr->childTablePtr; -#endif -} - -/* - *---------------------------------------------------------------------- - * - * TclLogCommandInfo -- - * - * This function is invoked after an error occurs in an interpreter. It - * adds information to iPtr->errorInfo/errorStack fields to describe the - * command that was being executed when the error occurred. When pc and - * tosPtr are non-NULL, conveying a bytecode execution "inner context", - * and the offending instruction is suitable, that inner context is - * recorded in errorStack. - * - * Results: - * None. - * - * Side effects: - * Information about the command is added to errorInfo/errorStack and the - * line number stored internally in the interpreter is set. - * - *---------------------------------------------------------------------- - */ - -void -TclLogCommandInfo( - Tcl_Interp *interp, /* Interpreter in which to log information. */ - const char *script, /* First character in script containing - * command (must be <= command). */ - const char *command, /* First character in command that generated - * the error. */ - int length, /* Number of bytes in command (-1 means use - * all bytes up to first null byte). */ - const unsigned char *pc, /* Current pc of bytecode execution context */ - Tcl_Obj **tosPtr) /* Current stack of bytecode execution - * context */ -{ - register const char *p; - Interp *iPtr = (Interp *) interp; - int overflow, limit = 150; - Var *varPtr, *arrayPtr; - - if (iPtr->flags & ERR_ALREADY_LOGGED) { - /* - * Someone else has already logged error information for this command; - * we shouldn't add anything more. - */ - - return; - } - - if (command != NULL) { - /* - * Compute the line number where the error occurred. - */ - - iPtr->errorLine = 1; - for (p = script; p != command; p++) { - if (*p == '\n') { - iPtr->errorLine++; - } - } - - if (length < 0) { - length = strlen(command); - } - overflow = (length > limit); - Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( - "\n %s\n\"%.*s%s\"", ((iPtr->errorInfo == NULL) - ? "while executing" : "invoked from within"), - (overflow ? limit : length), command, - (overflow ? "..." : ""))); - - varPtr = TclObjLookupVarEx(interp, iPtr->eiVar, NULL, TCL_GLOBAL_ONLY, - NULL, 0, 0, &arrayPtr); - if ((varPtr == NULL) || !TclIsVarTraced(varPtr)) { - /* - * Should not happen. - */ - - return; - } else { - Tcl_HashEntry *hPtr - = Tcl_FindHashEntry(&iPtr->varTraces, (char *) varPtr); - VarTrace *tracePtr = Tcl_GetHashValue(hPtr); - - if (tracePtr->traceProc != EstablishErrorInfoTraces) { - /* - * The most recent trace set on ::errorInfo is not the one the - * core itself puts on last. This means some other code is - * tracing the variable, and the additional trace(s) might be - * write traces that expect the timing of writes to - * ::errorInfo that existed Tcl releases before 8.5. To - * satisfy that compatibility need, we write the current - * -errorinfo value to the ::errorInfo variable. - */ - - Tcl_ObjSetVar2(interp, iPtr->eiVar, NULL, iPtr->errorInfo, - TCL_GLOBAL_ONLY); - } - } - } - - /* - * TIP #348 - */ - - if (Tcl_IsShared(iPtr->errorStack)) { - Tcl_Obj *newObj; - - newObj = Tcl_DuplicateObj(iPtr->errorStack); - Tcl_DecrRefCount(iPtr->errorStack); - Tcl_IncrRefCount(newObj); - iPtr->errorStack = newObj; - } - if (iPtr->resetErrorStack) { - int len; - - iPtr->resetErrorStack = 0; - Tcl_ListObjLength(interp, iPtr->errorStack, &len); - - /* - * Reset while keeping the list intrep as much as possible. - */ - - Tcl_ListObjReplace(interp, iPtr->errorStack, 0, len, 0, NULL); - if (pc != NULL) { - Tcl_Obj *innerContext; - - innerContext = TclGetInnerContext(interp, pc, tosPtr); - if (innerContext != NULL) { - Tcl_ListObjAppendElement(NULL, iPtr->errorStack, - iPtr->innerLiteral); - Tcl_ListObjAppendElement(NULL, iPtr->errorStack, innerContext); - } - } else if (command != NULL) { - Tcl_ListObjAppendElement(NULL, iPtr->errorStack, - iPtr->innerLiteral); - Tcl_ListObjAppendElement(NULL, iPtr->errorStack, - Tcl_NewStringObj(command, length)); - } - } - - if (!iPtr->framePtr->objc) { - /* - * Special frame, nothing to report. - */ - } else if (iPtr->varFramePtr != iPtr->framePtr) { - /* - * uplevel case, [lappend errorstack UP $relativelevel] - */ - - Tcl_ListObjAppendElement(NULL, iPtr->errorStack, iPtr->upLiteral); - Tcl_ListObjAppendElement(NULL, iPtr->errorStack, Tcl_NewIntObj( - iPtr->framePtr->level - iPtr->varFramePtr->level)); - } else if (iPtr->framePtr != iPtr->rootFramePtr) { - /* - * normal case, [lappend errorstack CALL [info level 0]] - */ - - Tcl_ListObjAppendElement(NULL, iPtr->errorStack, iPtr->callLiteral); - Tcl_ListObjAppendElement(NULL, iPtr->errorStack, Tcl_NewListObj( - iPtr->framePtr->objc, iPtr->framePtr->objv)); - } -} - -/* - *---------------------------------------------------------------------- - * - * TclErrorStackResetIf -- - * - * The TIP 348 reset/no-bc part of TLCI, for specific use by - * TclCompileSyntaxError. - * - * Results: - * None. - * - * Side effects: - * Reset errorstack if it needs be, and in that case remember the - * passed-in error message as inner context. - * - *---------------------------------------------------------------------- - */ - -void -TclErrorStackResetIf( - Tcl_Interp *interp, - const char *msg, - int length) -{ - Interp *iPtr = (Interp *) interp; - - if (Tcl_IsShared(iPtr->errorStack)) { - Tcl_Obj *newObj; - - newObj = Tcl_DuplicateObj(iPtr->errorStack); - Tcl_DecrRefCount(iPtr->errorStack); - Tcl_IncrRefCount(newObj); - iPtr->errorStack = newObj; - } - if (iPtr->resetErrorStack) { - int len; - - iPtr->resetErrorStack = 0; - Tcl_ListObjLength(interp, iPtr->errorStack, &len); - - /* - * Reset while keeping the list intrep as much as possible. - */ - - Tcl_ListObjReplace(interp, iPtr->errorStack, 0, len, 0, NULL); - Tcl_ListObjAppendElement(NULL, iPtr->errorStack, iPtr->innerLiteral); - Tcl_ListObjAppendElement(NULL, iPtr->errorStack, - Tcl_NewStringObj(msg, length)); - } -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_LogCommandInfo -- - * - * This function is invoked after an error occurs in an interpreter. It - * adds information to iPtr->errorInfo/errorStack fields to describe the - * command that was being executed when the error occurred. - * - * Results: - * None. - * - * Side effects: - * Information about the command is added to errorInfo/errorStack and the - * line number stored internally in the interpreter is set. - * - *---------------------------------------------------------------------- - */ - -void -Tcl_LogCommandInfo( - Tcl_Interp *interp, /* Interpreter in which to log information. */ - const char *script, /* First character in script containing - * command (must be <= command). */ - const char *command, /* First character in command that generated - * the error. */ - int length) /* Number of bytes in command (-1 means use - * all bytes up to first null byte). */ -{ - TclLogCommandInfo(interp, script, command, length, NULL, NULL); -} - - -/* - * Local Variables: - * mode: c - * c-basic-offset: 4 - * fill-column: 78 - * tab-width: 8 - * End: - */ |