From 1bac432efb8aafa278e4fff4f2e366fc889d1096 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Mon, 16 Dec 2024 09:36:17 +0000 Subject: [PATCH 1/2] Remove experimental (and untested) code related to BREAK_NAMESPACE_COMPAT. Backport some layout changes --- generic/tclInt.h | 16 ---- generic/tclNamesp.c | 192 +++++++++++++------------------------------ generic/tclResolve.c | 17 +--- 3 files changed, 58 insertions(+), 167 deletions(-) diff --git a/generic/tclInt.h b/generic/tclInt.h index 5a639e310d4d..987611b79641 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -213,15 +213,6 @@ typedef struct TclVarHashTable { #define TclVarHashFindVar(tablePtr, key) \ TclVarHashCreateVar((tablePtr), (key), NULL) -/* - * Define this to reduce the amount of space that the average namespace - * consumes by only allocating the table of child namespaces when necessary. - * Defining it breaks compatibility for Tcl extensions (e.g., itcl) which - * reach directly into the Namespace structure. - */ - -#undef BREAK_NAMESPACE_COMPAT - /* * The structure below defines a namespace. * Note: the first five fields must match exactly the fields in a @@ -244,15 +235,8 @@ typedef struct Namespace { struct Namespace *parentPtr;/* Points to the namespace that contains this * one. NULL if this is the global * namespace. */ -#ifndef BREAK_NAMESPACE_COMPAT Tcl_HashTable childTable; /* Contains any child namespaces. Indexed by * strings; values have type (Namespace *). */ -#else - Tcl_HashTable *childTablePtr; - /* Contains any child namespaces. Indexed by - * strings; values have type (Namespace *). If - * NULL, there are no children. */ -#endif long nsId; /* Unique id for the namespace. */ Tcl_Interp *interp; /* The interpreter containing this * namespace. */ diff --git a/generic/tclNamesp.c b/generic/tclNamesp.c index 64551251f88f..fa871cccf723 100644 --- a/generic/tclNamesp.c +++ b/generic/tclNamesp.c @@ -204,7 +204,7 @@ TclInitNamespaceSubsystem(void) Tcl_Namespace * Tcl_GetCurrentNamespace( - Tcl_Interp *interp)/* Interpreter whose current namespace is + Tcl_Interp *interp) /* Interpreter whose current namespace is * being queried. */ { return TclGetCurrentNamespace(interp); @@ -228,7 +228,7 @@ Tcl_GetCurrentNamespace( Tcl_Namespace * Tcl_GetGlobalNamespace( - Tcl_Interp *interp)/* Interpreter whose global namespace should + Tcl_Interp *interp) /* Interpreter whose global namespace should * be returned. */ { return TclGetGlobalNamespace(interp); @@ -527,8 +527,10 @@ ErrorCodeRead( return NULL; } if (NULL == Tcl_ObjGetVar2(interp, iPtr->ecVar, NULL, TCL_GLOBAL_ONLY)) { + Tcl_Obj *objPtr; + TclNewObj(objPtr); Tcl_ObjSetVar2(interp, iPtr->ecVar, NULL, - Tcl_NewObj(), TCL_GLOBAL_ONLY); + objPtr, TCL_GLOBAL_ONLY); } return NULL; } @@ -601,8 +603,10 @@ ErrorInfoRead( return NULL; } if (NULL == Tcl_ObjGetVar2(interp, iPtr->eiVar, NULL, TCL_GLOBAL_ONLY)) { + Tcl_Obj *objPtr; + TclNewObj(objPtr); Tcl_ObjSetVar2(interp, iPtr->eiVar, NULL, - Tcl_NewObj(), TCL_GLOBAL_ONLY); + objPtr, TCL_GLOBAL_ONLY); } return NULL; } @@ -729,12 +733,7 @@ Tcl_CreateNamespace( */ 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)); @@ -758,11 +757,7 @@ Tcl_CreateNamespace( 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; @@ -970,21 +965,21 @@ Tcl_DeleteNamespace( } /* - * If the namespace is on the call frame stack, it is marked as "dying" - * (NS_DYING is OR'd into its flags): Contents of the namespace are - * still available and visible until the namespace is later marked as - * NS_DEAD, and its commands and variables are still usable by any - * active call frames referring to th namespace. When all active call - * frames referring to the namespace have been popped from the Tcl - * stack, Tcl_PopCallFrame calls Tcl_DeleteNamespace again. 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 the namespace is on the call frame stack, it is marked as "dying" + * (NS_DYING is OR'd into its flags): Contents of the namespace are + * still available and visible until the namespace is later marked as + * NS_DEAD, and its commands and variables are still usable by any + * active call frames referring to th namespace. When all active call + * frames referring to the namespace have been popped from the Tcl + * stack, Tcl_PopCallFrame calls Tcl_DeleteNamespace again. 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)) { @@ -1007,7 +1002,7 @@ Tcl_DeleteNamespace( * being deleted, ignore any second call. */ - nsPtr->flags |= (NS_DYING|NS_KILLED); + nsPtr->flags |= (NS_DYING | NS_KILLED); TclTeardownNamespace(nsPtr); @@ -1021,14 +1016,7 @@ Tcl_DeleteNamespace( 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); nsPtr ->flags |= NS_DEAD; @@ -1178,7 +1166,6 @@ TclTeardownNamespace( * Important: leave the hash table itself still live. */ -#ifndef BREAK_NAMESPACE_COMPAT while (nsPtr->childTable.numEntries > 0) { int length = nsPtr->childTable.numEntries; Namespace **children = (Namespace **)TclStackAlloc(interp, @@ -1198,29 +1185,6 @@ TclTeardownNamespace( } TclStackFree(interp, children); } -#else - if (nsPtr->childTablePtr != NULL) { - while (nsPtr->childTablePtr->numEntries > 0) { - int length = nsPtr->childTablePtr->numEntries; - Namespace **children = TclStackAlloc(interp, - 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(interp, children); - } - } -#endif /* * Free the namespace's export pattern array. @@ -1274,7 +1238,7 @@ TclTeardownNamespace( static void NamespaceFree( - Namespace *nsPtr) /* Points to the namespace to free. */ + Namespace *nsPtr) /* Points to the namespace to free. */ { /* * Most of the namespace's contents are freed when the namespace is @@ -1472,7 +1436,8 @@ Tcl_Export( int Tcl_AppendExportList( - Tcl_Interp *interp, /* Interpreter used for error reporting. */ + Tcl_Interp *interp, /* Interpreter used for global NS and error + * reporting. */ Tcl_Namespace *namespacePtr,/* Points to the namespace whose export * pattern list is appended onto objPtr. NULL * for the current namespace. */ @@ -1573,7 +1538,7 @@ Tcl_Import( * want absence of the command to be a failure case. */ - if (Tcl_FindCommand(interp,"auto_import",NULL,TCL_GLOBAL_ONLY) != NULL) { + if (Tcl_FindCommand(interp, "auto_import", NULL, TCL_GLOBAL_ONLY) != NULL) { Tcl_Obj *objv[2]; int result; @@ -1921,7 +1886,7 @@ Tcl_ForgetImport( } origin = firstToken; } - if (Tcl_StringMatch(Tcl_GetCommandName(NULL, origin), simplePattern)){ + if (Tcl_StringMatch(Tcl_GetCommandName(NULL, origin), simplePattern)) { Tcl_DeleteCommandFromToken(interp, token); } } @@ -1957,14 +1922,13 @@ TclGetOriginalCommand( * command should be returned. */ { Command *cmdPtr = (Command *) command; - ImportedCmdData *dataPtr; if (cmdPtr->deleteProc != DeleteImportedCmd) { return NULL; } while (cmdPtr->deleteProc == DeleteImportedCmd) { - dataPtr = (ImportedCmdData *)cmdPtr->objClientData; + ImportedCmdData *dataPtr = (ImportedCmdData *)cmdPtr->objClientData; cmdPtr = dataPtr->realCmdPtr; } return (Tcl_Command) cmdPtr; @@ -2107,7 +2071,7 @@ DeleteImportedCmd( * 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, + * Tcl_CreateObjCommand 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 @@ -2214,13 +2178,13 @@ TclGetNamespaceForQualName( start = qualName; /* Points to start of qualifying * namespace. */ - if ((*qualName == ':') && (*(qualName+1) == ':')) { - start = qualName+2; /* Skip over the initial :: */ - while (*start == ':') { + if ((qualName[0] == ':') && (qualName[1] == ':')) { + start = qualName + 2; /* Skip over the initial :: */ + while (start[0] == ':') { start++; /* Skip over a subsequent : */ } nsPtr = globalNsPtr; - if (*start == '\0') { /* qualName is just two or more + if (start[0] == '\0') { /* qualName is just two or more * ":"s. */ *nsPtrPtr = globalNsPtr; *altNsPtrPtr = NULL; @@ -2260,7 +2224,7 @@ TclGetNamespaceForQualName( len = 0; for (end = start; *end != '\0'; end++) { - if ((*end == ':') && (*(end+1) == ':')) { + if ((end[0] == ':') && (end[1] == ':')) { end += 2; /* Skip over the initial :: */ while (*end == ':') { end++; /* Skip over the subsequent : */ @@ -2270,7 +2234,7 @@ TclGetNamespaceForQualName( len++; } - if (*end=='\0' && !(end-start>=2 && *(end-1)==':' && *(end-2)==':')) { + if (end[0]=='\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 @@ -2301,19 +2265,11 @@ TclGetNamespaceForQualName( * 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. + * Tcl_CreateObjCommand 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 = (Namespace *)Tcl_GetHashValue(entryPtr); } else if (flags & TCL_CREATE_NS_IF_UNKNOWN) { @@ -2344,15 +2300,7 @@ TclGetNamespaceForQualName( */ 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 = (Namespace *)Tcl_GetHashValue(entryPtr); } else { @@ -2390,7 +2338,7 @@ TclGetNamespaceForQualName( * variable name, trailing "::"s refer to the cmd or var named {}. */ - if ((flags & TCL_FIND_ONLY_NS) || (end>start && *(end-1)!=':')) { + if ((flags & TCL_FIND_ONLY_NS) || (end>start && end[-1]!=':')) { *simpleNamePtr = NULL; /* Found namespace name. */ } else { *simpleNamePtr = end; /* Found cmd/var: points to empty @@ -2477,7 +2425,7 @@ Tcl_FindNamespace( * points to namespace in which to resolve * name; if NULL, look up name in the current * namespace. */ - int flags) /* Flags controlling namespace lookup: an OR'd + int flags) /* Flags controlling namespace lookup: an OR'd * combination of TCL_GLOBAL_ONLY and * TCL_LEAVE_ERR_MSG flags. */ { @@ -2683,7 +2631,7 @@ Tcl_FindCommand( } if (cmdPtr != NULL) { - cmdPtr->flags &= ~CMD_VIA_RESOLVER; + cmdPtr->flags &= ~CMD_VIA_RESOLVER; return (Tcl_Command) cmdPtr; } @@ -2777,17 +2725,8 @@ TclResetShadowedCmdRefs( 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 = (Namespace *)Tcl_GetHashValue(hPtr); } else { @@ -2992,7 +2931,7 @@ NamespaceChildrenCmd( if (objc == 1) { nsPtr = (Namespace *) TclGetCurrentNamespace(interp); } else if ((objc == 2) || (objc == 3)) { - if (TclGetNamespaceFromObj(interp, objv[1], &namespacePtr) != TCL_OK){ + if (TclGetNamespaceFromObj(interp, objv[1], &namespacePtr) != TCL_OK) { return TCL_ERROR; } nsPtr = (Namespace *) namespacePtr; @@ -3009,7 +2948,7 @@ NamespaceChildrenCmd( if (objc == 3) { const char *name = TclGetString(objv[2]); - if ((*name == ':') && (*(name+1) == ':')) { + if ((name[0] == ':') && (name[1] == ':')) { pattern = name; } else { Tcl_DStringAppend(&buffer, nsPtr->fullName, -1); @@ -3034,26 +2973,14 @@ NamespaceChildrenCmd( 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 = (Namespace *)Tcl_GetHashValue(entryPtr); if ((pattern == NULL) @@ -3710,7 +3637,7 @@ NamespaceImportCmd( if (cmdPtr->deleteProc == DeleteImportedCmd) { Tcl_ListObjAppendElement(NULL, listPtr, Tcl_NewStringObj( - (char *)Tcl_GetHashKey(&nsPtr->cmdTable, hPtr) ,-1)); + (char *)Tcl_GetHashKey(&nsPtr->cmdTable, hPtr), -1)); } } Tcl_SetObjResult(interp, listPtr); @@ -4039,8 +3966,8 @@ NamespacePathCmd( goto badNamespace; } if (nsObjc != 0) { - namespaceList = (Tcl_Namespace **)TclStackAlloc(interp, - sizeof(Tcl_Namespace *) * nsObjc); + namespaceList = (Tcl_Namespace **) + TclStackAlloc(interp, sizeof(Tcl_Namespace *) * nsObjc); for (i = 0; i < nsObjc; i++) { if (TclGetNamespaceFromObj(interp, nsObjv[i], @@ -4091,8 +4018,8 @@ TclSetNsPath( Tcl_Namespace *pathAry[]) /* Array of namespaces that are the path. */ { if (pathLength != 0) { - NamespacePathEntry *tmpPathArray = - (NamespacePathEntry *)ckalloc(sizeof(NamespacePathEntry) * pathLength); + NamespacePathEntry *tmpPathArray = (NamespacePathEntry *) + ckalloc(sizeof(NamespacePathEntry) * pathLength); int i; for (i=0 ; i= name) { - if ((*p == ':') && (p > name) && (*(p-1) == ':')) { + if ((p[0] == ':') && (p > name) && (p[-1] == ':')) { p -= 2; /* Back up over the :: */ - while ((p >= name) && (*p == ':')) { + while ((p >= name) && (p[0] == ':')) { p--; /* Back up over the preceding : */ } break; @@ -4501,7 +4428,7 @@ NamespaceTailCmd( /* empty body */ } while (--p > name) { - if ((*p == ':') && (*(p-1) == ':')) { + if ((p[0] == ':') && (p[-1] == ':')) { p++; /* Just after the last "::" */ break; } @@ -4687,7 +4614,7 @@ NamespaceWhichCmd( static void FreeNsNameInternalRep( - Tcl_Obj *objPtr) /* nsName object with internal representation + Tcl_Obj *objPtr) /* nsName object with internal representation * to free. */ { ResolvedNsName *resNamePtr = (ResolvedNsName *)objPtr->internalRep.twoPtrValue.ptr1; @@ -4732,7 +4659,7 @@ FreeNsNameInternalRep( static void DupNsNameInternalRep( Tcl_Obj *srcPtr, /* Object with internal rep to copy. */ - Tcl_Obj *copyPtr) /* Object with internal rep to set. */ + Tcl_Obj *copyPtr) /* Object with internal rep to set. */ { ResolvedNsName *resNamePtr = (ResolvedNsName *)srcPtr->internalRep.twoPtrValue.ptr1; @@ -4768,7 +4695,7 @@ SetNsNameFromAny( Tcl_Interp *interp, /* Points to the namespace in which to resolve * name. Also used for error reporting if not * NULL. */ - Tcl_Obj *objPtr) /* The object to convert. */ + Tcl_Obj *objPtr) /* The object to convert. */ { const char *dummy; Namespace *nsPtr, *dummy1Ptr, *dummy2Ptr; @@ -4860,15 +4787,7 @@ TclGetNamespaceChildTable( Tcl_Namespace *nsPtr) { Namespace *nPtr = (Namespace *) nsPtr; -#ifndef BREAK_NAMESPACE_COMPAT return &nPtr->childTable; -#else - if (nPtr->childTablePtr == NULL) { - nPtr->childTablePtr = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable)); - Tcl_InitHashTable(nPtr->childTablePtr, TCL_STRING_KEYS); - } - return nPtr->childTablePtr; -#endif } /* @@ -5117,7 +5036,6 @@ Tcl_LogCommandInfo( { TclLogCommandInfo(interp, script, command, length, NULL, NULL); } - /* * Local Variables: diff --git a/generic/tclResolve.c b/generic/tclResolve.c index 974737e5191c..a07565eca0dc 100644 --- a/generic/tclResolve.c +++ b/generic/tclResolve.c @@ -101,9 +101,9 @@ Tcl_AddInterpResolvers( * list, so that it overrides existing schemes. */ - resPtr = ckalloc(sizeof(ResolverScheme)); + resPtr = (ResolverScheme *)ckalloc(sizeof(ResolverScheme)); len = strlen(name) + 1; - resPtr->name = ckalloc(len); + resPtr->name = (char *)ckalloc(len); memcpy(resPtr->name, name, len); resPtr->cmdResProc = cmdProc; resPtr->varResProc = varProc; @@ -262,23 +262,12 @@ BumpCmdRefEpochs( nsPtr->cmdRefEpoch++; -#ifndef BREAK_NAMESPACE_COMPAT for (entry = Tcl_FirstHashEntry(&nsPtr->childTable, &search); entry != NULL; entry = Tcl_NextHashEntry(&search)) { - Namespace *childNsPtr = Tcl_GetHashValue(entry); + Namespace *childNsPtr = (Namespace *)Tcl_GetHashValue(entry); BumpCmdRefEpochs(childNsPtr); } -#else - if (nsPtr->childTablePtr != NULL) { - for (entry = Tcl_FirstHashEntry(nsPtr->childTablePtr, &search); - entry != NULL; entry = Tcl_NextHashEntry(&search)) { - Namespace *childNsPtr = Tcl_GetHashValue(entry); - - BumpCmdRefEpochs(childNsPtr); - } - } -#endif TclInvalidateNsPath(nsPtr); } From 1232c6e82140d7aa6cfee27d689b45a247518f3a Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Mon, 16 Dec 2024 09:59:12 +0000 Subject: [PATCH 2/2] Fix [63449c0514]: [namespace children] doesn't match non-glob patterns below the global namespace. --- changes | 2 ++ generic/tclNamesp.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/changes b/changes index 5c6f9facfea9..e51771cd4724 100644 --- a/changes +++ b/changes @@ -9382,3 +9382,5 @@ Many code fixes to avoid overflow or undefined behavior. Thanks chrstphrchvz. 2024-12-05 (bug) [0b809c] Segfault in TclOO with coroutines (goth,sebres) - Released 8.6.16, Dec 11, 2024 - details at https://core.tcl-lang.org/tcl/ - + +2024-12-16 (bug) [63449c] [namespace children] doesn't match non-glob patterns below the global namespace (stu) diff --git a/generic/tclNamesp.c b/generic/tclNamesp.c index fa871cccf723..bd0cf405e4ed 100644 --- a/generic/tclNamesp.c +++ b/generic/tclNamesp.c @@ -2972,8 +2972,11 @@ NamespaceChildrenCmd( if (strncmp(pattern, nsPtr->fullName, length) != 0) { goto searchDone; } + /* + * Global namespace members are prefixed with "::", others not. Ticket [63449c0514] + */ if ( - Tcl_FindHashEntry(&nsPtr->childTable, pattern+length) != NULL + Tcl_FindHashEntry(&nsPtr->childTable, (nsPtr != globalNsPtr ? 2 : 0) + pattern+length) != NULL ) { Tcl_ListObjAppendElement(interp, listPtr, Tcl_NewStringObj(pattern, -1));