summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/GetIndex.36
-rw-r--r--generic/tcl.h3
-rw-r--r--generic/tclIndexObj.c20
-rw-r--r--generic/tclTest.c17
-rw-r--r--tests/indexObj.test4
5 files changed, 36 insertions, 14 deletions
diff --git a/doc/GetIndex.3 b/doc/GetIndex.3
index a788848..1af1663 100644
--- a/doc/GetIndex.3
+++ b/doc/GetIndex.3
@@ -54,7 +54,7 @@ Null-terminated string describing what is being looked up, such as
.AP int flags in
OR-ed combination of bits providing additional information for
operation. The only bits that are currently defined are \fBTCL_EXACT\fR
-and \fBTCL_INDEX_TEMP_TABLE\fR.
+, \fBTCL_INDEX_TEMP_TABLE\fR, and \fBTCL_INDEX_NULL_OK\fR.
.AP int *indexPtr out
The index of the string in \fItablePtr\fR that matches the value of
\fIobjPtr\fR is returned here.
@@ -91,7 +91,9 @@ operation. Note: \fBTcl_GetIndexFromObj\fR assumes that the entries
in \fItablePtr\fR are static: they must not change between
invocations. This caching mechanism can be disallowed by specifying
the \fBTCL_INDEX_TEMP_TABLE\fR flag.
-If the value of \fIobjPtr\fR is the empty string,
+If the \fBTCL_INDEX_NULL_OK\fR flag was specified, objPtr is allowed
+to be NULL or the empty string. The resulting index is -1.
+Otherwise, if the value of \fIobjPtr\fR is the empty string,
\fBTcl_GetIndexFromObj\fR will treat it as a non-matching value
and return \fBTCL_ERROR\fR.
.PP
diff --git a/generic/tcl.h b/generic/tcl.h
index 346b79c..b82cf0a 100644
--- a/generic/tcl.h
+++ b/generic/tcl.h
@@ -979,10 +979,13 @@ typedef struct Tcl_DString {
* TCL_EXACT disallows abbreviated strings.
* TCL_INDEX_TEMP_TABLE disallows caching of lookups. A possible use case is
* a table that will not live long enough to make it worthwhile.
+ * TCL_INDEX_NULL_OK allows the empty string or NULL to return TCL_OK.
+ * The returned value will be -1;
*/
#define TCL_EXACT 1
#define TCL_INDEX_TEMP_TABLE 2
+#define TCL_INDEX_NULL_OK 4
/*
*----------------------------------------------------------------------------
diff --git a/generic/tclIndexObj.c b/generic/tclIndexObj.c
index c2812ea..e9c453a 100644
--- a/generic/tclIndexObj.c
+++ b/generic/tclIndexObj.c
@@ -261,7 +261,7 @@ Tcl_GetIndexFromObjStruct(
int offset, /* The number of bytes between entries */
const char *msg, /* Identifying word to use in error
* messages. */
- int flags, /* 0 or TCL_EXACT */
+ int flags, /* 0, TCL_EXACT, TCL_INDEX_TEMP_TABLE or TCL_INDEX_NULL_OK */
int *indexPtr) /* Place to store resulting integer index. */
{
int index, idx, numAbbrev;
@@ -280,7 +280,10 @@ Tcl_GetIndexFromObjStruct(
* See if there is a valid cached result from a previous lookup.
*/
- if (!(flags & TCL_INDEX_TEMP_TABLE)) {
+ if (!objPtr && (flags & TCL_INDEX_NULL_OK)) {
+ *indexPtr = -1;
+ return TCL_OK;
+ } else if (objPtr && !(flags & TCL_INDEX_TEMP_TABLE)) {
irPtr = TclFetchInternalRep(objPtr, &indexType);
if (irPtr) {
indexRep = (IndexRep *)irPtr->twoPtrValue.ptr1;
@@ -296,10 +299,14 @@ Tcl_GetIndexFromObjStruct(
* abbreviations unless TCL_EXACT is set in flags.
*/
- key = TclGetString(objPtr);
+ key = objPtr ? TclGetString(objPtr) : "";
index = -1;
numAbbrev = 0;
+ if (!*key && (flags & TCL_INDEX_NULL_OK)) {
+ *indexPtr = -1;
+ return TCL_OK;
+ }
/*
* Scan the table looking for one of:
* - An exact match (always preferred)
@@ -344,7 +351,7 @@ Tcl_GetIndexFromObjStruct(
* operation.
*/
- if (!(flags & TCL_INDEX_TEMP_TABLE)) {
+ if (objPtr && !(flags & TCL_INDEX_TEMP_TABLE)) {
irPtr = TclFetchInternalRep(objPtr, &indexType);
if (irPtr) {
indexRep = (IndexRep *)irPtr->twoPtrValue.ptr1;
@@ -386,7 +393,7 @@ Tcl_GetIndexFromObjStruct(
*entryPtr, NULL);
entryPtr = NEXT_ENTRY(entryPtr, offset);
while (*entryPtr != NULL) {
- if (*NEXT_ENTRY(entryPtr, offset) == NULL) {
+ if ((*NEXT_ENTRY(entryPtr, offset) == NULL) && !(flags & TCL_INDEX_NULL_OK)) {
Tcl_AppendStringsToObj(resultPtr, (count > 0 ? "," : ""),
" or ", *entryPtr, NULL);
} else if (**entryPtr) {
@@ -395,6 +402,9 @@ Tcl_GetIndexFromObjStruct(
}
entryPtr = NEXT_ENTRY(entryPtr, offset);
}
+ if ((flags & TCL_INDEX_NULL_OK)) {
+ Tcl_AppendStringsToObj(resultPtr, ", or \"\"", NULL);
+ }
}
Tcl_SetObjResult(interp, resultPtr);
Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "INDEX", msg, key, NULL);
diff --git a/generic/tclTest.c b/generic/tclTest.c
index 46a1459..e18283d 100644
--- a/generic/tclTest.c
+++ b/generic/tclTest.c
@@ -6285,17 +6285,20 @@ TestGetIndexFromObjStructObjCmd(
const char *const ary[] = {
"a", "b", "c", "d", "e", "f", NULL, NULL
};
- int idx,target;
+ int idx,target, flags = 0;
- if (objc != 3) {
- Tcl_WrongNumArgs(interp, 1, objv, "argument targetvalue");
+ if (objc != 3 && objc != 4) {
+ Tcl_WrongNumArgs(interp, 1, objv, "argument targetvalue ?flags?");
return TCL_ERROR;
}
- if (Tcl_GetIndexFromObjStruct(interp, objv[1], ary, 2*sizeof(char *),
- "dummy", 0, &idx) != TCL_OK) {
+ if (Tcl_GetIntFromObj(interp, objv[2], &target) != TCL_OK) {
return TCL_ERROR;
}
- if (Tcl_GetIntFromObj(interp, objv[2], &target) != TCL_OK) {
+ if ((objc > 3) && (Tcl_GetIntFromObj(interp, objv[3], &flags) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIndexFromObjStruct(interp, (Tcl_GetString(objv[1])[0] ? objv[1] : NULL), ary, 2*sizeof(char *),
+ "dummy", flags, &idx) != TCL_OK) {
return TCL_ERROR;
}
if (idx != target) {
@@ -6307,7 +6310,7 @@ TestGetIndexFromObjStructObjCmd(
Tcl_AppendResult(interp, " when ", buffer, " expected", NULL);
return TCL_ERROR;
}
- Tcl_WrongNumArgs(interp, 3, objv, NULL);
+ Tcl_WrongNumArgs(interp, objc, objv, NULL);
return TCL_OK;
}
diff --git a/tests/indexObj.test b/tests/indexObj.test
index bd6a2c2..c615e15 100644
--- a/tests/indexObj.test
+++ b/tests/indexObj.test
@@ -131,6 +131,10 @@ test indexObj-6.4 {Tcl_GetIndexFromObjStruct} testindexobj {
testgetindexfromobjstruct $x 1
testgetindexfromobjstruct $x 1
} "wrong # args: should be \"testgetindexfromobjstruct c 1\""
+test indexObj-6.5 {Tcl_GetIndexFromObjStruct} testindexobj {
+ set x ""
+ testgetindexfromobjstruct $x -1 4
+} "wrong # args: should be \"testgetindexfromobjstruct {} -1 4\""
test indexObj-7.1 {Tcl_ParseArgsObjv} testparseargs {
testparseargs