summaryrefslogtreecommitdiffstats
path: root/generic/tclUtil.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclUtil.c')
-rw-r--r--generic/tclUtil.c442
1 files changed, 299 insertions, 143 deletions
diff --git a/generic/tclUtil.c b/generic/tclUtil.c
index d79fd97..7d8f1b0 100644
--- a/generic/tclUtil.c
+++ b/generic/tclUtil.c
@@ -12,6 +12,7 @@
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
+#include <assert.h>
#include "tclInt.h"
#include "tclParse.h"
#include "tclStringTrim.h"
@@ -102,14 +103,14 @@ static void ClearHash(Tcl_HashTable *tablePtr);
static void FreeProcessGlobalValue(void *clientData);
static void FreeThreadHash(void *clientData);
static int GetEndOffsetFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr,
- size_t endValue, Tcl_WideInt *indexPtr);
+ Tcl_WideInt endValue, Tcl_WideInt *indexPtr);
static Tcl_HashTable * GetThreadHash(Tcl_ThreadDataKey *keyPtr);
static int GetWideForIndex(Tcl_Interp *interp, Tcl_Obj *objPtr,
- size_t endValue, Tcl_WideInt *widePtr);
+ Tcl_WideInt endValue, Tcl_WideInt *widePtr);
static int FindElement(Tcl_Interp *interp, const char *string,
- size_t stringLength, const char *typeStr,
+ Tcl_Size stringLength, const char *typeStr,
const char *typeCode, const char **elementPtr,
- const char **nextPtr, size_t *sizePtr,
+ const char **nextPtr, Tcl_Size *sizePtr,
int *literalPtr);
/*
* The following is the Tcl object type definition for an object that
@@ -133,7 +134,7 @@ static const TclObjTypeWithAbstractList endOffsetType = {
)}
};
-size_t
+Tcl_Size
TclLengthOne(
TCL_UNUSED(Tcl_Obj *))
{
@@ -394,13 +395,13 @@ TclLengthOne(
*----------------------------------------------------------------------
*/
-int
+Tcl_Size
TclMaxListLength(
const char *bytes,
- size_t numBytes,
+ Tcl_Size numBytes,
const char **endPtr)
{
- size_t count = 0;
+ Tcl_Size count = 0;
if ((numBytes == 0) || ((numBytes == TCL_INDEX_NONE) && (*bytes == '\0'))) {
/* Empty string case - quick exit */
@@ -503,13 +504,13 @@ TclFindElement(
const char *list, /* Points to the first byte of a string
* containing a Tcl list with zero or more
* elements (possibly in braces). */
- size_t listLength, /* Number of bytes in the list's string. */
+ Tcl_Size listLength, /* Number of bytes in the list's string. */
const char **elementPtr, /* Where to put address of first significant
* character in first element of list. */
const char **nextPtr, /* Fill in with location of character just
* after all white space following end of
* argument (next arg or end of list). */
- size_t *sizePtr, /* If non-zero, fill in with size of
+ Tcl_Size *sizePtr, /* If non-zero, fill in with size of
* element. */
int *literalPtr) /* If non-zero, fill in with non-zero/zero to
* indicate that the substring of *sizePtr
@@ -531,14 +532,14 @@ TclFindDictElement(
* containing a Tcl dictionary with zero or
* more keys and values (possibly in
* braces). */
- size_t dictLength, /* Number of bytes in the dict's string. */
+ Tcl_Size dictLength, /* Number of bytes in the dict's string. */
const char **elementPtr, /* Where to put address of first significant
* character in the first element (i.e., key
* or value) of dict. */
const char **nextPtr, /* Fill in with location of character just
* after all white space following end of
* element (next arg or end of list). */
- size_t *sizePtr, /* If non-zero, fill in with size of
+ Tcl_Size *sizePtr, /* If non-zero, fill in with size of
* element. */
int *literalPtr) /* If non-zero, fill in with non-zero/zero to
* indicate that the substring of *sizePtr
@@ -560,7 +561,7 @@ FindElement(
* containing a Tcl list or dictionary with
* zero or more elements (possibly in
* braces). */
- size_t stringLength, /* Number of bytes in the string. */
+ Tcl_Size stringLength, /* Number of bytes in the string. */
const char *typeStr, /* The name of the type of thing we are
* parsing, for error messages. */
const char *typeCode, /* The type code for thing we are parsing, for
@@ -570,7 +571,7 @@ FindElement(
const char **nextPtr, /* Fill in with location of character just
* after all white space following end of
* argument (next arg or end of list/dict). */
- size_t *sizePtr, /* If non-zero, fill in with size of
+ Tcl_Size *sizePtr, /* If non-zero, fill in with size of
* element. */
int *literalPtr) /* If non-zero, fill in with non-zero/zero to
* indicate that the substring of *sizePtr
@@ -582,10 +583,10 @@ FindElement(
const char *p = string;
const char *elemStart; /* Points to first byte of first element. */
const char *limit; /* Points just after list/dict's last byte. */
- size_t openBraces = 0; /* Brace nesting level during parse. */
+ Tcl_Size openBraces = 0; /* Brace nesting level during parse. */
int inQuotes = 0;
- size_t size = 0;
- size_t numChars;
+ Tcl_Size size = 0;
+ Tcl_Size numChars;
int literal = 1;
const char *p2;
@@ -793,21 +794,21 @@ FindElement(
*----------------------------------------------------------------------
*/
-size_t
+Tcl_Size
TclCopyAndCollapse(
- size_t count, /* Number of byte to copy from src. */
+ Tcl_Size count, /* Number of byte to copy from src. */
const char *src, /* Copy from here... */
char *dst) /* ... to here. */
{
- size_t newCount = 0;
+ Tcl_Size newCount = 0;
while (count > 0) {
char c = *src;
if (c == '\\') {
char buf[4] = "";
- size_t numRead;
- size_t backslashCount = TclParseBackslash(src, count, &numRead, buf);
+ Tcl_Size numRead;
+ Tcl_Size backslashCount = TclParseBackslash(src, count, &numRead, buf);
memcpy(dst, buf, backslashCount);
dst += backslashCount;
@@ -860,7 +861,7 @@ Tcl_SplitList(
Tcl_Interp *interp, /* Interpreter to use for error reporting. If
* NULL, no error message is left. */
const char *list, /* Pointer to string with list structure. */
- size_t *argcPtr, /* Pointer to location to fill in with the
+ Tcl_Size *argcPtr, /* Pointer to location to fill in with the
* number of elements in the list. */
const char ***argvPtr) /* Pointer to place to store pointer to array
* of pointers to list elements. */
@@ -868,7 +869,7 @@ Tcl_SplitList(
const char **argv, *end, *element;
char *p;
int result;
- size_t length, size, i, elSize;
+ Tcl_Size length, size, i, elSize;
/*
* Allocate enough space to work in. A (const char *) for each (possible)
@@ -901,7 +902,7 @@ Tcl_SplitList(
Tcl_Free((void *)argv);
if (interp != NULL) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "internal error in Tcl_SplitList", TCL_INDEX_NONE));
+ "internal error in Tcl_SplitList", -1));
Tcl_SetErrorCode(interp, "TCL", "INTERNAL", "Tcl_SplitList",
NULL);
}
@@ -945,7 +946,7 @@ Tcl_SplitList(
*----------------------------------------------------------------------
*/
-size_t
+Tcl_Size
Tcl_ScanElement(
const char *src, /* String to convert to list element. */
int *flagPtr) /* Where to store information to guide
@@ -977,15 +978,15 @@ Tcl_ScanElement(
*----------------------------------------------------------------------
*/
-size_t
+Tcl_Size
Tcl_ScanCountedElement(
const char *src, /* String to convert to Tcl list element. */
- size_t length, /* Number of bytes in src, or TCL_INDEX_NONE. */
+ Tcl_Size length, /* Number of bytes in src, or TCL_INDEX_NONE. */
int *flagPtr) /* Where to store information to guide
* Tcl_ConvertElement. */
{
char flags = CONVERT_ANY;
- size_t numBytes = TclScanElement(src, length, &flags);
+ Tcl_Size numBytes = TclScanElement(src, length, &flags);
*flagPtr = flags;
return numBytes;
@@ -1024,12 +1025,12 @@ Tcl_ScanCountedElement(
TCL_HASH_TYPE
TclScanElement(
const char *src, /* String to convert to Tcl list element. */
- size_t length, /* Number of bytes in src, or TCL_INDEX_NONE. */
+ Tcl_Size length, /* Number of bytes in src, or TCL_INDEX_NONE. */
char *flagPtr) /* Where to store information to guide
* Tcl_ConvertElement. */
{
const char *p = src;
- size_t nestingLevel = 0; /* Brace nesting count */
+ Tcl_Size nestingLevel = 0; /* Brace nesting count */
int forbidNone = 0; /* Do not permit CONVERT_NONE mode. Something
* needs protection or escape. */
int requireEscape = 0; /* Force use of CONVERT_ESCAPE mode. For some
@@ -1143,13 +1144,13 @@ TclScanElement(
*/
requireEscape = 1;
- length -= (length+1 > 1);
+ length -= (length > 0);
p++;
break;
}
if ((p[1] == '{') || (p[1] == '}') || (p[1] == '\\')) {
extra++; /* Escape sequences all one byte longer. */
- length -= (length+1 > 1);
+ length -= (length > 0);
p++;
}
forbidNone = 1;
@@ -1174,7 +1175,7 @@ TclScanElement(
break;
}
}
- length -= (length+1 > 1);
+ length -= (length > 0);
p++;
}
@@ -1322,7 +1323,7 @@ TclScanElement(
*----------------------------------------------------------------------
*/
-size_t
+Tcl_Size
Tcl_ConvertElement(
const char *src, /* Source information for list element. */
char *dst, /* Place to put list-ified element. */
@@ -1352,14 +1353,14 @@ Tcl_ConvertElement(
*----------------------------------------------------------------------
*/
-size_t
+Tcl_Size
Tcl_ConvertCountedElement(
const char *src, /* Source information for list element. */
- size_t length, /* Number of bytes in src, or TCL_INDEX_NONE. */
+ Tcl_Size length, /* Number of bytes in src, or TCL_INDEX_NONE. */
char *dst, /* Place to put list-ified element. */
int flags) /* Flags produced by Tcl_ScanElement. */
{
- size_t numBytes = TclConvertElement(src, length, dst, flags);
+ Tcl_Size numBytes = TclConvertElement(src, length, dst, flags);
dst[numBytes] = '\0';
return numBytes;
}
@@ -1385,10 +1386,10 @@ Tcl_ConvertCountedElement(
*----------------------------------------------------------------------
*/
-size_t
+Tcl_Size
TclConvertElement(
const char *src, /* Source information for list element. */
- size_t length, /* Number of bytes in src, or TCL_INDEX_NONE. */
+ Tcl_Size length, /* Number of bytes in src, or TCL_INDEX_NONE. */
char *dst, /* Place to put list-ified element. */
int flags) /* Flags produced by Tcl_ScanElement. */
{
@@ -1423,7 +1424,7 @@ TclConvertElement(
p[1] = '#';
p += 2;
src++;
- length -= (length+1 > 1);
+ length -= (length > 0);
} else {
conversion = CONVERT_BRACE;
}
@@ -1473,7 +1474,7 @@ TclConvertElement(
* Formatted string is original string converted to escape sequences.
*/
- for ( ; length; src++, length -= (length+1 > 1)) {
+ for ( ; length; src++, length -= (length > 0)) {
switch (*src) {
case ']':
case '[':
@@ -1527,7 +1528,7 @@ TclConvertElement(
continue;
case '\0':
if (length == TCL_INDEX_NONE) {
- return (size_t)(p - dst);
+ return (p - dst);
}
/*
@@ -1543,7 +1544,7 @@ TclConvertElement(
*p = *src;
p++;
}
- return (size_t)(p - dst);
+ return (p - dst);
}
/*
@@ -1568,12 +1569,12 @@ TclConvertElement(
char *
Tcl_Merge(
- size_t argc, /* How many strings to merge. */
+ Tcl_Size argc, /* How many strings to merge. */
const char *const *argv) /* Array of string values. */
{
#define LOCAL_SIZE 64
char localFlags[LOCAL_SIZE], *flagPtr = NULL;
- size_t i, bytesNeeded = 0;
+ Tcl_Size i, bytesNeeded = 0;
char *result, *dst;
/*
@@ -1581,7 +1582,7 @@ Tcl_Merge(
* simpler.
*/
- if (argc == 0) {
+ if (argc <= 0) {
result = (char *)Tcl_Alloc(1);
result[0] = '\0';
return result;
@@ -1639,14 +1640,14 @@ Tcl_Merge(
*----------------------------------------------------------------------
*/
-size_t
+Tcl_Size
TclTrimRight(
const char *bytes, /* String to be trimmed... */
- size_t numBytes, /* ...and its length in bytes */
+ Tcl_Size numBytes, /* ...and its length in bytes */
/* Calls to TclUtfToUniChar() in this routine
* rely on (bytes[numBytes] == '\0'). */
const char *trim, /* String of trim characters... */
- size_t numTrim) /* ...and its length in bytes */
+ Tcl_Size numTrim) /* ...and its length in bytes */
/* Calls to TclUtfToUniChar() in this routine
* rely on (trim[numTrim] == '\0'). */
{
@@ -1664,7 +1665,7 @@ TclTrimRight(
do {
const char *q = trim;
- size_t pInc = 0, bytesLeft = numTrim;
+ Tcl_Size pInc = 0, bytesLeft = numTrim;
pp = Tcl_UtfPrev(p, bytes);
do {
@@ -1718,14 +1719,14 @@ TclTrimRight(
*----------------------------------------------------------------------
*/
-size_t
+Tcl_Size
TclTrimLeft(
const char *bytes, /* String to be trimmed... */
- size_t numBytes, /* ...and its length in bytes */
+ Tcl_Size numBytes, /* ...and its length in bytes */
/* Calls to TclUtfToUniChar() in this routine
* rely on (bytes[numBytes] == '\0'). */
const char *trim, /* String of trim characters... */
- size_t numTrim) /* ...and its length in bytes */
+ Tcl_Size numTrim) /* ...and its length in bytes */
/* Calls to TclUtfToUniChar() in this routine
* rely on (trim[numTrim] == '\0'). */
{
@@ -1742,16 +1743,16 @@ TclTrimLeft(
*/
do {
- size_t pInc = TclUtfToUCS4(p, &ch1);
+ Tcl_Size pInc = TclUtfToUCS4(p, &ch1);
const char *q = trim;
- size_t bytesLeft = numTrim;
+ Tcl_Size bytesLeft = numTrim;
/*
* Inner loop: scan trim string for match to current character.
*/
do {
- size_t qInc = TclUtfToUCS4(q, &ch2);
+ Tcl_Size qInc = TclUtfToUCS4(q, &ch2);
if (ch1 == ch2) {
break;
@@ -1792,19 +1793,19 @@ TclTrimLeft(
*----------------------------------------------------------------------
*/
-size_t
+Tcl_Size
TclTrim(
const char *bytes, /* String to be trimmed... */
- size_t numBytes, /* ...and its length in bytes */
+ Tcl_Size numBytes, /* ...and its length in bytes */
/* Calls in this routine
* rely on (bytes[numBytes] == '\0'). */
const char *trim, /* String of trim characters... */
- size_t numTrim, /* ...and its length in bytes */
+ Tcl_Size numTrim, /* ...and its length in bytes */
/* Calls in this routine
* rely on (trim[numTrim] == '\0'). */
- size_t *trimRightPtr) /* Offset from the end of the string. */
+ Tcl_Size *trimRightPtr) /* Offset from the end of the string. */
{
- size_t trimLeft = 0, trimRight = 0;
+ Tcl_Size trimLeft = 0, trimRight = 0;
/* Empty strings -> nothing to do */
if ((numBytes > 0) && (numTrim > 0)) {
@@ -1856,10 +1857,10 @@ TclTrim(
char *
Tcl_Concat(
- size_t argc, /* Number of strings to concatenate. */
+ Tcl_Size argc, /* Number of strings to concatenate. */
const char *const *argv) /* Array of strings to concatenate. */
{
- size_t i, needSpace = 0, bytesNeeded = 0;
+ Tcl_Size i, needSpace = 0, bytesNeeded = 0;
char *result, *p;
/*
@@ -1878,16 +1879,27 @@ Tcl_Concat(
for (i = 0; i < argc; i++) {
bytesNeeded += strlen(argv[i]);
+ if (bytesNeeded < 0) {
+ Tcl_Panic("Tcl_Concat: max size of Tcl value exceeded");
+ }
}
/*
* All element bytes + (argc - 1) spaces + 1 terminating NULL.
*/
+ if (bytesNeeded + argc - 1 < 0) {
+ /*
+ * Panic test could be tighter, but not going to bother for this
+ * legacy routine.
+ */
+
+ Tcl_Panic("Tcl_Concat: max size of Tcl value exceeded");
+ }
result = (char *)Tcl_Alloc(bytesNeeded + argc);
for (p = result, i = 0; i < argc; i++) {
- size_t triml, trimr, elemLength;
+ Tcl_Size triml, trimr, elemLength;
const char *element;
element = argv[i];
@@ -1945,11 +1957,11 @@ Tcl_Concat(
Tcl_Obj *
Tcl_ConcatObj(
- size_t objc, /* Number of objects to concatenate. */
+ Tcl_Size objc, /* Number of objects to concatenate. */
Tcl_Obj *const objv[]) /* Array of objects to concatenate. */
{
int needSpace = 0;
- size_t i, bytesNeeded = 0, elemLength;
+ Tcl_Size i, bytesNeeded = 0, elemLength;
const char *element;
Tcl_Obj *objPtr, *resPtr;
@@ -2011,6 +2023,9 @@ Tcl_ConcatObj(
for (i = 0; i < objc; i++) {
element = Tcl_GetStringFromObj(objv[i], &elemLength);
+ if (bytesNeeded > (TCL_SIZE_MAX - elemLength)) {
+ break; /* Overflow. Do not preallocate. See comment below. */
+ }
bytesNeeded += elemLength;
}
@@ -2025,7 +2040,7 @@ Tcl_ConcatObj(
Tcl_SetObjLength(resPtr, 0);
for (i = 0; i < objc; i++) {
- size_t triml, trimr;
+ Tcl_Size triml, trimr;
element = Tcl_GetStringFromObj(objv[i], &elemLength);
@@ -2578,13 +2593,13 @@ Tcl_DStringAppend(
Tcl_DString *dsPtr, /* Structure describing dynamic string. */
const char *bytes, /* String to append. If length is
* TCL_INDEX_NONE then this must be null-terminated. */
- size_t length) /* Number of bytes from "bytes" to append. If
+ Tcl_Size length) /* Number of bytes from "bytes" to append. If
* TCL_INDEX_NONE, then append all of bytes, up to null
* at end. */
{
- size_t newSize;
+ Tcl_Size newSize;
- if (length == TCL_INDEX_NONE) {
+ if (length < 0) {
length = strlen(bytes);
}
newSize = length + dsPtr->length;
@@ -2603,7 +2618,7 @@ Tcl_DStringAppend(
memcpy(newString, dsPtr->string, dsPtr->length);
dsPtr->string = newString;
} else {
- size_t index = TCL_INDEX_NONE;
+ Tcl_Size index = TCL_INDEX_NONE;
/* See [16896d49fd] */
if (bytes >= dsPtr->string
@@ -2613,7 +2628,7 @@ Tcl_DStringAppend(
dsPtr->string = (char *)Tcl_Realloc(dsPtr->string, dsPtr->spaceAvl);
- if (index != TCL_INDEX_NONE) {
+ if (index >= 0) {
bytes = dsPtr->string + index;
}
}
@@ -2645,7 +2660,7 @@ TclDStringAppendObj(
Tcl_DString *dsPtr,
Tcl_Obj *objPtr)
{
- size_t length;
+ Tcl_Size length;
const char *bytes = Tcl_GetStringFromObj(objPtr, &length);
return Tcl_DStringAppend(dsPtr, bytes, length);
@@ -2688,7 +2703,7 @@ Tcl_DStringAppendElement(
int needSpace = TclNeedSpace(dsPtr->string, dst);
char flags = 0;
int quoteHash = 1;
- size_t newSize;
+ Tcl_Size newSize;
if (needSpace) {
/*
@@ -2789,9 +2804,9 @@ Tcl_DStringAppendElement(
void
Tcl_DStringSetLength(
Tcl_DString *dsPtr, /* Structure describing dynamic string. */
- size_t length) /* New length for dynamic string. */
+ Tcl_Size length) /* New length for dynamic string. */
{
- size_t newsize;
+ Tcl_Size newsize;
if (length >= dsPtr->spaceAvl) {
/*
@@ -3268,14 +3283,14 @@ TclNeedSpace(
*----------------------------------------------------------------------
*/
-size_t
+Tcl_Size
TclFormatInt(
char *buffer, /* Points to the storage into which the
* formatted characters are written. */
Tcl_WideInt n) /* The integer to format. */
{
Tcl_WideUInt intVal;
- size_t i = 0, numFormatted, j;
+ int i = 0, numFormatted, j;
static const char digits[] = "0123456789";
/*
@@ -3338,7 +3353,7 @@ GetWideForIndex(
* NULL, then no error message is left after
* errors. */
Tcl_Obj *objPtr, /* Points to the value to be parsed */
- size_t endValue, /* The value to be stored at *widePtr if
+ Tcl_WideInt endValue, /* The value to be stored at *widePtr if
* objPtr holds "end".
* NOTE: this value may be TCL_INDEX_NONE. */
Tcl_WideInt *widePtr) /* Location filled in with a wide integer
@@ -3375,11 +3390,18 @@ GetWideForIndex(
* object. The string value 'objPtr' is expected have the format
* integer([+-]integer)? or end([+-]integer)?.
*
- * Value
+ * If the computed index lies within the valid range of Tcl indices
+ * (0..TCL_SIZE_MAX) it is returned. Higher values are returned as
+ * TCL_SIZE_MAX. Negative values are returned as TCL_INDEX_NONE (-1).
+ *
+ * Callers should pass reasonable values for endValue - one in the
+ * valid index range or TCL_INDEX_NONE (-1), for example for an empty
+ * list.
+ *
+ * Results:
* TCL_OK
*
- * The index is stored at the address given by by 'indexPtr'. If
- * 'objPtr' has the value "end", the value stored is 'endValue'.
+ * The index is stored at the address given by by 'indexPtr'.
*
* TCL_ERROR
*
@@ -3387,10 +3409,9 @@ GetWideForIndex(
* 'interp' is non-NULL, an error message is left in the interpreter's
* result object.
*
- * Effect
+ * Side effects:
*
- * The object referenced by 'objPtr' is converted, as needed, to an
- * integer, wide integer, or end-based-index object.
+ * The internal representation contained within objPtr may shimmer.
*
*----------------------------------------------------------------------
*/
@@ -3402,9 +3423,8 @@ Tcl_GetIntForIndex(
* errors. */
Tcl_Obj *objPtr, /* Points to an object containing either "end"
* or an integer. */
- size_t endValue, /* The value to be stored at "indexPtr" if
- * "objPtr" holds "end". */
- size_t *indexPtr) /* Location filled in with an integer
+ Tcl_Size endValue, /* The value corresponding to the "end" index */
+ Tcl_Size *indexPtr) /* Location filled in with an integer
* representing an index. May be NULL.*/
{
Tcl_WideInt wide;
@@ -3413,16 +3433,18 @@ Tcl_GetIntForIndex(
return TCL_ERROR;
}
if (indexPtr != NULL) {
- if ((wide < 0) && (endValue < TCL_INDEX_END)) {
- *indexPtr = TCL_INDEX_NONE;
- } else if ((Tcl_WideUInt)wide > TCL_INDEX_END && (endValue < TCL_INDEX_END)) {
- *indexPtr = TCL_INDEX_END;
+ /* Note: check against TCL_SIZE_MAX needed for 32-bit builds */
+ if (wide >= 0 && wide <= TCL_SIZE_MAX) {
+ *indexPtr = (Tcl_Size)wide;
+ } else if (wide > TCL_SIZE_MAX) {
+ *indexPtr = TCL_SIZE_MAX;
} else {
- *indexPtr = (size_t) wide;
+ *indexPtr = TCL_INDEX_NONE;
}
}
return TCL_OK;
}
+
/*
*----------------------------------------------------------------------
*
@@ -3439,7 +3461,8 @@ Tcl_GetIntForIndex(
* -2: Index "end-1"
* -1: Index "end"
* 0: Index "0"
- * WIDE_MAX-1: Index "end+n", for any n > 1
+ * WIDE_MAX-1: Index "end+n", for any n > 1. Distinguish from end+1 for
+ * commands like lset.
* WIDE_MAX: Index "end+1"
*
* Results:
@@ -3455,7 +3478,7 @@ static int
GetEndOffsetFromObj(
Tcl_Interp *interp,
Tcl_Obj *objPtr, /* Pointer to the object to parse */
- size_t endValue, /* The value to be stored at "indexPtr" if
+ Tcl_WideInt endValue, /* The value to be stored at "widePtr" if
* "objPtr" holds "end". */
Tcl_WideInt *widePtr) /* Location filled in with an integer
* representing an index. */
@@ -3466,7 +3489,7 @@ GetEndOffsetFromObj(
while ((irPtr = TclFetchInternalRep(objPtr, &endOffsetType.objType)) == NULL) {
Tcl_ObjInternalRep ir;
- size_t length;
+ Tcl_Size length;
const char *bytes = Tcl_GetStringFromObj(objPtr, &length);
if (*bytes != 'e') {
@@ -3656,15 +3679,26 @@ GetEndOffsetFromObj(
offset = irPtr->wideValue;
if (offset == WIDE_MAX) {
+ /*
+ * Encodes end+1. This is distinguished from end+n as noted above
+ * NOTE: this may wrap around if the caller passes (as lset does)
+ * listLen-1 as endValue and and listLen is 0. The -1 will be
+ * interpreted as FF...FF and adding 1 will result in 0 which
+ * is what we want. 2's complements shenanigans but it is what
+ * it is ...
+ */
*widePtr = endValue + 1;
} else if (offset == WIDE_MIN) {
+ /* -1 - position before first */
*widePtr = -1;
} else if (offset < 0) {
- /* Different signs, sum cannot overflow */
+ /* end-(n-1) - Different signs, sum cannot overflow */
*widePtr = endValue + offset + 1;
} else if (offset < WIDE_MAX) {
+ /* 0:WIDE_MAX-1 - plain old index. */
*widePtr = offset;
} else {
+ /* Huh, what case remains here? */
*widePtr = WIDE_MAX;
}
return TCL_OK;
@@ -3689,19 +3723,26 @@ GetEndOffsetFromObj(
*----------------------------------------------------------------------
*
* TclIndexEncode --
+ * IMPORTANT: function only encodes indices in the range that fits within
+ * an "int" type. Do NOT change this as the byte code compiler and engine
+ * which call this function cannot handle wider index types. Indices
+ * outside the range will result in the function returning an error.
*
* Parse objPtr to determine if it is an index value. Two cases
* are possible. The value objPtr might be parsed as an absolute
- * index value in the C signed int range. Note that this includes
+ * index value in the Tcl_Size range. Note that this includes
* index values that are integers as presented and it includes index
- * arithmetic expressions. The absolute index values that can be
+ * arithmetic expressions.
+ *
+ * The largest string supported in Tcl 8 has byte length TCL_SIZE_MAX.
+ * This means the largest supported character length is also TCL_SIZE_MAX,
+ * and the index of the last character in a string of length TCL_SIZE_MAX
+ * is TCL_SIZE_MAX-1. Thus the absolute index values that can be
* directly meaningful as an index into either a list or a string are
- * those integer values >= TCL_INDEX_START (0)
- * and < INT_MAX.
- * The largest string supported in Tcl 8 has bytelength INT_MAX.
- * This means the largest supported character length is also INT_MAX,
- * and the index of the last character in a string of length INT_MAX
- * is INT_MAX-1.
+ * integer values in the range 0 to TCL_SIZE_MAX - 1.
+ *
+ * This function however can only handle integer indices in the range
+ * 0 : INT_MAX-1.
*
* Any absolute index value parsed outside that range is encoded
* using the before and after values passed in by the
@@ -3726,12 +3767,9 @@ GetEndOffsetFromObj(
* if the tokens "end-0x7FFFFFFF" or "end+-0x80000000" are parsed,
* they can be encoded with the before value.
*
- * These details will require re-examination whenever string and
- * list length limits are increased, but that will likely also
- * mean a revised routine capable of returning Tcl_WideInt values.
- *
* Returns:
- * TCL_OK if parsing succeeded, and TCL_ERROR if it failed.
+ * TCL_OK if parsing succeeded, and TCL_ERROR if it failed or the
+ * index does not fit in an int type.
*
* Side effects:
* When TCL_OK is returned, the encoded index value is written
@@ -3744,51 +3782,138 @@ int
TclIndexEncode(
Tcl_Interp *interp, /* For error reporting, may be NULL */
Tcl_Obj *objPtr, /* Index value to parse */
- size_t before, /* Value to return for index before beginning */
- size_t after, /* Value to return for index after end */
+ int before, /* Value to return for index before beginning */
+ int after, /* Value to return for index after end */
int *indexPtr) /* Where to write the encoded answer, not NULL */
{
Tcl_WideInt wide;
int idx;
+ const Tcl_WideInt ENDVALUE = 2 * (Tcl_WideInt) INT_MAX;
+
+ assert(ENDVALUE < WIDE_MAX);
+ if (TCL_OK != GetWideForIndex(interp, objPtr, ENDVALUE, &wide)) {
+ return TCL_ERROR;
+ }
+ /*
+ * We passed 2*INT_MAX as the "end value" to GetWideForIndex. The computed
+ * index will in one of the following ranges that need to be distinguished
+ * for encoding purposes in the following code.
+ * (1) 0:INT_MAX when
+ * (a) objPtr was a pure non-negative numeric value in that range
+ * (b) objPtr was a numeric computation M+/-N with a result in that range
+ * (c) objPtr was of the form end-N where N was in range INT_MAX:2*INT_MAX
+ * (2) INT_MAX+1:2*INT_MAX when
+ * (a,b) as above
+ * (c) objPtr was of the form end-N where N was in range 0:INT_MAX-1
+ * (3) 2*INT_MAX:WIDE_MAX when
+ * (a,b) as above
+ * (c) objPtr was of the form end+N
+ * (4) (2*INT_MAX)-TCL_SIZE_MAX : -1 when
+ * (a,b) as above
+ * (c) objPtr was of the form end-N where N was in the range 0:TCL_SIZE_MAX
+ * (5) WIDE_MIN:(2*INT_MAX)-TCL_SIZE_MAX
+ * (a,b) as above
+ * (c) objPtr was of the form end-N where N was > TCL_SIZE_MAX
+ *
+ * For all cases (b) and (c), the internal representation of objPtr
+ * will be shimmered to endOffsetType. That allows us to distinguish between
+ * (for example) 1a (encodable) and 1c (not encodable) though the computed
+ * index value is the same.
+ *
+ * Further note, the values TCL_SIZE_MAX < N < WIDE_MAX come into play
+ * only in the 32-bit builds as TCL_SIZE_MAX == WIDE_MAX for 64-bits.
+ */
- if (TCL_OK == GetWideForIndex(interp, objPtr, (unsigned)TCL_INDEX_END , &wide)) {
- const Tcl_ObjInternalRep *irPtr = TclFetchInternalRep(objPtr, &endOffsetType.objType);
- if (irPtr && irPtr->wideValue >= 0) {
- /* "int[+-]int" syntax, works the same here as "int" */
- irPtr = NULL;
+ const Tcl_ObjInternalRep *irPtr =
+ TclFetchInternalRep(objPtr, &endOffsetType.objType);
+
+ if (irPtr && irPtr->wideValue >= 0) {
+ /*
+ * "int[+-]int" syntax, works the same here as "int".
+ * Note same does not hold for negative integers.
+ * Distinguishes 1b and 1c where wide will be in 0:INT_MAX for
+ * both but irPtr->wideValue will be negative for 1c.
+ */
+ irPtr = NULL;
+ }
+
+ if (irPtr == NULL) {
+ /* objPtr can be treated as a purely numeric value. */
+
+ /*
+ * On 64-bit systems, indices in the range INT_MAX:TCL_SIZE_MAX are
+ * valid indices but are not in the encodable range. Thus an
+ * error is raised. On 32-bit systems, indices in that range indicate
+ * the position after the end and so do not raise an error.
+ */
+ if ((sizeof(int) != sizeof(size_t)) &&
+ (wide > INT_MAX) && (wide < WIDE_MAX-1)) {
+ /* 2(a,b) on 64-bit systems*/
+ goto rangeerror;
}
+ if (wide > INT_MAX) {
+ /*
+ * 3(a,b) on 64-bit systems and 2(a,b), 3(a,b) on 32-bit systems
+ * Because of the check above, this case holds for indices
+ * greater than INT_MAX on 32-bit systems and > TCL_SIZE_MAX
+ * on 64-bit systems. Always maps to the element after the end.
+ */
+ idx = after;
+ } else if (wide < 0) {
+ /* 4(a,b) (32-bit systems), 5(a,b) - before the beginning */
+ idx = before;
+ } else {
+ /* 1(a,b) Encodable range */
+ idx = (int)wide;
+ }
+ } else {
+ /* objPtr is not purely numeric (end etc.) */
+
/*
- * We parsed an end+offset index value.
- * wide holds the offset value in the range WIDE_MIN...WIDE_MAX.
+ * On 64-bit systems, indices in the range end-LIST_MAX:end-INT_MAX
+ * are valid indices (with max size strings/lists) but are not in
+ * the encodable range. Thus an error is raised. On 32-bit systems,
+ * indices in that range indicate the position before the beginning
+ * and so do not raise an error.
*/
- if ((irPtr ? ((wide < INT_MIN) && ((size_t)-wide <= LIST_MAX))
- : ((wide > INT_MAX) && ((size_t)wide <= LIST_MAX))) && (sizeof(int) != sizeof(size_t))) {
- if (interp) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "index \"%s\" out of range",
- TclGetString(objPtr)));
- Tcl_SetErrorCode(interp, "TCL", "VALUE", "INDEX"
- "OUTOFRANGE", NULL);
- }
- return TCL_ERROR;
- } else if (wide > (unsigned)(irPtr ? TCL_INDEX_END : INT_MAX)) {
+ if ((sizeof(int) != sizeof(size_t)) &&
+ (wide > (ENDVALUE - LIST_MAX)) && (wide <= INT_MAX)) {
+ /* 1(c), 4(a,b) on 64-bit systems */
+ goto rangeerror;
+ }
+ if (wide > ENDVALUE) {
/*
- * All end+postive or end-negative expressions
+ * 2(c) (32-bit systems), 3(c)
+ * All end+positive or end-negative expressions
* always indicate "after the end".
+ * Note we will not reach here for a pure numeric value in this
+ * range because irPtr will be NULL in that case.
*/
idx = after;
- } else if (wide <= (irPtr ? INT_MAX : -1)) {
- /* These indices always indicate "before the beginning" */
+ } else if (wide <= INT_MAX) {
+ /* 1(c) (32-bit systems), 4(c) (32-bit systems), 5(c) */
idx = before;
} else {
- /* Encoded end-positive (or end+negative) are offset */
+ /* 2(c) Encodable end-positive (or end+negative) */
idx = (int)wide;
}
- } else {
- return TCL_ERROR;
}
*indexPtr = idx;
return TCL_OK;
+
+rangeerror:
+ if (interp) {
+ Tcl_SetObjResult(
+ interp,
+ Tcl_ObjPrintf("index \"%s\" out of range", TclGetString(objPtr)));
+ Tcl_SetErrorCode(interp,
+ "TCL",
+ "VALUE",
+ "INDEX"
+ "OUTOFRANGE",
+ NULL);
+ }
+ return TCL_ERROR;
}
/*
@@ -3806,21 +3931,52 @@ TclIndexEncode(
*----------------------------------------------------------------------
*/
-size_t
+Tcl_Size
TclIndexDecode(
int encoded, /* Value to decode */
- size_t endValue) /* Meaning of "end" to use, > TCL_INDEX_END */
+ Tcl_Size endValue) /* Meaning of "end" to use, > TCL_INDEX_END */
{
- if (encoded > (int)TCL_INDEX_END) {
+ if (encoded > TCL_INDEX_END) {
return encoded;
}
- if (endValue >= TCL_INDEX_END - encoded) {
- return endValue + encoded - TCL_INDEX_END;
+ endValue += encoded - TCL_INDEX_END;
+ if (endValue >= 0) {
+ return endValue;
}
return TCL_INDEX_NONE;
}
/*
+ *------------------------------------------------------------------------
+ *
+ * TclIndexInvalidError --
+ *
+ * Generates an error message including the invalid index.
+ *
+ * Results:
+ * Always return TCL_ERROR.
+ *
+ * Side effects:
+ * If interp is not-NULL, an error message is stored in it.
+ *
+ *------------------------------------------------------------------------
+ */
+int
+TclIndexInvalidError (
+ Tcl_Interp *interp, /* May be NULL */
+ const char *idxType, /* The descriptive string for idx. Defaults to "index" */
+ Tcl_Size idx) /* Invalid index value */
+{
+ if (interp) {
+ Tcl_SetObjResult(interp,
+ Tcl_ObjPrintf("Invalid %s value %" TCL_SIZE_MODIFIER "d.",
+ idxType ? idxType : "index",
+ idx));
+ }
+ return TCL_ERROR; /* Always */
+}
+
+/*
*----------------------------------------------------------------------
*
* ClearHash --
@@ -4410,7 +4566,7 @@ TclReToGlob(
invalidGlob:
if (interp != NULL) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(msg, TCL_INDEX_NONE));
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(msg, -1));
Tcl_SetErrorCode(interp, "TCL", "RE2GLOB", code, NULL);
}
Tcl_DStringFree(dsPtr);