summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/tclBasic.c14
-rw-r--r--generic/tclInt.h3
-rw-r--r--generic/tclNamesp.c20
-rw-r--r--tests/namespace.test16
4 files changed, 43 insertions, 10 deletions
diff --git a/generic/tclBasic.c b/generic/tclBasic.c
index ffd69c4..78685f0 100644
--- a/generic/tclBasic.c
+++ b/generic/tclBasic.c
@@ -4741,13 +4741,23 @@ TEOV_NotFound(
Namespace *dummyNsPtr;
const char *simpleName;
+ tryParentNS:
(void) TclGetNamespaceForQualName(interp, qualName, currNsPtr,
- TCL_NAMESPACE_ONLY, &currNsPtr, &dummyNsPtr, &dummyNsPtr,
- &simpleName);
+ TCL_NAMESPACE_ONLY | TCL_FIND_IF_NOT_SIMPLE, &currNsPtr,
+ &dummyNsPtr, &dummyNsPtr, &simpleName);
if ((currNsPtr == NULL) || (simpleName == NULL) ||
currNsPtr->unknownHandlerPtr == NULL ||
(currNsPtr->flags & (NS_DYING | NS_DEAD))
) {
+ /* traverse to alive parent namespace containing handler */
+ if (currNsPtr) {
+ qualName = currNsPtr->fullName;
+ qualLen = strlen(qualName);
+ if (qualLen > 2 && memchr(qualName, ':', qualLen)) {
+ currNsPtr = iPtr->globalNsPtr;
+ goto tryParentNS;
+ }
+ }
/* fallback to the global unknown */
currNsPtr = iPtr->globalNsPtr;
if (currNsPtr == NULL) {
diff --git a/generic/tclInt.h b/generic/tclInt.h
index 68c07f2..de92a7d 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -400,10 +400,13 @@ struct NamespacePathEntry {
* TCL_NAMESPACE_ONLY - (see tcl.h) Look only in the context ns.
* TCL_CREATE_NS_IF_UNKNOWN - Create unknown namespaces.
* TCL_FIND_ONLY_NS - The name sought is a namespace name.
+ * TCL_FIND_IF_NOT_SIMPLE - Retrieve last namespace even if the rest of
+ * name is not simple name (contains ::).
*/
#define TCL_CREATE_NS_IF_UNKNOWN 0x800
#define TCL_FIND_ONLY_NS 0x1000
+#define TCL_FIND_IF_NOT_SIMPLE 0x2000
/*
* The client data for an ensemble command. This consists of the table of
diff --git a/generic/tclNamesp.c b/generic/tclNamesp.c
index 290dcea..37092fe 100644
--- a/generic/tclNamesp.c
+++ b/generic/tclNamesp.c
@@ -2280,11 +2280,8 @@ TclGetNamespaceForQualName(
if (flags & TCL_FIND_ONLY_NS) {
nsName = start;
} else {
- *nsPtrPtr = nsPtr;
- *altNsPtrPtr = altNsPtr;
*simpleNamePtr = start;
- Tcl_DStringFree(&buffer);
- return TCL_OK;
+ goto done;
}
} else {
/*
@@ -2334,6 +2331,15 @@ TclGetNamespaceForQualName(
}
} else { /* Namespace not found and was not
* created. */
+ if (flags & TCL_FIND_IF_NOT_SIMPLE) {
+ /*
+ * return last found NS and not simple name relative it,
+ * e. g. ::A::B::C::D -> ::A::B and C::D, if
+ * namespace C cannot be found in ::A::B
+ */
+ *simpleNamePtr = start;
+ goto done;
+ }
nsPtr = NULL;
}
}
@@ -2364,11 +2370,8 @@ TclGetNamespaceForQualName(
*/
if ((nsPtr == NULL) && (altNsPtr == NULL)) {
- *nsPtrPtr = NULL;
- *altNsPtrPtr = NULL;
*simpleNamePtr = NULL;
- Tcl_DStringFree(&buffer);
- return TCL_OK;
+ goto done;
}
start = end;
@@ -2398,6 +2401,7 @@ TclGetNamespaceForQualName(
nsPtr = NULL;
}
+done:
*nsPtrPtr = nsPtr;
*altNsPtrPtr = altNsPtr;
Tcl_DStringFree(&buffer);
diff --git a/tests/namespace.test b/tests/namespace.test
index c8c1992..5a8f6f4 100644
--- a/tests/namespace.test
+++ b/tests/namespace.test
@@ -3056,6 +3056,22 @@ test namespace-52.13 {unknown: invocation outside of NS doesn't evade namespace
} -cleanup {
namespace delete ::foo
} -result {{::foo:bar:_unknown ::foo::bar xxx} {::foo:bar:_unknown ::foo bar::xxx} {::foo:bar:_unknown :: ::foo::bar::xxx} {::foo:bar:_unknown :: foo::bar::xxx}}
+test namespace-52.14 {unknown: invocation outside of NS doesn't evade namespace unknown for command with sub-NS, bug 910d67a229fe7f65} -body {
+ namespace eval ::foo::bar {
+ proc _unknown args {list ::foo:bar:_unknown [uplevel {namespace current}] $args}
+ namespace unknown [namespace current]::_unknown
+ }
+ set res {}
+ lappend res [namespace inscope ::foo::bar {xxx::yyy}] [namespace inscope ::foo {bar::xxx::yyy}] [::foo::bar::xxx::yyy] [namespace inscope :: {foo::bar::xxx::yyy}]
+ # now with existsing ::foo::bar::xxx, but without unknown handler inside (only parent ::foo::bar has a handler):
+ namespace eval ::foo::bar::xxx {}
+ lappend res [namespace inscope ::foo::bar {xxx::yyy}] [namespace inscope ::foo {bar::xxx::yyy}] [::foo::bar::xxx::yyy] [namespace inscope :: {foo::bar::xxx::yyy}]
+} -cleanup {
+ namespace delete ::foo
+ unset -nocomplain res
+} -result [lrepeat 2 \
+ {::foo:bar:_unknown ::foo::bar xxx::yyy} {::foo:bar:_unknown ::foo bar::xxx::yyy} {::foo:bar:_unknown :: ::foo::bar::xxx::yyy} {::foo:bar:_unknown :: foo::bar::xxx::yyy}
+]
# TIP 314 - ensembles with parameters
test namespace-53.1 {ensembles: parameters} {