From 516f8e365dd94bfc9af0fb2d86516cae5bb2ec7f Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Wed, 27 Jun 2012 15:12:46 +0000 Subject: Experimental support for UNC paths (through VFS) on UNIX/Mac --- doc/filename.n | 7 +++---- generic/tclFileName.c | 8 ++------ tests/cmdAH.test | 8 ++++---- tests/fileName.test | 6 +++--- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/doc/filename.n b/doc/filename.n index d481fc9..f1cd703 100644 --- a/doc/filename.n +++ b/doc/filename.n @@ -47,7 +47,8 @@ absolute, and file names may contain any character other than slash. The file names \fB\&.\fR and \fB\&..\fR are special and refer to the current directory and the parent of the current directory respectively. Multiple adjacent slash characters are interpreted as a single -separator. Any number of trailing slash characters at the end of a +separator, except for the first double slash \fB//\fR in absolute paths. +Any number of trailing slash characters at the end of a path are simply ignored, so the paths \fBfoo\fR, \fBfoo/\fR and \fBfoo//\fR are all identical, and in particular \fBfoo/\fR does not necessarily mean a directory is being referred. @@ -150,9 +151,7 @@ The safest approach is to use names consisting of alphanumeric characters only. Care should be taken with filenames which contain spaces (common on Windows systems) and filenames where the backslash is the directory separator (Windows -native path names). Also Windows 3.1 only supports file -names with a root of no more than 8 characters and an extension of no -more than 3 characters. +native path names). .PP On Windows platforms there are file and path length restrictions. Complete paths or filenames longer than about 260 characters will lead diff --git a/generic/tclFileName.c b/generic/tclFileName.c index 48c5454..3e78a05 100644 --- a/generic/tclFileName.c +++ b/generic/tclFileName.c @@ -424,14 +424,12 @@ TclpGetNativePathType( } #endif if (path[0] == '/') { -#ifdef __CYGWIN__ /* - * Check for Cygwin // network path prefix + * Check for "//" prefix */ if (path[1] == '/') { path++; } -#endif if (driveNameLengthPtr != NULL) { /* * We need this addition in case the QNX or Cygwin code was used. @@ -665,15 +663,13 @@ SplitUnixPath( if (*p == '/') { Tcl_Obj *rootElt = Tcl_NewStringObj("/", 1); p++; -#ifdef __CYGWIN__ /* - * Check for Cygwin // network path prefix + * Check for "//" prefix */ if (*p == '/') { Tcl_AppendToObj(rootElt, "/", 1); p++; } -#endif Tcl_ListObjAppendElement(NULL, result, rootElt); } diff --git a/tests/cmdAH.test b/tests/cmdAH.test index 291df8d..fbe51d2 100644 --- a/tests/cmdAH.test +++ b/tests/cmdAH.test @@ -303,19 +303,19 @@ test cmdAH-8.13 {Tcl_FileObjCmd: dirname} testsetplatform { test cmdAH-8.14 {Tcl_FileObjCmd: dirname} testsetplatform { testsetplatform unix file dirname //foo -} / +} // test cmdAH-8.15 {Tcl_FileObjCmd: dirname} testsetplatform { testsetplatform unix file dirname //foo/bar -} /foo +} //foo test cmdAH-8.16 {Tcl_FileObjCmd: dirname} testsetplatform { testsetplatform unix file dirname {//foo\/bar/baz} -} {/foo\/bar} +} {//foo\/bar} test cmdAH-8.17 {Tcl_FileObjCmd: dirname} testsetplatform { testsetplatform unix file dirname {//foo\/bar/baz/blat} -} {/foo\/bar/baz} +} {//foo\/bar/baz} test cmdAH-8.18 {Tcl_FileObjCmd: dirname} testsetplatform { testsetplatform unix file dirname /foo// diff --git a/tests/fileName.test b/tests/fileName.test index 251f12c..aab8d33 100644 --- a/tests/fileName.test +++ b/tests/fileName.test @@ -196,7 +196,7 @@ test filename-4.12 {Tcl_SplitPath: unix} {testsetplatform} { test filename-4.13 {Tcl_SplitPath: unix} {testsetplatform} { testsetplatform unix file split //foo -} "[file split //] foo" +} "// foo" test filename-4.14 {Tcl_SplitPath: unix} {testsetplatform} { testsetplatform unix file split foo//bar @@ -433,11 +433,11 @@ test filename-7.16 {Tcl_JoinPath: unix} {testsetplatform} { test filename-7.17 {Tcl_JoinPath: unix} {testsetplatform} { testsetplatform unix file join //a b -} "[file split //]a/b" +} "//a/b" test filename-7.18 {Tcl_JoinPath: unix} {testsetplatform} { testsetplatform unix file join /// a b -} "[file split //]a/b" +} "//a/b" test filename-9.1 {Tcl_JoinPath: win} {testsetplatform} { testsetplatform win -- cgit v0.12 From f942af920c1c31f578e24aae999d86191ec65f39 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Thu, 2 Aug 2012 15:44:12 +0000 Subject: integrate QNX special path handling better with TIP #402 --- generic/tclFileName.c | 46 +++++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/generic/tclFileName.c b/generic/tclFileName.c index 013f788..cae2657 100644 --- a/generic/tclFileName.c +++ b/generic/tclFileName.c @@ -411,28 +411,24 @@ TclpGetNativePathType( * Paths that begin with / are absolute. */ -#ifdef __QNX__ - /* - * Check for QNX // prefix - */ - if (*path && (pathLen > 3) && (path[0] == '/') - && (path[1] == '/') && isdigit(UCHAR(path[2]))) { - path += 3; - while (isdigit(UCHAR(*path))) { - path++; - } - } -#endif if (path[0] == '/') { /* * Check for "//" prefix */ if (path[1] == '/') { path++; +#ifdef __QNX__ + /* + * Check for QNX // prefix + */ + while (isdigit(UCHAR(path[1]))) { + path++; + } +#endif } if (driveNameLengthPtr != NULL) { /* - * We need this addition in case the QNX or Cygwin code was used. + * We need this addition in case the QNX or "//" code was used. */ *driveNameLengthPtr = (1 + path - origPath); @@ -645,20 +641,6 @@ SplitUnixPath( * Deal with the root directory as a special case. */ -#ifdef __QNX__ - /* - * Check for QNX // prefix - */ - - if ((path[0] == '/') && (path[1] == '/') - && isdigit(UCHAR(path[2]))) { /* INTL: digit */ - path += 3; - while (isdigit(UCHAR(*path))) { /* INTL: digit */ - path++; - } - } -#endif - p = path; if (*p == '/') { Tcl_Obj *rootElt = Tcl_NewStringObj("/", 1); @@ -669,6 +651,16 @@ SplitUnixPath( if (*p == '/') { Tcl_AppendToObj(rootElt, "/", 1); p++; +#ifdef __QNX__ */ + /* + * Check for QNX // prefix + */ + + while (isdigit(UCHAR(*p))) { /* INTL: digit */ + Tcl_AppendToObj(rootElt, p, 1); + p++; + } +#endif } Tcl_ListObjAppendElement(NULL, result, rootElt); } -- cgit v0.12 From e3074edc8ab3d499540f736d2beebe02d46200aa Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 22 Feb 2022 21:01:20 +0000 Subject: TIP #618: New Tcl_GetBool* functions with INDEX_NULL_OK flag --- doc/BoolObj.3 | 13 ++++++++--- doc/GetIndex.3 | 4 ++-- generic/tcl.decls | 13 +++++++++-- generic/tcl.h | 4 ++-- generic/tclCmdMZ.c | 2 +- generic/tclDecls.h | 61 +++++++++++++++++++++++++++++++++++++++++++++++---- generic/tclExecute.c | 8 +++---- generic/tclGet.c | 23 ++++++++++++++++--- generic/tclIndexObj.c | 8 +++---- generic/tclInt.h | 4 ++-- generic/tclObj.c | 57 ++++++++++++++++++++++++++++++++++++++--------- generic/tclStubInit.c | 9 ++++++++ generic/tclTest.c | 15 +++++++------ tests/indexObj.test | 4 ++-- 14 files changed, 179 insertions(+), 46 deletions(-) diff --git a/doc/BoolObj.3 b/doc/BoolObj.3 index 9bbdc7e..afbd1d1 100644 --- a/doc/BoolObj.3 +++ b/doc/BoolObj.3 @@ -9,7 +9,7 @@ .so man.macros .BS .SH NAME -Tcl_NewBooleanObj, Tcl_SetBooleanObj, Tcl_GetBooleanFromObj \- store/retrieve boolean value in a Tcl_Obj +Tcl_NewBooleanObj, Tcl_SetBooleanObj, Tcl_GetBooleanFromObj, Tcl_GetBoolFromObj \- store/retrieve boolean value in a Tcl_Obj .SH SYNOPSIS .nf \fB#include \fR @@ -21,6 +21,9 @@ Tcl_Obj * .sp int \fBTcl_GetBooleanFromObj\fR(\fIinterp, objPtr, boolPtr\fR) +.sp +int +\fBTcl_GetBoolFromObj\fR(\fIinterp, objPtr, flags. boolPtr\fR) .SH ARGUMENTS .AS Tcl_Interp boolValue in/out .AP int boolValue in @@ -32,9 +35,13 @@ retrieve a boolean value. If a boolean value cannot be retrieved, an error message is left in the interpreter's result value unless \fIinterp\fR is NULL. -.AP int *boolPtr out -Points to place where \fBTcl_GetBooleanFromObj\fR +.AP int | short | char *boolPtr out +Points to place where \fBTcl_GetBooleanFromObj\fR/\fBTcl_GetBoolFromObj\fR stores the boolean value (0 or 1) obtained from \fIobjPtr\fR. +.AP int flags in +Value 0 or TCL_NULL_OK. If TCL_NULL_OK, then the empty +string or NULL will result in \fBTcl_GetBoolFromObj\fR return +TCL_OK, the *boolPtr filled with the value -1; .BE .SH DESCRIPTION diff --git a/doc/GetIndex.3 b/doc/GetIndex.3 index 1169c6c..176b0b2 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 -, \fBTCL_INDEX_TEMP_TABLE\fR, and \fBTCL_INDEX_NULL_OK\fR. +, \fBTCL_INDEX_TEMP_TABLE\fR, and \fBTCL_NULL_OK\fR. .AP enum|char|short|int|long *indexPtr out If not (int *)NULL, the index of the string in \fItablePtr\fR that matches the value of \fIobjPtr\fR is returned here. The variable can @@ -93,7 +93,7 @@ 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 \fBTCL_INDEX_NULL_OK\fR flag was specified, objPtr is allowed +If the \fBTCL_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 diff --git a/generic/tcl.decls b/generic/tcl.decls index c137a88..4d630ca 100644 --- a/generic/tcl.decls +++ b/generic/tcl.decls @@ -136,11 +136,11 @@ declare 30 { void TclFreeObj(Tcl_Obj *objPtr) } declare 31 { - int Tcl_GetBoolean(Tcl_Interp *interp, const char *src, int *boolPtr) + int Tcl_GetBoolean(Tcl_Interp *interp, const char *src, void *boolPtr) } declare 32 { int Tcl_GetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, - int *boolPtr) + void *boolPtr) } declare 33 { unsigned char *Tcl_GetByteArrayFromObj(Tcl_Obj *objPtr, int *numBytesPtr) @@ -2442,6 +2442,15 @@ declare 660 { int Tcl_AsyncMarkFromSignal(Tcl_AsyncHandler async, int sigNumber) } +declare 668 { + int Tcl_GetBool(Tcl_Interp *interp, const char *src, int flags, + void *boolPtr) +} +declare 669 { + int Tcl_GetBoolFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, + int flags, void *boolPtr) +} + # ----- BASELINE -- FOR -- 8.7.0 ----- # ############################################################################## diff --git a/generic/tcl.h b/generic/tcl.h index b82cf0a..560d441 100644 --- a/generic/tcl.h +++ b/generic/tcl.h @@ -979,13 +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. + * TCL_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 +#define TCL_NULL_OK 32 /* *---------------------------------------------------------------------------- diff --git a/generic/tclCmdMZ.c b/generic/tclCmdMZ.c index f394035..6b991eb 100644 --- a/generic/tclCmdMZ.c +++ b/generic/tclCmdMZ.c @@ -1620,7 +1620,7 @@ StringIsCmd( result = length1 == 0; } } else if (index != STR_IS_BOOL) { - TclGetBooleanFromObj(NULL, objPtr, &i); + TclGetBoolFromObj(NULL, objPtr, 0, &i); if ((index == STR_IS_TRUE) ^ i) { result = 0; } diff --git a/generic/tclDecls.h b/generic/tclDecls.h index b5697ea..da28cb7 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -146,10 +146,10 @@ EXTERN Tcl_Obj * Tcl_DuplicateObj(Tcl_Obj *objPtr); EXTERN void TclFreeObj(Tcl_Obj *objPtr); /* 31 */ EXTERN int Tcl_GetBoolean(Tcl_Interp *interp, const char *src, - int *boolPtr); + void *boolPtr); /* 32 */ EXTERN int Tcl_GetBooleanFromObj(Tcl_Interp *interp, - Tcl_Obj *objPtr, int *boolPtr); + Tcl_Obj *objPtr, void *boolPtr); /* 33 */ EXTERN unsigned char * Tcl_GetByteArrayFromObj(Tcl_Obj *objPtr, int *numBytesPtr); @@ -1948,6 +1948,19 @@ EXTERN int Tcl_UniCharIsUnicode(int ch); /* 660 */ EXTERN int Tcl_AsyncMarkFromSignal(Tcl_AsyncHandler async, int sigNumber); +/* Slot 661 is reserved */ +/* Slot 662 is reserved */ +/* Slot 663 is reserved */ +/* Slot 664 is reserved */ +/* Slot 665 is reserved */ +/* Slot 666 is reserved */ +/* Slot 667 is reserved */ +/* 668 */ +EXTERN int Tcl_GetBool(Tcl_Interp *interp, const char *src, + int flags, void *boolPtr); +/* 669 */ +EXTERN int Tcl_GetBoolFromObj(Tcl_Interp *interp, + Tcl_Obj *objPtr, int flags, void *boolPtr); typedef struct { const struct TclPlatStubs *tclPlatStubs; @@ -2006,8 +2019,8 @@ typedef struct TclStubs { Tcl_Obj * (*tcl_DbNewStringObj) (const char *bytes, int length, const char *file, int line); /* 28 */ Tcl_Obj * (*tcl_DuplicateObj) (Tcl_Obj *objPtr); /* 29 */ void (*tclFreeObj) (Tcl_Obj *objPtr); /* 30 */ - int (*tcl_GetBoolean) (Tcl_Interp *interp, const char *src, int *boolPtr); /* 31 */ - int (*tcl_GetBooleanFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, int *boolPtr); /* 32 */ + int (*tcl_GetBoolean) (Tcl_Interp *interp, const char *src, void *boolPtr); /* 31 */ + int (*tcl_GetBooleanFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, void *boolPtr); /* 32 */ unsigned char * (*tcl_GetByteArrayFromObj) (Tcl_Obj *objPtr, int *numBytesPtr); /* 33 */ int (*tcl_GetDouble) (Tcl_Interp *interp, const char *src, double *doublePtr); /* 34 */ int (*tcl_GetDoubleFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, double *doublePtr); /* 35 */ @@ -2644,6 +2657,15 @@ typedef struct TclStubs { void (*reserved658)(void); void (*reserved659)(void); int (*tcl_AsyncMarkFromSignal) (Tcl_AsyncHandler async, int sigNumber); /* 660 */ + void (*reserved661)(void); + void (*reserved662)(void); + void (*reserved663)(void); + void (*reserved664)(void); + void (*reserved665)(void); + void (*reserved666)(void); + void (*reserved667)(void); + int (*tcl_GetBool) (Tcl_Interp *interp, const char *src, int flags, void *boolPtr); /* 668 */ + int (*tcl_GetBoolFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, int flags, void *boolPtr); /* 669 */ } TclStubs; extern const TclStubs *tclStubsPtr; @@ -3994,6 +4016,17 @@ extern const TclStubs *tclStubsPtr; /* Slot 659 is reserved */ #define Tcl_AsyncMarkFromSignal \ (tclStubsPtr->tcl_AsyncMarkFromSignal) /* 660 */ +/* Slot 661 is reserved */ +/* Slot 662 is reserved */ +/* Slot 663 is reserved */ +/* Slot 664 is reserved */ +/* Slot 665 is reserved */ +/* Slot 666 is reserved */ +/* Slot 667 is reserved */ +#define Tcl_GetBool \ + (tclStubsPtr->tcl_GetBool) /* 668 */ +#define Tcl_GetBoolFromObj \ + (tclStubsPtr->tcl_GetBoolFromObj) /* 669 */ #endif /* defined(USE_TCL_STUBS) */ @@ -4210,6 +4243,10 @@ extern const TclStubs *tclStubsPtr; Tcl_GetUnicodeFromObj(objPtr, (int *)NULL) #undef Tcl_GetBytesFromObj #undef Tcl_GetIndexFromObjStruct +#undef Tcl_GetBoolFromObj +#undef Tcl_GetBool +#undef Tcl_GetBooleanFromObj +#undef Tcl_GetBoolean #ifdef TCL_NO_DEPRECATED #undef Tcl_GetStringFromObj #undef Tcl_GetUnicodeFromObj @@ -4220,6 +4257,14 @@ extern const TclStubs *tclStubsPtr; (sizeof(*sizePtr) <= sizeof(int) ? tclStubsPtr->tclGetBytesFromObj(interp, objPtr, (int *)sizePtr) : tclStubsPtr->tcl_GetBytesFromObj(interp, objPtr, (size_t *)sizePtr)) #define Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, offset, msg, flags, indexPtr) \ (tclStubsPtr->tcl_GetIndexFromObjStruct((interp), (objPtr), (tablePtr), (offset), (msg), (flags)|(int)(sizeof(*indexPtr)<<8), (indexPtr))) +#define Tcl_GetBoolFromObj(interp, objPtr, flags, boolPtr) \ + (tclStubsPtr->tcl_GetBoolFromObj((interp), (objPtr), (flags)|(int)(sizeof(*boolPtr)<<8), (boolPtr))) +#define Tcl_GetBool(interp, objPtr, flags, boolPtr) \ + (tclStubsPtr->tcl_GetBool((interp), (objPtr), (flags)|(int)(sizeof(*boolPtr)<<8), (boolPtr))) +#define Tcl_GetBooleanFromObj(interp, objPtr, boolPtr) \ + (sizeof(*boolPtr) == sizeof(int) ? tclStubsPtr->tcl_GetBooleanFromObj(interp, objPtr, (int *)boolPtr) : Tcl_GetBoolFromObj(interp, objPtr, 0, boolPtr)) +#define Tcl_GetBoolean(interp, src, boolPtr) \ + (sizeof(*boolPtr) == sizeof(int) ? tclStubsPtr->tcl_GetBoolean(interp, src, (int *)boolPtr) : Tcl_GetBool(interp, src, 0, boolPtr)) #ifdef TCL_NO_DEPRECATED #define Tcl_GetStringFromObj(objPtr, sizePtr) \ (sizeof(*sizePtr) <= sizeof(int) ? tclStubsPtr->tcl_GetStringFromObj(objPtr, (int *)sizePtr) : tclStubsPtr->tclGetStringFromObj(objPtr, (size_t *)sizePtr)) @@ -4233,6 +4278,14 @@ extern const TclStubs *tclStubsPtr; (sizeof(*sizePtr) <= sizeof(int) ? (TclGetBytesFromObj)(interp, objPtr, (int *)sizePtr) : (Tcl_GetBytesFromObj)(interp, objPtr, (size_t *)sizePtr)) #define Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, offset, msg, flags, indexPtr) \ ((Tcl_GetIndexFromObjStruct)((interp), (objPtr), (tablePtr), (offset), (msg), (flags)|(int)(sizeof(*indexPtr)<<8), (indexPtr))) +#define Tcl_GetBoolFromObj(interp, objPtr, flags, boolPtr) \ + ((Tcl_GetBoolFromObj)((interp), (objPtr), (flags)|(int)(sizeof(*boolPtr)<<8), (boolPtr))) +#define Tcl_GetBool(interp, objPtr, flags, boolPtr) \ + ((Tcl_GetBool)((interp), (objPtr), (flags)|(int)(sizeof(*boolPtr)<<8), (boolPtr))) +#define Tcl_GetBooleanFromObj(interp, objPtr, boolPtr) \ + (sizeof(*boolPtr) == sizeof(int) ? (Tcl_GetBooleanFromObj)(interp, objPtr, (int *)boolPtr) : Tcl_GetBoolFromObj(interp, objPtr, 0, boolPtr)) +#define Tcl_GetBoolean(interp, src, boolPtr) \ + (sizeof(*boolPtr) == sizeof(int) ? (Tcl_GetBoolean)(interp, src, (int *)boolPtr) : Tcl_GetBool(interp, src, 0, boolPtr)) #ifdef TCL_NO_DEPRECATED #define Tcl_GetStringFromObj(objPtr, sizePtr) \ (sizeof(*sizePtr) <= sizeof(int) ? (Tcl_GetStringFromObj)(objPtr, (int *)sizePtr) : (TclGetStringFromObj)(objPtr, (size_t *)sizePtr)) diff --git a/generic/tclExecute.c b/generic/tclExecute.c index dfb195a..2c4cde4 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -4346,7 +4346,7 @@ TEBCresume( /* TODO - check claim that taking address of b harms performance */ /* TODO - consider optimization search for constants */ - if (TclGetBooleanFromObj(interp, valuePtr, &b) != TCL_OK) { + if (TclGetBoolFromObj(interp, valuePtr, 0, &b) != TCL_OK) { TRACE_ERROR(interp); goto gotError; } @@ -4414,7 +4414,7 @@ TEBCresume( value2Ptr = OBJ_AT_TOS; valuePtr = OBJ_UNDER_TOS; - if (TclGetBooleanFromObj(NULL, valuePtr, &i1) != TCL_OK) { + if (TclGetBoolFromObj(NULL, valuePtr, 0, &i1) != TCL_OK) { TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(valuePtr), (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); DECACHE_STACK_INFO(); @@ -4423,7 +4423,7 @@ TEBCresume( goto gotError; } - if (TclGetBooleanFromObj(NULL, value2Ptr, &i2) != TCL_OK) { + if (TclGetBoolFromObj(NULL, value2Ptr, 0, &i2) != TCL_OK) { TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(value2Ptr), (value2Ptr->typePtr? value2Ptr->typePtr->name : "null"))); DECACHE_STACK_INFO(); @@ -6222,7 +6222,7 @@ TEBCresume( /* TODO - check claim that taking address of b harms performance */ /* TODO - consider optimization search for constants */ - if (TclGetBooleanFromObj(NULL, valuePtr, &b) != TCL_OK) { + if (TclGetBoolFromObj(NULL, valuePtr, 0, &b) != TCL_OK) { TRACE(("\"%.20s\" => ERROR: illegal type %s\n", O2S(valuePtr), (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); DECACHE_STACK_INFO(); diff --git a/generic/tclGet.c b/generic/tclGet.c index 970e093..1beac24 100644 --- a/generic/tclGet.c +++ b/generic/tclGet.c @@ -121,17 +121,22 @@ Tcl_GetDouble( *---------------------------------------------------------------------- */ +#undef Tcl_GetBool int -Tcl_GetBoolean( +Tcl_GetBool( Tcl_Interp *interp, /* Interpreter used for error reporting. */ const char *src, /* String containing one of the boolean values * 1, 0, true, false, yes, no, on, off. */ - int *boolPtr) /* Place to store converted result, which will + int flags, + void *boolPtr) /* Place to store converted result, which will * be 0 or 1. */ { Tcl_Obj obj; int code; + if (((src == NULL) || (*src == '\0')) && (flags & TCL_NULL_OK)) { + return (Tcl_GetBoolFromObj)(NULL, NULL, flags, boolPtr); + } obj.refCount = 1; obj.bytes = (char *) src; obj.length = strlen(src); @@ -142,10 +147,22 @@ Tcl_GetBoolean( Tcl_Panic("invalid sharing of Tcl_Obj on C stack"); } if (code == TCL_OK) { - TclGetBooleanFromObj(NULL, &obj, boolPtr); + (Tcl_GetBoolFromObj)(NULL, &obj, flags, boolPtr); } return code; } + +#undef Tcl_GetBoolean +int +Tcl_GetBoolean( + Tcl_Interp *interp, /* Interpreter used for error reporting. */ + const char *src, /* String containing one of the boolean values + * 1, 0, true, false, yes, no, on, off. */ + void *boolPtr) /* Place to store converted result, which will + * be 0 or 1. */ +{ + return Tcl_GetBool(interp, src, 0, boolPtr); +} /* * Local Variables: diff --git a/generic/tclIndexObj.c b/generic/tclIndexObj.c index 1f600c5..f5e3958 100644 --- a/generic/tclIndexObj.c +++ b/generic/tclIndexObj.c @@ -263,7 +263,7 @@ Tcl_GetIndexFromObjStruct( int offset, /* The number of bytes between entries */ const char *msg, /* Identifying word to use in error * messages. */ - int flags, /* 0, TCL_EXACT, TCL_INDEX_TEMP_TABLE or TCL_INDEX_NULL_OK */ + int flags, /* 0, TCL_EXACT, TCL_INDEX_TEMP_TABLE or TCL_NULL_OK */ void *indexPtr) /* Place to store resulting index. */ { int index, idx, numAbbrev; @@ -304,7 +304,7 @@ Tcl_GetIndexFromObjStruct( index = -1; numAbbrev = 0; - if (!*key && (flags & TCL_INDEX_NULL_OK)) { + if (!*key && (flags & TCL_NULL_OK)) { goto uncachedDone; } /* @@ -411,7 +411,7 @@ Tcl_GetIndexFromObjStruct( *entryPtr, NULL); entryPtr = NEXT_ENTRY(entryPtr, offset); while (*entryPtr != NULL) { - if ((*NEXT_ENTRY(entryPtr, offset) == NULL) && !(flags & TCL_INDEX_NULL_OK)) { + if ((*NEXT_ENTRY(entryPtr, offset) == NULL) && !(flags & TCL_NULL_OK)) { Tcl_AppendStringsToObj(resultPtr, (count > 0 ? "," : ""), " or ", *entryPtr, NULL); } else if (**entryPtr) { @@ -420,7 +420,7 @@ Tcl_GetIndexFromObjStruct( } entryPtr = NEXT_ENTRY(entryPtr, offset); } - if ((flags & TCL_INDEX_NULL_OK)) { + if ((flags & TCL_NULL_OK)) { Tcl_AppendStringsToObj(resultPtr, ", or \"\"", NULL); } } diff --git a/generic/tclInt.h b/generic/tclInt.h index 75cd6e5..25593b2 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -2504,12 +2504,12 @@ typedef struct List { * WARNING: these macros eval their args more than once. */ -#define TclGetBooleanFromObj(interp, objPtr, boolPtr) \ +#define TclGetBoolFromObj(interp, objPtr, flags, boolPtr) \ (((objPtr)->typePtr == &tclIntType) \ ? (*(boolPtr) = ((objPtr)->internalRep.wideValue!=0), TCL_OK) \ : ((objPtr)->typePtr == &tclBooleanType) \ ? (*(boolPtr) = ((objPtr)->internalRep.longValue!=0), TCL_OK) \ - : Tcl_GetBooleanFromObj((interp), (objPtr), (boolPtr))) + : Tcl_GetBoolFromObj((interp), (objPtr), (flags), (boolPtr))) #ifdef TCL_WIDE_INT_IS_LONG #define TclGetLongFromObj(interp, objPtr, longPtr) \ diff --git a/generic/tclObj.c b/generic/tclObj.c index a06b8fd..636f8e0 100644 --- a/generic/tclObj.c +++ b/generic/tclObj.c @@ -2139,7 +2139,7 @@ Tcl_SetBooleanObj( /* *---------------------------------------------------------------------- * - * Tcl_GetBooleanFromObj -- + * Tcl_GetBoolFromObj, Tcl_GetBooleanFromObj -- * * Attempt to return a boolean from the Tcl object "objPtr". This * includes conversion from any of Tcl's numeric types. @@ -2155,20 +2155,28 @@ Tcl_SetBooleanObj( *---------------------------------------------------------------------- */ +#undef Tcl_GetBoolFromObj int -Tcl_GetBooleanFromObj( +Tcl_GetBoolFromObj( Tcl_Interp *interp, /* Used for error reporting if not NULL. */ Tcl_Obj *objPtr, /* The object from which to get boolean. */ - int *boolPtr) /* Place to store resulting boolean. */ + int flags, + void *boolPtr) /* Place to store resulting boolean. */ { + int result; + + if ((flags & TCL_NULL_OK) && (objPtr == NULL || Tcl_GetString(objPtr)[0] == '\0')) { + result = -1; + goto boolEnd; + } do { if (objPtr->typePtr == &tclIntType) { - *boolPtr = (objPtr->internalRep.wideValue != 0); - return TCL_OK; + result = (objPtr->internalRep.wideValue != 0); + goto boolEnd; } if (objPtr->typePtr == &tclBooleanType) { - *boolPtr = objPtr->internalRep.longValue != 0; - return TCL_OK; + result = objPtr->internalRep.longValue != 0; + goto boolEnd; } if (objPtr->typePtr == &tclDoubleType) { /* @@ -2184,11 +2192,30 @@ Tcl_GetBooleanFromObj( if (Tcl_GetDoubleFromObj(interp, objPtr, &d) != TCL_OK) { return TCL_ERROR; } - *boolPtr = (d != 0.0); - return TCL_OK; + result = (d != 0.0); + goto boolEnd; } if (objPtr->typePtr == &tclBignumType) { - *boolPtr = 1; + result = 1; + boolEnd: + if (boolPtr != NULL) { + if ((flags>>8) & (int)~sizeof(int)) { + if ((flags>>8) == sizeof(uint64_t)) { + *(uint64_t *)boolPtr = result; + return TCL_OK; + } else if ((flags>>8) == sizeof(uint32_t)) { + *(uint32_t *)boolPtr = result; + return TCL_OK; + } else if ((flags>>8) == sizeof(uint16_t)) { + *(uint16_t *)boolPtr = result; + return TCL_OK; + } else if ((flags>>8) == sizeof(uint8_t)) { + *(uint8_t *)boolPtr = result; + return TCL_OK; + } + } + *(int *)boolPtr = result; + } return TCL_OK; } } while ((ParseBoolean(objPtr) == TCL_OK) || (TCL_OK == @@ -2196,6 +2223,16 @@ Tcl_GetBooleanFromObj( return TCL_ERROR; } +#undef Tcl_GetBooleanFromObj +int +Tcl_GetBooleanFromObj( + Tcl_Interp *interp, /* Used for error reporting if not NULL. */ + Tcl_Obj *objPtr, /* The object from which to get boolean. */ + void *boolPtr) /* Place to store resulting boolean. */ +{ + return Tcl_GetBoolFromObj(interp, objPtr, 0, boolPtr); +} + /* *---------------------------------------------------------------------- * diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c index a1878c1..ff2b296 100644 --- a/generic/tclStubInit.c +++ b/generic/tclStubInit.c @@ -1944,6 +1944,15 @@ const TclStubs tclStubs = { 0, /* 658 */ 0, /* 659 */ Tcl_AsyncMarkFromSignal, /* 660 */ + 0, /* 661 */ + 0, /* 662 */ + 0, /* 663 */ + 0, /* 664 */ + 0, /* 665 */ + 0, /* 666 */ + 0, /* 667 */ + Tcl_GetBool, /* 668 */ + Tcl_GetBoolFromObj, /* 669 */ }; /* !END!: Do not edit above this line. */ diff --git a/generic/tclTest.c b/generic/tclTest.c index 009c95f..97fd57f 100644 --- a/generic/tclTest.c +++ b/generic/tclTest.c @@ -2174,7 +2174,7 @@ TesteventProc( Tcl_Obj *command = ev->command; int result = Tcl_EvalObjEx(interp, command, TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT); - int retval; + char retval[3]; if (result != TCL_OK) { Tcl_AddErrorInfo(interp, @@ -2183,18 +2183,18 @@ TesteventProc( return 1; /* Avoid looping on errors */ } if (Tcl_GetBooleanFromObj(interp, Tcl_GetObjResult(interp), - &retval) != TCL_OK) { + &retval[1]) != TCL_OK) { Tcl_AddErrorInfo(interp, " (return value from \"testevent\" callback)"); Tcl_BackgroundException(interp, TCL_ERROR); return 1; } - if (retval) { + if (retval[1]) { Tcl_DecrRefCount(ev->tag); Tcl_DecrRefCount(ev->command); } - return retval; + return retval[1]; } /* @@ -5188,7 +5188,8 @@ TestsaveresultCmd( Tcl_Obj *const objv[]) /* The argument objects. */ { Interp* iPtr = (Interp*) interp; - int discard, result, index; + int result, index; + short discard[3]; Tcl_SavedResult state; Tcl_Obj *objPtr; static const char *const optionStrings[] = { @@ -5210,7 +5211,7 @@ TestsaveresultCmd( &index) != TCL_OK) { return TCL_ERROR; } - if (Tcl_GetBooleanFromObj(interp, objv[3], &discard) != TCL_OK) { + if (Tcl_GetBooleanFromObj(interp, objv[3], &discard[1]) != TCL_OK) { return TCL_ERROR; } @@ -5247,7 +5248,7 @@ TestsaveresultCmd( result = Tcl_EvalEx(interp, Tcl_GetString(objv[2]), -1, 0); } - if (discard) { + if (discard[1]) { Tcl_DiscardResult(&state); } else { Tcl_RestoreResult(interp, &state); diff --git a/tests/indexObj.test b/tests/indexObj.test index c327274..f10bd2a 100644 --- a/tests/indexObj.test +++ b/tests/indexObj.test @@ -142,8 +142,8 @@ test indexObj-6.6 {Tcl_GetIndexFromObjStruct with NULL input} -constraints testi } -returnCodes error -result {ambiguous dummy "": must be a, c, or ee} test indexObj-6.7 {Tcl_GetIndexFromObjStruct} testindexobj { set x "" - testgetindexfromobjstruct $x -1 4 -} "wrong # args: should be \"testgetindexfromobjstruct {} -1 4\"" + testgetindexfromobjstruct $x -1 32 +} "wrong # args: should be \"testgetindexfromobjstruct {} -1 32\"" test indexObj-7.1 {Tcl_ParseArgsObjv} testparseargs { testparseargs -- cgit v0.12 From c9e33a6348a3521e24d190c2d8a653a70e62f0ee Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Thu, 31 Mar 2022 13:24:31 +0000 Subject: uint??_t -> int??_t --- generic/tclObj.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/generic/tclObj.c b/generic/tclObj.c index 636f8e0..11a8530 100644 --- a/generic/tclObj.c +++ b/generic/tclObj.c @@ -2200,17 +2200,17 @@ Tcl_GetBoolFromObj( boolEnd: if (boolPtr != NULL) { if ((flags>>8) & (int)~sizeof(int)) { - if ((flags>>8) == sizeof(uint64_t)) { - *(uint64_t *)boolPtr = result; + if ((flags>>8) == sizeof(int64_t)) { + *(int64_t *)boolPtr = result; return TCL_OK; - } else if ((flags>>8) == sizeof(uint32_t)) { - *(uint32_t *)boolPtr = result; + } else if ((flags>>8) == sizeof(int32_t)) { + *(int32_t *)boolPtr = result; return TCL_OK; - } else if ((flags>>8) == sizeof(uint16_t)) { - *(uint16_t *)boolPtr = result; + } else if ((flags>>8) == sizeof(int16_t)) { + *(int16_t *)boolPtr = result; return TCL_OK; - } else if ((flags>>8) == sizeof(uint8_t)) { - *(uint8_t *)boolPtr = result; + } else if ((flags>>8) == sizeof(int8_t)) { + *(int8_t *)boolPtr = result; return TCL_OK; } } -- cgit v0.12 From d78db33c23ca9fad833989314d1288dafbfd039e Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Thu, 31 Mar 2022 13:37:09 +0000 Subject: Handle objPtr == NULL / interp == NULL better --- generic/tclObj.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/generic/tclObj.c b/generic/tclObj.c index 11a8530..ae20e16 100644 --- a/generic/tclObj.c +++ b/generic/tclObj.c @@ -2168,6 +2168,13 @@ Tcl_GetBoolFromObj( if ((flags & TCL_NULL_OK) && (objPtr == NULL || Tcl_GetString(objPtr)[0] == '\0')) { result = -1; goto boolEnd; + } else if (objPtr == NULL) { + if (interp) { + TclNewObj(objPtr); + TclParseNumber(interp, objPtr, "boolean value", NULL,-1,NULL,0); + Tcl_DecrRefCount(objPtr); + } + return TCL_ERROR; } do { if (objPtr->typePtr == &tclIntType) { -- cgit v0.12 From 9ad31cfcf31c75506cd932dfb2d637d4ff299131 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Thu, 31 Mar 2022 13:48:51 +0000 Subject: Better errpr-handling --- generic/tclGet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/generic/tclGet.c b/generic/tclGet.c index 1beac24..27f3235 100644 --- a/generic/tclGet.c +++ b/generic/tclGet.c @@ -134,8 +134,8 @@ Tcl_GetBool( Tcl_Obj obj; int code; - if (((src == NULL) || (*src == '\0')) && (flags & TCL_NULL_OK)) { - return (Tcl_GetBoolFromObj)(NULL, NULL, flags, boolPtr); + if ((src == NULL) || (*src == '\0')) { + return (Tcl_GetBoolFromObj)(interp, NULL, flags, boolPtr); } obj.refCount = 1; obj.bytes = (char *) src; -- cgit v0.12 From bcedb2cdf604551b21205b0319c6876a108893e1 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Sun, 3 Apr 2022 12:11:25 +0000 Subject: Add Ashok's example --- generic/tclTest.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/generic/tclTest.c b/generic/tclTest.c index 72a055e..8d2272c 100644 --- a/generic/tclTest.c +++ b/generic/tclTest.c @@ -30,6 +30,7 @@ #endif #include "tclOO.h" #include +#include /* * Required for Testregexp*Cmd @@ -5277,7 +5278,7 @@ TestsaveresultCmd( { Interp* iPtr = (Interp*) interp; int result, index; - short discard[3]; + bool b[3]; Tcl_SavedResult state; Tcl_Obj *objPtr; static const char *const optionStrings[] = { @@ -5299,7 +5300,7 @@ TestsaveresultCmd( &index) != TCL_OK) { return TCL_ERROR; } - if (Tcl_GetBooleanFromObj(interp, objv[3], &discard[1]) != TCL_OK) { + if (Tcl_GetBoolFromObj(interp, objv[3], 0, b+1) != TCL_OK) { return TCL_ERROR; } @@ -5336,7 +5337,7 @@ TestsaveresultCmd( result = Tcl_EvalEx(interp, Tcl_GetString(objv[2]), -1, 0); } - if (discard[1]) { + if (b[1]) { Tcl_DiscardResult(&state); } else { Tcl_RestoreResult(interp, &state); -- cgit v0.12 From 9b8a5d56d248638f34ad54292129773ed663c63a Mon Sep 17 00:00:00 2001 From: apnadkarni Date: Mon, 4 Apr 2022 01:42:45 +0000 Subject: Panic if Testsaveresult call to Tcl_GetBoolFromObj overwrites memory. --- generic/tclTest.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/generic/tclTest.c b/generic/tclTest.c index 8d2272c..51d3764 100644 --- a/generic/tclTest.c +++ b/generic/tclTest.c @@ -5300,11 +5300,17 @@ TestsaveresultCmd( &index) != TCL_OK) { return TCL_ERROR; } - if (Tcl_GetBoolFromObj(interp, objv[3], 0, b+1) != TCL_OK) { - return TCL_ERROR; - } + b[0] = b[1] = b[2] = 100; + if (Tcl_GetBoolFromObj(interp, objv[3], 0, b + 1) != TCL_OK) + { + return TCL_ERROR; + } + if (b[0] != 100 || b[2] != 100) { + Tcl_Panic("MEMORY OVERWRITE IN Tcl_GetBoolFromObj"); + return TCL_ERROR; + } - freeCount = 0; + freeCount = 0; objPtr = NULL; /* Lint. */ switch ((enum options) index) { case RESULT_SMALL: -- cgit v0.12 From 706ec57375c74eee06320a7b6c722e464a10a9ae Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 15 Apr 2022 11:50:49 +0000 Subject: Use lower 5 bits of flags for sizeof(*(boolPtr)) --- generic/tclDecls.h | 8 ++++---- generic/tclObj.c | 16 +++++++--------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/generic/tclDecls.h b/generic/tclDecls.h index 4b8c09a..3cfbc42 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -4285,9 +4285,9 @@ extern const TclStubs *tclStubsPtr; #define Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, offset, msg, flags, indexPtr) \ (tclStubsPtr->tcl_GetIndexFromObjStruct((interp), (objPtr), (tablePtr), (offset), (msg), (flags)|(int)(sizeof(*(indexPtr))<<8), (indexPtr))) #define Tcl_GetBoolFromObj(interp, objPtr, flags, boolPtr) \ - (tclStubsPtr->tcl_GetBoolFromObj((interp), (objPtr), (flags)|(int)(sizeof(*(boolPtr))<<8), (boolPtr))) + (tclStubsPtr->tcl_GetBoolFromObj((interp), (objPtr), (flags)|(int)(sizeof(*(boolPtr))), (boolPtr))) #define Tcl_GetBool(interp, objPtr, flags, boolPtr) \ - (tclStubsPtr->tcl_GetBool((interp), (objPtr), (flags)|(int)(sizeof(*(boolPtr))<<8), (boolPtr))) + (tclStubsPtr->tcl_GetBool((interp), (objPtr), (flags)|(int)(sizeof(*(boolPtr))), (boolPtr))) #define Tcl_GetBooleanFromObj(interp, objPtr, boolPtr) \ (sizeof(*(boolPtr)) == sizeof(int) ? tclStubsPtr->tcl_GetBooleanFromObj(interp, objPtr, (int *)(boolPtr)) : Tcl_GetBoolFromObj(interp, objPtr, 0, boolPtr)) #define Tcl_GetBoolean(interp, src, boolPtr) \ @@ -4306,9 +4306,9 @@ extern const TclStubs *tclStubsPtr; #define Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, offset, msg, flags, indexPtr) \ ((Tcl_GetIndexFromObjStruct)((interp), (objPtr), (tablePtr), (offset), (msg), (flags)|(int)(sizeof(*(indexPtr))<<8), (indexPtr))) #define Tcl_GetBoolFromObj(interp, objPtr, flags, boolPtr) \ - ((Tcl_GetBoolFromObj)((interp), (objPtr), (flags)|(int)(sizeof(*(boolPtr))<<8), (boolPtr))) + ((Tcl_GetBoolFromObj)((interp), (objPtr), (flags)|(int)(sizeof(*(boolPtr))), (boolPtr))) #define Tcl_GetBool(interp, objPtr, flags, boolPtr) \ - ((Tcl_GetBool)((interp), (objPtr), (flags)|(int)(sizeof(*(boolPtr))<<8), (boolPtr))) + ((Tcl_GetBool)((interp), (objPtr), (flags)|(int)(sizeof(*(boolPtr))), (boolPtr))) #define Tcl_GetBooleanFromObj(interp, objPtr, boolPtr) \ (sizeof(*(boolPtr)) == sizeof(int) ? (Tcl_GetBooleanFromObj)(interp, objPtr, (int *)(boolPtr)) : Tcl_GetBoolFromObj(interp, objPtr, 0, boolPtr)) #define Tcl_GetBoolean(interp, src, boolPtr) \ diff --git a/generic/tclObj.c b/generic/tclObj.c index ae20e16..439e854 100644 --- a/generic/tclObj.c +++ b/generic/tclObj.c @@ -2206,22 +2206,20 @@ Tcl_GetBoolFromObj( result = 1; boolEnd: if (boolPtr != NULL) { - if ((flags>>8) & (int)~sizeof(int)) { - if ((flags>>8) == sizeof(int64_t)) { + flags &= (TCL_NULL_OK - 1); + if (flags & (int)~sizeof(int8_t)) { + if (flags == sizeof(int64_t)) { *(int64_t *)boolPtr = result; return TCL_OK; - } else if ((flags>>8) == sizeof(int32_t)) { + } else if (flags == sizeof(int32_t)) { *(int32_t *)boolPtr = result; return TCL_OK; - } else if ((flags>>8) == sizeof(int16_t)) { + } else if (flags == sizeof(int16_t)) { *(int16_t *)boolPtr = result; return TCL_OK; - } else if ((flags>>8) == sizeof(int8_t)) { - *(int8_t *)boolPtr = result; - return TCL_OK; - } + } } - *(int *)boolPtr = result; + *(int8_t *)boolPtr = result; } return TCL_OK; } -- cgit v0.12 From 42cd1c73d2440e5f4c6c5015bc740f13d0b8decd Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 15 Apr 2022 12:20:31 +0000 Subject: Restore Tcl_GetBoolenanFromObj/Tcl_GetBoolenan signatures --- generic/tcl.decls | 4 ++-- generic/tclDecls.h | 16 ++++------------ generic/tclGet.c | 4 ++-- generic/tclObj.c | 4 ++-- generic/tclTest.c | 4 ++-- 5 files changed, 12 insertions(+), 20 deletions(-) diff --git a/generic/tcl.decls b/generic/tcl.decls index 883312e..a450130 100644 --- a/generic/tcl.decls +++ b/generic/tcl.decls @@ -136,11 +136,11 @@ declare 30 { void TclFreeObj(Tcl_Obj *objPtr) } declare 31 { - int Tcl_GetBoolean(Tcl_Interp *interp, const char *src, void *boolPtr) + int Tcl_GetBoolean(Tcl_Interp *interp, const char *src, int *boolPtr) } declare 32 { int Tcl_GetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, - void *boolPtr) + int *boolPtr) } declare 33 { unsigned char *Tcl_GetByteArrayFromObj(Tcl_Obj *objPtr, int *numBytesPtr) diff --git a/generic/tclDecls.h b/generic/tclDecls.h index 3cfbc42..ebaa279 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -146,10 +146,10 @@ EXTERN Tcl_Obj * Tcl_DuplicateObj(Tcl_Obj *objPtr); EXTERN void TclFreeObj(Tcl_Obj *objPtr); /* 31 */ EXTERN int Tcl_GetBoolean(Tcl_Interp *interp, const char *src, - void *boolPtr); + int *boolPtr); /* 32 */ EXTERN int Tcl_GetBooleanFromObj(Tcl_Interp *interp, - Tcl_Obj *objPtr, void *boolPtr); + Tcl_Obj *objPtr, int *boolPtr); /* 33 */ EXTERN unsigned char * Tcl_GetByteArrayFromObj(Tcl_Obj *objPtr, int *numBytesPtr); @@ -2032,8 +2032,8 @@ typedef struct TclStubs { Tcl_Obj * (*tcl_DbNewStringObj) (const char *bytes, int length, const char *file, int line); /* 28 */ Tcl_Obj * (*tcl_DuplicateObj) (Tcl_Obj *objPtr); /* 29 */ void (*tclFreeObj) (Tcl_Obj *objPtr); /* 30 */ - int (*tcl_GetBoolean) (Tcl_Interp *interp, const char *src, void *boolPtr); /* 31 */ - int (*tcl_GetBooleanFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, void *boolPtr); /* 32 */ + int (*tcl_GetBoolean) (Tcl_Interp *interp, const char *src, int *boolPtr); /* 31 */ + int (*tcl_GetBooleanFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, int *boolPtr); /* 32 */ unsigned char * (*tcl_GetByteArrayFromObj) (Tcl_Obj *objPtr, int *numBytesPtr); /* 33 */ int (*tcl_GetDouble) (Tcl_Interp *interp, const char *src, double *doublePtr); /* 34 */ int (*tcl_GetDoubleFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, double *doublePtr); /* 35 */ @@ -4288,10 +4288,6 @@ extern const TclStubs *tclStubsPtr; (tclStubsPtr->tcl_GetBoolFromObj((interp), (objPtr), (flags)|(int)(sizeof(*(boolPtr))), (boolPtr))) #define Tcl_GetBool(interp, objPtr, flags, boolPtr) \ (tclStubsPtr->tcl_GetBool((interp), (objPtr), (flags)|(int)(sizeof(*(boolPtr))), (boolPtr))) -#define Tcl_GetBooleanFromObj(interp, objPtr, boolPtr) \ - (sizeof(*(boolPtr)) == sizeof(int) ? tclStubsPtr->tcl_GetBooleanFromObj(interp, objPtr, (int *)(boolPtr)) : Tcl_GetBoolFromObj(interp, objPtr, 0, boolPtr)) -#define Tcl_GetBoolean(interp, src, boolPtr) \ - (sizeof(*(boolPtr)) == sizeof(int) ? tclStubsPtr->tcl_GetBoolean(interp, src, (int *)(boolPtr)) : Tcl_GetBool(interp, src, 0, boolPtr)) #ifdef TCL_NO_DEPRECATED #define Tcl_GetStringFromObj(objPtr, sizePtr) \ (sizeof(*(sizePtr)) <= sizeof(int) ? tclStubsPtr->tcl_GetStringFromObj(objPtr, (int *)(sizePtr)) : tclStubsPtr->tclGetStringFromObj(objPtr, (size_t *)(sizePtr))) @@ -4309,10 +4305,6 @@ extern const TclStubs *tclStubsPtr; ((Tcl_GetBoolFromObj)((interp), (objPtr), (flags)|(int)(sizeof(*(boolPtr))), (boolPtr))) #define Tcl_GetBool(interp, objPtr, flags, boolPtr) \ ((Tcl_GetBool)((interp), (objPtr), (flags)|(int)(sizeof(*(boolPtr))), (boolPtr))) -#define Tcl_GetBooleanFromObj(interp, objPtr, boolPtr) \ - (sizeof(*(boolPtr)) == sizeof(int) ? (Tcl_GetBooleanFromObj)(interp, objPtr, (int *)(boolPtr)) : Tcl_GetBoolFromObj(interp, objPtr, 0, boolPtr)) -#define Tcl_GetBoolean(interp, src, boolPtr) \ - (sizeof(*(boolPtr)) == sizeof(int) ? (Tcl_GetBoolean)(interp, src, (int *)(boolPtr)) : Tcl_GetBool(interp, src, 0, boolPtr)) #ifdef TCL_NO_DEPRECATED #define Tcl_GetStringFromObj(objPtr, sizePtr) \ (sizeof(*(sizePtr)) <= sizeof(int) ? (Tcl_GetStringFromObj)(objPtr, (int *)(sizePtr)) : (TclGetStringFromObj)(objPtr, (size_t *)(sizePtr))) diff --git a/generic/tclGet.c b/generic/tclGet.c index 27f3235..9a1b3c0 100644 --- a/generic/tclGet.c +++ b/generic/tclGet.c @@ -158,10 +158,10 @@ Tcl_GetBoolean( Tcl_Interp *interp, /* Interpreter used for error reporting. */ const char *src, /* String containing one of the boolean values * 1, 0, true, false, yes, no, on, off. */ - void *boolPtr) /* Place to store converted result, which will + int *boolPtr) /* Place to store converted result, which will * be 0 or 1. */ { - return Tcl_GetBool(interp, src, 0, boolPtr); + return Tcl_GetBool(interp, src, sizeof(int), boolPtr); } /* diff --git a/generic/tclObj.c b/generic/tclObj.c index 439e854..89b576c 100644 --- a/generic/tclObj.c +++ b/generic/tclObj.c @@ -2233,9 +2233,9 @@ int Tcl_GetBooleanFromObj( Tcl_Interp *interp, /* Used for error reporting if not NULL. */ Tcl_Obj *objPtr, /* The object from which to get boolean. */ - void *boolPtr) /* Place to store resulting boolean. */ + int *boolPtr) /* Place to store resulting boolean. */ { - return Tcl_GetBoolFromObj(interp, objPtr, 0, boolPtr); + return Tcl_GetBoolFromObj(interp, objPtr, sizeof(int), boolPtr); } /* diff --git a/generic/tclTest.c b/generic/tclTest.c index 646987b..db25379 100644 --- a/generic/tclTest.c +++ b/generic/tclTest.c @@ -2270,8 +2270,8 @@ TesteventProc( Tcl_BackgroundException(interp, TCL_ERROR); return 1; /* Avoid looping on errors */ } - if (Tcl_GetBooleanFromObj(interp, Tcl_GetObjResult(interp), - &retval[1]) != TCL_OK) { + if (Tcl_GetBoolFromObj(interp, Tcl_GetObjResult(interp), + 0, &retval[1]) != TCL_OK) { Tcl_AddErrorInfo(interp, " (return value from \"testevent\" callback)"); Tcl_BackgroundException(interp, TCL_ERROR); -- cgit v0.12 From 757c9098818f6fa4cb48fa8e522e767d6c0dde4d Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 15 Apr 2022 12:27:39 +0000 Subject: Remove macro's for Tcl_GetBoolFromObj/Tcl_GetBool --- generic/tclDecls.h | 12 ------------ generic/tclTest.c | 4 ++-- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/generic/tclDecls.h b/generic/tclDecls.h index ebaa279..790af99 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -4270,10 +4270,6 @@ extern const TclStubs *tclStubsPtr; Tcl_GetUnicodeFromObj(objPtr, (int *)NULL) #undef Tcl_GetBytesFromObj #undef Tcl_GetIndexFromObjStruct -#undef Tcl_GetBoolFromObj -#undef Tcl_GetBool -#undef Tcl_GetBooleanFromObj -#undef Tcl_GetBoolean #ifdef TCL_NO_DEPRECATED #undef Tcl_GetStringFromObj #undef Tcl_GetUnicodeFromObj @@ -4284,10 +4280,6 @@ extern const TclStubs *tclStubsPtr; (sizeof(*(sizePtr)) <= sizeof(int) ? tclStubsPtr->tclGetBytesFromObj(interp, objPtr, (int *)(sizePtr)) : tclStubsPtr->tcl_GetBytesFromObj(interp, objPtr, (size_t *)(sizePtr))) #define Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, offset, msg, flags, indexPtr) \ (tclStubsPtr->tcl_GetIndexFromObjStruct((interp), (objPtr), (tablePtr), (offset), (msg), (flags)|(int)(sizeof(*(indexPtr))<<8), (indexPtr))) -#define Tcl_GetBoolFromObj(interp, objPtr, flags, boolPtr) \ - (tclStubsPtr->tcl_GetBoolFromObj((interp), (objPtr), (flags)|(int)(sizeof(*(boolPtr))), (boolPtr))) -#define Tcl_GetBool(interp, objPtr, flags, boolPtr) \ - (tclStubsPtr->tcl_GetBool((interp), (objPtr), (flags)|(int)(sizeof(*(boolPtr))), (boolPtr))) #ifdef TCL_NO_DEPRECATED #define Tcl_GetStringFromObj(objPtr, sizePtr) \ (sizeof(*(sizePtr)) <= sizeof(int) ? tclStubsPtr->tcl_GetStringFromObj(objPtr, (int *)(sizePtr)) : tclStubsPtr->tclGetStringFromObj(objPtr, (size_t *)(sizePtr))) @@ -4301,10 +4293,6 @@ extern const TclStubs *tclStubsPtr; (sizeof(*(sizePtr)) <= sizeof(int) ? (TclGetBytesFromObj)(interp, objPtr, (int *)(sizePtr)) : (Tcl_GetBytesFromObj)(interp, objPtr, (size_t *)(sizePtr))) #define Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, offset, msg, flags, indexPtr) \ ((Tcl_GetIndexFromObjStruct)((interp), (objPtr), (tablePtr), (offset), (msg), (flags)|(int)(sizeof(*(indexPtr))<<8), (indexPtr))) -#define Tcl_GetBoolFromObj(interp, objPtr, flags, boolPtr) \ - ((Tcl_GetBoolFromObj)((interp), (objPtr), (flags)|(int)(sizeof(*(boolPtr))), (boolPtr))) -#define Tcl_GetBool(interp, objPtr, flags, boolPtr) \ - ((Tcl_GetBool)((interp), (objPtr), (flags)|(int)(sizeof(*(boolPtr))), (boolPtr))) #ifdef TCL_NO_DEPRECATED #define Tcl_GetStringFromObj(objPtr, sizePtr) \ (sizeof(*(sizePtr)) <= sizeof(int) ? (Tcl_GetStringFromObj)(objPtr, (int *)(sizePtr)) : (TclGetStringFromObj)(objPtr, (size_t *)(sizePtr))) diff --git a/generic/tclTest.c b/generic/tclTest.c index db25379..4cd9bab 100644 --- a/generic/tclTest.c +++ b/generic/tclTest.c @@ -2271,7 +2271,7 @@ TesteventProc( return 1; /* Avoid looping on errors */ } if (Tcl_GetBoolFromObj(interp, Tcl_GetObjResult(interp), - 0, &retval[1]) != TCL_OK) { + sizeof(retval[1]), &retval[1]) != TCL_OK) { Tcl_AddErrorInfo(interp, " (return value from \"testevent\" callback)"); Tcl_BackgroundException(interp, TCL_ERROR); @@ -5300,7 +5300,7 @@ TestsaveresultCmd( return TCL_ERROR; } b[0] = b[1] = b[2] = 100; - if (Tcl_GetBoolFromObj(interp, objv[3], 0, b + 1) != TCL_OK) + if (Tcl_GetBoolFromObj(interp, objv[3], sizeof(b[1]), b + 1) != TCL_OK) { return TCL_ERROR; } -- cgit v0.12 From ad9c975e41ced7cdc2f156f683fa8845fb33735a Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 15 Apr 2022 14:46:00 +0000 Subject: Update documentation --- doc/BoolObj.3 | 18 +++++++++++++----- doc/GetInt.3 | 21 ++++++++++++++++----- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/doc/BoolObj.3 b/doc/BoolObj.3 index afbd1d1..c5bb05f 100644 --- a/doc/BoolObj.3 +++ b/doc/BoolObj.3 @@ -20,7 +20,7 @@ Tcl_Obj * \fBTcl_SetBooleanObj\fR(\fIobjPtr, boolValue\fR) .sp int -\fBTcl_GetBooleanFromObj\fR(\fIinterp, objPtr, boolPtr\fR) +\fBTcl_GetBooleanFromObj\fR(\fIinterp, objPtr, intPtr\fR) .sp int \fBTcl_GetBoolFromObj\fR(\fIinterp, objPtr, flags. boolPtr\fR) @@ -35,13 +35,16 @@ retrieve a boolean value. If a boolean value cannot be retrieved, an error message is left in the interpreter's result value unless \fIinterp\fR is NULL. +.AP int *intPtr out +Points to place where \fBTcl_GetBooleanFromObj\fR +stores the boolean value (0 or 1) obtained from \fIobjPtr\fR. .AP int | short | char *boolPtr out -Points to place where \fBTcl_GetBooleanFromObj\fR/\fBTcl_GetBoolFromObj\fR +Points to place where \fBTcl_GetBoolFromObj\fR stores the boolean value (0 or 1) obtained from \fIobjPtr\fR. .AP int flags in -Value 0 or TCL_NULL_OK. If TCL_NULL_OK, then the empty -string or NULL will result in \fBTcl_GetBoolFromObj\fR return -TCL_OK, the *boolPtr filled with the value -1; +sizeof(*(boolPtr)), possibly combined with TCL_NULL_OK. If TCL_NULL_OK +is used, then the empty string or NULL will result in \fBTcl_GetBoolFromObj\fR +return TCL_OK, the *boolPtr filled with the value -1; .BE .SH DESCRIPTION @@ -83,6 +86,11 @@ fields of \fI*objPtr\fR so that future calls to \fBTcl_GetBooleanFromObj\fR on the same \fIobjPtr\fR can be performed more efficiently. .PP +\fBTcl_GetBoolFromObj\fR functions almost the same as +\fBTcl_GetBooleanFromObj\fR, but it has an additional parameter +\fBflags\fR, which can be used to specify the size of the \fBboolPtr\fR +variable, and also whether the empty string or NULL is accepted as valid. +.PP Note that the routines \fBTcl_GetBooleanFromObj\fR and \fBTcl_GetBoolean\fR are not functional equivalents. The set of values for which \fBTcl_GetBooleanFromObj\fR diff --git a/doc/GetInt.3 b/doc/GetInt.3 index f9b91a2..edce6c1 100644 --- a/doc/GetInt.3 +++ b/doc/GetInt.3 @@ -21,7 +21,10 @@ int \fBTcl_GetDouble\fR(\fIinterp, src, doublePtr\fR) .sp int -\fBTcl_GetBoolean\fR(\fIinterp, src, boolPtr\fR) +\fBTcl_GetBoolean\fR(\fIinterp, src, intPtr\fR) +.sp +int +\fBTcl_GetBool\fR(\fIinterp, src, flags, boolPtr\fR) .SH ARGUMENTS .AS Tcl_Interp *doublePtr out .AP Tcl_Interp *interp in @@ -34,7 +37,11 @@ Points to place to store integer value converted from \fIsrc\fR. Points to place to store double-precision floating-point value converted from \fIsrc\fR. .AP int | short | char *boolPtr out -Points to place to store boolean value (0 or 1) converted from \fIsrc\fR. +Points to place to store boolean value (0 or 1) value converted from \fIsrc\fR. +.AP int flags in +sizeof(*(boolPtr)), possibly combined with TCL_NULL_OK. If TCL_NULL_OK +is used, then the empty string or NULL will result in \fBTcl_GetBool\fR +return TCL_OK, the *boolPtr filled with the value -1; .BE .SH DESCRIPTION @@ -94,11 +101,15 @@ inter-digit separator be present. \fBTcl_GetBoolean\fR expects \fIsrc\fR to specify a boolean value. If \fIsrc\fR is any of \fB0\fR, \fBfalse\fR, \fBno\fR, or \fBoff\fR, then \fBTcl_GetBoolean\fR stores a zero -value at \fI*boolPtr\fR. +value at \fI*intPtr\fR. If \fIsrc\fR is any of \fB1\fR, \fBtrue\fR, \fByes\fR, or \fBon\fR, -then 1 is stored at \fI*boolPtr\fR. +then 1 is stored at \fI*intPtr\fR. Any of these values may be abbreviated, and upper-case spellings are also acceptable. - +.PP +\fBTcl_GetBool\fR functions almost the same as \fBTcl_GetBoolean\fR, +but it has an additional parameter \fBflags\fR, which can be used +to specify the size of the \fBboolPtr\fR variable, and also whether +the empty string or NULL is accepted as valid. .SH KEYWORDS boolean, conversion, double, floating-point, integer -- cgit v0.12 From 43ea8e68e8f54392631b95557d6dc9c621afc667 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Wed, 20 Apr 2022 14:26:06 +0000 Subject: Simplify TclGetBoolFromObj() macro --- generic/tclCmdMZ.c | 2 +- generic/tclExecute.c | 8 ++++---- generic/tclInt.h | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/generic/tclCmdMZ.c b/generic/tclCmdMZ.c index 3eaf055..f394035 100644 --- a/generic/tclCmdMZ.c +++ b/generic/tclCmdMZ.c @@ -1620,7 +1620,7 @@ StringIsCmd( result = length1 == 0; } } else if (index != STR_IS_BOOL) { - TclGetBoolFromObj(NULL, objPtr, sizeof(i), &i); + TclGetBooleanFromObj(NULL, objPtr, &i); if ((index == STR_IS_TRUE) ^ i) { result = 0; } diff --git a/generic/tclExecute.c b/generic/tclExecute.c index 1e16da5..0ec2404 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -4346,7 +4346,7 @@ TEBCresume( /* TODO - check claim that taking address of b harms performance */ /* TODO - consider optimization search for constants */ - if (TclGetBoolFromObj(interp, valuePtr, sizeof(b), &b) != TCL_OK) { + if (TclGetBooleanFromObj(interp, valuePtr, &b) != TCL_OK) { TRACE_ERROR(interp); goto gotError; } @@ -4414,7 +4414,7 @@ TEBCresume( value2Ptr = OBJ_AT_TOS; valuePtr = OBJ_UNDER_TOS; - if (TclGetBoolFromObj(NULL, valuePtr, sizeof(i1), &i1) != TCL_OK) { + if (TclGetBooleanFromObj(NULL, valuePtr, &i1) != TCL_OK) { TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(valuePtr), (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); DECACHE_STACK_INFO(); @@ -4423,7 +4423,7 @@ TEBCresume( goto gotError; } - if (TclGetBoolFromObj(NULL, value2Ptr, sizeof(i2), &i2) != TCL_OK) { + if (TclGetBooleanFromObj(NULL, value2Ptr, &i2) != TCL_OK) { TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n", O2S(value2Ptr), (value2Ptr->typePtr? value2Ptr->typePtr->name : "null"))); DECACHE_STACK_INFO(); @@ -6223,7 +6223,7 @@ TEBCresume( /* TODO - check claim that taking address of b harms performance */ /* TODO - consider optimization search for constants */ - if (TclGetBoolFromObj(NULL, valuePtr, sizeof(b), &b) != TCL_OK) { + if (TclGetBooleanFromObj(NULL, valuePtr, &b) != TCL_OK) { TRACE(("\"%.20s\" => ERROR: illegal type %s\n", O2S(valuePtr), (valuePtr->typePtr? valuePtr->typePtr->name : "null"))); DECACHE_STACK_INFO(); diff --git a/generic/tclInt.h b/generic/tclInt.h index e3ebe57..c39a9f6 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -2507,12 +2507,12 @@ typedef struct List { * WARNING: these macros eval their args more than once. */ -#define TclGetBoolFromObj(interp, objPtr, flags, boolPtr) \ +#define TclGetBooleanFromObj(interp, objPtr, intPtr) \ (((objPtr)->typePtr == &tclIntType) \ - ? (*(boolPtr) = ((objPtr)->internalRep.wideValue!=0), TCL_OK) \ + ? (*(intPtr) = ((objPtr)->internalRep.wideValue!=0), TCL_OK) \ : ((objPtr)->typePtr == &tclBooleanType) \ - ? (*(boolPtr) = ((objPtr)->internalRep.longValue!=0), TCL_OK) \ - : Tcl_GetBoolFromObj((interp), (objPtr), (flags), (boolPtr))) + ? (*(intPtr) = ((objPtr)->internalRep.longValue!=0), TCL_OK) \ + : (Tcl_GetBoolFromObj)((interp), (objPtr), (int)sizeof(int), (intPtr))) #ifdef TCL_WIDE_INT_IS_LONG #define TclGetLongFromObj(interp, objPtr, longPtr) \ -- cgit v0.12 From d3662a9dca03f16538eae7240e56fb57589bd9e5 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 29 Apr 2022 20:09:48 +0000 Subject: re-structure, add more examples --- generic/tcl.decls | 4 ++-- generic/tclCompCmdsGR.c | 3 ++- generic/tclCompCmdsSZ.c | 4 +++- generic/tclCompExpr.c | 3 ++- generic/tclDecls.h | 26 ++++++++++++++++++++++---- generic/tclGet.c | 9 +++++++-- generic/tclInt.h | 2 +- generic/tclObj.c | 30 ++++++++++++------------------ generic/tclTest.c | 29 ++++++++++++----------------- 9 files changed, 63 insertions(+), 47 deletions(-) diff --git a/generic/tcl.decls b/generic/tcl.decls index 3d59139..2c19545 100644 --- a/generic/tcl.decls +++ b/generic/tcl.decls @@ -2457,11 +2457,11 @@ declare 668 { declare 674 { int Tcl_GetBool(Tcl_Interp *interp, const char *src, int flags, - void *boolPtr) + char *boolPtr) } declare 675 { int Tcl_GetBoolFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, - int flags, void *boolPtr) + int flags, char *boolPtr) } diff --git a/generic/tclCompCmdsGR.c b/generic/tclCompCmdsGR.c index da557a4..839fbde 100644 --- a/generic/tclCompCmdsGR.c +++ b/generic/tclCompCmdsGR.c @@ -17,6 +17,7 @@ #include "tclInt.h" #include "tclCompile.h" #include +#include /* * Prototypes for procedures defined later in this file: @@ -185,7 +186,7 @@ TclCompileIfCmd( const char *word; int realCond = 1; /* Set to 0 for static conditions: * "if 0 {..}" */ - int boolVal; /* Value of static condition. */ + bool boolVal; /* Value of static condition. */ int compileScripts = 1; /* diff --git a/generic/tclCompCmdsSZ.c b/generic/tclCompCmdsSZ.c index cd3bd37..fa490a1 100644 --- a/generic/tclCompCmdsSZ.c +++ b/generic/tclCompCmdsSZ.c @@ -18,6 +18,7 @@ #include "tclInt.h" #include "tclCompile.h" #include "tclStringTrim.h" +#include /* * Prototypes for procedures defined later in this file: @@ -3759,7 +3760,8 @@ TclCompileWhileCmd( DefineLineInformation; /* TIP #280 */ Tcl_Token *testTokenPtr, *bodyTokenPtr; JumpFixup jumpEvalCondFixup; - int testCodeOffset, bodyCodeOffset, jumpDist, range, code, boolVal; + int testCodeOffset, bodyCodeOffset, jumpDist, range, code; + bool boolVal; int loopMayEnd = 1; /* This is set to 0 if it is recognized as an * infinite loop. */ Tcl_Obj *boolObj; diff --git a/generic/tclCompExpr.c b/generic/tclCompExpr.c index 23d8711..c245b4e 100644 --- a/generic/tclCompExpr.c +++ b/generic/tclCompExpr.c @@ -13,6 +13,7 @@ #include "tclInt.h" #include "tclCompile.h" /* CompileEnv */ +#include /* * Expression parsing takes place in the routine ParseExpr(). It takes a @@ -708,7 +709,7 @@ ParseExpr( */ if ((NODE_TYPE & lexeme) == 0) { - int b; + bool b; switch (lexeme) { case COMMENT: diff --git a/generic/tclDecls.h b/generic/tclDecls.h index d75e605..04f8aa3 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -1970,10 +1970,10 @@ EXTERN int Tcl_UniCharLen(const int *uniStr); /* Slot 673 is reserved */ /* 674 */ EXTERN int Tcl_GetBool(Tcl_Interp *interp, const char *src, - int flags, void *boolPtr); + int flags, char *boolPtr); /* 675 */ EXTERN int Tcl_GetBoolFromObj(Tcl_Interp *interp, - Tcl_Obj *objPtr, int flags, void *boolPtr); + Tcl_Obj *objPtr, int flags, char *boolPtr); typedef struct { const struct TclPlatStubs *tclPlatStubs; @@ -2683,8 +2683,8 @@ typedef struct TclStubs { void (*reserved671)(void); void (*reserved672)(void); void (*reserved673)(void); - int (*tcl_GetBool) (Tcl_Interp *interp, const char *src, int flags, void *boolPtr); /* 674 */ - int (*tcl_GetBoolFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, int flags, void *boolPtr); /* 675 */ + int (*tcl_GetBool) (Tcl_Interp *interp, const char *src, int flags, char *boolPtr); /* 674 */ + int (*tcl_GetBoolFromObj) (Tcl_Interp *interp, Tcl_Obj *objPtr, int flags, char *boolPtr); /* 675 */ } TclStubs; extern const TclStubs *tclStubsPtr; @@ -4270,12 +4270,22 @@ extern const TclStubs *tclStubsPtr; Tcl_GetUnicodeFromObj(objPtr, (int *)NULL) #undef Tcl_GetBytesFromObj #undef Tcl_GetIndexFromObjStruct +#undef Tcl_GetBoolean +#undef Tcl_GetBooleanFromObj #ifdef TCL_NO_DEPRECATED #undef Tcl_GetStringFromObj #undef Tcl_GetUnicodeFromObj #undef Tcl_GetByteArrayFromObj #endif #if defined(USE_TCL_STUBS) +#define Tcl_GetBoolean(interp, objPtr, boolPtr) \ + (sizeof(*(boolPtr)) == sizeof(int) ? tclStubsPtr->tcl_GetBoolean(interp, objPtr, (int *)(boolPtr)) : \ + (sizeof(*(boolPtr)) == sizeof(char) ? tclStubsPtr->tcl_GetBool(interp, objPtr, 0, (char *)(boolPtr)) : \ + (Tcl_Panic("Invalid boolean variable: sizeof() must be 1 or 4"), TCL_ERROR))) +#define Tcl_GetBooleanFromObj(interp, objPtr, boolPtr) \ + (sizeof(*(boolPtr)) == sizeof(int) ? tclStubsPtr->tcl_GetBooleanFromObj(interp, objPtr, (int *)(boolPtr)) : \ + (sizeof(*(boolPtr)) == sizeof(char) ? tclStubsPtr->tcl_GetBoolFromObj(interp, objPtr, 0, (char *)(boolPtr)) : \ + (Tcl_Panic("Invalid boolean variable: sizeof() must be 1 or 4"), TCL_ERROR))) #define Tcl_GetBytesFromObj(interp, objPtr, sizePtr) \ (sizeof(*(sizePtr)) <= sizeof(int) ? tclStubsPtr->tclGetBytesFromObj(interp, objPtr, (int *)(sizePtr)) : tclStubsPtr->tcl_GetBytesFromObj(interp, objPtr, (size_t *)(sizePtr))) #define Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, offset, msg, flags, indexPtr) \ @@ -4289,6 +4299,14 @@ extern const TclStubs *tclStubsPtr; (sizeof(*(sizePtr)) <= sizeof(int) ? tclStubsPtr->tcl_GetUnicodeFromObj(objPtr, (int *)(sizePtr)) : tclStubsPtr->tclGetUnicodeFromObj(objPtr, (size_t *)(sizePtr))) #endif #else +#define Tcl_GetBoolean(interp, objPtr, boolPtr) \ + (sizeof(*(boolPtr)) == sizeof(int) ? (Tcl_GetBoolean)(interp, objPtr, (int *)(boolPtr)) : \ + (sizeof(*(boolPtr)) == sizeof(char) ? (Tcl_GetBool)(interp, objPtr, 0, (char *)(boolPtr)) : \ + (Tcl_Panic("Invalid boolean variable: sizeof() must be 1 or 4"), TCL_ERROR))) +#define Tcl_GetBooleanFromObj(interp, objPtr, boolPtr) \ + (sizeof(*(boolPtr)) == sizeof(int) ? (Tcl_GetBooleanFromObj)(interp, objPtr, (int *)(boolPtr)) : \ + (sizeof(*(boolPtr)) == sizeof(char) ? (Tcl_GetBoolFromObj)(interp, objPtr, 0, (char *)(boolPtr)) : \ + (Tcl_Panic("Invalid boolean variable: sizeof() must be 1 or 4"), TCL_ERROR))) #define Tcl_GetBytesFromObj(interp, objPtr, sizePtr) \ (sizeof(*(sizePtr)) <= sizeof(int) ? (TclGetBytesFromObj)(interp, objPtr, (int *)(sizePtr)) : (Tcl_GetBytesFromObj)(interp, objPtr, (size_t *)(sizePtr))) #define Tcl_GetIndexFromObjStruct(interp, objPtr, tablePtr, offset, msg, flags, indexPtr) \ diff --git a/generic/tclGet.c b/generic/tclGet.c index 0e07da1..a60d3a6 100644 --- a/generic/tclGet.c +++ b/generic/tclGet.c @@ -128,7 +128,7 @@ Tcl_GetBool( const char *src, /* String containing one of the boolean values * 1, 0, true, false, yes, no, on, off. */ int flags, - void *boolPtr) /* Place to store converted result, which will + char *boolPtr) /* Place to store converted result, which will * be 0 or 1. */ { Tcl_Obj obj; @@ -161,7 +161,12 @@ Tcl_GetBoolean( int *intPtr) /* Place to store converted result, which will * be 0 or 1. */ { - return Tcl_GetBool(interp, src, sizeof(int), intPtr); + char boolValue; + int result = Tcl_GetBool(interp, src, 0, &boolValue); + if (intPtr) { + *intPtr = boolValue; + } + return result; } /* diff --git a/generic/tclInt.h b/generic/tclInt.h index 2ee22f3..61cc3b3 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -2512,7 +2512,7 @@ typedef struct List { ? (*(intPtr) = ((objPtr)->internalRep.wideValue!=0), TCL_OK) \ : ((objPtr)->typePtr == &tclBooleanType) \ ? (*(intPtr) = ((objPtr)->internalRep.longValue!=0), TCL_OK) \ - : Tcl_GetBoolFromObj((interp), (objPtr), (int)sizeof(int), (intPtr))) + : Tcl_GetBooleanFromObj((interp), (objPtr), (intPtr))) #ifdef TCL_WIDE_INT_IS_LONG #define TclGetLongFromObj(interp, objPtr, longPtr) \ diff --git a/generic/tclObj.c b/generic/tclObj.c index 40fc73b..7842d0d 100644 --- a/generic/tclObj.c +++ b/generic/tclObj.c @@ -2161,7 +2161,7 @@ Tcl_GetBoolFromObj( Tcl_Interp *interp, /* Used for error reporting if not NULL. */ Tcl_Obj *objPtr, /* The object from which to get boolean. */ int flags, - void *boolPtr) /* Place to store resulting boolean. */ + char *boolPtr) /* Place to store resulting boolean. */ { int result; @@ -2171,7 +2171,8 @@ Tcl_GetBoolFromObj( } else if (objPtr == NULL) { if (interp) { TclNewObj(objPtr); - TclParseNumber(interp, objPtr, "boolean value", NULL,-1,NULL,0); + TclParseNumber(interp, objPtr, (flags & TCL_NULL_OK) + ? "boolean value or \"\"" : "boolean value", NULL, -1, NULL, 0); Tcl_DecrRefCount(objPtr); } return TCL_ERROR; @@ -2206,25 +2207,13 @@ Tcl_GetBoolFromObj( result = 1; boolEnd: if (boolPtr != NULL) { - flags &= (TCL_NULL_OK - 1); - if (flags & (int)~sizeof(int8_t)) { - if (flags == sizeof(int16_t)) { - *(int16_t *)boolPtr = result; - return TCL_OK; - } else if (flags == sizeof(int32_t)) { - *(int32_t *)boolPtr = result; - return TCL_OK; - } else if (flags == sizeof(int64_t)) { - *(int64_t *)boolPtr = result; - return TCL_OK; - } - } - *(int8_t *)boolPtr = result; + *boolPtr = result; } return TCL_OK; } } while ((ParseBoolean(objPtr) == TCL_OK) || (TCL_OK == - TclParseNumber(interp, objPtr, "boolean value", NULL,-1,NULL,0))); + TclParseNumber(interp, objPtr, (flags & TCL_NULL_OK) + ? "boolean value or \"\"" : "boolean value", NULL,-1,NULL,0))); return TCL_ERROR; } @@ -2235,7 +2224,12 @@ Tcl_GetBooleanFromObj( Tcl_Obj *objPtr, /* The object from which to get boolean. */ int *intPtr) /* Place to store resulting boolean. */ { - return Tcl_GetBoolFromObj(interp, objPtr, sizeof(int), intPtr); + char boolValue; + int result = Tcl_GetBoolFromObj(interp, objPtr, 0, &boolValue); + if (intPtr) { + *intPtr = boolValue; + } + return result; } /* diff --git a/generic/tclTest.c b/generic/tclTest.c index 4cd9bab..39364d6 100644 --- a/generic/tclTest.c +++ b/generic/tclTest.c @@ -30,6 +30,7 @@ #endif #include "tclOO.h" #include +#include /* * Required for Testregexp*Cmd @@ -2262,7 +2263,7 @@ TesteventProc( Tcl_Obj *command = ev->command; int result = Tcl_EvalObjEx(interp, command, TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT); - char retval[3]; + bool retval; if (result != TCL_OK) { Tcl_AddErrorInfo(interp, @@ -2270,19 +2271,19 @@ TesteventProc( Tcl_BackgroundException(interp, TCL_ERROR); return 1; /* Avoid looping on errors */ } - if (Tcl_GetBoolFromObj(interp, Tcl_GetObjResult(interp), - sizeof(retval[1]), &retval[1]) != TCL_OK) { + if (Tcl_GetBooleanFromObj(interp, Tcl_GetObjResult(interp), + &retval) != TCL_OK) { Tcl_AddErrorInfo(interp, " (return value from \"testevent\" callback)"); Tcl_BackgroundException(interp, TCL_ERROR); return 1; } - if (retval[1]) { + if (retval) { Tcl_DecrRefCount(ev->tag); Tcl_DecrRefCount(ev->command); } - return retval[1]; + return retval; } /* @@ -5277,7 +5278,7 @@ TestsaveresultCmd( { Interp* iPtr = (Interp*) interp; int result, index; - char b[3]; + bool discard; Tcl_SavedResult state; Tcl_Obj *objPtr; static const char *const optionStrings[] = { @@ -5299,17 +5300,11 @@ TestsaveresultCmd( &index) != TCL_OK) { return TCL_ERROR; } - b[0] = b[1] = b[2] = 100; - if (Tcl_GetBoolFromObj(interp, objv[3], sizeof(b[1]), b + 1) != TCL_OK) - { - return TCL_ERROR; - } - if (b[0] != 100 || b[2] != 100) { - Tcl_Panic("MEMORY OVERWRITE IN Tcl_GetBoolFromObj"); - return TCL_ERROR; - } + if (Tcl_GetBooleanFromObj(interp, objv[3], &discard) != TCL_OK) { + return TCL_ERROR; + } - freeCount = 0; + freeCount = 0; objPtr = NULL; /* Lint. */ switch ((enum options) index) { case RESULT_SMALL: @@ -5342,7 +5337,7 @@ TestsaveresultCmd( result = Tcl_EvalEx(interp, Tcl_GetString(objv[2]), -1, 0); } - if (b[1]) { + if (discard) { Tcl_DiscardResult(&state); } else { Tcl_RestoreResult(interp, &state); -- cgit v0.12 From a83edfe07c35a66fbcf357a99349c43e103e6d9e Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 10 May 2022 10:18:44 +0000 Subject: Update doc --- doc/BoolObj.3 | 4 ++-- doc/GetInt.3 | 3 +-- generic/tclGet.c | 6 +++--- generic/tclObj.c | 30 +++++++++++++++--------------- 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/doc/BoolObj.3 b/doc/BoolObj.3 index cc8729e..47a2189 100644 --- a/doc/BoolObj.3 +++ b/doc/BoolObj.3 @@ -88,8 +88,8 @@ performed more efficiently. .PP \fBTcl_GetBoolFromObj\fR functions almost the same as \fBTcl_GetBooleanFromObj\fR, but it has an additional parameter -\fBflags\fR, which can be used to specify the size of the \fBbool\fR -variable, and also whether the empty string or NULL is accepted as valid. +\fBflags\fR, which can be used to specify whether the empty +string or NULL is accepted as valid. .PP Note that the routines \fBTcl_GetBooleanFromObj\fR and \fBTcl_GetBoolean\fR are not functional equivalents. diff --git a/doc/GetInt.3 b/doc/GetInt.3 index 62e8f51..f15c12d 100644 --- a/doc/GetInt.3 +++ b/doc/GetInt.3 @@ -109,8 +109,7 @@ are also acceptable. .PP \fBTcl_GetBool\fR functions almost the same as \fBTcl_GetBoolean\fR, but it has an additional parameter \fBflags\fR, which can be used -to specify the size of the \fBbool\fR variable, and also whether -the empty string or NULL is accepted as valid. +to specify whether the empty string or NULL is accepted as valid. .SH KEYWORDS boolean, conversion, double, floating-point, integer diff --git a/generic/tclGet.c b/generic/tclGet.c index 9670450..3c458dc 100644 --- a/generic/tclGet.c +++ b/generic/tclGet.c @@ -161,10 +161,10 @@ Tcl_GetBoolean( int *intPtr) /* Place to store converted result, which will * be 0 or 1. */ { - char boolValue; - int result = Tcl_GetBool(interp, src, 0, &boolValue); + char charValue; + int result = Tcl_GetBool(interp, src, 0, &charValue); if (intPtr) { - *intPtr = boolValue; + *intPtr = charValue; } return result; } diff --git a/generic/tclObj.c b/generic/tclObj.c index f7d9dfc..ce13638 100644 --- a/generic/tclObj.c +++ b/generic/tclObj.c @@ -2004,7 +2004,7 @@ Tcl_FreeInternalRep( * * This function is normally called when not debugging: i.e., when * TCL_MEM_DEBUG is not defined. It creates a new Tcl_Obj and - * initializes it from the argument boolean value. A nonzero "boolValue" + * initializes it from the argument boolean value. A nonzero "intValue" * is coerced to 1. * * When TCL_MEM_DEBUG is defined, this function just returns the result @@ -2025,20 +2025,20 @@ Tcl_FreeInternalRep( Tcl_Obj * Tcl_NewBooleanObj( - int boolValue) /* Boolean used to initialize new object. */ + int intValue) /* Boolean used to initialize new object. */ { - return Tcl_DbNewWideIntObj(boolValue!=0, "unknown", 0); + return Tcl_DbNewWideIntObj(intValue!=0, "unknown", 0); } #else /* if not TCL_MEM_DEBUG */ Tcl_Obj * Tcl_NewBooleanObj( - int boolValue) /* Boolean used to initialize new object. */ + int intValue) /* Boolean used to initialize new object. */ { Tcl_Obj *objPtr; - TclNewIntObj(objPtr, boolValue!=0); + TclNewIntObj(objPtr, intValue!=0); return objPtr; } #endif /* TCL_MEM_DEBUG */ @@ -2075,7 +2075,7 @@ Tcl_NewBooleanObj( Tcl_Obj * Tcl_DbNewBooleanObj( - int boolValue, /* Boolean used to initialize new object. */ + int intValue, /* Boolean used to initialize new object. */ const char *file, /* The name of the source file calling this * function; used for debugging. */ int line) /* Line number in the source file; used for @@ -2087,7 +2087,7 @@ Tcl_DbNewBooleanObj( /* Optimized TclInvalidateStringRep() */ objPtr->bytes = NULL; - objPtr->internalRep.wideValue = (boolValue != 0); + objPtr->internalRep.wideValue = (intValue != 0); objPtr->typePtr = &tclIntType; return objPtr; } @@ -2096,11 +2096,11 @@ Tcl_DbNewBooleanObj( Tcl_Obj * Tcl_DbNewBooleanObj( - int boolValue, /* Boolean used to initialize new object. */ + int intValue, /* Boolean used to initialize new object. */ TCL_UNUSED(const char *) /*file*/, TCL_UNUSED(int) /*line*/) { - return Tcl_NewBooleanObj(boolValue); + return Tcl_NewBooleanObj(intValue); } #endif /* TCL_MEM_DEBUG */ @@ -2110,7 +2110,7 @@ Tcl_DbNewBooleanObj( * Tcl_SetBooleanObj -- * * Modify an object to be a boolean object and to have the specified - * boolean value. A nonzero "boolValue" is coerced to 1. + * boolean value. A nonzero "intValue" is coerced to 1. * * Results: * None. @@ -2126,13 +2126,13 @@ Tcl_DbNewBooleanObj( void Tcl_SetBooleanObj( Tcl_Obj *objPtr, /* Object whose internal rep to init. */ - int boolValue) /* Boolean used to set object's value. */ + int intValue) /* Boolean used to set object's value. */ { if (Tcl_IsShared(objPtr)) { Tcl_Panic("%s called with shared object", "Tcl_SetBooleanObj"); } - TclSetIntObj(objPtr, boolValue!=0); + TclSetIntObj(objPtr, intValue!=0); } #endif /* TCL_NO_DEPRECATED */ @@ -2224,10 +2224,10 @@ Tcl_GetBooleanFromObj( Tcl_Obj *objPtr, /* The object from which to get boolean. */ int *intPtr) /* Place to store resulting boolean. */ { - char boolValue; - int result = Tcl_GetBoolFromObj(interp, objPtr, 0, &boolValue); + char charValue; + int result = Tcl_GetBoolFromObj(interp, objPtr, 0, &charValue); if (intPtr) { - *intPtr = boolValue; + *intPtr = charValue; } return result; } -- cgit v0.12 -- cgit v0.12 From c0481e830577e5c171081870edce6c95d6f6ef87 Mon Sep 17 00:00:00 2001 From: apnadkarni Date: Sun, 7 Aug 2022 07:16:38 +0000 Subject: TIP 631 - lsubst command --- generic/tclBasic.c | 1 + generic/tclCmdIL.c | 117 +++++++++++++++++++++ generic/tclInt.h | 3 + tests/lreplace.test | 295 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 416 insertions(+) diff --git a/generic/tclBasic.c b/generic/tclBasic.c index a0c5a91..f7e0929 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -324,6 +324,7 @@ static const CmdInfo builtInCmds[] = { {"lsearch", Tcl_LsearchObjCmd, NULL, NULL, CMD_IS_SAFE}, {"lset", Tcl_LsetObjCmd, TclCompileLsetCmd, NULL, CMD_IS_SAFE}, {"lsort", Tcl_LsortObjCmd, NULL, NULL, CMD_IS_SAFE}, + {"lsubst", Tcl_LsubstObjCmd, NULL, NULL, CMD_IS_SAFE}, {"package", Tcl_PackageObjCmd, NULL, TclNRPackageObjCmd, CMD_IS_SAFE}, {"proc", Tcl_ProcObjCmd, NULL, NULL, CMD_IS_SAFE}, {"regexp", Tcl_RegexpObjCmd, TclCompileRegexpCmd, NULL, CMD_IS_SAFE}, diff --git a/generic/tclCmdIL.c b/generic/tclCmdIL.c index cdc302c..7776c78 100644 --- a/generic/tclCmdIL.c +++ b/generic/tclCmdIL.c @@ -4486,6 +4486,123 @@ Tcl_LsortObjCmd( /* *---------------------------------------------------------------------- * + * Tcl_LsubstObjCmd -- + * + * This procedure is invoked to process the "lsubst" Tcl command. See the + * user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_LsubstObjCmd( + TCL_UNUSED(ClientData), + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument values. */ +{ + Tcl_Obj *listPtr; /* Pointer to the list being altered. */ + Tcl_Obj *finalValuePtr; /* Value finally assigned to the variable. */ + int createdNewObj; + int result; + int first; + int last; + int listLen; + int numToDelete; + + if (objc < 4) { + Tcl_WrongNumArgs(interp, 1, objv, + "listVar first last ?element ...?"); + return TCL_ERROR; + } + + listPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG); + if (listPtr == NULL) { + return TCL_ERROR; + } + + /* + * TODO - refactor the index extraction into a common function shared + * by Tcl_{Lrange,Lreplace,Lsubst}ObjCmd + */ + + result = TclListObjLengthM(interp, listPtr, &listLen); + if (result != TCL_OK) { + return result; + } + + result = TclGetIntForIndexM(interp, objv[2], /*end*/ listLen-1, &first); + if (result != TCL_OK) { + return result; + } + + result = TclGetIntForIndexM(interp, objv[3], /*end*/ listLen-1, &last); + if (result != TCL_OK) { + return result; + } + + if (first == TCL_INDEX_NONE) { + first = 0; + } else if (first > listLen) { + first = listLen; + } + + if (last >= listLen) { + last = listLen - 1; + } + if (first <= last) { + numToDelete = last - first + 1; + } else { + numToDelete = 0; + } + + if (Tcl_IsShared(listPtr)) { + listPtr = TclListObjCopy(NULL, listPtr); + createdNewObj = 1; + } else { + createdNewObj = 0; + } + + result = + Tcl_ListObjReplace(interp, listPtr, first, numToDelete, objc - 4, objv + 4); + if (result != TCL_OK) { + if (createdNewObj) { + Tcl_DecrRefCount(listPtr); + } + return result; + } + + /* + * Tcl_ObjSetVar2 mau return a value different from listPtr in the + * presence of traces etc.. Note that finalValuePtr will always have a + * reference count of at least 1 corresponding to the reference from the + * var. If it is same as listPtr, then ref count will be at least 2 + * since we are incr'ing the latter below (safer when calling + * Tcl_ObjSetVar2 which can release it in some cases). Note that we + * leave the incrref of listPtr this late because we want to pass it as + * unshared to Tcl_ListObjReplace above if possible. + */ + Tcl_IncrRefCount(listPtr); + finalValuePtr = + Tcl_ObjSetVar2(interp, objv[1], NULL, listPtr, TCL_LEAVE_ERR_MSG); + Tcl_DecrRefCount(listPtr); /* safe irrespective of createdNewObj */ + if (finalValuePtr == NULL) { + return TCL_ERROR; + } + + Tcl_SetObjResult(interp, finalValuePtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * * MergeLists - * * This procedure combines two sorted lists of SortElement structures diff --git a/generic/tclInt.h b/generic/tclInt.h index 06ec2ad..562140c 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -3711,6 +3711,9 @@ MODULE_SCOPE int Tcl_LsetObjCmd(ClientData clientData, MODULE_SCOPE int Tcl_LsortObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); +MODULE_SCOPE int Tcl_LsubstObjCmd(ClientData clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); MODULE_SCOPE Tcl_Command TclInitNamespaceCmd(Tcl_Interp *interp); MODULE_SCOPE int TclNamespaceEnsembleCmd(ClientData dummy, Tcl_Interp *interp, int objc, diff --git a/tests/lreplace.test b/tests/lreplace.test index 0b26e86..4204c2f 100644 --- a/tests/lreplace.test +++ b/tests/lreplace.test @@ -236,6 +236,301 @@ apply {{} { } }} +# Essentially same tests as above but for lsubst +test lsubst-1.1 {lsubst command} { + set l {1 2 3 4 5} + list [lsubst l 0 0 a] $l +} {{a 2 3 4 5} {a 2 3 4 5}} +test lsubst-1.2 {lsubst command} { + set l {1 2 3 4 5} + list [lsubst l 1 1 a] $l +} {{1 a 3 4 5} {1 a 3 4 5}} +test lsubst-1.3 {lsubst command} { + set l {1 2 3 4 5} + list [lsubst l 2 2 a] $l +} {{1 2 a 4 5} {1 2 a 4 5}} +test lsubst-1.4 {lsubst command} { + set l {1 2 3 4 5} + list [lsubst l 3 3 a] $l +} {{1 2 3 a 5} {1 2 3 a 5}} +test lsubst-1.5 {lsubst command} { + set l {1 2 3 4 5} + list [lsubst l 4 4 a] $l +} {{1 2 3 4 a} {1 2 3 4 a}} +test lsubst-1.6 {lsubst command} { + set l {1 2 3 4 5} + list [lsubst l 4 5 a] $l +} {{1 2 3 4 a} {1 2 3 4 a}} +test lsubst-1.7 {lsubst command} { + set l {1 2 3 4 5} + list [lsubst l -1 -1 a] $l +} {{a 1 2 3 4 5} {a 1 2 3 4 5}} +test lsubst-1.8 {lsubst command} { + set l {1 2 3 4 5} + list [lsubst l 2 end a b c d] $l +} {{1 2 a b c d} {1 2 a b c d}} +test lsubst-1.9 {lsubst command} { + set l {1 2 3 4 5} + list [lsubst l 0 3] $l +} {5 5} +test lsubst-1.10 {lsubst command} { + set l {1 2 3 4 5} + list [lsubst l 0 4] $l +} {{} {}} +test lsubst-1.11 {lsubst command} { + set l {1 2 3 4 5} + list [lsubst l 0 1] $l +} {{3 4 5} {3 4 5}} +test lsubst-1.12 {lsubst command} { + set l {1 2 3 4 5} + list [lsubst l 2 3] $l +} {{1 2 5} {1 2 5}} +test lsubst-1.13 {lsubst command} { + set l {1 2 3 4 5} + list [lsubst l 3 end] $l +} {{1 2 3} {1 2 3}} +test lsubst-1.14 {lsubst command} { + set l {1 2 3 4 5} + list [lsubst l -1 4 a b c] $l +} {{a b c} {a b c}} +test lsubst-1.15 {lsubst command} { + set l {a b "c c" d e f} + list [lsubst l 3 3] $l +} {{a b {c c} e f} {a b {c c} e f}} +test lsubst-1.16 {lsubst command} { + set l { 1 2 3 4 5} + list [lsubst l 0 0 a] $l +} {{a 2 3 4 5} {a 2 3 4 5}} +test lsubst-1.17 {lsubst command} { + set l {1 2 3 4 "5 6"} + list [lsubst l 4 4 a] $l +} {{1 2 3 4 a} {1 2 3 4 a}} +test lsubst-1.18 {lsubst command} { + set l {1 2 3 4 {5 6}} + list [lsubst l 4 4 a] $l +} {{1 2 3 4 a} {1 2 3 4 a}} +test lsubst-1.19 {lsubst command} { + set l {1 2 3 4} + list [lsubst l 2 end x y z] $l +} {{1 2 x y z} {1 2 x y z}} +test lsubst-1.20 {lsubst command} { + set l {1 2 3 4} + list [lsubst l end end a] $l +} {{1 2 3 a} {1 2 3 a}} +test lsubst-1.21 {lsubst command} { + set l {1 2 3 4} + list [lsubst l end 3 a] $l +} {{1 2 3 a} {1 2 3 a}} +test lsubst-1.22 {lsubst command} { + set l {1 2 3 4} + list [lsubst l end end] $l +} {{1 2 3} {1 2 3}} +test lsubst-1.23 {lsubst command} { + set l {1 2 3 4} + list [lsubst l 2 -1 xy] $l +} {{1 2 xy 3 4} {1 2 xy 3 4}} +test lsubst-1.24 {lsubst command} { + set l {1 2 3 4} + list [lsubst l end -1 z] $l +} {{1 2 3 z 4} {1 2 3 z 4}} +test lsubst-1.25 {lsubst command} { + set l {\}\ hello} + concat \"[lsubst l end end]\" $l +} {"\}\ " \}\ } +test lsubst-1.26 {lsubst command} { + catch {unset foo} + set foo {a b} + list [lsubst foo end end] $foo \ + [lsubst foo end end] $foo \ + [lsubst foo end end] $foo +} {a a {} {} {} {}} +test lsubst-1.27 {lsubset command} -body { + set l x + list [lsubst l 1 1] $l +} -result {x x} +test lsubst-1.28 {lsubst command} -body { + set l x + list [lsubst l 1 1 y] $l +} -result {{x y} {x y}} +test lsubst-1.29 {lsubst command} -body { + set l x + lsubst l 1 1 [error foo] +} -returnCodes 1 -result {foo} +test lsubst-1.30 {lsubst command} -body { + set l {not {}alist} + lsubst l 0 0 [error foo] +} -returnCodes 1 -result {foo} +test lsubst-1.31 {lsubst command} -body { + unset -nocomplain arr + set arr(x) {a b} + list [lsubst arr(x) 0 0 c] $arr(x) +} -result {{c b} {c b}} + +test lsubst-2.1 {lsubst errors} -body { + list [catch lsubst msg] $msg +} -result {1 {wrong # args: should be "lsubst listVar first last ?element ...?"}} +test lsubst-2.2 {lsubst errors} -body { + unset -nocomplain x + list [catch {lsubst l b} msg] $msg +} -result {1 {wrong # args: should be "lsubst listVar first last ?element ...?"}} +test lsubst-2.3 {lsubst errors} -body { + set x {} + list [catch {lsubst x a 10} msg] $msg +} -result {1 {bad index "a": must be integer?[+-]integer? or end?[+-]integer?}} +test lsubst-2.4 {lsubst errors} -body { + set l {} + list [catch {lsubst l 10 x} msg] $msg +} -result {1 {bad index "x": must be integer?[+-]integer? or end?[+-]integer?}} +test lsubst-2.5 {lsubst errors} -body { + set l {} + list [catch {lsubst l 10 1x} msg] $msg +} -result {1 {bad index "1x": must be integer?[+-]integer? or end?[+-]integer?}} +test lsubst-2.6 {lsubst errors} -body { + set l x + list [catch {lsubst l 3 2} msg] $msg +} -result {0 x} +test lsubst-2.7 {lsubst errors} -body { + set l x + list [catch {lsubst l 2 2} msg] $msg +} -result {0 x} +test lsubst-2.8 {lsubst errors} -body { + unset -nocomplain l + lsubst l 0 0 x +} -returnCodes error -result {can't read "l": no such variable} +test lsubst-2.9 {lsubst errors} -body { + unset -nocomplain arr + lsubst arr(x) 0 0 x +} -returnCodes error -result {can't read "arr(x)": no such variable} +test lsubst-2.10 {lsubst errors} -body { + unset -nocomplain arr + set arr(y) y + lsubst arr(x) 0 0 x +} -returnCodes error -result {can't read "arr(x)": no such element in array} + +test lsubst-3.1 {lsubst won't modify shared argument objects} { + proc p {} { + set l "a b c" + lsubst l 1 1 "x y" + # The literal in locals table should be unmodified + return [list "a b c" $l] + } + p +} {{a b c} {a {x y} c}} + +# Following bugs were in lreplace. Make sure lsubst does not have them +test lsubst-4.1 {Bug ccc2c2cc98: lreplace edge case} { + set l {} + list [lsubst l 1 1] $l +} {{} {}} +test lsubst-4.2 {Bug ccc2c2cc98: lreplace edge case} { + set l { } + list [lsubst l 1 1] $l +} {{} {}} +test lsubst-4.3 {lreplace edge case} { + set l {1 2 3} + lsubst l 2 0 +} {1 2 3} +test lsubst-4.4 {lsubst edge case} { + set l {1 2 3 4 5} + list [lsubst l 3 1] $l +} {{1 2 3 4 5} {1 2 3 4 5}} +test lreplace-4.5 {lreplace edge case} { + lreplace {1 2 3 4 5} 3 0 _ +} {1 2 3 _ 4 5} +test lsubst-4.6 {lsubst end-x: bug a4cb3f06c4} { + set l {0 1 2 3 4} + list [lsubst l 0 end-2] $l +} {{3 4} {3 4}} +test lsubst-4.6.1 {lsubst end-x: bug a4cb3f06c4} { + set l {0 1 2 3 4} + list [lsubst l 0 end-2 a b c] $l +} {{a b c 3 4} {a b c 3 4}} +test lsubst-4.7 {lsubst with two end-indexes: increasing} { + set l {0 1 2 3 4} + list [lsubst l end-2 end-1] $l +} {{0 1 4} {0 1 4}} +test lsubst-4.7.1 {lsubst with two end-indexes: increasing} { + set l {0 1 2 3 4} + list [lsubst l end-2 end-1 a b c] $l +} {{0 1 a b c 4} {0 1 a b c 4}} +test lsubst-4.8 {lsubst with two end-indexes: equal} { + set l {0 1 2 3 4} + list [lsubst l end-2 end-2] $l +} {{0 1 3 4} {0 1 3 4}} +test lsubst-4.8.1 {lsubst with two end-indexes: equal} { + set l {0 1 2 3 4} + list [lsubst l end-2 end-2 a b c] $l +} {{0 1 a b c 3 4} {0 1 a b c 3 4}} +test lsubst-4.9 {lsubst with two end-indexes: decreasing} { + set l {0 1 2 3 4} + list [lsubst l end-2 end-3] $l +} {{0 1 2 3 4} {0 1 2 3 4}} +test lsubst-4.9.1 {lsubst with two end-indexes: decreasing} { + set l {0 1 2 3 4} + list [lsubst l end-2 end-3 a b c] $l +} {{0 1 a b c 2 3 4} {0 1 a b c 2 3 4}} +test lsubst-4.10 {lsubst with two equal indexes} { + set l {0 1 2 3 4} + list [lsubst l 2 2] $l +} {{0 1 3 4} {0 1 3 4}} +test lsubst-4.10.1 {lsubst with two equal indexes} { + set l {0 1 2 3 4} + list [lsubst l 2 2 a b c] $l +} {{0 1 a b c 3 4} {0 1 a b c 3 4}} +test lsubst-4.11 {lsubst end index first} { + set l {0 1 2 3 4} + list [lsubst l end-2 1 a b c] $l +} {{0 1 a b c 2 3 4} {0 1 a b c 2 3 4}} +test lsubst-4.12 {lsubst end index first} { + set l {0 1 2 3 4} + list [lsubst l end-2 2 a b c] $l +} {{0 1 a b c 3 4} {0 1 a b c 3 4}} +test lsubst-4.13 {lsubst empty list} { + set l {} + list [lsubst l 1 1 1] $l +} {1 1} +test lsubst-4.14 {lsubst empty list} { + set l {} + list [lsubst l 2 2 2] $l +} {2 2} + +test lsubst-5.1 {compiled lreplace: Bug 47ac84309b} { + apply {x { + lsubst x end 0 + }} {a b c} +} {a b c} +test lsubst-5.2 {compiled lreplace: Bug 47ac84309b} { + apply {x { + lsubst x end 0 A + }} {a b c} +} {a b A c} + +# Testing for compiled behaviour. Far too many variations to check with +# spelt-out tests. Note that this *just* checks whether the compiled version +# and the interpreted version are the same, not whether the interpreted +# version is correct. +apply {{} { + set lss {{} {a} {a b c} {a b c d}} + set ins {{} A {A B}} + set idxs {-2 -1 0 1 2 3 end-3 end-2 end-1 end end+1 end+2} + set lreplace lreplace + + foreach ls $lss { + foreach a $idxs { + foreach b $idxs { + foreach i $ins { + set expected [list [catch {$lreplace $ls $a $b {*}$i} m] $m] + set tester [list lsubst ls $a $b {*}$i] + set script [list catch $tester m] + set script "list \[$script\] \$m" + test lsubst-6.[incr n] {lsubst battery} -body \ + [list apply [list {ls} $script] $ls] -result $expected + } + } + } + } +}} + # cleanup catch {unset foo} ::tcltest::cleanupTests -- cgit v0.12 From 0c5c7cc5eb0499b1b4f2b9000364ebe52186adf5 Mon Sep 17 00:00:00 2001 From: apnadkarni Date: Fri, 16 Sep 2022 07:29:15 +0000 Subject: Rename lsubst to ledit, add docs --- doc/interp.n | 20 ++-- doc/lappend.n | 2 +- doc/lassign.n | 2 +- doc/ledit.n | 91 ++++++++++++++++++ doc/lindex.n | 2 +- doc/linsert.n | 2 +- doc/list.n | 2 +- doc/llength.n | 2 +- doc/lmap.n | 2 +- doc/lpop.n | 2 +- doc/lrange.n | 2 +- doc/lremove.n | 2 +- doc/lrepeat.n | 2 +- doc/lreplace.n | 2 +- doc/lreverse.n | 2 +- doc/lsearch.n | 2 +- doc/lset.n | 2 +- doc/lsort.n | 2 +- generic/tclBasic.c | 2 +- generic/tclCmdIL.c | 8 +- generic/tclInt.h | 6 +- tests/lreplace.test | 264 ++++++++++++++++++++++++++-------------------------- 22 files changed, 257 insertions(+), 166 deletions(-) create mode 100644 doc/ledit.n diff --git a/doc/interp.n b/doc/interp.n index 2943404..b3cc918 100644 --- a/doc/interp.n +++ b/doc/interp.n @@ -591,16 +591,16 @@ built-in commands: \fBflush\fR \fBfor\fR \fBforeach\fR \fBformat\fR \fBgets\fR \fBglobal\fR \fBif\fR \fBincr\fR \fBinfo\fR \fBinterp\fR \fBjoin\fR \fBlappend\fR -\fBlassign\fR \fBlindex\fR \fBlinsert\fR \fBlist\fR -\fBllength\fR \fBlrange\fR \fBlrepeat\fR \fBlreplace\fR -\fBlsearch\fR \fBlset\fR \fBlsort\fR \fBnamespace\fR -\fBpackage\fR \fBpid\fR \fBproc\fR \fBputs\fR -\fBread\fR \fBregexp\fR \fBregsub\fR \fBrename\fR -\fBreturn\fR \fBscan\fR \fBseek\fR \fBset\fR -\fBsplit\fR \fBstring\fR \fBsubst\fR \fBswitch\fR -\fBtell\fR \fBtime\fR \fBtrace\fR \fBunset\fR -\fBupdate\fR \fBuplevel\fR \fBupvar\fR \fBvariable\fR -\fBvwait\fR \fBwhile\fR +\fBlassign\fR \fBledit\fR \fBlindex\fR \fBlinsert\fR +\fBlist\fR \fBllength\fR \fBlrange\fR \fBlrepeat\fR +\fBlreplace\fR \fBlsearch\fR \fBlset\fR \fBlsort\fR +\fBnamespace\fR \fBpackage\fR \fBpid\fR \fBproc\fR +\fBputs\fR \fBread\fR \fBregexp\fR \fBregsub\fR +\fBrename\fR \fBreturn\fR \fBscan\fR \fBseek\fR +\fBset\fR \fBsplit\fR \fBstring\fR \fBsubst\fR +\fBswitch\fR \fBtell\fR \fBtime\fR \fBtrace\fR +\fBunset\fR \fBupdate\fR \fBuplevel\fR \fBupvar\fR +\fBvariable\fR \fBvwait\fR \fBwhile\fR .DE The following commands are hidden by \fBinterp create\fR when it creates a safe interpreter: diff --git a/doc/lappend.n b/doc/lappend.n index 89b6909..3ddb36c 100644 --- a/doc/lappend.n +++ b/doc/lappend.n @@ -49,7 +49,7 @@ Using \fBlappend\fR to build up a list of numbers. 1 2 3 4 5 .CE .SH "SEE ALSO" -list(n), lassign(n), lindex(n), linsert(n), llength(n), +list(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), lreverse(n), lsearch(n), lset(n), lsort(n) .SH KEYWORDS diff --git a/doc/lassign.n b/doc/lassign.n index 67048ba..ac53322 100644 --- a/doc/lassign.n +++ b/doc/lassign.n @@ -52,7 +52,7 @@ command in many shell languages like this: set ::argv [\fBlassign\fR $::argv argumentToReadOff] .CE .SH "SEE ALSO" -list(n), lappend(n), lindex(n), linsert(n), llength(n), +list(n), lappend(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), lreverse(n), lsearch(n), lset(n), lsort(n) .SH KEYWORDS diff --git a/doc/ledit.n b/doc/ledit.n new file mode 100644 index 0000000..f7704ed --- /dev/null +++ b/doc/ledit.n @@ -0,0 +1,91 @@ +'\" +'\" Copyright (c) 2022 Ashok P. Nadkarni . All rights reserved. +'\" +'\" See the file "license.terms" for information on usage and redistribution +'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. +'\" +.TH ledit n 8.7 Tcl "Tcl Built-In Commands" +.so man.macros +.BS +'\" Note: do not modify the .SH NAME line immediately below! +.SH NAME +ledit \- Replace elements of a list stored in variable +.SH SYNOPSIS +\fBledit \fIlistVar first last \fR?\fIvalue value ...\fR? +.BE +.SH DESCRIPTION +.PP +The command fetches the list value in variable \fIlistVar\fR and replaces the +elements in the range given by indices \fIfirst\fR to \fIlast\fR (inclusive) +with the \fIvalue\fR arguments. The resulting list is then stored back in +\fIlistVar\fR and returned as the result of the command. +.PP +Arguments \fIfirst\fR and \fIlast\fR are index values specifying the first and +last elements of the range to replace. They are interpreted +the same as index values for the command \fBstring index\fR, +supporting simple index arithmetic and indices relative to the +end of the list. The index 0 refers to the first element of the +list, and \fBend\fR refers to the last element of the list. +.PP +If either \fIfirst\fR or \fIlast\fR is less than zero, it is considered to +refer to the position before the first element of the list. This allows +elements to be prepended. +.PP +If either \fIfirst\fR or \fIlast\fR indicates a position greater than the +index of the last element of the list, it is treated as if it is an +index one greater than the last element. This allows elements to be appended. +.PP +If \fIlast\fR is less than \fIfirst\fR, then any specified elements +will be inserted into the list before the element specified by \fIfirst\fR +with no elements being deleted. +.PP +The \fIvalue\fR arguments specify zero or more new elements to +be added to the list in place of those that were deleted. +Each \fIvalue\fR argument will become a separate element of +the list. If no \fIvalue\fR arguments are specified, then the elements +between \fIfirst\fR and \fIlast\fR are simply deleted. +.SH EXAMPLES +.PP +Prepend to a list. +.PP +.CS +% set lst {c d e f g} +c d e f g +% ledit lst -1 -1 a b +a b c d e f g +.CE +.PP +Append to the list. +.PP +.CS +% ledit lst end+1 end+1 h i +a b c d e f g h i +.CE +.PP +Delete third and fourth elements. +.PP +.CS +% ledit lst 2 3 +a b e f g h i +.CE +.PP +Replace two elements with three. +.PP +.CS +% ledit lst 2 3 x y z +a b x y z g h i +% set lst +a b x y z g h i +.CE +.PP +.SH "SEE ALSO" +list(n), lappend(n), lassign(n), lindex(n), linsert(n), llength(n), +lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), +lreverse(n), lsearch(n), lset(n), lsort(n), +string(n) +.SH KEYWORDS +element, list, replace +.\" Local variables: +.\" mode: nroff +.\" fill-column: 78 +.\" End: diff --git a/doc/lindex.n b/doc/lindex.n index 75fe5e8..0ba30a4 100644 --- a/doc/lindex.n +++ b/doc/lindex.n @@ -115,7 +115,7 @@ set idx 3 \fI\(-> f\fR .CE .SH "SEE ALSO" -list(n), lappend(n), lassign(n), linsert(n), llength(n), +list(n), lappend(n), lassign(n), ledit(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), lreverse(n), lsearch(n), lset(n), lsort(n), string(n) diff --git a/doc/linsert.n b/doc/linsert.n index 3179256..685b563 100644 --- a/doc/linsert.n +++ b/doc/linsert.n @@ -45,7 +45,7 @@ set newList [\fBlinsert\fR $midList end-1 lazy] set newerList [\fBlinsert\fR [\fBlinsert\fR $oldList end-1 quick] 1 lazy] .CE .SH "SEE ALSO" -list(n), lappend(n), lassign(n), lindex(n), llength(n), +list(n), lappend(n), lassign(n), ledit(n), lindex(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), lreverse(n), lsearch(n), lset(n), lsort(n), string(n) diff --git a/doc/list.n b/doc/list.n index 3fa1975..1792560 100644 --- a/doc/list.n +++ b/doc/list.n @@ -46,7 +46,7 @@ while \fBconcat\fR with the same arguments will return \fBa b c d e f {g h}\fR .CE .SH "SEE ALSO" -lappend(n), lassign(n), lindex(n), linsert(n), llength(n), +lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), lreverse(n), lsearch(n), lset(n), lsort(n) .SH KEYWORDS diff --git a/doc/llength.n b/doc/llength.n index 26824a0..7a3e6de 100644 --- a/doc/llength.n +++ b/doc/llength.n @@ -49,7 +49,7 @@ An empty list is not necessarily an empty string: 1,0 .CE .SH "SEE ALSO" -list(n), lappend(n), lassign(n), lindex(n), linsert(n), +list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), lreverse(n), lsearch(n), lset(n), lsort(n) .SH KEYWORDS diff --git a/doc/lmap.n b/doc/lmap.n index 026e9d0..29b1242 100644 --- a/doc/lmap.n +++ b/doc/lmap.n @@ -78,7 +78,7 @@ set prefix [\fBlmap\fR x $values {expr { .CE .SH "SEE ALSO" break(n), continue(n), for(n), foreach(n), while(n), -list(n), lappend(n), lassign(n), lindex(n), linsert(n), llength(n), +list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), lreverse(n), lsearch(n), lset(n), lsort(n) .SH KEYWORDS diff --git a/doc/lpop.n b/doc/lpop.n index 3d88638..0a156ee 100644 --- a/doc/lpop.n +++ b/doc/lpop.n @@ -86,7 +86,7 @@ The indicated value becomes the new value of \fIx\fR. \fI\(-> {{a b} {c d}} {{e f} h}\fR .CE .SH "SEE ALSO" -list(n), lappend(n), lassign(n), lindex(n), linsert(n), llength(n), +list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), lreverse(n), lsearch(n), lset(n), lsort(n), string(n) diff --git a/doc/lrange.n b/doc/lrange.n index 0d4b261..c0434bb 100644 --- a/doc/lrange.n +++ b/doc/lrange.n @@ -71,7 +71,7 @@ elements to {elements to} .CE .SH "SEE ALSO" -list(n), lappend(n), lassign(n), lindex(n), linsert(n), llength(n), +list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lremove(n), lrepeat(n), lreplace(n), lreverse(n), lsearch(n), lset(n), lsort(n), string(n) diff --git a/doc/lremove.n b/doc/lremove.n index 59d261b..e71f607 100644 --- a/doc/lremove.n +++ b/doc/lremove.n @@ -46,7 +46,7 @@ Removing the same element indicated in two different ways: a b d e .CE .SH "SEE ALSO" -list(n), lappend(n), lassign(n), lindex(n), linsert(n), llength(n), +list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lrepeat(n), lreplace(n), lreverse(n), lsearch(n), lset(n), lsort(n) .SH KEYWORDS diff --git a/doc/lrepeat.n b/doc/lrepeat.n index 9a3fc88..de7ba54 100644 --- a/doc/lrepeat.n +++ b/doc/lrepeat.n @@ -32,7 +32,7 @@ is identical to \fBlist element ...\fR. \fI\(-> {a a} b c {a a} b c {a a} b c\fR .CE .SH "SEE ALSO" -list(n), lappend(n), lassign(n), lindex(n), linsert(n), llength(n), +list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lreplace(n), lreverse(n), lsearch(n), lset(n), lsort(n) .SH KEYWORDS diff --git a/doc/lreplace.n b/doc/lreplace.n index bc9d7ca..6694ad7 100644 --- a/doc/lreplace.n +++ b/doc/lreplace.n @@ -95,7 +95,7 @@ a b c d e f g h i .CE .VE TIP505 .SH "SEE ALSO" -list(n), lappend(n), lassign(n), lindex(n), linsert(n), llength(n), +list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreverse(n), lsearch(n), lset(n), lsort(n), string(n) diff --git a/doc/lreverse.n b/doc/lreverse.n index e2e3b69..0f0b6d6 100644 --- a/doc/lreverse.n +++ b/doc/lreverse.n @@ -25,7 +25,7 @@ input list, \fIlist\fR, except with the elements in the reverse order. \fI\(-> f e {c d} b a\fR .CE .SH "SEE ALSO" -list(n), lappend(n), lassign(n), lindex(n), linsert(n), llength(n), +list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), lsearch(n), lset(n), lsort(n) .SH KEYWORDS diff --git a/doc/lsearch.n b/doc/lsearch.n index c5dc98f..85b8609 100644 --- a/doc/lsearch.n +++ b/doc/lsearch.n @@ -229,7 +229,7 @@ The same thing for a flattened list: .CE .SH "SEE ALSO" foreach(n), -list(n), lappend(n), lassign(n), lindex(n), linsert(n), llength(n), +list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), lreverse(n), lset(n), lsort(n), string(n) diff --git a/doc/lset.n b/doc/lset.n index 4b97ed6..588a0a5 100644 --- a/doc/lset.n +++ b/doc/lset.n @@ -136,7 +136,7 @@ The indicated return value also becomes the new value of \fIx\fR. \fI\(-> {{a b} {c d}} {{e f} {j h}}\fR .CE .SH "SEE ALSO" -list(n), lappend(n), lassign(n), lindex(n), linsert(n), llength(n), +list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), lreverse(n), lsearch(n), lsort(n) string(n) diff --git a/doc/lsort.n b/doc/lsort.n index 2018e30..ddf9ed1 100644 --- a/doc/lsort.n +++ b/doc/lsort.n @@ -264,7 +264,7 @@ More complex sorting using a comparison function: {1 dingo} {2 banana} {0x2 carrot} {3 apple} .CE .SH "SEE ALSO" -list(n), lappend(n), lassign(n), lindex(n), linsert(n), llength(n), +list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), lreverse(n), lsearch(n), lset(n) .SH KEYWORDS diff --git a/generic/tclBasic.c b/generic/tclBasic.c index b013909..21503b4 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -324,7 +324,7 @@ static const CmdInfo builtInCmds[] = { {"lsearch", Tcl_LsearchObjCmd, NULL, NULL, CMD_IS_SAFE}, {"lset", Tcl_LsetObjCmd, TclCompileLsetCmd, NULL, CMD_IS_SAFE}, {"lsort", Tcl_LsortObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"lsubst", Tcl_LsubstObjCmd, NULL, NULL, CMD_IS_SAFE}, + {"ledit", Tcl_LeditObjCmd, NULL, NULL, CMD_IS_SAFE}, {"package", Tcl_PackageObjCmd, NULL, TclNRPackageObjCmd, CMD_IS_SAFE}, {"proc", Tcl_ProcObjCmd, NULL, NULL, CMD_IS_SAFE}, {"regexp", Tcl_RegexpObjCmd, TclCompileRegexpCmd, NULL, CMD_IS_SAFE}, diff --git a/generic/tclCmdIL.c b/generic/tclCmdIL.c index 7776c78..b2e3ac8 100644 --- a/generic/tclCmdIL.c +++ b/generic/tclCmdIL.c @@ -4486,9 +4486,9 @@ Tcl_LsortObjCmd( /* *---------------------------------------------------------------------- * - * Tcl_LsubstObjCmd -- + * Tcl_LeditObjCmd -- * - * This procedure is invoked to process the "lsubst" Tcl command. See the + * This procedure is invoked to process the "ledit" Tcl command. See the * user documentation for details on what it does. * * Results: @@ -4501,7 +4501,7 @@ Tcl_LsortObjCmd( */ int -Tcl_LsubstObjCmd( +Tcl_LeditObjCmd( TCL_UNUSED(ClientData), Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ @@ -4529,7 +4529,7 @@ Tcl_LsubstObjCmd( /* * TODO - refactor the index extraction into a common function shared - * by Tcl_{Lrange,Lreplace,Lsubst}ObjCmd + * by Tcl_{Lrange,Lreplace,Ledit}ObjCmd */ result = TclListObjLengthM(interp, listPtr, &listLen); diff --git a/generic/tclInt.h b/generic/tclInt.h index 155bb82..863251b 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -3686,6 +3686,9 @@ MODULE_SCOPE int Tcl_LappendObjCmd(void *clientData, MODULE_SCOPE int Tcl_LassignObjCmd(void *clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); +MODULE_SCOPE int Tcl_LeditObjCmd(ClientData clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); MODULE_SCOPE int Tcl_LindexObjCmd(void *clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); @@ -3731,9 +3734,6 @@ MODULE_SCOPE int Tcl_LsetObjCmd(void *clientData, MODULE_SCOPE int Tcl_LsortObjCmd(void *clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); -MODULE_SCOPE int Tcl_LsubstObjCmd(ClientData clientData, - Tcl_Interp *interp, int objc, - Tcl_Obj *const objv[]); MODULE_SCOPE Tcl_Command TclInitNamespaceCmd(Tcl_Interp *interp); MODULE_SCOPE int TclNamespaceEnsembleCmd(void *dummy, Tcl_Interp *interp, int objc, diff --git a/tests/lreplace.test b/tests/lreplace.test index 4204c2f..2952899 100644 --- a/tests/lreplace.test +++ b/tests/lreplace.test @@ -236,272 +236,272 @@ apply {{} { } }} -# Essentially same tests as above but for lsubst -test lsubst-1.1 {lsubst command} { +# Essentially same tests as above but for ledit +test ledit-1.1 {ledit command} { set l {1 2 3 4 5} - list [lsubst l 0 0 a] $l + list [ledit l 0 0 a] $l } {{a 2 3 4 5} {a 2 3 4 5}} -test lsubst-1.2 {lsubst command} { +test ledit-1.2 {ledit command} { set l {1 2 3 4 5} - list [lsubst l 1 1 a] $l + list [ledit l 1 1 a] $l } {{1 a 3 4 5} {1 a 3 4 5}} -test lsubst-1.3 {lsubst command} { +test ledit-1.3 {ledit command} { set l {1 2 3 4 5} - list [lsubst l 2 2 a] $l + list [ledit l 2 2 a] $l } {{1 2 a 4 5} {1 2 a 4 5}} -test lsubst-1.4 {lsubst command} { +test ledit-1.4 {ledit command} { set l {1 2 3 4 5} - list [lsubst l 3 3 a] $l + list [ledit l 3 3 a] $l } {{1 2 3 a 5} {1 2 3 a 5}} -test lsubst-1.5 {lsubst command} { +test ledit-1.5 {ledit command} { set l {1 2 3 4 5} - list [lsubst l 4 4 a] $l + list [ledit l 4 4 a] $l } {{1 2 3 4 a} {1 2 3 4 a}} -test lsubst-1.6 {lsubst command} { +test ledit-1.6 {ledit command} { set l {1 2 3 4 5} - list [lsubst l 4 5 a] $l + list [ledit l 4 5 a] $l } {{1 2 3 4 a} {1 2 3 4 a}} -test lsubst-1.7 {lsubst command} { +test ledit-1.7 {ledit command} { set l {1 2 3 4 5} - list [lsubst l -1 -1 a] $l + list [ledit l -1 -1 a] $l } {{a 1 2 3 4 5} {a 1 2 3 4 5}} -test lsubst-1.8 {lsubst command} { +test ledit-1.8 {ledit command} { set l {1 2 3 4 5} - list [lsubst l 2 end a b c d] $l + list [ledit l 2 end a b c d] $l } {{1 2 a b c d} {1 2 a b c d}} -test lsubst-1.9 {lsubst command} { +test ledit-1.9 {ledit command} { set l {1 2 3 4 5} - list [lsubst l 0 3] $l + list [ledit l 0 3] $l } {5 5} -test lsubst-1.10 {lsubst command} { +test ledit-1.10 {ledit command} { set l {1 2 3 4 5} - list [lsubst l 0 4] $l + list [ledit l 0 4] $l } {{} {}} -test lsubst-1.11 {lsubst command} { +test ledit-1.11 {ledit command} { set l {1 2 3 4 5} - list [lsubst l 0 1] $l + list [ledit l 0 1] $l } {{3 4 5} {3 4 5}} -test lsubst-1.12 {lsubst command} { +test ledit-1.12 {ledit command} { set l {1 2 3 4 5} - list [lsubst l 2 3] $l + list [ledit l 2 3] $l } {{1 2 5} {1 2 5}} -test lsubst-1.13 {lsubst command} { +test ledit-1.13 {ledit command} { set l {1 2 3 4 5} - list [lsubst l 3 end] $l + list [ledit l 3 end] $l } {{1 2 3} {1 2 3}} -test lsubst-1.14 {lsubst command} { +test ledit-1.14 {ledit command} { set l {1 2 3 4 5} - list [lsubst l -1 4 a b c] $l + list [ledit l -1 4 a b c] $l } {{a b c} {a b c}} -test lsubst-1.15 {lsubst command} { +test ledit-1.15 {ledit command} { set l {a b "c c" d e f} - list [lsubst l 3 3] $l + list [ledit l 3 3] $l } {{a b {c c} e f} {a b {c c} e f}} -test lsubst-1.16 {lsubst command} { +test ledit-1.16 {ledit command} { set l { 1 2 3 4 5} - list [lsubst l 0 0 a] $l + list [ledit l 0 0 a] $l } {{a 2 3 4 5} {a 2 3 4 5}} -test lsubst-1.17 {lsubst command} { +test ledit-1.17 {ledit command} { set l {1 2 3 4 "5 6"} - list [lsubst l 4 4 a] $l + list [ledit l 4 4 a] $l } {{1 2 3 4 a} {1 2 3 4 a}} -test lsubst-1.18 {lsubst command} { +test ledit-1.18 {ledit command} { set l {1 2 3 4 {5 6}} - list [lsubst l 4 4 a] $l + list [ledit l 4 4 a] $l } {{1 2 3 4 a} {1 2 3 4 a}} -test lsubst-1.19 {lsubst command} { +test ledit-1.19 {ledit command} { set l {1 2 3 4} - list [lsubst l 2 end x y z] $l + list [ledit l 2 end x y z] $l } {{1 2 x y z} {1 2 x y z}} -test lsubst-1.20 {lsubst command} { +test ledit-1.20 {ledit command} { set l {1 2 3 4} - list [lsubst l end end a] $l + list [ledit l end end a] $l } {{1 2 3 a} {1 2 3 a}} -test lsubst-1.21 {lsubst command} { +test ledit-1.21 {ledit command} { set l {1 2 3 4} - list [lsubst l end 3 a] $l + list [ledit l end 3 a] $l } {{1 2 3 a} {1 2 3 a}} -test lsubst-1.22 {lsubst command} { +test ledit-1.22 {ledit command} { set l {1 2 3 4} - list [lsubst l end end] $l + list [ledit l end end] $l } {{1 2 3} {1 2 3}} -test lsubst-1.23 {lsubst command} { +test ledit-1.23 {ledit command} { set l {1 2 3 4} - list [lsubst l 2 -1 xy] $l + list [ledit l 2 -1 xy] $l } {{1 2 xy 3 4} {1 2 xy 3 4}} -test lsubst-1.24 {lsubst command} { +test ledit-1.24 {ledit command} { set l {1 2 3 4} - list [lsubst l end -1 z] $l + list [ledit l end -1 z] $l } {{1 2 3 z 4} {1 2 3 z 4}} -test lsubst-1.25 {lsubst command} { +test ledit-1.25 {ledit command} { set l {\}\ hello} - concat \"[lsubst l end end]\" $l + concat \"[ledit l end end]\" $l } {"\}\ " \}\ } -test lsubst-1.26 {lsubst command} { +test ledit-1.26 {ledit command} { catch {unset foo} set foo {a b} - list [lsubst foo end end] $foo \ - [lsubst foo end end] $foo \ - [lsubst foo end end] $foo + list [ledit foo end end] $foo \ + [ledit foo end end] $foo \ + [ledit foo end end] $foo } {a a {} {} {} {}} -test lsubst-1.27 {lsubset command} -body { +test ledit-1.27 {lsubset command} -body { set l x - list [lsubst l 1 1] $l + list [ledit l 1 1] $l } -result {x x} -test lsubst-1.28 {lsubst command} -body { +test ledit-1.28 {ledit command} -body { set l x - list [lsubst l 1 1 y] $l + list [ledit l 1 1 y] $l } -result {{x y} {x y}} -test lsubst-1.29 {lsubst command} -body { +test ledit-1.29 {ledit command} -body { set l x - lsubst l 1 1 [error foo] + ledit l 1 1 [error foo] } -returnCodes 1 -result {foo} -test lsubst-1.30 {lsubst command} -body { +test ledit-1.30 {ledit command} -body { set l {not {}alist} - lsubst l 0 0 [error foo] + ledit l 0 0 [error foo] } -returnCodes 1 -result {foo} -test lsubst-1.31 {lsubst command} -body { +test ledit-1.31 {ledit command} -body { unset -nocomplain arr set arr(x) {a b} - list [lsubst arr(x) 0 0 c] $arr(x) + list [ledit arr(x) 0 0 c] $arr(x) } -result {{c b} {c b}} -test lsubst-2.1 {lsubst errors} -body { - list [catch lsubst msg] $msg -} -result {1 {wrong # args: should be "lsubst listVar first last ?element ...?"}} -test lsubst-2.2 {lsubst errors} -body { +test ledit-2.1 {ledit errors} -body { + list [catch ledit msg] $msg +} -result {1 {wrong # args: should be "ledit listVar first last ?element ...?"}} +test ledit-2.2 {ledit errors} -body { unset -nocomplain x - list [catch {lsubst l b} msg] $msg -} -result {1 {wrong # args: should be "lsubst listVar first last ?element ...?"}} -test lsubst-2.3 {lsubst errors} -body { + list [catch {ledit l b} msg] $msg +} -result {1 {wrong # args: should be "ledit listVar first last ?element ...?"}} +test ledit-2.3 {ledit errors} -body { set x {} - list [catch {lsubst x a 10} msg] $msg + list [catch {ledit x a 10} msg] $msg } -result {1 {bad index "a": must be integer?[+-]integer? or end?[+-]integer?}} -test lsubst-2.4 {lsubst errors} -body { +test ledit-2.4 {ledit errors} -body { set l {} - list [catch {lsubst l 10 x} msg] $msg + list [catch {ledit l 10 x} msg] $msg } -result {1 {bad index "x": must be integer?[+-]integer? or end?[+-]integer?}} -test lsubst-2.5 {lsubst errors} -body { +test ledit-2.5 {ledit errors} -body { set l {} - list [catch {lsubst l 10 1x} msg] $msg + list [catch {ledit l 10 1x} msg] $msg } -result {1 {bad index "1x": must be integer?[+-]integer? or end?[+-]integer?}} -test lsubst-2.6 {lsubst errors} -body { +test ledit-2.6 {ledit errors} -body { set l x - list [catch {lsubst l 3 2} msg] $msg + list [catch {ledit l 3 2} msg] $msg } -result {0 x} -test lsubst-2.7 {lsubst errors} -body { +test ledit-2.7 {ledit errors} -body { set l x - list [catch {lsubst l 2 2} msg] $msg + list [catch {ledit l 2 2} msg] $msg } -result {0 x} -test lsubst-2.8 {lsubst errors} -body { +test ledit-2.8 {ledit errors} -body { unset -nocomplain l - lsubst l 0 0 x + ledit l 0 0 x } -returnCodes error -result {can't read "l": no such variable} -test lsubst-2.9 {lsubst errors} -body { +test ledit-2.9 {ledit errors} -body { unset -nocomplain arr - lsubst arr(x) 0 0 x + ledit arr(x) 0 0 x } -returnCodes error -result {can't read "arr(x)": no such variable} -test lsubst-2.10 {lsubst errors} -body { +test ledit-2.10 {ledit errors} -body { unset -nocomplain arr set arr(y) y - lsubst arr(x) 0 0 x + ledit arr(x) 0 0 x } -returnCodes error -result {can't read "arr(x)": no such element in array} -test lsubst-3.1 {lsubst won't modify shared argument objects} { +test ledit-3.1 {ledit won't modify shared argument objects} { proc p {} { set l "a b c" - lsubst l 1 1 "x y" + ledit l 1 1 "x y" # The literal in locals table should be unmodified return [list "a b c" $l] } p } {{a b c} {a {x y} c}} -# Following bugs were in lreplace. Make sure lsubst does not have them -test lsubst-4.1 {Bug ccc2c2cc98: lreplace edge case} { +# Following bugs were in lreplace. Make sure ledit does not have them +test ledit-4.1 {Bug ccc2c2cc98: lreplace edge case} { set l {} - list [lsubst l 1 1] $l + list [ledit l 1 1] $l } {{} {}} -test lsubst-4.2 {Bug ccc2c2cc98: lreplace edge case} { +test ledit-4.2 {Bug ccc2c2cc98: lreplace edge case} { set l { } - list [lsubst l 1 1] $l + list [ledit l 1 1] $l } {{} {}} -test lsubst-4.3 {lreplace edge case} { +test ledit-4.3 {lreplace edge case} { set l {1 2 3} - lsubst l 2 0 + ledit l 2 0 } {1 2 3} -test lsubst-4.4 {lsubst edge case} { +test ledit-4.4 {ledit edge case} { set l {1 2 3 4 5} - list [lsubst l 3 1] $l + list [ledit l 3 1] $l } {{1 2 3 4 5} {1 2 3 4 5}} test lreplace-4.5 {lreplace edge case} { lreplace {1 2 3 4 5} 3 0 _ } {1 2 3 _ 4 5} -test lsubst-4.6 {lsubst end-x: bug a4cb3f06c4} { +test ledit-4.6 {ledit end-x: bug a4cb3f06c4} { set l {0 1 2 3 4} - list [lsubst l 0 end-2] $l + list [ledit l 0 end-2] $l } {{3 4} {3 4}} -test lsubst-4.6.1 {lsubst end-x: bug a4cb3f06c4} { +test ledit-4.6.1 {ledit end-x: bug a4cb3f06c4} { set l {0 1 2 3 4} - list [lsubst l 0 end-2 a b c] $l + list [ledit l 0 end-2 a b c] $l } {{a b c 3 4} {a b c 3 4}} -test lsubst-4.7 {lsubst with two end-indexes: increasing} { +test ledit-4.7 {ledit with two end-indexes: increasing} { set l {0 1 2 3 4} - list [lsubst l end-2 end-1] $l + list [ledit l end-2 end-1] $l } {{0 1 4} {0 1 4}} -test lsubst-4.7.1 {lsubst with two end-indexes: increasing} { +test ledit-4.7.1 {ledit with two end-indexes: increasing} { set l {0 1 2 3 4} - list [lsubst l end-2 end-1 a b c] $l + list [ledit l end-2 end-1 a b c] $l } {{0 1 a b c 4} {0 1 a b c 4}} -test lsubst-4.8 {lsubst with two end-indexes: equal} { +test ledit-4.8 {ledit with two end-indexes: equal} { set l {0 1 2 3 4} - list [lsubst l end-2 end-2] $l + list [ledit l end-2 end-2] $l } {{0 1 3 4} {0 1 3 4}} -test lsubst-4.8.1 {lsubst with two end-indexes: equal} { +test ledit-4.8.1 {ledit with two end-indexes: equal} { set l {0 1 2 3 4} - list [lsubst l end-2 end-2 a b c] $l + list [ledit l end-2 end-2 a b c] $l } {{0 1 a b c 3 4} {0 1 a b c 3 4}} -test lsubst-4.9 {lsubst with two end-indexes: decreasing} { +test ledit-4.9 {ledit with two end-indexes: decreasing} { set l {0 1 2 3 4} - list [lsubst l end-2 end-3] $l + list [ledit l end-2 end-3] $l } {{0 1 2 3 4} {0 1 2 3 4}} -test lsubst-4.9.1 {lsubst with two end-indexes: decreasing} { +test ledit-4.9.1 {ledit with two end-indexes: decreasing} { set l {0 1 2 3 4} - list [lsubst l end-2 end-3 a b c] $l + list [ledit l end-2 end-3 a b c] $l } {{0 1 a b c 2 3 4} {0 1 a b c 2 3 4}} -test lsubst-4.10 {lsubst with two equal indexes} { +test ledit-4.10 {ledit with two equal indexes} { set l {0 1 2 3 4} - list [lsubst l 2 2] $l + list [ledit l 2 2] $l } {{0 1 3 4} {0 1 3 4}} -test lsubst-4.10.1 {lsubst with two equal indexes} { +test ledit-4.10.1 {ledit with two equal indexes} { set l {0 1 2 3 4} - list [lsubst l 2 2 a b c] $l + list [ledit l 2 2 a b c] $l } {{0 1 a b c 3 4} {0 1 a b c 3 4}} -test lsubst-4.11 {lsubst end index first} { +test ledit-4.11 {ledit end index first} { set l {0 1 2 3 4} - list [lsubst l end-2 1 a b c] $l + list [ledit l end-2 1 a b c] $l } {{0 1 a b c 2 3 4} {0 1 a b c 2 3 4}} -test lsubst-4.12 {lsubst end index first} { +test ledit-4.12 {ledit end index first} { set l {0 1 2 3 4} - list [lsubst l end-2 2 a b c] $l + list [ledit l end-2 2 a b c] $l } {{0 1 a b c 3 4} {0 1 a b c 3 4}} -test lsubst-4.13 {lsubst empty list} { +test ledit-4.13 {ledit empty list} { set l {} - list [lsubst l 1 1 1] $l + list [ledit l 1 1 1] $l } {1 1} -test lsubst-4.14 {lsubst empty list} { +test ledit-4.14 {ledit empty list} { set l {} - list [lsubst l 2 2 2] $l + list [ledit l 2 2 2] $l } {2 2} -test lsubst-5.1 {compiled lreplace: Bug 47ac84309b} { +test ledit-5.1 {compiled lreplace: Bug 47ac84309b} { apply {x { - lsubst x end 0 + ledit x end 0 }} {a b c} } {a b c} -test lsubst-5.2 {compiled lreplace: Bug 47ac84309b} { +test ledit-5.2 {compiled lreplace: Bug 47ac84309b} { apply {x { - lsubst x end 0 A + ledit x end 0 A }} {a b c} } {a b A c} @@ -520,10 +520,10 @@ apply {{} { foreach b $idxs { foreach i $ins { set expected [list [catch {$lreplace $ls $a $b {*}$i} m] $m] - set tester [list lsubst ls $a $b {*}$i] + set tester [list ledit ls $a $b {*}$i] set script [list catch $tester m] set script "list \[$script\] \$m" - test lsubst-6.[incr n] {lsubst battery} -body \ + test ledit-6.[incr n] {ledit battery} -body \ [list apply [list {ls} $script] $ls] -result $expected } } -- cgit v0.12 From dda585be8bc4eb50870c491eb7cd1b29eb42cef1 Mon Sep 17 00:00:00 2001 From: griffin Date: Sun, 25 Sep 2022 18:05:53 +0000 Subject: Fix out-of-bounds length bug. --- generic/tclArithSeries.c | 48 +++++++++++++++++++++++++++++++++++++----------- generic/tclArithSeries.h | 13 ++++++++----- generic/tclCmdIL.c | 26 +++++++++++++++++++------- generic/tclExecute.c | 6 +++++- generic/tclListObj.c | 2 +- tests/lseq.test | 19 +++++++++++++++++++ 6 files changed, 89 insertions(+), 25 deletions(-) diff --git a/generic/tclArithSeries.c b/generic/tclArithSeries.c index 93177a7..3974808 100755 --- a/generic/tclArithSeries.c +++ b/generic/tclArithSeries.c @@ -270,8 +270,16 @@ assignNumber(int useDoubles, Tcl_WideInt *intNumberPtr, double *dblNumberPtr, Tc * None. *---------------------------------------------------------------------- */ -Tcl_Obj * -TclNewArithSeriesObj(int useDoubles, Tcl_Obj *startObj, Tcl_Obj *endObj, Tcl_Obj *stepObj, Tcl_Obj *lenObj) +int +TclNewArithSeriesObj( + Tcl_Interp *interp, /* For error reporting */ + Tcl_Obj **arithSeriesObj, /* return value */ + int useDoubles, /* Flag indicates values start, + ** end, step, are treated as doubles */ + Tcl_Obj *startObj, /* Starting value */ + Tcl_Obj *endObj, /* Ending limit */ + Tcl_Obj *stepObj, /* increment value */ + Tcl_Obj *lenObj) /* Number of elements */ { double dstart, dend, dstep; Tcl_WideInt start, end, step, len; @@ -290,7 +298,8 @@ TclNewArithSeriesObj(int useDoubles, Tcl_Obj *startObj, Tcl_Obj *endObj, Tcl_Obj dstep = step; } if (dstep == 0) { - return Tcl_NewObj(); + *arithSeriesObj = Tcl_NewObj(); + return TCL_OK; } } if (endObj) { @@ -330,11 +339,20 @@ TclNewArithSeriesObj(int useDoubles, Tcl_Obj *startObj, Tcl_Obj *endObj, Tcl_Obj } } - if (useDoubles) { - return TclNewArithSeriesDbl(dstart, dend, dstep, len); - } else { - return TclNewArithSeriesInt(start, end, step, len); + if (len > ListSizeT_MAX) { + Tcl_SetObjResult( + interp, + Tcl_NewStringObj("max length of a Tcl list exceeded", -1)); + Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); + return TCL_ERROR; } + + if (arithSeriesObj) { + *arithSeriesObj = (useDoubles) + ? TclNewArithSeriesDbl(dstart, dend, dstep, len) + : TclNewArithSeriesInt(start, end, step, len); + } + return TCL_OK; } /* @@ -684,6 +702,7 @@ TclArithSeriesObjCopy( Tcl_Obj * TclArithSeriesObjRange( + Tcl_Interp *interp, /* For error message(s) */ Tcl_Obj *arithSeriesPtr, /* List object to take a range from. */ int fromIdx, /* Index of first element to include. */ int toIdx) /* Index of last element to include. */ @@ -711,8 +730,12 @@ TclArithSeriesObjRange( if (Tcl_IsShared(arithSeriesPtr) || ((arithSeriesPtr->refCount > 1))) { - Tcl_Obj *newSlicePtr = TclNewArithSeriesObj(arithSeriesRepPtr->isDouble, - startObj, endObj, stepObj, NULL); + Tcl_Obj *newSlicePtr; + if (TclNewArithSeriesObj(interp, &newSlicePtr, + arithSeriesRepPtr->isDouble, startObj, endObj, + stepObj, NULL) != TCL_OK) { + newSlicePtr = NULL; + } Tcl_DecrRefCount(startObj); Tcl_DecrRefCount(endObj); Tcl_DecrRefCount(stepObj); @@ -875,6 +898,7 @@ TclArithSeriesGetElements( Tcl_Obj * TclArithSeriesObjReverse( + Tcl_Interp *interp, /* For error message(s) */ Tcl_Obj *arithSeriesPtr) /* List object to reverse. */ { ArithSeries *arithSeriesRepPtr; @@ -910,8 +934,10 @@ TclArithSeriesObjReverse( if (Tcl_IsShared(arithSeriesPtr) || ((arithSeriesPtr->refCount > 1))) { Tcl_Obj *lenObj = Tcl_NewWideIntObj(len); - resultObj = TclNewArithSeriesObj(isDouble, - startObj, endObj, stepObj, lenObj); + if (TclNewArithSeriesObj(interp, &resultObj, + isDouble, startObj, endObj, stepObj, lenObj) != TCL_OK) { + resultObj = NULL; + } Tcl_DecrRefCount(lenObj); } else { diff --git a/generic/tclArithSeries.h b/generic/tclArithSeries.h index f855c22..3ace052 100644 --- a/generic/tclArithSeries.h +++ b/generic/tclArithSeries.h @@ -40,9 +40,10 @@ MODULE_SCOPE int TclArithSeriesObjStep(Tcl_Obj *arithSeriesPtr, MODULE_SCOPE int TclArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr, Tcl_WideInt index, Tcl_Obj **elementObj); MODULE_SCOPE Tcl_WideInt TclArithSeriesObjLength(Tcl_Obj *arithSeriesPtr); -MODULE_SCOPE Tcl_Obj * TclArithSeriesObjRange(Tcl_Obj *arithSeriesPtr, - int fromIdx, int toIdx); -MODULE_SCOPE Tcl_Obj * TclArithSeriesObjReverse(Tcl_Obj *arithSeriesPtr); +MODULE_SCOPE Tcl_Obj * TclArithSeriesObjRange(Tcl_Interp *interp, + Tcl_Obj *arithSeriesPtr, int fromIdx, int toIdx); +MODULE_SCOPE Tcl_Obj * TclArithSeriesObjReverse(Tcl_Interp *interp, + Tcl_Obj *arithSeriesPtr); MODULE_SCOPE int TclArithSeriesGetElements(Tcl_Interp *interp, Tcl_Obj *objPtr, int *objcPtr, Tcl_Obj ***objvPtr); MODULE_SCOPE Tcl_Obj * TclNewArithSeriesInt(Tcl_WideInt start, @@ -50,5 +51,7 @@ MODULE_SCOPE Tcl_Obj * TclNewArithSeriesInt(Tcl_WideInt start, Tcl_WideInt len); MODULE_SCOPE Tcl_Obj * TclNewArithSeriesDbl(double start, double end, double step, Tcl_WideInt len); -MODULE_SCOPE Tcl_Obj * TclNewArithSeriesObj(int useDoubles, Tcl_Obj *startObj, - Tcl_Obj *endObj, Tcl_Obj *stepObj, Tcl_Obj *lenObj); +MODULE_SCOPE int TclNewArithSeriesObj(Tcl_Interp *interp, + Tcl_Obj **arithSeriesObj, int useDoubles, + Tcl_Obj *startObj, Tcl_Obj *endObj, + Tcl_Obj *stepObj, Tcl_Obj *lenObj); diff --git a/generic/tclCmdIL.c b/generic/tclCmdIL.c index 9430eb5..f9dcc0f 100644 --- a/generic/tclCmdIL.c +++ b/generic/tclCmdIL.c @@ -2720,7 +2720,6 @@ Tcl_LrangeObjCmd( /* Argument objects. */ { int listLen, first, last, result; - if (objc != 4) { Tcl_WrongNumArgs(interp, 1, objv, "list first last"); return TCL_ERROR; @@ -2744,7 +2743,13 @@ Tcl_LrangeObjCmd( } if (TclHasInternalRep(objv[1],&tclArithSeriesType)) { - Tcl_SetObjResult(interp, TclArithSeriesObjRange(objv[1], first, last)); + Tcl_Obj *rangeObj; + rangeObj = TclArithSeriesObjRange(interp, objv[1], first, last); + if (rangeObj) { + Tcl_SetObjResult(interp, rangeObj); + } else { + return TCL_ERROR; + } } else { Tcl_SetObjResult(interp, TclListObjRange(objv[1], first, last)); } @@ -3137,8 +3142,13 @@ Tcl_LreverseObjCmd( * just to reverse it. */ if (TclHasInternalRep(objv[1],&tclArithSeriesType)) { - Tcl_SetObjResult(interp, TclArithSeriesObjReverse(objv[1])); - return TCL_OK; + Tcl_Obj *resObj = TclArithSeriesObjReverse(interp, objv[1]); + if (resObj) { + Tcl_SetObjResult(interp, resObj); + return TCL_OK; + } else { + return TCL_ERROR; + } } /* end ArithSeries */ /* True List */ @@ -4422,10 +4432,12 @@ Tcl_LseqObjCmd( /* * Success! Now lets create the series object. */ - arithSeriesPtr = TclNewArithSeriesObj(useDoubles, start, end, step, elementCount); + status = TclNewArithSeriesObj(interp, &arithSeriesPtr, + useDoubles, start, end, step, elementCount); - Tcl_SetObjResult(interp, arithSeriesPtr); - status = TCL_OK; + if (status == TCL_OK) { + Tcl_SetObjResult(interp, arithSeriesPtr); + } done: // Free number arguments. diff --git a/generic/tclExecute.c b/generic/tclExecute.c index f8d5493..5f29bfa 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -5154,7 +5154,11 @@ TEBCresume( fromIdx = TclIndexDecode(fromIdx, objc - 1); if (TclHasInternalRep(valuePtr,&tclArithSeriesType)) { - objResultPtr = TclArithSeriesObjRange(valuePtr, fromIdx, toIdx); + objResultPtr = TclArithSeriesObjRange(interp, valuePtr, fromIdx, toIdx); + if (objResultPtr == NULL) { + TRACE_ERROR(interp); + goto gotError; + } } else { objResultPtr = TclListObjRange(valuePtr, fromIdx, toIdx); } diff --git a/generic/tclListObj.c b/generic/tclListObj.c index 5034174..12b8386 100644 --- a/generic/tclListObj.c +++ b/generic/tclListObj.c @@ -2632,7 +2632,7 @@ TclLindexFlat( /* Handle ArithSeries as special case */ if (TclHasInternalRep(listObj,&tclArithSeriesType)) { - ListSizeT index, listLen = TclArithSeriesObjLength(listObj); + Tcl_WideInt index, listLen = TclArithSeriesObjLength(listObj); Tcl_Obj *elemObj = NULL; for (i=0 ; i Date: Mon, 26 Sep 2022 15:51:20 +0000 Subject: Make Tcl_SaveResult() and friends _really_ deprecated, so make gcc/clang warn when it's used --- generic/tcl.decls | 6 +++--- generic/tclDecls.h | 25 ++++++++++++++++++------- generic/tclTest.c | 9 +++++++++ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/generic/tcl.decls b/generic/tcl.decls index aab5cb5..3b00f4a 100644 --- a/generic/tcl.decls +++ b/generic/tcl.decls @@ -1043,7 +1043,7 @@ declare 288 { declare 289 { void Tcl_DeleteThreadExitHandler(Tcl_ExitProc *proc, void *clientData) } -declare 290 { +declare 290 {deprecated {Use Tcl_DiscardInterpState}} { void Tcl_DiscardResult(Tcl_SavedResult *statePtr) } declare 291 { @@ -1126,10 +1126,10 @@ declare 313 { int Tcl_ReadChars(Tcl_Channel channel, Tcl_Obj *objPtr, int charsToRead, int appendFlag) } -declare 314 { +declare 314 {deprecated {Use Tcl_RestoreInterpState}} { void Tcl_RestoreResult(Tcl_Interp *interp, Tcl_SavedResult *statePtr) } -declare 315 { +declare 315 {deprecated {Use Tcl_SaveInterpState}} { void Tcl_SaveResult(Tcl_Interp *interp, Tcl_SavedResult *statePtr) } declare 316 { diff --git a/generic/tclDecls.h b/generic/tclDecls.h index 562ea1a..ea5c187 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -900,7 +900,8 @@ EXTERN void Tcl_CreateThreadExitHandler(Tcl_ExitProc *proc, EXTERN void Tcl_DeleteThreadExitHandler(Tcl_ExitProc *proc, void *clientData); /* 290 */ -EXTERN void Tcl_DiscardResult(Tcl_SavedResult *statePtr); +TCL_DEPRECATED("Use Tcl_DiscardInterpState") +void Tcl_DiscardResult(Tcl_SavedResult *statePtr); /* 291 */ EXTERN int Tcl_EvalEx(Tcl_Interp *interp, const char *script, int numBytes, int flags); @@ -965,10 +966,12 @@ EXTERN int Tcl_NumUtfChars(const char *src, int length); EXTERN int Tcl_ReadChars(Tcl_Channel channel, Tcl_Obj *objPtr, int charsToRead, int appendFlag); /* 314 */ -EXTERN void Tcl_RestoreResult(Tcl_Interp *interp, +TCL_DEPRECATED("Use Tcl_RestoreInterpState") +void Tcl_RestoreResult(Tcl_Interp *interp, Tcl_SavedResult *statePtr); /* 315 */ -EXTERN void Tcl_SaveResult(Tcl_Interp *interp, +TCL_DEPRECATED("Use Tcl_SaveInterpState") +void Tcl_SaveResult(Tcl_Interp *interp, Tcl_SavedResult *statePtr); /* 316 */ EXTERN int Tcl_SetSystemEncoding(Tcl_Interp *interp, @@ -2327,7 +2330,7 @@ typedef struct TclStubs { Tcl_Encoding (*tcl_CreateEncoding) (const Tcl_EncodingType *typePtr); /* 287 */ void (*tcl_CreateThreadExitHandler) (Tcl_ExitProc *proc, void *clientData); /* 288 */ void (*tcl_DeleteThreadExitHandler) (Tcl_ExitProc *proc, void *clientData); /* 289 */ - void (*tcl_DiscardResult) (Tcl_SavedResult *statePtr); /* 290 */ + TCL_DEPRECATED_API("Use Tcl_DiscardInterpState") void (*tcl_DiscardResult) (Tcl_SavedResult *statePtr); /* 290 */ int (*tcl_EvalEx) (Tcl_Interp *interp, const char *script, int numBytes, int flags); /* 291 */ int (*tcl_EvalObjv) (Tcl_Interp *interp, int objc, Tcl_Obj *const objv[], int flags); /* 292 */ int (*tcl_EvalObjEx) (Tcl_Interp *interp, Tcl_Obj *objPtr, int flags); /* 293 */ @@ -2351,8 +2354,8 @@ typedef struct TclStubs { void (*tcl_ConditionWait) (Tcl_Condition *condPtr, Tcl_Mutex *mutexPtr, const Tcl_Time *timePtr); /* 311 */ int (*tcl_NumUtfChars) (const char *src, int length); /* 312 */ int (*tcl_ReadChars) (Tcl_Channel channel, Tcl_Obj *objPtr, int charsToRead, int appendFlag); /* 313 */ - void (*tcl_RestoreResult) (Tcl_Interp *interp, Tcl_SavedResult *statePtr); /* 314 */ - void (*tcl_SaveResult) (Tcl_Interp *interp, Tcl_SavedResult *statePtr); /* 315 */ + TCL_DEPRECATED_API("Use Tcl_RestoreInterpState") void (*tcl_RestoreResult) (Tcl_Interp *interp, Tcl_SavedResult *statePtr); /* 314 */ + TCL_DEPRECATED_API("Use Tcl_SaveInterpState") void (*tcl_SaveResult) (Tcl_Interp *interp, Tcl_SavedResult *statePtr); /* 315 */ int (*tcl_SetSystemEncoding) (Tcl_Interp *interp, const char *name); /* 316 */ Tcl_Obj * (*tcl_SetVar2Ex) (Tcl_Interp *interp, const char *part1, const char *part2, Tcl_Obj *newValuePtr, int flags); /* 317 */ void (*tcl_ThreadAlert) (Tcl_ThreadId threadId); /* 318 */ @@ -4228,22 +4231,30 @@ extern const TclStubs *tclStubsPtr; #define Tcl_GlobalEval(interp, objPtr) \ Tcl_EvalEx(interp, objPtr, TCL_INDEX_NONE, TCL_EVAL_GLOBAL) #undef Tcl_SaveResult +inline TCL_DEPRECATED_API("Use Tcl_SaveInterpState") void Tcl_SaveResult_(void) {} #define Tcl_SaveResult(interp, statePtr) \ do { \ + Tcl_SaveResult_(); \ (statePtr)->objResultPtr = Tcl_GetObjResult(interp); \ Tcl_IncrRefCount((statePtr)->objResultPtr); \ Tcl_SetObjResult(interp, Tcl_NewObj()); \ } while(0) #undef Tcl_RestoreResult +inline TCL_DEPRECATED_API("Use Tcl_RestoreInterpState") void Tcl_RestoreResult_(void) {} #define Tcl_RestoreResult(interp, statePtr) \ do { \ + Tcl_RestoreResult_(); \ Tcl_ResetResult(interp); \ Tcl_SetObjResult(interp, (statePtr)->objResultPtr); \ Tcl_DecrRefCount((statePtr)->objResultPtr); \ } while(0) #undef Tcl_DiscardResult +inline TCL_DEPRECATED_API("Use Tcl_DiscardInterpState") void Tcl_DiscardResult_(void) {} #define Tcl_DiscardResult(statePtr) \ - Tcl_DecrRefCount((statePtr)->objResultPtr) + do { \ + Tcl_DiscardResult_(); \ + Tcl_DecrRefCount((statePtr)->objResultPtr); \ + } while(0) #undef Tcl_SetResult #define Tcl_SetResult(interp, result, freeProc) \ do { \ diff --git a/generic/tclTest.c b/generic/tclTest.c index dcd86db..354ea9c 100644 --- a/generic/tclTest.c +++ b/generic/tclTest.c @@ -176,6 +176,15 @@ typedef struct TestChannel { static TestChannel *firstDetached; +#ifdef __GNUC__ +/* + * The rest of this file shouldn't warn about deprecated functions; they're + * there because we intend them to be so and know that this file is OK to + * touch those fields. + */ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + /* * Forward declarations for procedures defined later in this file: */ -- cgit v0.12 From 196a720845760665e592d261a61fd8111db2bc67 Mon Sep 17 00:00:00 2001 From: griffin Date: Mon, 26 Sep 2022 16:27:01 +0000 Subject: Fix compile error. --- generic/tclListObj.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/generic/tclListObj.c b/generic/tclListObj.c index 12b8386..623689b 100644 --- a/generic/tclListObj.c +++ b/generic/tclListObj.c @@ -2632,7 +2632,8 @@ TclLindexFlat( /* Handle ArithSeries as special case */ if (TclHasInternalRep(listObj,&tclArithSeriesType)) { - Tcl_WideInt index, listLen = TclArithSeriesObjLength(listObj); + Tcl_WideInt listLen = TclArithSeriesObjLength(listObj); + int index; Tcl_Obj *elemObj = NULL; for (i=0 ; i Date: Tue, 27 Sep 2022 03:17:28 +0000 Subject: Add lseq to list command cross references --- doc/interp.n | 16 ++++++++-------- doc/lappend.n | 2 +- doc/lassign.n | 2 +- doc/ledit.n | 2 +- doc/lindex.n | 2 +- doc/linsert.n | 2 +- doc/list.n | 2 +- doc/llength.n | 2 +- doc/lmap.n | 2 +- doc/lpop.n | 2 +- doc/lrange.n | 2 +- doc/lremove.n | 2 +- doc/lrepeat.n | 2 +- doc/lreplace.n | 2 +- doc/lreverse.n | 2 +- doc/lsearch.n | 2 +- doc/lseq.n | 4 ++-- doc/lset.n | 2 +- doc/lsort.n | 2 +- 19 files changed, 27 insertions(+), 27 deletions(-) diff --git a/doc/interp.n b/doc/interp.n index b3cc918..08bed1c 100644 --- a/doc/interp.n +++ b/doc/interp.n @@ -593,14 +593,14 @@ built-in commands: \fBinfo\fR \fBinterp\fR \fBjoin\fR \fBlappend\fR \fBlassign\fR \fBledit\fR \fBlindex\fR \fBlinsert\fR \fBlist\fR \fBllength\fR \fBlrange\fR \fBlrepeat\fR -\fBlreplace\fR \fBlsearch\fR \fBlset\fR \fBlsort\fR -\fBnamespace\fR \fBpackage\fR \fBpid\fR \fBproc\fR -\fBputs\fR \fBread\fR \fBregexp\fR \fBregsub\fR -\fBrename\fR \fBreturn\fR \fBscan\fR \fBseek\fR -\fBset\fR \fBsplit\fR \fBstring\fR \fBsubst\fR -\fBswitch\fR \fBtell\fR \fBtime\fR \fBtrace\fR -\fBunset\fR \fBupdate\fR \fBuplevel\fR \fBupvar\fR -\fBvariable\fR \fBvwait\fR \fBwhile\fR +\fBlreplace\fR \fBlsearch\fR \fBlseq\fR \fBlset\fR +\fBlsort\fR \fBnamespace\fR \fBpackage\fR \fBpid\fR +\fBproc\fR \fBputs\fR \fBread\fR \fBregexp\fR +\fBregsub\fR \fBrename\fR \fBreturn\fR \fBscan\fR +\fBseek\fR \fBset\fR \fBsplit\fR \fBstring\fR +\fBsubst\fR \fBswitch\fR \fBtell\fR \fBtime\fR +\fBtrace\fR \fBunset\fR \fBupdate\fR \fBuplevel\fR +\fBupvar\fR \fBvariable\fR \fBvwait\fR \fBwhile\fR .DE The following commands are hidden by \fBinterp create\fR when it creates a safe interpreter: diff --git a/doc/lappend.n b/doc/lappend.n index 3ddb36c..3fbda79 100644 --- a/doc/lappend.n +++ b/doc/lappend.n @@ -51,7 +51,7 @@ Using \fBlappend\fR to build up a list of numbers. .SH "SEE ALSO" list(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), -lreverse(n), lsearch(n), lset(n), lsort(n) +lreverse(n), lsearch(n), lseq(n), lset(n), lsort(n) .SH KEYWORDS append, element, list, variable .\" Local variables: diff --git a/doc/lassign.n b/doc/lassign.n index ac53322..d23509a 100644 --- a/doc/lassign.n +++ b/doc/lassign.n @@ -54,7 +54,7 @@ set ::argv [\fBlassign\fR $::argv argumentToReadOff] .SH "SEE ALSO" list(n), lappend(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), -lreverse(n), lsearch(n), lset(n), lsort(n) +lreverse(n), lsearch(n), lseq(n), lset(n), lsort(n) .SH KEYWORDS assign, element, list, multiple, set, variable '\"Local Variables: diff --git a/doc/ledit.n b/doc/ledit.n index f7704ed..48e6da5 100644 --- a/doc/ledit.n +++ b/doc/ledit.n @@ -81,7 +81,7 @@ a b x y z g h i .SH "SEE ALSO" list(n), lappend(n), lassign(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), -lreverse(n), lsearch(n), lset(n), lsort(n), +lreverse(n), lsearch(n), lseq(n), lset(n), lsort(n), string(n) .SH KEYWORDS element, list, replace diff --git a/doc/lindex.n b/doc/lindex.n index 0ba30a4..d4d845d 100644 --- a/doc/lindex.n +++ b/doc/lindex.n @@ -117,7 +117,7 @@ set idx 3 .SH "SEE ALSO" list(n), lappend(n), lassign(n), ledit(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), -lreverse(n), lsearch(n), lset(n), lsort(n), +lreverse(n), lsearch(n), lseq(n), lset(n), lsort(n), string(n) .SH KEYWORDS element, index, list diff --git a/doc/linsert.n b/doc/linsert.n index 685b563..014f9cd 100644 --- a/doc/linsert.n +++ b/doc/linsert.n @@ -47,7 +47,7 @@ set newerList [\fBlinsert\fR [\fBlinsert\fR $oldList end-1 quick] 1 lazy] .SH "SEE ALSO" list(n), lappend(n), lassign(n), ledit(n), lindex(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), -lreverse(n), lsearch(n), lset(n), lsort(n), +lreverse(n), lsearch(n), lseq(n), lset(n), lsort(n), string(n) .SH KEYWORDS element, insert, list diff --git a/doc/list.n b/doc/list.n index 1792560..08a6fe7 100644 --- a/doc/list.n +++ b/doc/list.n @@ -48,7 +48,7 @@ while \fBconcat\fR with the same arguments will return .SH "SEE ALSO" lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), -lreverse(n), lsearch(n), lset(n), lsort(n) +lreverse(n), lsearch(n), lseq(n), lset(n), lsort(n) .SH KEYWORDS element, list, quoting '\"Local Variables: diff --git a/doc/llength.n b/doc/llength.n index 7a3e6de..574834f 100644 --- a/doc/llength.n +++ b/doc/llength.n @@ -51,7 +51,7 @@ An empty list is not necessarily an empty string: .SH "SEE ALSO" list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), -lreverse(n), lsearch(n), lset(n), lsort(n) +lreverse(n), lsearch(n), lseq(n), lset(n), lsort(n) .SH KEYWORDS element, list, length '\" Local Variables: diff --git a/doc/lmap.n b/doc/lmap.n index 29b1242..36a0c7c 100644 --- a/doc/lmap.n +++ b/doc/lmap.n @@ -80,7 +80,7 @@ set prefix [\fBlmap\fR x $values {expr { break(n), continue(n), for(n), foreach(n), while(n), list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), -lreverse(n), lsearch(n), lset(n), lsort(n) +lreverse(n), lsearch(n), lseq(n), lset(n), lsort(n) .SH KEYWORDS foreach, iteration, list, loop, map '\" Local Variables: diff --git a/doc/lpop.n b/doc/lpop.n index 0a156ee..2a464eb 100644 --- a/doc/lpop.n +++ b/doc/lpop.n @@ -88,7 +88,7 @@ The indicated value becomes the new value of \fIx\fR. .SH "SEE ALSO" list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), -lreverse(n), lsearch(n), lset(n), lsort(n), +lreverse(n), lsearch(n), lseq(n), lset(n), lsort(n), string(n) .SH KEYWORDS element, index, list, remove, pop, stack, queue diff --git a/doc/lrange.n b/doc/lrange.n index c0434bb..38c4abf 100644 --- a/doc/lrange.n +++ b/doc/lrange.n @@ -73,7 +73,7 @@ elements to .SH "SEE ALSO" list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lremove(n), lrepeat(n), lreplace(n), -lreverse(n), lsearch(n), lset(n), lsort(n), +lreverse(n), lsearch(n), lseq(n), lset(n), lsort(n), string(n) .SH KEYWORDS element, list, range, sublist diff --git a/doc/lremove.n b/doc/lremove.n index e71f607..8763ea6 100644 --- a/doc/lremove.n +++ b/doc/lremove.n @@ -48,7 +48,7 @@ a b d e .SH "SEE ALSO" list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lrepeat(n), lreplace(n), -lreverse(n), lsearch(n), lset(n), lsort(n) +lreverse(n), lsearch(n), lseq(n), lset(n), lsort(n) .SH KEYWORDS element, list, remove .\" Local variables: diff --git a/doc/lrepeat.n b/doc/lrepeat.n index de7ba54..cd672db 100644 --- a/doc/lrepeat.n +++ b/doc/lrepeat.n @@ -34,7 +34,7 @@ is identical to \fBlist element ...\fR. .SH "SEE ALSO" list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lreplace(n), -lreverse(n), lsearch(n), lset(n), lsort(n) +lreverse(n), lsearch(n), lseq(n), lset(n), lsort(n) .SH KEYWORDS element, index, list '\" Local Variables: diff --git a/doc/lreplace.n b/doc/lreplace.n index 6694ad7..47d33f9 100644 --- a/doc/lreplace.n +++ b/doc/lreplace.n @@ -97,7 +97,7 @@ a b c d e f g h i .SH "SEE ALSO" list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), -lreverse(n), lsearch(n), lset(n), lsort(n), +lreverse(n), lsearch(n), lseq(n), lset(n), lsort(n), string(n) .SH KEYWORDS element, list, replace diff --git a/doc/lreverse.n b/doc/lreverse.n index 0f0b6d6..bb0703d 100644 --- a/doc/lreverse.n +++ b/doc/lreverse.n @@ -27,7 +27,7 @@ input list, \fIlist\fR, except with the elements in the reverse order. .SH "SEE ALSO" list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), -lsearch(n), lset(n), lsort(n) +lsearch(n), lseq(n), lset(n), lsort(n) .SH KEYWORDS element, list, reverse '\" Local Variables: diff --git a/doc/lsearch.n b/doc/lsearch.n index 85b8609..dc6d1f7 100644 --- a/doc/lsearch.n +++ b/doc/lsearch.n @@ -231,7 +231,7 @@ The same thing for a flattened list: foreach(n), list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), -lreverse(n), lset(n), lsort(n), +lreverse(n), lseq(n), lset(n), lsort(n), string(n) .SH KEYWORDS binary search, linear search, diff --git a/doc/lseq.n b/doc/lseq.n index 5c7d03b..df8a8bc 100644 --- a/doc/lseq.n +++ b/doc/lseq.n @@ -81,8 +81,8 @@ must be numeric; a non-numeric string will result in an error. .\" .CE .SH "SEE ALSO" -foreach(n), list(n), lappend(n), lassign(n), lindex(n), linsert(n), llength(n), -lmap(n), lpop(n), lrange(n), lremove(n), lreplace(n), +foreach(n), list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), +llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lreplace(n), lreverse(n), lsearch(n), lset(n), lsort(n) .SH KEYWORDS element, index, list diff --git a/doc/lset.n b/doc/lset.n index 588a0a5..e2e1590 100644 --- a/doc/lset.n +++ b/doc/lset.n @@ -138,7 +138,7 @@ The indicated return value also becomes the new value of \fIx\fR. .SH "SEE ALSO" list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), -lreverse(n), lsearch(n), lsort(n) +lreverse(n), lsearch(n), lseq(n), lsort(n) string(n) .SH KEYWORDS element, index, list, replace, set diff --git a/doc/lsort.n b/doc/lsort.n index ddf9ed1..1695ea8 100644 --- a/doc/lsort.n +++ b/doc/lsort.n @@ -266,7 +266,7 @@ More complex sorting using a comparison function: .SH "SEE ALSO" list(n), lappend(n), lassign(n), ledit(n), lindex(n), linsert(n), llength(n), lmap(n), lpop(n), lrange(n), lremove(n), lrepeat(n), lreplace(n), -lreverse(n), lsearch(n), lset(n) +lreverse(n), lsearch(n), lseq(n), lset(n) .SH KEYWORDS element, list, order, sort '\" Local Variables: -- cgit v0.12 From a0ee4e463ed283418dc94c826f9b65933fe5ba7d Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 27 Sep 2022 08:52:31 +0000 Subject: Since 'inline' doesn't seem to work, use MODULE_SCOPE --- generic/tclDecls.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/generic/tclDecls.h b/generic/tclDecls.h index ea5c187..5d6e184 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -4231,7 +4231,7 @@ extern const TclStubs *tclStubsPtr; #define Tcl_GlobalEval(interp, objPtr) \ Tcl_EvalEx(interp, objPtr, TCL_INDEX_NONE, TCL_EVAL_GLOBAL) #undef Tcl_SaveResult -inline TCL_DEPRECATED_API("Use Tcl_SaveInterpState") void Tcl_SaveResult_(void) {} +MODULE_SCOPE TCL_DEPRECATED_API("Use Tcl_SaveInterpState") void Tcl_SaveResult_(void) {} #define Tcl_SaveResult(interp, statePtr) \ do { \ Tcl_SaveResult_(); \ @@ -4240,7 +4240,7 @@ inline TCL_DEPRECATED_API("Use Tcl_SaveInterpState") void Tcl_SaveResult_(void) Tcl_SetObjResult(interp, Tcl_NewObj()); \ } while(0) #undef Tcl_RestoreResult -inline TCL_DEPRECATED_API("Use Tcl_RestoreInterpState") void Tcl_RestoreResult_(void) {} +MODULE_SCOPE TCL_DEPRECATED_API("Use Tcl_RestoreInterpState") void Tcl_RestoreResult_(void) {} #define Tcl_RestoreResult(interp, statePtr) \ do { \ Tcl_RestoreResult_(); \ @@ -4249,7 +4249,7 @@ inline TCL_DEPRECATED_API("Use Tcl_RestoreInterpState") void Tcl_RestoreResult_( Tcl_DecrRefCount((statePtr)->objResultPtr); \ } while(0) #undef Tcl_DiscardResult -inline TCL_DEPRECATED_API("Use Tcl_DiscardInterpState") void Tcl_DiscardResult_(void) {} +MODULE_SCOPE TCL_DEPRECATED_API("Use Tcl_DiscardInterpState") void Tcl_DiscardResult_(void) {} #define Tcl_DiscardResult(statePtr) \ do { \ Tcl_DiscardResult_(); \ -- cgit v0.12 From 8b5a187d44e2ed11ef57b3a94e26e349a20ae2f0 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Wed, 28 Sep 2022 08:30:06 +0000 Subject: Still doesn't work. Use static in stead of MODULE_SCOPE --- generic/tclDecls.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/generic/tclDecls.h b/generic/tclDecls.h index 5d6e184..25adc95 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -4231,7 +4231,7 @@ extern const TclStubs *tclStubsPtr; #define Tcl_GlobalEval(interp, objPtr) \ Tcl_EvalEx(interp, objPtr, TCL_INDEX_NONE, TCL_EVAL_GLOBAL) #undef Tcl_SaveResult -MODULE_SCOPE TCL_DEPRECATED_API("Use Tcl_SaveInterpState") void Tcl_SaveResult_(void) {} +static TCL_DEPRECATED_API("Use Tcl_SaveInterpState") void Tcl_SaveResult_(void) {} #define Tcl_SaveResult(interp, statePtr) \ do { \ Tcl_SaveResult_(); \ @@ -4240,7 +4240,7 @@ MODULE_SCOPE TCL_DEPRECATED_API("Use Tcl_SaveInterpState") void Tcl_SaveResult_( Tcl_SetObjResult(interp, Tcl_NewObj()); \ } while(0) #undef Tcl_RestoreResult -MODULE_SCOPE TCL_DEPRECATED_API("Use Tcl_RestoreInterpState") void Tcl_RestoreResult_(void) {} +static TCL_DEPRECATED_API("Use Tcl_RestoreInterpState") void Tcl_RestoreResult_(void) {} #define Tcl_RestoreResult(interp, statePtr) \ do { \ Tcl_RestoreResult_(); \ @@ -4249,7 +4249,7 @@ MODULE_SCOPE TCL_DEPRECATED_API("Use Tcl_RestoreInterpState") void Tcl_RestoreRe Tcl_DecrRefCount((statePtr)->objResultPtr); \ } while(0) #undef Tcl_DiscardResult -MODULE_SCOPE TCL_DEPRECATED_API("Use Tcl_DiscardInterpState") void Tcl_DiscardResult_(void) {} +static TCL_DEPRECATED_API("Use Tcl_DiscardInterpState") void Tcl_DiscardResult_(void) {} #define Tcl_DiscardResult(statePtr) \ do { \ Tcl_DiscardResult_(); \ -- cgit v0.12 From 93e50d1448aba1ed4b5eb113ea5c9b5debee85dc Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Wed, 28 Sep 2022 12:58:58 +0000 Subject: int -> ListSizeT, and a few more simplifications --- generic/tclArithSeries.c | 10 ++++++---- generic/tclListObj.c | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/generic/tclArithSeries.c b/generic/tclArithSeries.c index 3974808..868ce74 100755 --- a/generic/tclArithSeries.c +++ b/generic/tclArithSeries.c @@ -106,8 +106,10 @@ ArithSeriesLen(Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step) { Tcl_WideInt len; - if (step == 0) return 0; - len = (step ? (1 + (((end-start))/step)) : 0); + if (step == 0) { + return 0; + } + len = 1 + ((end-start)/step); return (len < 0) ? -1 : len; } @@ -233,7 +235,7 @@ assignNumber(int useDoubles, Tcl_WideInt *intNumberPtr, double *dblNumberPtr, Tc } *number; int tcl_number_type; - if (TclGetNumberFromObj(NULL, numberObj, (ClientData*)&number, &tcl_number_type) != TCL_OK) { + if (TclGetNumberFromObj(NULL, numberObj, (void **)&number, &tcl_number_type) != TCL_OK) { return; } if (useDoubles) { @@ -818,7 +820,7 @@ TclArithSeriesGetElements( Tcl_Interp *interp, /* Used to report errors if not NULL. */ Tcl_Obj *objPtr, /* AbstractList object for which an element * array is to be returned. */ - int *objcPtr, /* Where to store the count of objects + ListSizeT *objcPtr, /* Where to store the count of objects * referenced by objv. */ Tcl_Obj ***objvPtr) /* Where to store the pointer to an array of * pointers to the list's objects. */ diff --git a/generic/tclListObj.c b/generic/tclListObj.c index 623689b..d18ad59 100644 --- a/generic/tclListObj.c +++ b/generic/tclListObj.c @@ -2633,7 +2633,7 @@ TclLindexFlat( /* Handle ArithSeries as special case */ if (TclHasInternalRep(listObj,&tclArithSeriesType)) { Tcl_WideInt listLen = TclArithSeriesObjLength(listObj); - int index; + ListSizeT index; Tcl_Obj *elemObj = NULL; for (i=0 ; i Date: Wed, 28 Sep 2022 13:57:51 +0000 Subject: Fix wrong TclGetNumberFromObj() usage: this will crash if mp_int's are involved. Everywhere else in Tcl it is used correctly --- generic/tclArithSeries.c | 18 ++++++++---------- generic/tclCmdIL.c | 7 ++----- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/generic/tclArithSeries.c b/generic/tclArithSeries.c index 868ce74..61b4a9b 100755 --- a/generic/tclArithSeries.c +++ b/generic/tclArithSeries.c @@ -229,26 +229,24 @@ TclNewArithSeriesDbl(double start, double end, double step, Tcl_WideInt len) static void assignNumber(int useDoubles, Tcl_WideInt *intNumberPtr, double *dblNumberPtr, Tcl_Obj *numberObj) { - union { - double d; - Tcl_WideInt i; - } *number; + void *clientData; int tcl_number_type; - if (TclGetNumberFromObj(NULL, numberObj, (void **)&number, &tcl_number_type) != TCL_OK) { + if (TclGetNumberFromObj(NULL, numberObj, &clientData, &tcl_number_type) != TCL_OK + || tcl_number_type == TCL_NUMBER_BIG) { return; } if (useDoubles) { - if (tcl_number_type == TCL_NUMBER_DOUBLE) { - *dblNumberPtr = number->d; + if (tcl_number_type != TCL_NUMBER_INT) { + *dblNumberPtr = *(double *)clientData; } else { - *dblNumberPtr = (double)number->i; + *dblNumberPtr = (double)*(Tcl_WideInt *)clientData; } } else { if (tcl_number_type == TCL_NUMBER_INT) { - *intNumberPtr = number->i; + *intNumberPtr = *(Tcl_WideInt *)clientData; } else { - *intNumberPtr = (Tcl_WideInt)number->d; + *intNumberPtr = (Tcl_WideInt)*(double *)clientData; } } } diff --git a/generic/tclCmdIL.c b/generic/tclCmdIL.c index 5821a35..62ceeea 100644 --- a/generic/tclCmdIL.c +++ b/generic/tclCmdIL.c @@ -4077,12 +4077,9 @@ SequenceIdentifyArgument( int status; SequenceOperators opmode; SequenceByMode bymode; - union { - Tcl_WideInt i; - double d; - } nvalue; + void *clientData; - status = TclGetNumberFromObj(NULL, argPtr, (ClientData*)&nvalue, keywordIndexPtr); + status = TclGetNumberFromObj(NULL, argPtr, &clientData, keywordIndexPtr); if (status == TCL_OK) { if (numValuePtr) { *numValuePtr = argPtr; -- cgit v0.12 From bec96305308d0c234215d25b194f1ff8417dc8b4 Mon Sep 17 00:00:00 2001 From: griffin Date: Thu, 29 Sep 2022 16:10:07 +0000 Subject: Fix bug-99e834bf33 --- generic/tclExecute.c | 2 +- tests/lseq.test | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/generic/tclExecute.c b/generic/tclExecute.c index 5f29bfa..fa0dfa2 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -4938,7 +4938,7 @@ TEBCresume( /* Decode end-offset index values. */ - index = TclIndexDecode(opnd, length); + index = TclIndexDecode(opnd, length-1); /* Compute value @ index */ if (index >= 0 && index < length) { diff --git a/tests/lseq.test b/tests/lseq.test index e05b32d..518a7bb 100644 --- a/tests/lseq.test +++ b/tests/lseq.test @@ -489,9 +489,19 @@ test lseq-4.4 {lseq corner case} -body { lappend res $s $e } eval $tcmd +} -cleanup { + unset res } -result {0 10 1 {max length of a Tcl list exceeded} 1 {max length of a Tcl list exceeded} 0 10 0 2147483638} +# Ticket 99e834bf33 - lseq, lindex end off by one + +test lseq-4.5 {lindex off by one} -body { + lappend res [eval {lindex [lseq 1 4] end}] + lappend res [eval {lindex [lseq 1 4] end-1}] +} -result {4 3} + + # cleanup ::tcltest::cleanupTests return -- cgit v0.12 From b01f9536cb1fe19d6b97c9a81b4dac4fb98dd5dd Mon Sep 17 00:00:00 2001 From: griffin Date: Fri, 30 Sep 2022 00:03:55 +0000 Subject: Fix various issues with refCounts. --- generic/tclArithSeries.c | 6 ++---- generic/tclCmdAH.c | 7 +++++++ generic/tclListObj.c | 2 -- tests/lseq.test | 14 ++++++++++++-- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/generic/tclArithSeries.c b/generic/tclArithSeries.c index 61b4a9b..ee201fa 100755 --- a/generic/tclArithSeries.c +++ b/generic/tclArithSeries.c @@ -392,6 +392,7 @@ TclArithSeriesObjStep( } else { *stepObj = Tcl_NewWideIntObj(arithSeriesRepPtr->step); } + Tcl_IncrRefCount(*stepObj); return TCL_OK; } @@ -436,6 +437,7 @@ TclArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr, Tcl_WideInt index, Tcl_Obj **ele } else { *elementObj = Tcl_NewWideIntObj(ArithSeriesIndexM(arithSeriesRepPtr, index)); } + Tcl_IncrRefCount(*elementObj); return TCL_OK; } @@ -722,11 +724,8 @@ TclArithSeriesObjRange( } TclArithSeriesObjIndex(arithSeriesPtr, fromIdx, &startObj); - Tcl_IncrRefCount(startObj); TclArithSeriesObjIndex(arithSeriesPtr, toIdx, &endObj); - Tcl_IncrRefCount(endObj); TclArithSeriesObjStep(arithSeriesPtr, &stepObj); - Tcl_IncrRefCount(stepObj); if (Tcl_IsShared(arithSeriesPtr) || ((arithSeriesPtr->refCount > 1))) { @@ -857,7 +856,6 @@ TclArithSeriesGetElements( } return TCL_ERROR; } - Tcl_IncrRefCount(objv[i]); } } } else { diff --git a/generic/tclCmdAH.c b/generic/tclCmdAH.c index 07541bd..3048e82 100644 --- a/generic/tclCmdAH.c +++ b/generic/tclCmdAH.c @@ -3027,6 +3027,13 @@ ForeachAssignments( varValuePtr = Tcl_ObjSetVar2(interp, statePtr->varvList[i][v], NULL, valuePtr, TCL_LEAVE_ERR_MSG); + if (isarithseries) { + /* arith values have implicit reference + ** Make sure value is cleaned up when var goes away + */ + Tcl_DecrRefCount(valuePtr); + } + if (varValuePtr == NULL) { Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( "\n (setting %s loop variable \"%s\")", diff --git a/generic/tclListObj.c b/generic/tclListObj.c index d18ad59..598ff6f 100644 --- a/generic/tclListObj.c +++ b/generic/tclListObj.c @@ -2641,7 +2641,6 @@ TclLindexFlat( } if (i==0) { TclArithSeriesObjIndex(listObj, index, &elemObj); - Tcl_IncrRefCount(elemObj); } else if (index > 0) { Tcl_DecrRefCount(elemObj); TclNewObj(elemObj); @@ -3304,7 +3303,6 @@ SetListFromAny( if (TclArithSeriesObjIndex(objPtr, j, &elemPtrs[j]) != TCL_OK) { return TCL_ERROR; } - Tcl_IncrRefCount(elemPtrs[j]);/* Since list now holds ref to it. */ } } else { diff --git a/tests/lseq.test b/tests/lseq.test index 518a7bb..7daa59c 100644 --- a/tests/lseq.test +++ b/tests/lseq.test @@ -223,6 +223,8 @@ test lseq-3.1 {experiement} { if {$ans eq {}} { set ans OK } + unset factor + unset l set ans } {OK} @@ -376,13 +378,18 @@ test lseq-3.26 {lsort shimmer} arithSeriesShimmer { list ${rep-before} $lexical_sort ${rep-after} } {arithseries {0 1 10 11 12 13 14 15 2 3 4 5 6 7 8 9} arithseries} -test lseq-3.27 {lreplace shimmer} arithSeriesShimmer { +test lseq-3.27 {lreplace shimmer} -constraints arithSeriesShimmer -body { set r [lseq 15 0] set rep-before [lindex [tcl::unsupported::representation $r] 3] set lexical_sort [lreplace $r 3 5 A B C] set rep-after [lindex [tcl::unsupported::representation $r] 3] list ${rep-before} $lexical_sort ${rep-after} -} {arithseries {15 14 13 A B C 9 8 7 6 5 4 3 2 1 0} arithseries} +} -cleanup { + unset r + unset rep-before + unset lexical_sort + unset rep-after +} -result {arithseries {15 14 13 A B C 9 8 7 6 5 4 3 2 1 0} arithseries} test lseq-3.28 {lreverse bug in ArithSeries} {} { set r [lseq -5 17 3] @@ -499,11 +506,14 @@ test lseq-4.4 {lseq corner case} -body { test lseq-4.5 {lindex off by one} -body { lappend res [eval {lindex [lseq 1 4] end}] lappend res [eval {lindex [lseq 1 4] end-1}] +} -cleanup { + unset res } -result {4 3} # cleanup ::tcltest::cleanupTests + return # Local Variables: -- cgit v0.12 From 3f371a5084c05daba396645abd9a25deb3d023d1 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 4 Oct 2022 15:56:08 +0000 Subject: =?UTF-8?q?Fix=20g++=20warning:=20tclEvent.c:1519:10:=20warning:?= =?UTF-8?q?=20declaration=20of=20=E2=80=98enum=20Tcl=5FVwaitObjCmd(void*,?= =?UTF-8?q?=20Tcl=5FInterp*,=20int,=20Tcl=5FObj*=20const*)::options?= =?UTF-8?q?=E2=80=99=20shadows=20a=20previous=20local=20[-Wshadow]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- generic/tclEvent.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/generic/tclEvent.c b/generic/tclEvent.c index 183ac82..1e2e7bf 100644 --- a/generic/tclEvent.c +++ b/generic/tclEvent.c @@ -1511,12 +1511,12 @@ Tcl_VwaitObjCmd( Tcl_Channel chan; Tcl_WideInt diff = -1; VwaitItem localItems[32], *vwaitItems = localItems; - static const char *const options[] = { + static const char *const vWaitOptionStrings[] = { "-all", "-extended", "-nofileevents", "-noidleevents", "-notimerevents", "-nowindowevents", "-readable", "-timeout", "-variable", "-writable", "--", NULL }; - enum options { + enum vWaitOptions { OPT_ALL, OPT_EXTD, OPT_NO_FEVTS, OPT_NO_IEVTS, OPT_NO_TEVTS, OPT_NO_WEVTS, OPT_READABLE, OPT_TIMEOUT, OPT_VARIABLE, OPT_WRITABLE, OPT_LAST @@ -1541,7 +1541,7 @@ Tcl_VwaitObjCmd( if (name[0] != '-') { break; } - if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, + if (Tcl_GetIndexFromObj(interp, objv[i], vWaitOptionStrings, "option", 0, &index) != TCL_OK) { result = TCL_ERROR; goto done; @@ -1570,7 +1570,7 @@ Tcl_VwaitObjCmd( needArg: Tcl_ResetResult(interp); Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "argument required for \"%s\"", options[index])); + "argument required for \"%s\"", vWaitOptionStrings[index])); Tcl_SetErrorCode(interp, "TCL", "EVENT", "ARGUMENT", NULL); result = TCL_ERROR; goto done; -- cgit v0.12 From 00199ad335823ec6b18983d1188f70b0b065b25e Mon Sep 17 00:00:00 2001 From: griffin Date: Tue, 4 Oct 2022 20:15:38 +0000 Subject: Fix some bugs in lseq --- generic/tclArithSeries.c | 9 +++++++-- generic/tclCmdAH.c | 17 +++++------------ generic/tclExecute.c | 7 ++++++- generic/tclListObj.c | 8 ++++---- tests/lseq.test | 2 +- 5 files changed, 23 insertions(+), 20 deletions(-) diff --git a/generic/tclArithSeries.c b/generic/tclArithSeries.c index ee201fa..6a02caa 100755 --- a/generic/tclArithSeries.c +++ b/generic/tclArithSeries.c @@ -392,7 +392,6 @@ TclArithSeriesObjStep( } else { *stepObj = Tcl_NewWideIntObj(arithSeriesRepPtr->step); } - Tcl_IncrRefCount(*stepObj); return TCL_OK; } @@ -437,7 +436,6 @@ TclArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr, Tcl_WideInt index, Tcl_Obj **ele } else { *elementObj = Tcl_NewWideIntObj(ArithSeriesIndexM(arithSeriesRepPtr, index)); } - Tcl_IncrRefCount(*elementObj); return TCL_OK; } @@ -724,8 +722,11 @@ TclArithSeriesObjRange( } TclArithSeriesObjIndex(arithSeriesPtr, fromIdx, &startObj); + Tcl_IncrRefCount(startObj); TclArithSeriesObjIndex(arithSeriesPtr, toIdx, &endObj); + Tcl_IncrRefCount(endObj); TclArithSeriesObjStep(arithSeriesPtr, &stepObj); + Tcl_IncrRefCount(stepObj); if (Tcl_IsShared(arithSeriesPtr) || ((arithSeriesPtr->refCount > 1))) { @@ -856,6 +857,7 @@ TclArithSeriesGetElements( } return TCL_ERROR; } + Tcl_IncrRefCount(objv[i]); } } } else { @@ -912,8 +914,11 @@ TclArithSeriesObjReverse( len = arithSeriesRepPtr->len; TclArithSeriesObjIndex(arithSeriesPtr, (len-1), &startObj); + Tcl_IncrRefCount(startObj); TclArithSeriesObjIndex(arithSeriesPtr, 0, &endObj); + Tcl_IncrRefCount(endObj); TclArithSeriesObjStep(arithSeriesPtr, &stepObj); + Tcl_IncrRefCount(stepObj); if (isDouble) { Tcl_GetDoubleFromObj(NULL, startObj, &dstart); diff --git a/generic/tclCmdAH.c b/generic/tclCmdAH.c index 3048e82..a5c5330 100644 --- a/generic/tclCmdAH.c +++ b/generic/tclCmdAH.c @@ -2866,13 +2866,13 @@ EachloopCmd( /* Values */ if (TclHasInternalRep(objv[2+i*2],&tclArithSeriesType)) { /* Special case for Arith Series */ - statePtr->vCopyList[i] = TclArithSeriesObjCopy(interp, objv[2+i*2]); - if (statePtr->vCopyList[i] == NULL) { + statePtr->aCopyList[i] = TclArithSeriesObjCopy(interp, objv[2+i*2]); + if (statePtr->aCopyList[i] == NULL) { result = TCL_ERROR; goto done; } /* Don't compute values here, wait until the last momement */ - statePtr->argcList[i] = TclArithSeriesObjLength(statePtr->vCopyList[i]); + statePtr->argcList[i] = TclArithSeriesObjLength(statePtr->aCopyList[i]); } else { /* List values */ statePtr->aCopyList[i] = TclListObjCopy(interp, objv[2+i*2]); @@ -3005,12 +3005,12 @@ ForeachAssignments( Tcl_Obj *valuePtr, *varValuePtr; for (i=0 ; inumLists ; i++) { - int isarithseries = TclHasInternalRep(statePtr->vCopyList[i],&tclArithSeriesType); + int isarithseries = TclHasInternalRep(statePtr->aCopyList[i],&tclArithSeriesType); for (v=0 ; vvarcList[i] ; v++) { k = statePtr->index[i]++; if (k < statePtr->argcList[i]) { if (isarithseries) { - if (TclArithSeriesObjIndex(statePtr->vCopyList[i], k, &valuePtr) != TCL_OK) { + if (TclArithSeriesObjIndex(statePtr->aCopyList[i], k, &valuePtr) != TCL_OK) { Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( "\n (setting %s loop variable \"%s\")", (statePtr->resultList != NULL ? "lmap" : "foreach"), @@ -3027,13 +3027,6 @@ ForeachAssignments( varValuePtr = Tcl_ObjSetVar2(interp, statePtr->varvList[i][v], NULL, valuePtr, TCL_LEAVE_ERR_MSG); - if (isarithseries) { - /* arith values have implicit reference - ** Make sure value is cleaned up when var goes away - */ - Tcl_DecrRefCount(valuePtr); - } - if (varValuePtr == NULL) { Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( "\n (setting %s loop variable \"%s\")", diff --git a/generic/tclExecute.c b/generic/tclExecute.c index fa0dfa2..7c7bbfd 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -4883,6 +4883,7 @@ TEBCresume( TRACE_ERROR(interp); goto gotError; } + Tcl_IncrRefCount(objResultPtr); // reference held here goto lindexDone; } @@ -5187,7 +5188,11 @@ TEBCresume( */ do { - Tcl_ListObjIndex(NULL, value2Ptr, i, &o); + if (isArithSeries) { + TclArithSeriesObjIndex(value2Ptr, i, &o); + } else { + Tcl_ListObjIndex(NULL, value2Ptr, i, &o); + } if (o != NULL) { s2 = TclGetStringFromObj(o, &s2len); } else { diff --git a/generic/tclListObj.c b/generic/tclListObj.c index 598ff6f..62bc162 100644 --- a/generic/tclListObj.c +++ b/generic/tclListObj.c @@ -1369,6 +1369,9 @@ TclListObjCopy( Tcl_Obj *copyObj; if (!TclHasInternalRep(listObj, &tclListType)) { + if (TclHasInternalRep(listObj,&tclArithSeriesType)) { + return TclArithSeriesObjCopy(interp, listObj); + } if (SetListFromAny(interp, listObj) != TCL_OK) { return NULL; } @@ -1943,10 +1946,6 @@ Tcl_ListObjIndex( Tcl_Obj **elemObjs; ListSizeT numElems; - if (TclHasInternalRep(listObj,&tclArithSeriesType)) { - return TclArithSeriesObjIndex(listObj, index, objPtrPtr); - } - /* * TODO * Unlike the original list code, this does not optimize for lindex'ing @@ -2642,6 +2641,7 @@ TclLindexFlat( if (i==0) { TclArithSeriesObjIndex(listObj, index, &elemObj); } else if (index > 0) { + /* ArithSeries cannot be a list of lists */ Tcl_DecrRefCount(elemObj); TclNewObj(elemObj); Tcl_IncrRefCount(elemObj); diff --git a/tests/lseq.test b/tests/lseq.test index 7daa59c..2e5d7e1 100644 --- a/tests/lseq.test +++ b/tests/lseq.test @@ -16,7 +16,7 @@ if {"::tcltest" ni [namespace children]} { testConstraint arithSeriesDouble 1 testConstraint arithSeriesShimmer 1 -testConstraint arithSeriesShimmerOk 0 +testConstraint arithSeriesShimmerOk 1 ## Arg errors test lseq-1.1 {error cases} -body { -- cgit v0.12 From 3cb6c489a3c0515c7b3aade0aaa139e637400559 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Wed, 5 Oct 2022 06:41:28 +0000 Subject: Missing error-check in Tcl_GetWideIntFromObj (backported from 9.0, was already fixed there) --- generic/tclArithSeries.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/generic/tclArithSeries.c b/generic/tclArithSeries.c index 6a02caa..11a4254 100755 --- a/generic/tclArithSeries.c +++ b/generic/tclArithSeries.c @@ -306,7 +306,9 @@ TclNewArithSeriesObj( assignNumber(useDoubles, &end, &dend, endObj); } if (lenObj) { - Tcl_GetWideIntFromObj(NULL, lenObj, &len); + if (TCL_OK != Tcl_GetWideIntFromObj(interp, lenObj, &len)) { + return TCL_ERROR; + } } if (startObj && endObj) { @@ -339,7 +341,7 @@ TclNewArithSeriesObj( } } - if (len > ListSizeT_MAX) { + if (TCL_MAJOR_VERSION < 9 && len > ListSizeT_MAX) { Tcl_SetObjResult( interp, Tcl_NewStringObj("max length of a Tcl list exceeded", -1)); -- cgit v0.12 From 4721ffe64fe11287997ec892d58c375a73e3876d Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Wed, 5 Oct 2022 15:31:31 +0000 Subject: Fix [1599352cca] and related issues --- generic/tclDictObj.c | 51 ++++++++++++++++++++++++++------------------------- generic/tclInt.h | 2 +- generic/tclListObj.c | 7 ++++--- generic/tclUtil.c | 13 +++++++------ 4 files changed, 38 insertions(+), 35 deletions(-) diff --git a/generic/tclDictObj.c b/generic/tclDictObj.c index ba9ab98..3fe1800 100644 --- a/generic/tclDictObj.c +++ b/generic/tclDictObj.c @@ -265,7 +265,7 @@ DeleteChainTable( ChainEntry *cPtr; for (cPtr=dict->entryChainHead ; cPtr!=NULL ; cPtr=cPtr->nextPtr) { - Tcl_Obj *valuePtr = Tcl_GetHashValue(&cPtr->entry); + Tcl_Obj *valuePtr = (Tcl_Obj *)Tcl_GetHashValue(&cPtr->entry); TclDecrRefCount(valuePtr); } @@ -312,7 +312,7 @@ DeleteChainEntry( if (cPtr == NULL) { return 0; } else { - Tcl_Obj *valuePtr = Tcl_GetHashValue(&cPtr->entry); + Tcl_Obj *valuePtr = (Tcl_Obj *)Tcl_GetHashValue(&cPtr->entry); TclDecrRefCount(valuePtr); } @@ -364,7 +364,7 @@ DupDictInternalRep( Tcl_Obj *copyPtr) { Dict *oldDict = DICT(srcPtr); - Dict *newDict = ckalloc(sizeof(Dict)); + Dict *newDict = (Dict *)ckalloc(sizeof(Dict)); ChainEntry *cPtr; /* @@ -373,8 +373,8 @@ DupDictInternalRep( InitChainTable(newDict); for (cPtr=oldDict->entryChainHead ; cPtr!=NULL ; cPtr=cPtr->nextPtr) { - Tcl_Obj *key = Tcl_GetHashKey(&oldDict->table, &cPtr->entry); - Tcl_Obj *valuePtr = Tcl_GetHashValue(&cPtr->entry); + Tcl_Obj *key = (Tcl_Obj *)Tcl_GetHashKey(&oldDict->table, &cPtr->entry); + Tcl_Obj *valuePtr = (Tcl_Obj *)Tcl_GetHashValue(&cPtr->entry); int n; Tcl_HashEntry *hPtr = CreateChainEntry(newDict, key, &n); @@ -492,7 +492,8 @@ UpdateStringOfDict( Dict *dict = DICT(dictPtr); ChainEntry *cPtr; Tcl_Obj *keyPtr, *valuePtr; - int i, length, bytesNeeded = 0; + int i, length; + unsigned int bytesNeeded = 0; const char *elem; char *dst; @@ -517,7 +518,7 @@ UpdateStringOfDict( if (numElems <= LOCAL_SIZE) { flagPtr = localFlags; } else { - flagPtr = ckalloc(numElems); + flagPtr = (char *)ckalloc(numElems); } for (i=0,cPtr=dict->entryChainHead; inextPtr) { /* @@ -526,22 +527,22 @@ UpdateStringOfDict( */ flagPtr[i] = ( i ? TCL_DONT_QUOTE_HASH : 0 ); - keyPtr = Tcl_GetHashKey(&dict->table, &cPtr->entry); + keyPtr = (Tcl_Obj *)Tcl_GetHashKey(&dict->table, &cPtr->entry); elem = TclGetStringFromObj(keyPtr, &length); bytesNeeded += TclScanElement(elem, length, flagPtr+i); - if (bytesNeeded < 0) { + if (bytesNeeded > INT_MAX) { Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); } flagPtr[i+1] = TCL_DONT_QUOTE_HASH; - valuePtr = Tcl_GetHashValue(&cPtr->entry); + valuePtr = (Tcl_Obj *)Tcl_GetHashValue(&cPtr->entry); elem = TclGetStringFromObj(valuePtr, &length); bytesNeeded += TclScanElement(elem, length, flagPtr+i+1); - if (bytesNeeded < 0) { + if (bytesNeeded > INT_MAX) { Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); } } - if (bytesNeeded > INT_MAX - numElems + 1) { + if (bytesNeeded + numElems > INT_MAX + 1U) { Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); } bytesNeeded += numElems; @@ -555,13 +556,13 @@ UpdateStringOfDict( dst = dictPtr->bytes; for (i=0,cPtr=dict->entryChainHead; inextPtr) { flagPtr[i] |= ( i ? TCL_DONT_QUOTE_HASH : 0 ); - keyPtr = Tcl_GetHashKey(&dict->table, &cPtr->entry); + keyPtr = (Tcl_Obj *)Tcl_GetHashKey(&dict->table, &cPtr->entry); elem = TclGetStringFromObj(keyPtr, &length); dst += TclConvertElement(elem, length, dst, flagPtr[i]); *dst++ = ' '; flagPtr[i+1] |= TCL_DONT_QUOTE_HASH; - valuePtr = Tcl_GetHashValue(&cPtr->entry); + valuePtr = (Tcl_Obj *)Tcl_GetHashValue(&cPtr->entry); elem = TclGetStringFromObj(valuePtr, &length); dst += TclConvertElement(elem, length, dst, flagPtr[i+1]); *dst++ = ' '; @@ -600,7 +601,7 @@ SetDictFromAny( { Tcl_HashEntry *hPtr; int isNew; - Dict *dict = ckalloc(sizeof(Dict)); + Dict *dict = (Dict *)ckalloc(sizeof(Dict)); InitChainTable(dict); @@ -625,7 +626,7 @@ SetDictFromAny( /* Store key and value in the hash table we're building. */ hPtr = CreateChainEntry(dict, objv[i], &isNew); if (!isNew) { - Tcl_Obj *discardedValue = Tcl_GetHashValue(hPtr); + Tcl_Obj *discardedValue = (Tcl_Obj *)Tcl_GetHashValue(hPtr); /* * Not really a well-formed dictionary as there are duplicate @@ -690,7 +691,7 @@ SetDictFromAny( /* Store key and value in the hash table we're building. */ hPtr = CreateChainEntry(dict, keyPtr, &isNew); if (!isNew) { - Tcl_Obj *discardedValue = Tcl_GetHashValue(hPtr); + Tcl_Obj *discardedValue = (Tcl_Obj *)Tcl_GetHashValue(hPtr); TclDecrRefCount(keyPtr); TclDecrRefCount(discardedValue); @@ -809,7 +810,7 @@ TclTraceDictPath( Tcl_IncrRefCount(tmpObj); Tcl_SetHashValue(hPtr, tmpObj); } else { - tmpObj = Tcl_GetHashValue(hPtr); + tmpObj = (Tcl_Obj *)Tcl_GetHashValue(hPtr); if (tmpObj->typePtr != &tclDictType && SetDictFromAny(interp, tmpObj) != TCL_OK) { return NULL; @@ -919,7 +920,7 @@ Tcl_DictObjPut( hPtr = CreateChainEntry(dict, keyPtr, &isNew); Tcl_IncrRefCount(valuePtr); if (!isNew) { - Tcl_Obj *oldValuePtr = Tcl_GetHashValue(hPtr); + Tcl_Obj *oldValuePtr = (Tcl_Obj *)Tcl_GetHashValue(hPtr); TclDecrRefCount(oldValuePtr); } @@ -969,7 +970,7 @@ Tcl_DictObjGet( if (hPtr == NULL) { *valuePtrPtr = NULL; } else { - *valuePtrPtr = Tcl_GetHashValue(hPtr); + *valuePtrPtr = (Tcl_Obj *)Tcl_GetHashValue(hPtr); } return TCL_OK; } @@ -1115,10 +1116,10 @@ Tcl_DictObjFirst( searchPtr->next = cPtr->nextPtr; dict->refCount++; if (keyPtrPtr != NULL) { - *keyPtrPtr = Tcl_GetHashKey(&dict->table, &cPtr->entry); + *keyPtrPtr = (Tcl_Obj *)Tcl_GetHashKey(&dict->table, &cPtr->entry); } if (valuePtrPtr != NULL) { - *valuePtrPtr = Tcl_GetHashValue(&cPtr->entry); + *valuePtrPtr = (Tcl_Obj *)Tcl_GetHashValue(&cPtr->entry); } } return TCL_OK; @@ -1181,7 +1182,7 @@ Tcl_DictObjNext( Tcl_Panic("concurrent dictionary modification and search"); } - cPtr = searchPtr->next; + cPtr = (ChainEntry *)searchPtr->next; if (cPtr == NULL) { Tcl_DictObjDone(searchPtr); *donePtr = 1; @@ -1191,11 +1192,11 @@ Tcl_DictObjNext( searchPtr->next = cPtr->nextPtr; *donePtr = 0; if (keyPtrPtr != NULL) { - *keyPtrPtr = Tcl_GetHashKey( + *keyPtrPtr = (Tcl_Obj *)Tcl_GetHashKey( &((Dict *)searchPtr->dictionaryPtr)->table, &cPtr->entry); } if (valuePtrPtr != NULL) { - *valuePtrPtr = Tcl_GetHashValue(&cPtr->entry); + *valuePtrPtr = (Tcl_Obj *)Tcl_GetHashValue(&cPtr->entry); } } diff --git a/generic/tclInt.h b/generic/tclInt.h index 63fcf62..8c3efb5 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -3146,7 +3146,7 @@ MODULE_SCOPE void TclRemoveScriptLimitCallbacks(Tcl_Interp *interp); MODULE_SCOPE int TclReToGlob(Tcl_Interp *interp, const char *reStr, int reStrLen, Tcl_DString *dsPtr, int *flagsPtr, int *quantifiersFoundPtr); -MODULE_SCOPE int TclScanElement(const char *string, int length, +MODULE_SCOPE unsigned int TclScanElement(const char *string, int length, char *flagPtr); MODULE_SCOPE void TclSetBgErrorHandler(Tcl_Interp *interp, Tcl_Obj *cmdPrefix); diff --git a/generic/tclListObj.c b/generic/tclListObj.c index 88a332f..a994fd7 100644 --- a/generic/tclListObj.c +++ b/generic/tclListObj.c @@ -1945,7 +1945,8 @@ UpdateStringOfList( char localFlags[LOCAL_SIZE], *flagPtr = NULL; List *listRepPtr = ListRepPtr(listPtr); int numElems = listRepPtr->elemCount; - int i, length, bytesNeeded = 0; + int i, length; + unsigned int bytesNeeded = 0; const char *elem; char *dst; Tcl_Obj **elemPtrs; @@ -1986,11 +1987,11 @@ UpdateStringOfList( flagPtr[i] = (i ? TCL_DONT_QUOTE_HASH : 0); elem = TclGetStringFromObj(elemPtrs[i], &length); bytesNeeded += TclScanElement(elem, length, flagPtr+i); - if (bytesNeeded < 0) { + if (bytesNeeded > INT_MAX) { Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); } } - if (bytesNeeded > INT_MAX - numElems + 1) { + if (bytesNeeded + numElems > INT_MAX + 1U) { Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); } bytesNeeded += numElems; diff --git a/generic/tclUtil.c b/generic/tclUtil.c index 8d2347b..cacd23e 100644 --- a/generic/tclUtil.c +++ b/generic/tclUtil.c @@ -1010,7 +1010,7 @@ Tcl_ScanCountedElement( *---------------------------------------------------------------------- */ -int +unsigned int TclScanElement( const char *src, /* String to convert to Tcl list element. */ int length, /* Number of bytes in src, or -1. */ @@ -1026,7 +1026,7 @@ TclScanElement( int extra = 0; /* Count of number of extra bytes needed for * formatted element, assuming we use escape * sequences in formatting. */ - int bytesNeeded; /* Buffer length computed to complete the + unsigned int bytesNeeded; /* Buffer length computed to complete the * element formatting in the selected mode. */ #if COMPAT int preferEscape = 0; /* Use preferences to track whether to use */ @@ -1290,7 +1290,7 @@ TclScanElement( *flagPtr = CONVERT_NONE; overflowCheck: - if (bytesNeeded < 0) { + if (bytesNeeded > INT_MAX) { Tcl_Panic("TclScanElement: string length overflow"); } return bytesNeeded; @@ -1568,7 +1568,8 @@ Tcl_Merge( { #define LOCAL_SIZE 64 char localFlags[LOCAL_SIZE], *flagPtr = NULL; - int i, bytesNeeded = 0; + int i; + unsigned int bytesNeeded = 0; char *result, *dst; /* @@ -1594,11 +1595,11 @@ Tcl_Merge( for (i = 0; i < argc; i++) { flagPtr[i] = ( i ? TCL_DONT_QUOTE_HASH : 0 ); bytesNeeded += TclScanElement(argv[i], -1, &flagPtr[i]); - if (bytesNeeded < 0) { + if (bytesNeeded > INT_MAX) { Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); } } - if (bytesNeeded > INT_MAX - argc + 1) { + if (bytesNeeded + argc > INT_MAX + 1U) { Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); } bytesNeeded += argc; -- cgit v0.12 From 9dd5a63f35590c88db321bf5f70429c61ed5a3b5 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Thu, 6 Oct 2022 13:12:11 +0000 Subject: TIP #640: Remove Tcl_SaveResult (in Tcl 8.7 it's only removed when compiled with -DTCL_NO_DEPRECATED) --- doc/SaveInterpState.3 | 85 ++++++++++++++++++++++++++++++++++++ doc/SaveResult.3 | 85 ------------------------------------ generic/tcl.h | 2 + generic/tclDecls.h | 29 +++--------- generic/tclTest.c | 8 ++++ macosx/Tcl.xcodeproj/project.pbxproj | 4 +- win/tcl.dsp | 2 +- 7 files changed, 105 insertions(+), 110 deletions(-) create mode 100644 doc/SaveInterpState.3 delete mode 100644 doc/SaveResult.3 diff --git a/doc/SaveInterpState.3 b/doc/SaveInterpState.3 new file mode 100644 index 0000000..804f9ec --- /dev/null +++ b/doc/SaveInterpState.3 @@ -0,0 +1,85 @@ +'\" +'\" Copyright (c) 1997 Sun Microsystems, Inc. +'\" Contributions from Don Porter, NIST, 2004. (not subject to US copyright) +'\" Copyright (c) 2018 Nathan Coulter. +'\" +'\" See the file "license.terms" for information on usage and redistribution +'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. +'\" +.TH Tcl_SaveResult 3 8.1 Tcl "Tcl Library Procedures" +.so man.macros +.BS +.SH NAME +Tcl_SaveInterpState, Tcl_RestoreInterpState, Tcl_DiscardInterpState, +Tcl_SaveResult, Tcl_RestoreResult, Tcl_DiscardResult \- Save and restore the +state of an an interpreter. +.SH SYNOPSIS +.nf +\fB#include \fR +.sp +Tcl_InterpState +\fBTcl_SaveInterpState\fR(\fIinterp, status\fR) +.sp +int +\fBTcl_RestoreInterpState\fR(\fIinterp, state\fR) +.sp +\fBTcl_DiscardInterpState\fR(\fIstate\fR) +.sp +\fBTcl_SaveResult\fR(\fIinterp, savedPtr\fR) +.sp +\fBTcl_RestoreResult\fR(\fIinterp, savedPtr\fR) +.sp +\fBTcl_DiscardResult\fR(\fIsavedPtr\fR) +.SH ARGUMENTS +.AS Tcl_InterpState savedPtr +.AP Tcl_Interp *interp in +The interpreter for the operation. +.AP int status in +The return code for the state. +.AP Tcl_InterpState state in +A token for saved state. +.AP Tcl_SavedResult *savedPtr in +A pointer to storage for saved state. +.BE +.SH DESCRIPTION +.PP +These routines save the state of an interpreter before a call to a routine such +as \fBTcl_Eval\fR, and restore the state afterwards. +.PP +\fBTcl_SaveInterpState\fR saves the parts of \fIinterp\fR that comprise the +result of a script, including the resulting value, the return code passed as +\fIstatus\fR, and any options such as \fB\-errorinfo\fR and \fB\-errorcode\fR. +It returns a token for the saved state. The interpreter result is not reset +and no interpreter state is changed. +.PP +\fBTcl_RestoreInterpState\fR restores the state indicated by \fIstate\fR and +returns the \fIstatus\fR originally passed in the corresponding call to +\fBTcl_SaveInterpState\fR. +.PP +If a saved state is not restored, \fBTcl_DiscardInterpState\fR must be called +to release it. A token used to discard or restore state must not be used +again. +.PP +\fBTcl_SaveResult\fR, \fBTcl_RestoreResult\fR, and \fBTcl_DiscardResult\fR are +deprecated. Instead use \fBTcl_SaveInterpState\fR, +\fBTcl_RestoreInterpState\fR, and \fBTcl_DiscardInterpState\fR, which are more +capable. +.PP +\fBTcl_SaveResult\fR moves the result of \fIinterp\fR to the location +\fIstatePtr\fR points to and returns the interpreter result to its initial +state. It does not save options such as \fB\-errorcode\fR or +\fB\-errorinfo\fR. +.PP +\fBTcl_RestoreResult\fR clears any existing result or error in \fIinterp\fR and +moves the result from \fIstatePtr\fR back to \fIinterp\fR. \fIstatePtr\fR is +then in an undefined state and must not be used until passed again to +\fBTcl_SaveResult\fR. +.PP +\fBTcl_DiscardResult\fR releases the state stored at \fBstatePtr\fR, which is +then in an undefined state and must not be used until passed again to +\fBTcl_SaveResult\fR. +.PP +If a saved result is not restored, \fBTcl_DiscardResult\fR must be called to +release it. +.SH KEYWORDS +result, state, interp diff --git a/doc/SaveResult.3 b/doc/SaveResult.3 deleted file mode 100644 index 804f9ec..0000000 --- a/doc/SaveResult.3 +++ /dev/null @@ -1,85 +0,0 @@ -'\" -'\" Copyright (c) 1997 Sun Microsystems, Inc. -'\" Contributions from Don Porter, NIST, 2004. (not subject to US copyright) -'\" Copyright (c) 2018 Nathan Coulter. -'\" -'\" See the file "license.terms" for information on usage and redistribution -'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES. -'\" -.TH Tcl_SaveResult 3 8.1 Tcl "Tcl Library Procedures" -.so man.macros -.BS -.SH NAME -Tcl_SaveInterpState, Tcl_RestoreInterpState, Tcl_DiscardInterpState, -Tcl_SaveResult, Tcl_RestoreResult, Tcl_DiscardResult \- Save and restore the -state of an an interpreter. -.SH SYNOPSIS -.nf -\fB#include \fR -.sp -Tcl_InterpState -\fBTcl_SaveInterpState\fR(\fIinterp, status\fR) -.sp -int -\fBTcl_RestoreInterpState\fR(\fIinterp, state\fR) -.sp -\fBTcl_DiscardInterpState\fR(\fIstate\fR) -.sp -\fBTcl_SaveResult\fR(\fIinterp, savedPtr\fR) -.sp -\fBTcl_RestoreResult\fR(\fIinterp, savedPtr\fR) -.sp -\fBTcl_DiscardResult\fR(\fIsavedPtr\fR) -.SH ARGUMENTS -.AS Tcl_InterpState savedPtr -.AP Tcl_Interp *interp in -The interpreter for the operation. -.AP int status in -The return code for the state. -.AP Tcl_InterpState state in -A token for saved state. -.AP Tcl_SavedResult *savedPtr in -A pointer to storage for saved state. -.BE -.SH DESCRIPTION -.PP -These routines save the state of an interpreter before a call to a routine such -as \fBTcl_Eval\fR, and restore the state afterwards. -.PP -\fBTcl_SaveInterpState\fR saves the parts of \fIinterp\fR that comprise the -result of a script, including the resulting value, the return code passed as -\fIstatus\fR, and any options such as \fB\-errorinfo\fR and \fB\-errorcode\fR. -It returns a token for the saved state. The interpreter result is not reset -and no interpreter state is changed. -.PP -\fBTcl_RestoreInterpState\fR restores the state indicated by \fIstate\fR and -returns the \fIstatus\fR originally passed in the corresponding call to -\fBTcl_SaveInterpState\fR. -.PP -If a saved state is not restored, \fBTcl_DiscardInterpState\fR must be called -to release it. A token used to discard or restore state must not be used -again. -.PP -\fBTcl_SaveResult\fR, \fBTcl_RestoreResult\fR, and \fBTcl_DiscardResult\fR are -deprecated. Instead use \fBTcl_SaveInterpState\fR, -\fBTcl_RestoreInterpState\fR, and \fBTcl_DiscardInterpState\fR, which are more -capable. -.PP -\fBTcl_SaveResult\fR moves the result of \fIinterp\fR to the location -\fIstatePtr\fR points to and returns the interpreter result to its initial -state. It does not save options such as \fB\-errorcode\fR or -\fB\-errorinfo\fR. -.PP -\fBTcl_RestoreResult\fR clears any existing result or error in \fIinterp\fR and -moves the result from \fIstatePtr\fR back to \fIinterp\fR. \fIstatePtr\fR is -then in an undefined state and must not be used until passed again to -\fBTcl_SaveResult\fR. -.PP -\fBTcl_DiscardResult\fR releases the state stored at \fBstatePtr\fR, which is -then in an undefined state and must not be used until passed again to -\fBTcl_SaveResult\fR. -.PP -If a saved result is not restored, \fBTcl_DiscardResult\fR must be called to -release it. -.SH KEYWORDS -result, state, interp diff --git a/generic/tcl.h b/generic/tcl.h index f17d43e..c8a76c5 100644 --- a/generic/tcl.h +++ b/generic/tcl.h @@ -817,6 +817,7 @@ typedef struct Tcl_Obj { * typically allocated on the stack. */ +#ifndef TCL_NO_DEPRECATED typedef struct Tcl_SavedResult { char *result; Tcl_FreeProc *freeProc; @@ -826,6 +827,7 @@ typedef struct Tcl_SavedResult { int appendUsed; char resultSpace[200+1]; } Tcl_SavedResult; +#endif /* *---------------------------------------------------------------------------- diff --git a/generic/tclDecls.h b/generic/tclDecls.h index 25adc95..62b9604 100644 --- a/generic/tclDecls.h +++ b/generic/tclDecls.h @@ -14,6 +14,10 @@ #include /* for size_t */ +#ifdef TCL_NO_DEPRECATED +# define Tcl_SavedResult void +#endif /* TCL_NO_DEPRECATED */ + #undef TCL_STORAGE_CLASS #ifdef BUILD_tcl # define TCL_STORAGE_CLASS DLLEXPORT @@ -4231,30 +4235,8 @@ extern const TclStubs *tclStubsPtr; #define Tcl_GlobalEval(interp, objPtr) \ Tcl_EvalEx(interp, objPtr, TCL_INDEX_NONE, TCL_EVAL_GLOBAL) #undef Tcl_SaveResult -static TCL_DEPRECATED_API("Use Tcl_SaveInterpState") void Tcl_SaveResult_(void) {} -#define Tcl_SaveResult(interp, statePtr) \ - do { \ - Tcl_SaveResult_(); \ - (statePtr)->objResultPtr = Tcl_GetObjResult(interp); \ - Tcl_IncrRefCount((statePtr)->objResultPtr); \ - Tcl_SetObjResult(interp, Tcl_NewObj()); \ - } while(0) #undef Tcl_RestoreResult -static TCL_DEPRECATED_API("Use Tcl_RestoreInterpState") void Tcl_RestoreResult_(void) {} -#define Tcl_RestoreResult(interp, statePtr) \ - do { \ - Tcl_RestoreResult_(); \ - Tcl_ResetResult(interp); \ - Tcl_SetObjResult(interp, (statePtr)->objResultPtr); \ - Tcl_DecrRefCount((statePtr)->objResultPtr); \ - } while(0) #undef Tcl_DiscardResult -static TCL_DEPRECATED_API("Use Tcl_DiscardInterpState") void Tcl_DiscardResult_(void) {} -#define Tcl_DiscardResult(statePtr) \ - do { \ - Tcl_DiscardResult_(); \ - Tcl_DecrRefCount((statePtr)->objResultPtr); \ - } while(0) #undef Tcl_SetResult #define Tcl_SetResult(interp, result, freeProc) \ do { \ @@ -4492,6 +4474,9 @@ static TCL_DEPRECATED_API("Use Tcl_DiscardInterpState") void Tcl_DiscardResult_( * Deprecated Tcl procedures: */ +#ifdef TCL_NO_DEPRECATED +# undef Tcl_SavedResult +#endif /* TCL_NO_DEPRECATED */ #undef Tcl_EvalObj #define Tcl_EvalObj(interp, objPtr) \ Tcl_EvalObjEx(interp, objPtr, 0) diff --git a/generic/tclTest.c b/generic/tclTest.c index 354ea9c..95f4d2f 100644 --- a/generic/tclTest.c +++ b/generic/tclTest.c @@ -145,7 +145,9 @@ typedef struct { * was called for a result. */ +#ifndef TCL_NO_DEPRECATED static int freeCount; +#endif /* TCL_NO_DEPRECATED */ /* * Boolean flag used by the "testsetmainloop" and "testexitmainloop" commands. @@ -297,8 +299,10 @@ static Tcl_ObjCmdProc TestregexpObjCmd; static Tcl_ObjCmdProc TestreturnObjCmd; static void TestregexpXflags(const char *string, int length, int *cflagsPtr, int *eflagsPtr); +#ifndef TCL_NO_DEPRECATED static Tcl_ObjCmdProc TestsaveresultCmd; static void TestsaveresultFree(char *blockPtr); +#endif /* TCL_NO_DEPRECATED */ static Tcl_CmdProc TestsetassocdataCmd; static Tcl_CmdProc TestsetCmd; static Tcl_CmdProc Testset2Cmd; @@ -690,8 +694,10 @@ Tcltest_Init( NULL, NULL); Tcl_CreateObjCommand(interp, "testreturn", TestreturnObjCmd, NULL, NULL); +#ifndef TCL_NO_DEPRECATED Tcl_CreateObjCommand(interp, "testsaveresult", TestsaveresultCmd, NULL, NULL); +#endif Tcl_CreateCommand(interp, "testservicemode", TestServiceModeCmd, NULL, NULL); Tcl_CreateCommand(interp, "testsetassocdata", TestsetassocdataCmd, @@ -5522,6 +5528,7 @@ Testset2Cmd( *---------------------------------------------------------------------- */ +#ifndef TCL_NO_DEPRECATED static int TestsaveresultCmd( TCL_UNUSED(void *), @@ -5635,6 +5642,7 @@ TestsaveresultFree( { freeCount++; } +#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- diff --git a/macosx/Tcl.xcodeproj/project.pbxproj b/macosx/Tcl.xcodeproj/project.pbxproj index 90896e2..4143128 100644 --- a/macosx/Tcl.xcodeproj/project.pbxproj +++ b/macosx/Tcl.xcodeproj/project.pbxproj @@ -376,7 +376,7 @@ F96D3E9108F272A6004A47F5 /* rename.n */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; path = rename.n; sourceTree = ""; }; F96D3E9208F272A6004A47F5 /* return.n */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; path = return.n; sourceTree = ""; }; F96D3E9308F272A6004A47F5 /* safe.n */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; path = safe.n; sourceTree = ""; }; - F96D3E9408F272A6004A47F5 /* SaveResult.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; path = SaveResult.3; sourceTree = ""; }; + F96D3E9408F272A6004A47F5 /* SaveInterpState.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; path = SaveInterpState.3; sourceTree = ""; }; F96D3E9508F272A6004A47F5 /* scan.n */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; path = scan.n; sourceTree = ""; }; F96D3E9608F272A6004A47F5 /* seek.n */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; path = seek.n; sourceTree = ""; }; F96D3E9708F272A6004A47F5 /* set.n */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; path = set.n; sourceTree = ""; }; @@ -1123,7 +1123,7 @@ F96D3E9108F272A6004A47F5 /* rename.n */, F96D3E9208F272A6004A47F5 /* return.n */, F96D3E9308F272A6004A47F5 /* safe.n */, - F96D3E9408F272A6004A47F5 /* SaveResult.3 */, + F96D3E9408F272A6004A47F5 /* SaveInterpState.3 */, F96D3E9508F272A6004A47F5 /* scan.n */, F96D3E9608F272A6004A47F5 /* seek.n */, F93599D80DF1F98300E04F67 /* self.n */, diff --git a/win/tcl.dsp b/win/tcl.dsp index cc9d173..aff1000 100644 --- a/win/tcl.dsp +++ b/win/tcl.dsp @@ -760,7 +760,7 @@ SOURCE=..\doc\safe.n # End Source File # Begin Source File -SOURCE=..\doc\SaveResult.3 +SOURCE=..\doc\SaveInterpState.3 # End Source File # Begin Source File -- cgit v0.12 From 6f2284ab12177714d29ad979fa1f1420e61f836b Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 7 Oct 2022 10:18:33 +0000 Subject: Follow-up to [1599352cca]: Tcl_Merge(): out-of-bounds write, more signed integer overflow. Better panic message when argc < 0. --- generic/tclUtil.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/generic/tclUtil.c b/generic/tclUtil.c index cacd23e..a8bf795 100644 --- a/generic/tclUtil.c +++ b/generic/tclUtil.c @@ -1577,7 +1577,10 @@ Tcl_Merge( * simpler. */ - if (argc == 0) { + if (argc <= 0) { + if (argc < 0) { + Tcl_Panic("Tcl_Merge called with negative argc (%d)", argc); + } result = (char *)ckalloc(1); result[0] = '\0'; return result; -- cgit v0.12 From 948f556e5200e88aa563402d1f0ad7019d0c291b Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 7 Oct 2022 11:02:05 +0000 Subject: More -1 -> TCL_INDEX_NONE --- generic/tclUtil.c | 131 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 68 insertions(+), 63 deletions(-) diff --git a/generic/tclUtil.c b/generic/tclUtil.c index 2b1305c..f10187b 100644 --- a/generic/tclUtil.c +++ b/generic/tclUtil.c @@ -375,10 +375,10 @@ static const Tcl_ObjType endOffsetType = { * * Given 'bytes' pointing to 'numBytes' bytes, scan through them and * count the number of whitespace runs that could be list element - * separators. If 'numBytes' is -1, scan to the terminating '\0'. Not a - * full list parser. Typically used to get a quick and dirty overestimate - * of length size in order to allocate space for an actual list parser to - * operate with. + * separators. If 'numBytes' is TCL_INDEX_NONE, scan to the terminating + * '\0'. Not a full list parser. Typically used to get a quick and dirty + * overestimate of length size in order to allocate space for an actual + * list parser to operate with. * * Results: * Returns the largest number of list elements that could possibly be in @@ -399,7 +399,7 @@ TclMaxListLength( { int count = 0; - if ((numBytes == 0) || ((numBytes == -1) && (*bytes == '\0'))) { + if ((numBytes == 0) || ((numBytes == TCL_INDEX_NONE) && (*bytes == '\0'))) { /* Empty string case - quick exit */ goto done; } @@ -415,7 +415,7 @@ TclMaxListLength( */ while (numBytes) { - if ((numBytes == -1) && (*bytes == '\0')) { + if ((numBytes == TCL_INDEX_NONE) && (*bytes == '\0')) { break; } if (TclIsSpaceProcM(*bytes)) { @@ -426,9 +426,9 @@ TclMaxListLength( count++; do { bytes++; - numBytes -= (numBytes != -1); + numBytes -= (numBytes != TCL_INDEX_NONE); } while (numBytes && TclIsSpaceProcM(*bytes)); - if ((numBytes == 0) || ((numBytes == -1) && (*bytes == '\0'))) { + if ((numBytes == 0) || ((numBytes == TCL_INDEX_NONE) && (*bytes == '\0'))) { break; } @@ -437,7 +437,7 @@ TclMaxListLength( */ } bytes++; - numBytes -= (numBytes != -1); + numBytes -= (numBytes != TCL_INDEX_NONE); } /* @@ -874,7 +874,7 @@ Tcl_SplitList( * string gets re-purposed to hold '\0' characters in the argv array. */ - size = TclMaxListLength(list, -1, &end) + 1; + size = TclMaxListLength(list, TCL_INDEX_NONE, &end) + 1; length = end - list; argv = (const char **)ckalloc((size * sizeof(char *)) + length + 1); @@ -897,7 +897,7 @@ Tcl_SplitList( ckfree(argv); if (interp != NULL) { Tcl_SetObjResult(interp, Tcl_NewStringObj( - "internal error in Tcl_SplitList", -1)); + "internal error in Tcl_SplitList", TCL_INDEX_NONE)); Tcl_SetErrorCode(interp, "TCL", "INTERNAL", "Tcl_SplitList", NULL); } @@ -945,9 +945,9 @@ int Tcl_ScanElement( const char *src, /* String to convert to list element. */ int *flagPtr) /* Where to store information to guide - * Tcl_ConvertCountedElement. */ + * Tcl_ConvertCountedElement. */ { - return Tcl_ScanCountedElement(src, -1, flagPtr); + return Tcl_ScanCountedElement(src, TCL_INDEX_NONE, flagPtr); } /* @@ -958,8 +958,8 @@ Tcl_ScanElement( * This function is a companion function to Tcl_ConvertCountedElement. It * scans a string to see what needs to be done to it (e.g. add * backslashes or enclosing braces) to make the string into a valid Tcl - * list element. If length is -1, then the string is scanned from src up - * to the first null byte. + * list element. If length is TCL_INDEX_NONE, then the string is scanned + * from src up to the first null byte. * * Results: * The return value is an overestimate of the number of bytes that will @@ -976,7 +976,7 @@ Tcl_ScanElement( int Tcl_ScanCountedElement( const char *src, /* String to convert to Tcl list element. */ - int length, /* Number of bytes in src, or -1. */ + int length, /* Number of bytes in src, or TCL_INDEX_NONE. */ int *flagPtr) /* Where to store information to guide * Tcl_ConvertElement. */ { @@ -995,7 +995,7 @@ Tcl_ScanCountedElement( * This function is a companion function to TclConvertElement. It scans a * string to see what needs to be done to it (e.g. add backslashes or * enclosing braces) to make the string into a valid Tcl list element. If - * length is -1, then the string is scanned from src up to the first null + * length is TCL_INDEX_NONE, then the string is scanned from src up to the first null * byte. A NULL value for src is treated as an empty string. The incoming * value of *flagPtr is a report from the caller what additional flags it * will pass to TclConvertElement(). @@ -1017,10 +1017,10 @@ Tcl_ScanCountedElement( *---------------------------------------------------------------------- */ -unsigned int +TCL_HASH_TYPE TclScanElement( const char *src, /* String to convert to Tcl list element. */ - int length, /* Number of bytes in src, or -1. */ + int length, /* Number of bytes in src, or TCL_INDEX_NONE. */ char *flagPtr) /* Where to store information to guide * Tcl_ConvertElement. */ { @@ -1033,7 +1033,7 @@ TclScanElement( int extra = 0; /* Count of number of extra bytes needed for * formatted element, assuming we use escape * sequences in formatting. */ - unsigned int bytesNeeded; /* Buffer length computed to complete the + TCL_HASH_TYPE bytesNeeded; /* Buffer length computed to complete the * element formatting in the selected mode. */ #if COMPAT int preferEscape = 0; /* Use preferences to track whether to use */ @@ -1041,7 +1041,7 @@ TclScanElement( int braceCount = 0; /* Count of all braces '{' '}' seen. */ #endif /* COMPAT */ - if ((p == NULL) || (length == 0) || ((*p == '\0') && (length == -1))) { + if ((p == NULL) || (length == 0) || ((*p == '\0') && (length == TCL_INDEX_NONE))) { /* * Empty string element must be brace quoted. */ @@ -1124,7 +1124,7 @@ TclScanElement( break; case '\\': /* TYPE_SUBS */ extra++; /* Escape '\' => '\\' */ - if ((length == 1) || ((length == -1) && (p[1] == '\0'))) { + if ((length == 1) || ((length == TCL_INDEX_NONE) && (p[1] == '\0'))) { /* * Final backslash. Cannot format with brace quoting. */ @@ -1155,7 +1155,7 @@ TclScanElement( #endif /* COMPAT */ break; case '\0': /* TYPE_SUBS */ - if (length == -1) { + if (length == TCL_INDEX_NONE) { goto endOfString; } /* TODO: Panic on improper encoding? */ @@ -1330,7 +1330,7 @@ Tcl_ConvertElement( char *dst, /* Place to put list-ified element. */ int flags) /* Flags produced by Tcl_ScanElement. */ { - return Tcl_ConvertCountedElement(src, -1, dst, flags); + return Tcl_ConvertCountedElement(src, TCL_INDEX_NONE, dst, flags); } /* @@ -1357,7 +1357,7 @@ Tcl_ConvertElement( int Tcl_ConvertCountedElement( const char *src, /* Source information for list element. */ - int length, /* Number of bytes in src, or -1. */ + int 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. */ { @@ -1390,7 +1390,7 @@ Tcl_ConvertCountedElement( int TclConvertElement( const char *src, /* Source information for list element. */ - int length, /* Number of bytes in src, or -1. */ + int 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. */ { @@ -1409,7 +1409,7 @@ TclConvertElement( * No matter what the caller demands, empty string must be braced! */ - if ((src == NULL) || (length == 0) || (*src == '\0' && length == -1)) { + if ((src == NULL) || (length == 0) || (*src == '\0' && length == TCL_INDEX_NONE)) { p[0] = '{'; p[1] = '}'; return 2; @@ -1436,7 +1436,7 @@ TclConvertElement( */ if (conversion == CONVERT_NONE) { - if (length == -1) { + if (length == TCL_INDEX_NONE) { /* TODO: INT_MAX overflow? */ while (*src) { *p++ = *src++; @@ -1455,7 +1455,7 @@ TclConvertElement( if (conversion == CONVERT_BRACE) { *p = '{'; p++; - if (length == -1) { + if (length == TCL_INDEX_NONE) { /* TODO: INT_MAX overflow? */ while (*src) { *p++ = *src++; @@ -1528,7 +1528,7 @@ TclConvertElement( p++; continue; case '\0': - if (length == -1) { + if (length == TCL_INDEX_NONE) { return p - dst; } @@ -1604,7 +1604,7 @@ Tcl_Merge( } for (i = 0; i < argc; i++) { flagPtr[i] = ( i ? TCL_DONT_QUOTE_HASH : 0 ); - bytesNeeded += TclScanElement(argv[i], -1, &flagPtr[i]); + bytesNeeded += TclScanElement(argv[i], TCL_INDEX_NONE, &flagPtr[i]); if (bytesNeeded > INT_MAX) { Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); } @@ -1622,7 +1622,7 @@ Tcl_Merge( dst = result; for (i = 0; i < argc; i++) { flagPtr[i] |= ( i ? TCL_DONT_QUOTE_HASH : 0 ); - dst += TclConvertElement(argv[i], -1, dst, flagPtr[i]); + dst += TclConvertElement(argv[i], TCL_INDEX_NONE, dst, flagPtr[i]); *dst = ' '; dst++; } @@ -2665,8 +2665,8 @@ Tcl_DStringInit( char * Tcl_DStringAppend( Tcl_DString *dsPtr, /* Structure describing dynamic string. */ - const char *bytes, /* String to append. If length is -1 then this - * must be null-terminated. */ + const char *bytes, /* String to append. If length is + * < 0 then this must be null-terminated. */ int length) /* Number of bytes from "bytes" to append. If * < 0, then append all of bytes, up to null * at end. */ @@ -2692,18 +2692,18 @@ Tcl_DStringAppend( memcpy(newString, dsPtr->string, dsPtr->length); dsPtr->string = newString; } else { - int offset = -1; + int index = TCL_INDEX_NONE; /* See [16896d49fd] */ if (bytes >= dsPtr->string && bytes <= dsPtr->string + dsPtr->length) { - offset = bytes - dsPtr->string; + index = bytes - dsPtr->string; } dsPtr->string = (char *)ckrealloc(dsPtr->string, dsPtr->spaceAvl); - if (offset >= 0) { - bytes = dsPtr->string + offset; + if (index >= 0) { + bytes = dsPtr->string + index; } } } @@ -2802,7 +2802,7 @@ Tcl_DStringAppendElement( if (!quoteHash) { flags |= TCL_DONT_QUOTE_HASH; } - newSize = dsPtr->length + needSpace + TclScanElement(element, -1, &flags); + newSize = dsPtr->length + needSpace + TclScanElement(element, TCL_INDEX_NONE, &flags); if (!quoteHash) { flags |= TCL_DONT_QUOTE_HASH; } @@ -2851,7 +2851,7 @@ Tcl_DStringAppendElement( dsPtr->length++; } - dsPtr->length += TclConvertElement(element, -1, dst, flags); + dsPtr->length += TclConvertElement(element, TCL_INDEX_NONE, dst, flags); dsPtr->string[dsPtr->length] = '\0'; return dsPtr->string; } @@ -3263,7 +3263,7 @@ Tcl_PrintDouble( */ if (*precisionPtr == 0) { - digits = TclDoubleDigits(value, -1, TCL_DD_SHORTEST, + digits = TclDoubleDigits(value, TCL_INDEX_NONE, TCL_DD_SHORTEST, &exponent, &signum, &end); } else { /* @@ -3637,11 +3637,11 @@ TclFormatInt( static int GetWideForIndex( Tcl_Interp *interp, /* Interpreter to use for error reporting. If - * NULL, then no error message is left after - * errors. */ + * 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 - * objPtr holds "end". + * objPtr holds "end". * NOTE: this value may be TCL_INDEX_NONE. */ Tcl_WideInt *widePtr) /* Location filled in with a wide integer * representing an index. */ @@ -3673,21 +3673,26 @@ GetWideForIndex( * * Tcl_GetIntForIndex -- * - * This function returns an integer corresponding to the list index held - * in a Tcl object. The Tcl object's value is expected to be in the - * format integer([+-]integer)? or the format end([+-]integer)?. + * Provides an integer corresponding to the list index held in a Tcl + * object. The string value 'objPtr' is expected have the format + * integer([+-]integer)? or end([+-]integer)?. * - * Results: - * The return value is normally TCL_OK, which means that the index was - * successfully stored into the location referenced by "indexPtr". If the - * Tcl object referenced by "objPtr" has the value "end", the value - * stored is "endValue". If "objPtr"s values is not of one of the - * expected formats, TCL_ERROR is returned and, if "interp" is non-NULL, - * an error message is left in the interpreter's result object. + * Value + * TCL_OK * - * Side effects: - * The object referenced by "objPtr" might be converted to an integer, - * wide integer, or end-based-index object. + * The index is stored at the address given by by 'indexPtr'. If + * 'objPtr' has the value "end", the value stored is 'endValue'. + * + * TCL_ERROR + * + * The value of 'objPtr' does not have one of the expected formats. If + * 'interp' is non-NULL, an error message is left in the interpreter's + * result object. + * + * Effect + * + * The object referenced by 'objPtr' is converted, as needed, to an + * integer, wide integer, or end-based-index object. * *---------------------------------------------------------------------- */ @@ -3711,7 +3716,7 @@ Tcl_GetIntForIndex( } if (indexPtr != NULL) { if ((wide < 0) && (endValue >= 0)) { - *indexPtr = -1; + *indexPtr = TCL_INDEX_NONE; } else if (wide > INT_MAX) { *indexPtr = INT_MAX; } else if (wide < INT_MIN) { @@ -3788,7 +3793,7 @@ GetEndOffsetFromObj( * Quick scan to see if multi-value list is even possible. * This relies on TclGetString() returning a NUL-terminated string. */ - if ((TclMaxListLength(bytes, -1, NULL) > 1) + if ((TclMaxListLength(bytes, TCL_INDEX_NONE, NULL) > 1) /* If it's possible, do the full list parse. */ && (TCL_OK == TclListObjLengthM(NULL, objPtr, &length)) @@ -3797,7 +3802,7 @@ GetEndOffsetFromObj( } /* Passed the list screen, so parse for index arithmetic expression */ - if (TCL_OK == TclParseNumber(NULL, objPtr, NULL, NULL, -1, &opPtr, + if (TCL_OK == TclParseNumber(NULL, objPtr, NULL, NULL, TCL_INDEX_NONE, &opPtr, TCL_PARSE_INTEGER_ONLY)) { Tcl_WideInt w1=0, w2=0; @@ -3813,7 +3818,7 @@ GetEndOffsetFromObj( } if (TCL_OK == TclParseNumber(NULL, objPtr, NULL, opPtr + 1, - -1, NULL, TCL_PARSE_INTEGER_ONLY)) { + TCL_INDEX_NONE, NULL, TCL_PARSE_INTEGER_ONLY)) { /* ... value concludes with second valid integer */ /* Save second integer as wide if possible */ @@ -4172,7 +4177,7 @@ TclCheckBadOctal( */ Tcl_AppendToObj(Tcl_GetObjResult(interp), - " (looks like invalid octal number)", -1); + " (looks like invalid octal number)", TCL_INDEX_NONE); } return 1; } @@ -4794,7 +4799,7 @@ TclReToGlob( invalidGlob: if (interp != NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(msg, -1)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(msg, TCL_INDEX_NONE)); Tcl_SetErrorCode(interp, "TCL", "RE2GLOB", code, NULL); } Tcl_DStringFree(dsPtr); -- cgit v0.12 From a9fba66be576e55d089b69f8531d514cdc05c61e Mon Sep 17 00:00:00 2001 From: apnadkarni Date: Fri, 7 Oct 2022 11:23:18 +0000 Subject: Add memory leak/refcount tests for lists, spans and lseq --- generic/tclTestObj.c | 323 ++++++++++++++++++++++++++++++++++----------------- tests/listObj.test | 68 +++++++++++ 2 files changed, 284 insertions(+), 107 deletions(-) diff --git a/generic/tclTestObj.c b/generic/tclTestObj.c index a03a60a..93af3c0 100644 --- a/generic/tclTestObj.c +++ b/generic/tclTestObj.c @@ -841,6 +841,35 @@ TestintobjCmd( * test a few possible corner cases in list object manipulation from * C code that cannot occur at the Tcl level. * + * Following new commands are added for 8.7 as regression tests for + * memory leaks and use-after-free. Unlike 8.6, 8.7 has multiple internal + * representations for lists. It has to be ensured that corresponding + * implementations obey the invariants of the C list API. The script + * level tests do not suffice as Tcl list commands do not execute + * the same exact code path as the exported C API. + * + * Note these new commands are only useful when Tcl is compiled with + * TCL_MEM_DEBUG defined. + * + * indexmemcheck - loops calling Tcl_ListObjIndex on each element. This + * is to test that abstract lists returning elements do not depend + * on caller to free them. The test case should check allocated counts + * with the following sequence: + * set before + * testobj set VARINDEX [list a b c] (or lseq etc.) + * testlistobj indexnoop VARINDEX + * testobj unset VARINDEX + * set after + * after calling this command AND freeing the passed list. The targeted + * bug is if Tcl_LOI returns a ephemeral Tcl_Obj with no other reference + * resulting in a memory leak. Conversely, the command also checks + * that the Tcl_Obj returned by Tcl_LOI does not have a zero reference + * count since it is supposed to have at least one reference held + * by the list implementation. Returns a message in interp otherwise. + * + * getelementsmemcheck - as above but for Tcl_ListObjGetElements + + * * Results: * A standard Tcl object result. * @@ -861,25 +890,36 @@ TestlistobjCmd( const char* subcommands[] = { "set", "get", - "replace" + "replace", + "indexmemcheck", + "getelementsmemcheck", + NULL }; enum listobjCmdIndex { LISTOBJ_SET, LISTOBJ_GET, - LISTOBJ_REPLACE + LISTOBJ_REPLACE, + LISTOBJ_INDEXMEMCHECK, + LISTOBJ_GETELEMENTSMEMCHECK, } cmdIndex; size_t varIndex; /* Variable number converted to binary */ Tcl_WideInt first; /* First index in the list */ Tcl_WideInt count; /* Count of elements in a list */ Tcl_Obj **varPtr; + int i; +#if TCL_VERSION_MAJOR < 9 + int len; +#else + size_t len; +#endif if (objc < 3) { Tcl_WrongNumArgs(interp, 1, objv, "option arg ?arg...?"); return TCL_ERROR; } varPtr = GetVarPtr(interp); - if (GetVariableIndex(interp, objv[2], &varIndex) != TCL_OK) { + if (GetVariableIndex(interp, objv[2], &varIndex) != TCL_OK) { return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], subcommands, "command", @@ -923,6 +963,58 @@ TestlistobjCmd( Tcl_ResetResult(interp); return Tcl_ListObjReplace(interp, varPtr[varIndex], first, count, objc-5, objv+5); + + case LISTOBJ_INDEXMEMCHECK: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "varIndex"); + return TCL_ERROR; + } + if (CheckIfVarUnset(interp, varPtr, varIndex)) { + return TCL_ERROR; + } + if (Tcl_ListObjLength(interp, varPtr[varIndex], &len) != TCL_OK) { + return TCL_ERROR; + } + for (i = 0; i < len; ++i) { + Tcl_Obj *objP; + if (Tcl_ListObjIndex(interp, varPtr[varIndex], i, &objP) + != TCL_OK) { + return TCL_ERROR; + } + if (objP->refCount <= 0) { + Tcl_SetResult( + interp, + "Tcl_ListObjIndex returned object with ref count <= 0", + TCL_STATIC); + /* Keep looping since we are also looping for leaks */ + } + } + break; + + case LISTOBJ_GETELEMENTSMEMCHECK: + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "varIndex"); + return TCL_ERROR; + } + if (CheckIfVarUnset(interp, varPtr, varIndex)) { + return TCL_ERROR; + } else { + Tcl_Obj **elems; + if (Tcl_ListObjGetElements(interp, varPtr[varIndex], &len, &elems) + != TCL_OK) { + return TCL_ERROR; + } + for (i = 0; i < len; ++i) { + if (elems[i]->refCount <= 0) { + Tcl_SetResult( + interp, + "Tcl_ListObjGetElements element has ref count <= 0", + TCL_STATIC); + break; + } + } + } + break; } return TCL_OK; } @@ -953,9 +1045,21 @@ TestobjCmd( { size_t varIndex, destIndex; int i; - const char *subCmd; const Tcl_ObjType *targetType; Tcl_Obj **varPtr; + const char *subcommands[] = { + "freeallvars", "bug3598580", "types", + "objtype", "newobj", "set", + "assign", "convert", "duplicate", + "invalidateStringRep", "refcount", "type", + NULL + }; + enum testobjCmdIndex { + TESTOBJ_FREEALLVARS, TESTOBJ_BUG3598580, TESTOBJ_TYPES, + TESTOBJ_OBJTYPE, TESTOBJ_NEWOBJ, TESTOBJ_SET, + TESTOBJ_ASSIGN, TESTOBJ_CONVERT, TESTOBJ_DUPLICATE, + TESTOBJ_INVALIDATESTRINGREP, TESTOBJ_REFCOUNT, TESTOBJ_TYPE, + } cmdIndex; if (objc < 2) { wrongNumArgs: @@ -964,142 +1068,159 @@ TestobjCmd( } varPtr = GetVarPtr(interp); - subCmd = Tcl_GetString(objv[1]); - if (strcmp(subCmd, "assign") == 0) { - if (objc != 4) { + if (Tcl_GetIndexFromObj( + interp, objv[1], subcommands, "command", 0, &cmdIndex) + != TCL_OK) { + return TCL_ERROR; + } + switch (cmdIndex) { + case TESTOBJ_FREEALLVARS: + if (objc != 2) { goto wrongNumArgs; } - if (GetVariableIndex(interp, objv[2], &varIndex) != TCL_OK) { - return TCL_ERROR; - } - if (CheckIfVarUnset(interp, varPtr,varIndex)) { - return TCL_ERROR; + for (i = 0; i < NUMBER_OF_OBJECT_VARS; i++) { + if (varPtr[i] != NULL) { + Tcl_DecrRefCount(varPtr[i]); + varPtr[i] = NULL; + } } - if (GetVariableIndex(interp, objv[3], &destIndex) != TCL_OK) { - return TCL_ERROR; + return TCL_OK; + case TESTOBJ_BUG3598580: + if (objc != 2) { + goto wrongNumArgs; + } else { + Tcl_Obj *listObjPtr, *elemObjPtr; + elemObjPtr = Tcl_NewWideIntObj(123); + listObjPtr = Tcl_NewListObj(1, &elemObjPtr); + /* Replace the single list element through itself, nonsense but + * legal. */ + Tcl_ListObjReplace(interp, listObjPtr, 0, 1, 1, &elemObjPtr); + Tcl_SetObjResult(interp, listObjPtr); } - SetVarToObj(varPtr, destIndex, varPtr[varIndex]); - Tcl_SetObjResult(interp, varPtr[destIndex]); - } else if (strcmp(subCmd, "bug3598580") == 0) { - Tcl_Obj *listObjPtr, *elemObjPtr; + return TCL_OK; + case TESTOBJ_TYPES: if (objc != 2) { goto wrongNumArgs; + } else { + Tcl_Obj *typesObj = Tcl_NewListObj(0, NULL); + Tcl_AppendAllObjTypes(interp, typesObj); + Tcl_SetObjResult(interp, typesObj); } - elemObjPtr = Tcl_NewWideIntObj(123); - listObjPtr = Tcl_NewListObj(1, &elemObjPtr); - /* Replace the single list element through itself, nonsense but legal. */ - Tcl_ListObjReplace(interp, listObjPtr, 0, 1, 1, &elemObjPtr); - Tcl_SetObjResult(interp, listObjPtr); return TCL_OK; - } else if (strcmp(subCmd, "convert") == 0) { + case TESTOBJ_OBJTYPE: + /* + * Return an object containing the name of the argument's type of + * internal rep. If none exists, return "none". + */ - if (objc != 4) { + if (objc != 3) { goto wrongNumArgs; + } else { + const char *typeName; + + if (objv[2]->typePtr == NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("none", -1)); + } + else { + typeName = objv[2]->typePtr->name; + if (!strcmp(typeName, "utf32string")) + typeName = "string"; +#ifndef TCL_WIDE_INT_IS_LONG + else if (!strcmp(typeName, "wideInt")) typeName = "int"; +#endif + Tcl_SetObjResult(interp, Tcl_NewStringObj(typeName, -1)); + } } - if (GetVariableIndex(interp, objv[2], &varIndex) != TCL_OK) { - return TCL_ERROR; - } - if (CheckIfVarUnset(interp, varPtr,varIndex)) { - return TCL_ERROR; - } - if ((targetType = Tcl_GetObjType(Tcl_GetString(objv[3]))) == NULL) { - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), - "no type ", Tcl_GetString(objv[3]), " found", NULL); - return TCL_ERROR; + return TCL_OK; + case TESTOBJ_NEWOBJ: + if (objc != 3) { + goto wrongNumArgs; } - if (Tcl_ConvertToType(interp, varPtr[varIndex], targetType) - != TCL_OK) { + if (GetVariableIndex(interp, objv[2], &varIndex) != TCL_OK) { return TCL_ERROR; } + SetVarToObj(varPtr, varIndex, Tcl_NewObj()); Tcl_SetObjResult(interp, varPtr[varIndex]); - } else if (strcmp(subCmd, "duplicate") == 0) { + return TCL_OK; + case TESTOBJ_SET: if (objc != 4) { goto wrongNumArgs; } if (GetVariableIndex(interp, objv[2], &varIndex) != TCL_OK) { return TCL_ERROR; } - if (CheckIfVarUnset(interp, varPtr,varIndex)) { - return TCL_ERROR; + SetVarToObj(varPtr, varIndex, objv[3]); + return TCL_OK; + + default: + break; + } + + /* All further commands expect an occupied varindex argument */ + if (objc < 3) { + goto wrongNumArgs; + } + + if (GetVariableIndex(interp, objv[2], &varIndex) != TCL_OK) { + return TCL_ERROR; + } + if (CheckIfVarUnset(interp, varPtr, varIndex)) { + return TCL_ERROR; + } + + switch (cmdIndex) { + case TESTOBJ_ASSIGN: + if (objc != 4) { + goto wrongNumArgs; } if (GetVariableIndex(interp, objv[3], &destIndex) != TCL_OK) { return TCL_ERROR; } - SetVarToObj(varPtr, destIndex, Tcl_DuplicateObj(varPtr[varIndex])); + SetVarToObj(varPtr, destIndex, varPtr[varIndex]); Tcl_SetObjResult(interp, varPtr[destIndex]); - } else if (strcmp(subCmd, "freeallvars") == 0) { - if (objc != 2) { - goto wrongNumArgs; - } - for (i = 0; i < NUMBER_OF_OBJECT_VARS; i++) { - if (varPtr[i] != NULL) { - Tcl_DecrRefCount(varPtr[i]); - varPtr[i] = NULL; - } - } - } else if (strcmp(subCmd, "invalidateStringRep") == 0) { - if (objc != 3) { + break; + case TESTOBJ_CONVERT: + if (objc != 4) { goto wrongNumArgs; } - if (GetVariableIndex(interp, objv[2], &varIndex) != TCL_OK) { + if ((targetType = Tcl_GetObjType(Tcl_GetString(objv[3]))) == NULL) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "no type ", Tcl_GetString(objv[3]), " found", NULL); return TCL_ERROR; } - if (CheckIfVarUnset(interp, varPtr,varIndex)) { + if (Tcl_ConvertToType(interp, varPtr[varIndex], targetType) + != TCL_OK) { return TCL_ERROR; } - Tcl_InvalidateStringRep(varPtr[varIndex]); Tcl_SetObjResult(interp, varPtr[varIndex]); - } else if (strcmp(subCmd, "newobj") == 0) { - if (objc != 3) { + break; + case TESTOBJ_DUPLICATE: + if (objc != 4) { goto wrongNumArgs; } - if (GetVariableIndex(interp, objv[2], &varIndex) != TCL_OK) { + if (GetVariableIndex(interp, objv[3], &destIndex) != TCL_OK) { return TCL_ERROR; } - SetVarToObj(varPtr, varIndex, Tcl_NewObj()); - Tcl_SetObjResult(interp, varPtr[varIndex]); - } else if (strcmp(subCmd, "objtype") == 0) { - const char *typeName; - - /* - * Return an object containing the name of the argument's type of - * internal rep. If none exists, return "none". - */ - + SetVarToObj(varPtr, destIndex, Tcl_DuplicateObj(varPtr[varIndex])); + Tcl_SetObjResult(interp, varPtr[destIndex]); + break; + case TESTOBJ_INVALIDATESTRINGREP: if (objc != 3) { goto wrongNumArgs; } - if (objv[2]->typePtr == NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("none", -1)); - } else { - typeName = objv[2]->typePtr->name; - if (!strcmp(typeName, "utf32string")) typeName = "string"; -#ifndef TCL_WIDE_INT_IS_LONG - else if (!strcmp(typeName, "wideInt")) typeName = "int"; -#endif - Tcl_SetObjResult(interp, Tcl_NewStringObj(typeName, -1)); - } - } else if (strcmp(subCmd, "refcount") == 0) { + Tcl_InvalidateStringRep(varPtr[varIndex]); + Tcl_SetObjResult(interp, varPtr[varIndex]); + break; + case TESTOBJ_REFCOUNT: if (objc != 3) { goto wrongNumArgs; } - if (GetVariableIndex(interp, objv[2], &varIndex) != TCL_OK) { - return TCL_ERROR; - } - if (CheckIfVarUnset(interp, varPtr,varIndex)) { - return TCL_ERROR; - } Tcl_SetObjResult(interp, Tcl_NewWideIntObj(varPtr[varIndex]->refCount)); - } else if (strcmp(subCmd, "type") == 0) { + break; + case TESTOBJ_TYPE: if (objc != 3) { goto wrongNumArgs; } - if (GetVariableIndex(interp, objv[2], &varIndex) != TCL_OK) { - return TCL_ERROR; - } - if (CheckIfVarUnset(interp, varPtr,varIndex)) { - return TCL_ERROR; - } if (varPtr[varIndex]->typePtr == NULL) { /* a string! */ Tcl_AppendToObj(Tcl_GetObjResult(interp), "string", -1); #ifndef TCL_WIDE_INT_IS_LONG @@ -1111,21 +1232,9 @@ TestobjCmd( Tcl_AppendToObj(Tcl_GetObjResult(interp), varPtr[varIndex]->typePtr->name, -1); } - } else if (strcmp(subCmd, "types") == 0) { - if (objc != 2) { - goto wrongNumArgs; - } - if (Tcl_AppendAllObjTypes(interp, - Tcl_GetObjResult(interp)) != TCL_OK) { - return TCL_ERROR; - } - } else { - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), - "bad option \"", Tcl_GetString(objv[1]), - "\": must be assign, convert, duplicate, freeallvars, " - "newobj, objcount, objtype, refcount, type, or types", NULL); - return TCL_ERROR; + break; } + return TCL_OK; } diff --git a/tests/listObj.test b/tests/listObj.test index 0b64635..0f43648 100644 --- a/tests/listObj.test +++ b/tests/listObj.test @@ -20,6 +20,7 @@ if {"::tcltest" ni [namespace children]} { catch [list package require -exact tcl::test [info patchlevel]] testConstraint testobj [llength [info commands testobj]] +testConstraint memory [llength [info commands memory]] catch {unset x} test listobj-1.1 {Tcl_GetListObjType} emptyTest { @@ -210,6 +211,73 @@ test listobj-11.1 {Bug 3598580: Tcl_ListObjReplace refcount management} testobj testobj bug3598580 } 123 +# Stolen from dict.test +proc listobjmemcheck script { + set end [lindex [split [memory info] \n] 3 3] + for {set i 0} {$i < 5} {incr i} { + uplevel 1 $script + set tmp $end + set end [lindex [split [memory info] \n] 3 3] + } + expr {$end - $tmp} +} + +test listobj-12.1 {Tcl_ListObjIndex memory leaks for native lists} -constraints { + testobj memory +} -body { + list [listobjmemcheck { + testobj set 1 [lrepeat 1000 x] + set errorMessage [testlistobj indexmemcheck 1] + testobj freeallvars + }] $errorMessage +} -result {0 {}} +test listobj-12.2 {Tcl_ListObjIndex memory leaks for native lists with spans} -constraints { + testobj memory +} -body { + list [listobjmemcheck { + testobj set 1 [testlistrep new 1000 100 100] + set errorMessage [testlistobj indexmemcheck 1] + testobj freeallvars + }] $errorMessage +} -result {0 {}} +test listobj-12.3 {Tcl_ListObjIndex memory leaks for lseq} -constraints { + testobj memory +} -body { + list [listobjmemcheck { + testobj set 1 [lseq 1000] + set errorMessage [testlistobj indexmemcheck 1] + testobj freeallvars + }] $errorMessage +} -result {0 {}} + +test listobj-13.1 {Tcl_ListObjGetElements memory leaks for native lists} -constraints { + testobj memory +} -body { + list [listobjmemcheck { + testobj set 1 [lrepeat 1000 x] + set errorMessage [testlistobj getelementsmemcheck 1] + testobj freeallvars + }] $errorMessage +} -result {0 {}} +test listobj-13.2 {Tcl_ListObjElements memory leaks for native lists with spans} -constraints { + testobj memory +} -body { + list [listobjmemcheck { + testobj set 1 [testlistrep new 1000 100 100] + set errorMessage [testlistobj getelementsmemcheck 1] + testobj freeallvars + }] $errorMessage +} -result {0 {}} +test listobj-13.3 {Tcl_ListObjElements memory leaks for lseq} -constraints { + testobj memory +} -body { + list [listobjmemcheck { + testobj set 1 [lseq 1000] + set errorMessage [testlistobj getelementsmemcheck 1] + testobj freeallvars + }] $errorMessage +} -result {0 {}} + # cleanup ::tcltest::cleanupTests return -- cgit v0.12 From 4b6d8abfe47494263e6fde30cbb9e9d9f880086e Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 7 Oct 2022 15:18:36 +0000 Subject: Use GotFlag/SetFlag/ResetFlag macro's wherever appropriate --- generic/tclIO.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/generic/tclIO.c b/generic/tclIO.c index 5dff604..408a1d3 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -1478,7 +1478,7 @@ Tcl_GetChannel( chanPtr = (Channel *)Tcl_GetHashValue(hPtr); chanPtr = chanPtr->state->bottomChanPtr; if (modePtr != NULL) { - *modePtr = chanPtr->state->flags & (TCL_READABLE|TCL_WRITABLE); + *modePtr = GotFlag(chanPtr->state, TCL_READABLE|TCL_WRITABLE); } return (Tcl_Channel) chanPtr; @@ -1572,7 +1572,7 @@ TclGetChannelFromObj( *channelPtr = (Tcl_Channel) statePtr->bottomChanPtr; if (modePtr != NULL) { - *modePtr = statePtr->flags & (TCL_READABLE|TCL_WRITABLE); + *modePtr = GotFlag(statePtr, TCL_READABLE|TCL_WRITABLE); } return TCL_OK; @@ -1877,7 +1877,7 @@ Tcl_StackChannel( * --+---+---+---+----+ */ - if ((mask & (statePtr->flags & (TCL_READABLE | TCL_WRITABLE))) == 0) { + if ((mask & GotFlag(statePtr, TCL_READABLE|TCL_WRITABLE)) == 0) { if (interp) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "reading and writing both disallowed for channel \"%s\"", @@ -2170,8 +2170,8 @@ Tcl_UnstackChannel( * TIP #220: This is done with maximum privileges (as created). */ - statePtr->flags &= ~(TCL_READABLE|TCL_WRITABLE); - statePtr->flags |= statePtr->maxPerms; + ResetFlag(statePtr, TCL_READABLE|TCL_WRITABLE); + SetFlag(statePtr, statePtr->maxPerms); result = ChanClose(chanPtr, interp); ChannelFree(chanPtr); @@ -2378,7 +2378,7 @@ Tcl_GetChannelMode( ChannelState *statePtr = ((Channel *) chan)->state; /* State of actual channel. */ - return (statePtr->flags & (TCL_READABLE | TCL_WRITABLE)); + return GotFlag(statePtr, TCL_READABLE|TCL_WRITABLE); } /* @@ -2481,12 +2481,12 @@ Tcl_RemoveChannelMode( emsg = "Illegal mode value."; goto error; } - if (0 == (statePtr->flags & (TCL_READABLE | TCL_WRITABLE) & ~mode)) { + if (0 == (GotFlag(statePtr, TCL_READABLE|TCL_WRITABLE) & ~mode)) { emsg = "Bad mode, would make channel inacessible"; goto error; } - statePtr->flags &= ~mode; + ResetFlag(statePtr, mode); return TCL_OK; error: @@ -3706,7 +3706,7 @@ Tcl_CloseEx( * opened for that direction). */ - if (!(statePtr->flags & (TCL_READABLE | TCL_WRITABLE) & flags)) { + if (!(GotFlag(statePtr, TCL_READABLE|TCL_WRITABLE) & flags)) { const char *msg; if (flags & TCL_CLOSE_READ) { @@ -6416,7 +6416,7 @@ ReadChars( return 1; } - } else if (statePtr->flags & CHANNEL_EOF) { + } else if (GotFlag(statePtr, CHANNEL_EOF)) { /* * The bare \r is the only char and we will never read a * subsequent char to make the determination. @@ -6682,7 +6682,7 @@ TranslateInputEOL( char *dst = dstStart; int lesser; - if ((statePtr->flags & INPUT_SAW_CR) && srcLen) { + if (GotFlag(statePtr, INPUT_SAW_CR) && srcLen) { if (*src == '\n') { src++; srcLen--; } ResetFlag(statePtr, INPUT_SAW_CR); } @@ -7452,7 +7452,7 @@ CheckChannelErrors( * Fail if the channel is not opened for desired operation. */ - if ((statePtr->flags & direction) == 0) { + if (GotFlag(statePtr, direction) == 0) { Tcl_SetErrno(EACCES); return -1; } @@ -9138,7 +9138,7 @@ Tcl_FileEventObjCmd( } chanPtr = (Channel *) chan; statePtr = chanPtr->state; - if ((statePtr->flags & mask) == 0) { + if (GotFlag(statePtr, mask) == 0) { Tcl_SetObjResult(interp, Tcl_ObjPrintf("channel is not %s", (mask == TCL_READABLE) ? "readable" : "writable")); return TCL_ERROR; @@ -9305,8 +9305,8 @@ TclCopyChannel( * Make sure the output side is unbuffered. */ - outStatePtr->flags = (outStatePtr->flags & ~CHANNEL_LINEBUFFERED) - | CHANNEL_UNBUFFERED; + ResetFlag(outStatePtr, CHANNEL_LINEBUFFERED); + SetFlag(outStatePtr, CHANNEL_UNBUFFERED); /* * Test for conditions where we know we can just move bytes from input @@ -10085,7 +10085,7 @@ DoRead( * There's no more buffered data... */ - if (statePtr->flags & CHANNEL_EOF) { + if (GotFlag(statePtr, CHANNEL_EOF)) { /* * ...and there never will be. */ @@ -10093,7 +10093,7 @@ DoRead( *p++ = '\r'; bytesToRead--; bufPtr->nextRemoved++; - } else if (statePtr->flags & CHANNEL_BLOCKED) { + } else if (GotFlag(statePtr, CHANNEL_BLOCKED)) { /* * ...and we cannot get more now. */ @@ -10226,20 +10226,20 @@ StopCopy( */ nonBlocking = csPtr->readFlags & CHANNEL_NONBLOCKING; - if (nonBlocking != (inStatePtr->flags & CHANNEL_NONBLOCKING)) { + if (nonBlocking != GotFlag(inStatePtr, CHANNEL_NONBLOCKING)) { SetBlockMode(NULL, csPtr->readPtr, nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING); } if (csPtr->readPtr != csPtr->writePtr) { nonBlocking = csPtr->writeFlags & CHANNEL_NONBLOCKING; - if (nonBlocking != (outStatePtr->flags & CHANNEL_NONBLOCKING)) { + if (nonBlocking != GotFlag(outStatePtr, CHANNEL_NONBLOCKING)) { SetBlockMode(NULL, csPtr->writePtr, nonBlocking ? TCL_MODE_NONBLOCKING : TCL_MODE_BLOCKING); } } ResetFlag(outStatePtr, CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED); - outStatePtr->flags |= - csPtr->writeFlags & (CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED); + SetFlag(outStatePtr, + csPtr->writeFlags & (CHANNEL_LINEBUFFERED | CHANNEL_UNBUFFERED)); if (csPtr->cmdPtr) { Tcl_DeleteChannelHandler(inChan, CopyEventProc, csPtr); -- cgit v0.12 From 12f23af5456f4a87b8bc4d58f9dcfc0edf2c9676 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 7 Oct 2022 15:19:36 +0000 Subject: On Windows, env(HOME) should be handled case-insensitive in fCmd.test --- tests/fCmd.test | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/fCmd.test b/tests/fCmd.test index 73118f4..8c9f799 100644 --- a/tests/fCmd.test +++ b/tests/fCmd.test @@ -2598,8 +2598,8 @@ test fCmd-31.6 {file home USER} -body { # Note - as in 8.x this form does NOT necessarily give same result as # env(HOME) even when user is current user. Assume result contains user # name, else not sure how to check - file home $::tcl_platform(user) -} -match glob -result "*$::tcl_platform(user)*" + string tolower [file home $::tcl_platform(user)] +} -match glob -result [string tolower "*$::tcl_platform(user)*"] test fCmd-31.7 {file home UNKNOWNUSER} -body { file home nosuchuser } -returnCodes error -result {user "nosuchuser" doesn't exist} @@ -2640,8 +2640,8 @@ test fCmd-32.5 {file tildeexpand ~USER} -body { # Note - as in 8.x this form does NOT necessarily give same result as # env(HOME) even when user is current user. Assume result contains user # name, else not sure how to check - file tildeexpand ~$::tcl_platform(user) -} -match glob -result "*$::tcl_platform(user)*" + string tolower [file tildeexpand ~$::tcl_platform(user)] +} -match glob -result [string tolower "*$::tcl_platform(user)*"] test fCmd-32.6 {file tildeexpand ~UNKNOWNUSER} -body { file tildeexpand ~nosuchuser } -returnCodes error -result {user "nosuchuser" doesn't exist} @@ -2655,8 +2655,8 @@ test fCmd-32.9 {file tildeexpand ~USER/bar} -body { # Note - as in 8.x this form does NOT necessarily give same result as # env(HOME) even when user is current user. Assume result contains user # name, else not sure how to check - file tildeexpand ~$::tcl_platform(user)/bar -} -match glob -result "*$::tcl_platform(user)*/bar" + string tolower [file tildeexpand ~$::tcl_platform(user)/bar] +} -match glob -result [string tolower "*$::tcl_platform(user)*/bar"] test fCmd-32.10 {file tildeexpand ~UNKNOWNUSER} -body { file tildeexpand ~nosuchuser/foo } -returnCodes error -result {user "nosuchuser" doesn't exist} @@ -2679,8 +2679,8 @@ test fCmd-32.16 {file tildeexpand ~USER\\bar} -body { # Note - as in 8.x this form does NOT necessarily give same result as # env(HOME) even when user is current user. Assume result contains user # name, else not sure how to check - file tildeexpand ~$::tcl_platform(user)\\bar -} -constraints win -match glob -result "*$::tcl_platform(user)*/bar" + string tolower [file tildeexpand ~$::tcl_platform(user)\\bar] +} -constraints win -match glob -result [string tolower "*$::tcl_platform(user)*/bar"] # cleanup -- cgit v0.12