From 134d7c7295b6c696cb821a6ca105d77988af5df3 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Wed, 7 Feb 2024 16:19:19 +0000 Subject: Those testcases are not a bug, but actually correct. See TIP #619: Starting with Tcl 9.0, surrogates are no longer combined automatically. This has no relation with -profile, which only specifies whether to throw an exception or use a replacement character. --- tests/encodingVectors.tcl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/encodingVectors.tcl b/tests/encodingVectors.tcl index a0bd552..c73b7f7 100644 --- a/tests/encodingVectors.tcl +++ b/tests/encodingVectors.tcl @@ -343,10 +343,10 @@ lappend encInvalidBytes {*}{ utf-8 EDBFBF tcl8 \uDFFF -1 {} {Low surrogate} utf-8 EDBFBF replace \uFFFD -1 {} {Low surrogate} utf-8 EDBFBF strict {} 0 {} {Low surrogate} - utf-8 EDA080EDB080 tcl8 \U00010000 -1 {knownBug} {High low surrogate pair} + utf-8 EDA080EDB080 tcl8 \uD800\uDC00 -1 {} {High low surrogate pair} utf-8 EDA080EDB080 replace \uFFFD\uFFFD -1 {} {High low surrogate pair} utf-8 EDA080EDB080 strict {} 0 {} {High low surrogate pair} - utf-8 EDAFBFEDBFBF tcl8 \U0010FFFF -1 {knownBug} {High low surrogate pair} + utf-8 EDAFBFEDBFBF tcl8 \uDBFF\uDFFF -1 {} {High low surrogate pair} utf-8 EDAFBFEDBFBF replace \uFFFD\uFFFD -1 {} {High low surrogate pair} utf-8 EDAFBFEDBFBF strict {} 0 {} {High low surrogate pair} -- cgit v0.12 From 633de596cd792a1e3288427815f01939c391bef4 Mon Sep 17 00:00:00 2001 From: Torsten Date: Thu, 8 Feb 2024 08:09:00 +0000 Subject: Fixed wrongly placed punctuation in .QW macro of clock.n manual page --- doc/clock.n | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/clock.n b/doc/clock.n index 871a942..4affb05 100644 --- a/doc/clock.n +++ b/doc/clock.n @@ -889,13 +889,13 @@ an error may result if these years are used. \fIISO 8601 point-in-time\fR . An ISO 8601 point-in-time specification, such as -.QW "\fICCyymmdd\fBT\fIhhmmss\fR", +.QW "\fICCyymmdd\fBT\fIhhmmss\fR" , where \fBT\fR is the literal .QW T , .QW "\fICCyymmdd hhmmss\fR" , .QW "\fICCyymmdd\fBT\fIhh:mm:ss\fR" , or -.QW "\fICCyy-mm-dd\fBT\fIhh\fB:\fImm\fB:\fIss\fR". +.QW "\fICCyy-mm-dd\fBT\fIhh\fB:\fImm\fB:\fIss\fR" . Note that only these four formats are accepted. The command does \fInot\fR accept the full range of point-in-time specifications specified in ISO8601. Other formats can be recognized by -- cgit v0.12 From d19f6af5e27e3854bfbdc23a1168418f467ede7b Mon Sep 17 00:00:00 2001 From: Torsten Date: Thu, 8 Feb 2024 09:57:42 +0000 Subject: re-introduced an already fixed formatting error - now correct again --- doc/clock.n | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/clock.n b/doc/clock.n index 4affb05..e1253aa 100644 --- a/doc/clock.n +++ b/doc/clock.n @@ -895,7 +895,7 @@ where \fBT\fR is the literal .QW "\fICCyymmdd hhmmss\fR" , .QW "\fICCyymmdd\fBT\fIhh:mm:ss\fR" , or -.QW "\fICCyy-mm-dd\fBT\fIhh\fB:\fImm\fB:\fIss\fR" . +.QW "\fICCyy-mm-dd\fBT\fIhh:mm:ss\fR" . Note that only these four formats are accepted. The command does \fInot\fR accept the full range of point-in-time specifications specified in ISO8601. Other formats can be recognized by -- cgit v0.12 From 6285eef0fb3271d6eeea72ab2e47e069bb4036e2 Mon Sep 17 00:00:00 2001 From: griffin Date: Thu, 15 Feb 2024 01:50:53 +0000 Subject: Fix bug 578b7e273c03. -- Round computed end value to match precision of given arguments. --- generic/tclArithSeries.c | 6 +++++- tests/lseq.test | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/generic/tclArithSeries.c b/generic/tclArithSeries.c index 1a244db..4ab0087 100755 --- a/generic/tclArithSeries.c +++ b/generic/tclArithSeries.c @@ -553,8 +553,12 @@ TclNewArithSeriesObj( if (!endObj) { if (useDoubles) { + // Compute precision based on given command argument values + int precision = maxPrecision(dstart,len,dstep); dend = dstart + (dstep * (len-1)); - end = dend; + // Make computed end value match argument(s) precision + dend = ArithRound(dend, precision); + end = dend; } else { end = start + (step * (len-1)); dend = end; diff --git a/tests/lseq.test b/tests/lseq.test index 7e25654..02e5e38 100644 --- a/tests/lseq.test +++ b/tests/lseq.test @@ -773,6 +773,25 @@ test lseq-bug-54329e39c7 {does not cause memory bloat} -constraints { expr {[string match *purify* [tcl::build-info]] || ($postmem - $premem < 10) ? 1 : ($postmem - $premem)} } -result 1 +test lseq-bug-578b7e273c03-1 {Arithmetic Series Objects get wrong precision when end value is not specified} -body { + set bl [expr {2.8 in [lseq 0 count 100 by .1]}] + lappend bl [expr {2.8 in [lseq 0 count 200 by .1]}] + lappend bl [expr {0.28 in [lseq 0 count 100 by .01]}] + lappend bl [expr {0.28 in [lseq 0 count 200 by .01]}] + lappend bl [expr {0.286 in [lseq 0 count 100 by .011]}] + lappend bl [expr {0.286 in [lseq 0 count 200 by .011]}] +} -result {1 1 1 1 1 1} + +test lseq-bug-578b7e273c03-2 {Arithmetic Series Objects get wrong precision when end value is not specified} -body { + set ll [llength [lseq 0 count 100 by .1]] + lappend ll [llength [lseq 0 count 200 by .1]] + lappend ll [llength [lseq 0 count 100 by .01]] + lappend ll [llength [lseq 0 count 200 by .01]] + lappend ll [llength [lseq 0 count 100 by .011]] + lappend ll [llength [lseq 0 count 200 by .011]] +} -result {100 200 100 200 100 200} + + # cleanup ::tcltest::cleanupTests -- cgit v0.12 From 348a15e956c5e8efa00e0b87aae997ff631bd869 Mon Sep 17 00:00:00 2001 From: dkf Date: Fri, 16 Feb 2024 14:17:09 +0000 Subject: Reduce the number of warnings on Windows: channel names generated in one place --- win/tclWinChan.c | 33 ++++++++++++++++++++++++++++++--- win/tclWinConsole.c | 2 +- win/tclWinInt.h | 3 ++- win/tclWinPipe.c | 2 +- win/tclWinSerial.c | 3 +-- win/tclWinSock.c | 19 +++++++++---------- 6 files changed, 44 insertions(+), 18 deletions(-) diff --git a/win/tclWinChan.c b/win/tclWinChan.c index 8743afe..5f03138 100644 --- a/win/tclWinChan.c +++ b/win/tclWinChan.c @@ -145,6 +145,33 @@ static const Tcl_ChannelType fileChannelType = { /* *---------------------------------------------------------------------- * + * TclWinGenerateChannelName -- + * + * This function generates names for channels. + * + * Results: + * None. + * + * Side effects: + * Creates a new window and creates an exit handler. + * + *---------------------------------------------------------------------- + */ +void +TclWinGenerateChannelName( + char channelName[], /* Buffer to accept the name. */ + const char *channelTypeName,/* Name of type of channel. */ + void *channelImpl) /* Pointer to channel implementation + * structure, used to generate a unique + * ID. */ +{ + snprintf(channelName, 16 + TCL_INTEGER_SPACE, "%s%" TCL_Z_MODIFIER "x", + channelTypeName, (size_t) channelImpl); +} + +/* + *---------------------------------------------------------------------- + * * FileInit -- * * This function creates the window used to simulate file events. @@ -1488,7 +1515,8 @@ OpenFileChannel( for (infoPtr = tsdPtr->firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) { if (infoPtr->handle == (HANDLE) handle) { - return ((permissions & (TCL_READABLE|TCL_WRITABLE|TCL_EXCEPTION))==infoPtr->validMask) ? infoPtr->channel : NULL; + return ((permissions & (TCL_READABLE|TCL_WRITABLE|TCL_EXCEPTION))==infoPtr->validMask) + ? infoPtr->channel : NULL; } } @@ -1506,8 +1534,7 @@ OpenFileChannel( infoPtr->flags = appendMode; infoPtr->handle = handle; infoPtr->dirty = 0; - snprintf(channelName, 16 + TCL_INTEGER_SPACE, "file%" TCL_Z_MODIFIER "x", (size_t) infoPtr); - + TclWinGenerateChannelName(channelName, "file", infoPtr); infoPtr->channel = Tcl_CreateChannel(&fileChannelType, channelName, infoPtr, permissions); diff --git a/win/tclWinConsole.c b/win/tclWinConsole.c index 5b30fc4..eb81370 100644 --- a/win/tclWinConsole.c +++ b/win/tclWinConsole.c @@ -2116,7 +2116,7 @@ TclWinOpenConsoleChannel( * for instance). */ - snprintf(channelName, 16 + TCL_INTEGER_SPACE, "file%" TCL_Z_MODIFIER "x", (size_t) chanInfoPtr); + TclWinGenerateChannelName(channelName, "file", chanInfoPtr); if (permissions & TCL_READABLE) { /* diff --git a/win/tclWinInt.h b/win/tclWinInt.h index 1267f3f..6de1432 100644 --- a/win/tclWinInt.h +++ b/win/tclWinInt.h @@ -52,7 +52,8 @@ MODULE_SCOPE int TclWinSymLinkCopyDirectory(const WCHAR *LinkOriginal, MODULE_SCOPE int TclWinSymLinkDelete(const WCHAR *LinkOriginal, int linkOnly); MODULE_SCOPE int TclWinFileOwned(Tcl_Obj *); - +MODULE_SCOPE void TclWinGenerateChannelName(char channelName[], + const char *channelTypeName, void *channelImpl); MODULE_SCOPE const char*TclpGetUserName(Tcl_DString *bufferPtr); /* Needed by tclWinFile.c and tclWinFCmd.c */ diff --git a/win/tclWinPipe.c b/win/tclWinPipe.c index 5a18ee3..60764e6 100644 --- a/win/tclWinPipe.c +++ b/win/tclWinPipe.c @@ -1837,7 +1837,7 @@ TclpCreateCommandChannel( * unique, in case channels share handles (stdin/stdout). */ - snprintf(channelName, sizeof(channelName), "file%" TCL_Z_MODIFIER "x", (size_t) infoPtr); + TclWinGenerateChannelName(channelName, "file", infoPtr); infoPtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName, infoPtr, infoPtr->validMask); diff --git a/win/tclWinSerial.c b/win/tclWinSerial.c index 48a0ffc..14f36fd 100644 --- a/win/tclWinSerial.c +++ b/win/tclWinSerial.c @@ -1476,8 +1476,7 @@ TclWinOpenSerialChannel( * are shared between multiple channels (stdin/stdout). */ - snprintf(channelName, 16 + TCL_INTEGER_SPACE, "file%" TCL_Z_MODIFIER "x", (size_t) infoPtr); - + TclWinGenerateChannelName(channelName, "file", infoPtr); infoPtr->channel = Tcl_CreateChannel(&serialChannelType, channelName, infoPtr, permissions); diff --git a/win/tclWinSock.c b/win/tclWinSock.c index f54d8a1..3fab851 100644 --- a/win/tclWinSock.c +++ b/win/tclWinSock.c @@ -265,7 +265,7 @@ static Tcl_DriverGetHandleProc TcpGetHandleProc; static const Tcl_ChannelType tcpChannelType = { "tcp", /* Type name. */ TCL_CHANNEL_VERSION_5, /* v5 channel */ - NULL, /* Close proc. */ + NULL, /* Close proc. */ TcpInputProc, /* Input proc. */ TcpOutputProc, /* Output proc. */ NULL, /* Seek proc. */ @@ -2022,8 +2022,7 @@ Tcl_OpenTcpClient( return NULL; } - snprintf(channelName, sizeof(channelName), SOCK_TEMPLATE, statePtr); - + TclWinGenerateChannelName(channelName, "sock", statePtr); statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, statePtr, (TCL_READABLE | TCL_WRITABLE)); if (TCL_ERROR == Tcl_SetChannelOption(NULL, statePtr->channel, @@ -2056,7 +2055,7 @@ Tcl_OpenTcpClient( Tcl_Channel Tcl_MakeTcpClientChannel( - void *sock) /* The socket to wrap up into a channel. */ + void *sock) /* The socket to wrap up into a channel. */ { TcpState *statePtr; char channelName[SOCK_CHAN_LENGTH]; @@ -2081,7 +2080,7 @@ Tcl_MakeTcpClientChannel( statePtr->selectEvents = FD_READ | FD_CLOSE | FD_WRITE; SendSelectMessage(tsdPtr, SELECT, statePtr); - snprintf(channelName, sizeof(channelName), SOCK_TEMPLATE, statePtr); + TclWinGenerateChannelName(channelName, "sock", statePtr); statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, statePtr, (TCL_READABLE | TCL_WRITABLE)); Tcl_SetChannelOption(NULL, statePtr->channel, "-translation", "auto crlf"); @@ -2253,7 +2252,7 @@ Tcl_OpenTcpServerEx( statePtr->acceptProc = acceptProc; statePtr->acceptProcData = acceptProcData; - snprintf(channelName, sizeof(channelName), SOCK_TEMPLATE, statePtr); + TclWinGenerateChannelName(channelName, "sock", statePtr); statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, statePtr, 0); /* @@ -2307,9 +2306,9 @@ Tcl_OpenTcpServerEx( static void TcpAccept( - TcpFdList *fds, /* Server socket that accepted newSocket. */ - SOCKET newSocket, /* Newly accepted socket. */ - address addr) /* Address of new socket. */ + TcpFdList *fds, /* Server socket that accepted newSocket. */ + SOCKET newSocket, /* Newly accepted socket. */ + address addr) /* Address of new socket. */ { TcpState *newInfoPtr; TcpState *statePtr = fds->statePtr; @@ -2338,7 +2337,7 @@ TcpAccept( newInfoPtr->selectEvents = (FD_READ | FD_WRITE | FD_CLOSE); SendSelectMessage(tsdPtr, SELECT, newInfoPtr); - snprintf(channelName, sizeof(channelName), SOCK_TEMPLATE, newInfoPtr); + TclWinGenerateChannelName(channelName, "sock", statePtr); newInfoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, newInfoPtr, (TCL_READABLE | TCL_WRITABLE)); if (Tcl_SetChannelOption(NULL, newInfoPtr->channel, "-translation", -- cgit v0.12 From 97a62e80f79407860fc7bb5ba1d73c435d607a2b Mon Sep 17 00:00:00 2001 From: dkf Date: Fri, 16 Feb 2024 16:23:28 +0000 Subject: Derpfix... --- win/tclWinSock.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/win/tclWinSock.c b/win/tclWinSock.c index 3fab851..d600f1f 100644 --- a/win/tclWinSock.c +++ b/win/tclWinSock.c @@ -65,8 +65,7 @@ #define GOT_BITS(var, bits) (((var) & (bits)) != 0) /* "sock" + a pointer in hex + \0 */ -#define SOCK_CHAN_LENGTH (4 + sizeof(void *) * 2 + 1) -#define SOCK_TEMPLATE "sock%p" +#define SOCK_CHAN_LENGTH (16 + TCL_INTEGER_SPACE) /* * The following variable is used to tell whether this module has been @@ -2337,7 +2336,7 @@ TcpAccept( newInfoPtr->selectEvents = (FD_READ | FD_WRITE | FD_CLOSE); SendSelectMessage(tsdPtr, SELECT, newInfoPtr); - TclWinGenerateChannelName(channelName, "sock", statePtr); + TclWinGenerateChannelName(channelName, "sock", newInfoPtr); newInfoPtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName, newInfoPtr, (TCL_READABLE | TCL_WRITABLE)); if (Tcl_SetChannelOption(NULL, newInfoPtr->channel, "-translation", -- cgit v0.12 From 3b5d778d4d95b88a73185695da3cb7cfcc51421c Mon Sep 17 00:00:00 2001 From: apnadkarni Date: Mon, 19 Feb 2024 16:58:48 +0000 Subject: Starton [bda99f2393]. --- win/tclWinConsole.c | 45 ++++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/win/tclWinConsole.c b/win/tclWinConsole.c index eb81370..811577d 100644 --- a/win/tclWinConsole.c +++ b/win/tclWinConsole.c @@ -1630,9 +1630,16 @@ ConsoleReaderThread( { ConsoleHandleInfo *handleInfoPtr = (ConsoleHandleInfo *) arg; ConsoleHandleInfo **iterator; - char inputChars[200]; /* Temporary buffer */ Tcl_Size inputLen = 0; Tcl_Size inputOffset = 0; + Tcl_Size lastReadSize = 0; + DWORD sleepTime; + /* + * ReadConsole will limit input to the greater of 256 characters + * and the size of the input buffer. 8.6 used 8192 (4096 chars) + * and so do we. + */ + char inputChars[8192]; /* * Keep looping until one of the following happens. @@ -1666,7 +1673,6 @@ ConsoleReaderThread( Tcl_Size nStored; assert((inputLen - inputOffset) > 0); - nStored = RingBufferIn(&handleInfoPtr->buffer, inputOffset + inputChars, inputLen - inputOffset, @@ -1713,21 +1719,27 @@ ConsoleReaderThread( continue; } + assert(inputLen == 0); + /* - * Both shared buffer and private buffer are empty. Need to go get - * data from console but do not want to read ahead because the - * interp thread might change the read mode, e.g. turning off echo - * for password input. So only do so if at least one interpreter has - * requested data. + * Read more data in two cases: + * 1. The previous read filled the buffer and there could be more + * data in the console internal *text* buffer. Note + * ConsolePendingInput (checked in ConsoleDataAvailable) will NOT + * show this. It holds input events not yet translated to text. + * 2. Tcl threads want more data AND there is data in the + * ConsolePendingInput buffer. The latter check necessary because + * we do not want to read ahead because the interp thread might + * change the read mode, e.g. turning off echo for password + * input. So only do so if at least one interpreter has requested + * data. */ - if ((handleInfoPtr->flags & CONSOLE_DATA_AWAITED) - && ConsoleDataAvailable(handleInfoPtr->console)) { + if (lastReadSize == sizeof(inputChars) + || ((handleInfoPtr->flags & CONSOLE_DATA_AWAITED) + && ConsoleDataAvailable(handleInfoPtr->console))) { DWORD error; /* Do not hold the lock while blocked in console */ ReleaseSRWLockExclusive(&handleInfoPtr->lock); - /* - * Note - the temporary buffer serves two purposes. It - */ error = ReadConsoleChars(handleInfoPtr->console, (WCHAR *)inputChars, sizeof(inputChars) / sizeof(WCHAR), @@ -1735,17 +1747,21 @@ ConsoleReaderThread( AcquireSRWLockExclusive(&handleInfoPtr->lock); if (error == 0) { inputLen *= sizeof(WCHAR); - } else { + lastReadSize = inputLen; + } + else { /* * We only store the last error. It is up to channel * handlers whether to close or not in case of errors. */ + lastReadSize = 0; handleInfoPtr->lastError = error; if (handleInfoPtr->lastError == ERROR_INVALID_HANDLE) { handleInfoPtr->console = INVALID_HANDLE_VALUE; } } - } else { + } + else { /* * Either no one was asking for data, or no data was available. * In the former case, wait until someone wakes us asking for @@ -1753,7 +1769,6 @@ ConsoleReaderThread( * poll since ReadConsole does not support async operation. * So sleep for a short while and loop back to retry. */ - DWORD sleepTime; sleepTime = handleInfoPtr->flags & CONSOLE_DATA_AWAITED ? 50 : INFINITE; SleepConditionVariableSRW(&handleInfoPtr->consoleThreadCV, -- cgit v0.12 From f4aad3d78b94f1250006763f3f9c7db09de934a6 Mon Sep 17 00:00:00 2001 From: apnadkarni Date: Tue, 20 Feb 2024 05:47:19 +0000 Subject: Fix EOF test for TIP 646. Add tests for Bug [bda99f2393]. Remove Scriptics copyright (none of their code remains). --- tests/winConsole.test | 61 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/tests/winConsole.test b/tests/winConsole.test index 5aa130b..3597fe3 100644 --- a/tests/winConsole.test +++ b/tests/winConsole.test @@ -4,18 +4,17 @@ # built-in commands. Sourcing this file into Tcl runs the tests and # generates output for errors. No output means no errors were found. # -# Copyright © 1999 Scriptics Corporation. +# NOTE THIS CANNOT BE RUN VIA nmake/make test since stdin is connected to +# nmake in that case. # # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. -if {"::tcltest" ni [namespace children]} { - package require tcltest 2.5 - namespace import -force ::tcltest::* -} +package require tcltest +namespace import ::tcltest::test -catch {package require twapi} ;# Only to bring window to foreground. Not critical +catch {package require twapi} ;# Only to bring window to foreground. Not critical ::tcltest::ConstraintInitializer haveThread { expr {![catch {package require Thread}]} } # Prompt user for a yes/no response @@ -155,6 +154,54 @@ test console-input-2.1 {Console file channel: non-blocking read} -constraints { set result } -result abc +test console-input-3.0 {Console gets blocking - long lines bug-bda99f2393} -constraints { + win interactive bug-bda99f2393 +} -body { + prompt "Try typing a line of at least 256 characters. Hit ENTER exactly once unless you don't see another prompt.\n" + gets stdin line + set len [string length $line] + list [yesno "Did you hit ENTER only once?"] [expr {$len > 256}] [yesno "Line length was $len characters. Is this correct?"] +} -result {1 1 1} + +test console-input-3.1 {Console gets blocking, small channel buffer size - long lines bug-bda99f2393} -constraints { + win interactive bug-bda99f2393 +} -body { + prompt "Try typing a line of at least 256 characters. Hit ENTER exactly once unless you don't see another prompt.\n" + set bufSize [fconfigure stdin -buffersize] + fconfigure stdin -buffersize 10 + gets stdin line + fconfigure stdin -buffersize $bufSize + set len [string length $line] + list [yesno "Did you hit ENTER only once?"] [expr {$len > 256}] [yesno "Line length was $len characters. Is this correct?"] +} -result {1 1 1} + +test console-input-3.2 {Console gets nonblocking - long lines bug-bda99f2393} -constraints { + win interactive bug-bda99f2393 +} -body { + prompt "Try typing a line of at least 256 characters. Hit ENTER exactly once unless you don't see another prompt.\n" + fconfigure stdin -blocking 0 + while {[gets stdin line] < 0} { + after 1000 + } + fconfigure stdin -blocking 1 + set len [string length $line] + list [yesno "Did you hit ENTER only once?"] [expr {$len > 256}] [yesno "Line length was $len characters. Is this correct?"] +} -result {1 1 1} + +test console-input-3.3 {Console gets nonblocking small channel buffer size - long lines bug-bda99f2393} -constraints { + win interactive bug-bda99f2393 +} -body { + prompt "Try typing a line of at least 256 characters. Hit ENTER exactly once unless you don't see another prompt.\n" + set bufSize [fconfigure stdin -buffersize] + fconfigure stdin -blocking 0 -buffersize 10 + while {[gets stdin line] < 0} { + after 1000 + } + fconfigure stdin -blocking 1 -buffersize $bufSize + set len [string length $line] + list [yesno "Did you hit ENTER only once?"] [expr {$len > 256}] [yesno "Line length was $len characters. Is this correct?"] +} -result {1 1 1} + # Output tests test console-output-1.0 {Console blocking puts stdout} -constraints {win interactive} -body { @@ -218,7 +265,7 @@ test console-fconfigure-get-1.[incr testnum] { Console get stdin option -eofchar } -constraints {win interactive} -body { fconfigure stdin -eofchar -} -result \x1A +} -result "" test console-fconfigure-get-1.[incr testnum] { fconfigure -winsize -- cgit v0.12 From bd822cee645a36744cdbb369c2d4466cd8ba61ad Mon Sep 17 00:00:00 2001 From: apnadkarni Date: Tue, 20 Feb 2024 05:49:14 +0000 Subject: Bump blocking read buffer size to 8192 irrespective of Tcl channel buffer size --- win/tclWinConsole.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/win/tclWinConsole.c b/win/tclWinConsole.c index 811577d..acd5851 100644 --- a/win/tclWinConsole.c +++ b/win/tclWinConsole.c @@ -70,14 +70,23 @@ static int gInitialized = 0; /* - * Permit CONSOLE_BUFFER_SIZE to be defined on build command for stress test. - * + * INPUT_BUFFER_SIZE is size of buffer passed to ReadConsole in bytes. + * Note that ReadConsole will only allow reading of line lengths up to the + * max of 256 and buffer size passed to it. So dropping this below 512 + * means user can type at most 256 chars. + */ +#ifndef INPUT_BUFFER_SIZE +#define INPUT_BUFFER_SIZE 8192 /* In bytes, so 4096 chars */ +#endif + +/* + * CONSOLE_BUFFER_SIZE is size of storage used in ring buffers. * In theory, at least sizeof(WCHAR) but note the Tcl channel bug * https://core.tcl-lang.org/tcl/tktview/b3977d199b08e3979a8da970553d5209b3042e9c * will cause failures in test suite if close to max input line in the suite. */ #ifndef CONSOLE_BUFFER_SIZE -#define CONSOLE_BUFFER_SIZE 8000 /* In bytes */ +#define CONSOLE_BUFFER_SIZE 8192 /* In bytes */ #endif /* @@ -1143,15 +1152,22 @@ ConsoleInputProc( /* * Blocking read. Just get data from directly from console. There - * is a small complication in that we can only read even number - * of bytes (wide-character API) and the destination buffer should be - * WCHAR aligned. If either condition is not met, we defer to the - * reader thread which handles these case rather than dealing with + * is a small complication in that + * 1. The destination buffer should be WCHAR aligned. + * 2. We can only read even number of bytes (wide-character API). + * 3. Caller has large enough buffer (else length of line user can + * enter will be limited) + * If any condition is not met, we defer to the + * reader thread which handles these cases rather than dealing with * them here (which is a little trickier than it might sound.) + * + * TODO - not clear this block is a useful optimization. bufSize by + * default is 4K which is < INPUT_BUFFER_SIZE and will rarely be + * increased on stdin. */ if ((1 & (size_t)bufPtr) == 0 /* aligned buffer */ - && bufSize > 1 /* Not single byte read */ - ) { + && (1 & bufSize) == 0 /* Even number of bytes */ + && bufSize > INPUT_BUFFER_SIZE) { DWORD lastError; Tcl_Size numChars; ReleaseSRWLockExclusive(&handleInfoPtr->lock); @@ -1634,12 +1650,7 @@ ConsoleReaderThread( Tcl_Size inputOffset = 0; Tcl_Size lastReadSize = 0; DWORD sleepTime; - /* - * ReadConsole will limit input to the greater of 256 characters - * and the size of the input buffer. 8.6 used 8192 (4096 chars) - * and so do we. - */ - char inputChars[8192]; + char inputChars[INPUT_BUFFER_SIZE]; /* * Keep looping until one of the following happens. -- cgit v0.12 From ff85c5650b6bb193f31108662dbb788c279ba3ed Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Wed, 21 Feb 2024 23:14:16 +0000 Subject: Remove some dead code --- generic/tclCmdIL.c | 48 +----------------------------------------------- generic/tclIO.c | 15 +++------------ 2 files changed, 4 insertions(+), 59 deletions(-) diff --git a/generic/tclCmdIL.c b/generic/tclCmdIL.c index c759a54..0079167 100644 --- a/generic/tclCmdIL.c +++ b/generic/tclCmdIL.c @@ -1839,9 +1839,6 @@ InfoProcsCmd( const char *cmdName, *pattern; const char *simplePattern; Namespace *nsPtr; -#ifdef INFO_PROCS_SEARCH_GLOBAL_NS - Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp); -#endif Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp); Tcl_Obj *listPtr, *elemObjPtr; int specificNsInPattern = 0;/* Init. to avoid compiler warning. */ @@ -1893,7 +1890,6 @@ InfoProcsCmd( */ listPtr = Tcl_NewListObj(0, NULL); -#ifndef INFO_PROCS_SEARCH_GLOBAL_NS if (simplePattern != NULL && TclMatchIsTrivial(simplePattern)) { entryPtr = Tcl_FindHashEntry(&nsPtr->cmdTable, simplePattern); if (entryPtr != NULL) { @@ -1917,9 +1913,7 @@ InfoProcsCmd( Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr); } } - } else -#endif /* !INFO_PROCS_SEARCH_GLOBAL_NS */ - { + } else { entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search); while (entryPtr != NULL) { cmdName = (const char *)Tcl_GetHashKey(&nsPtr->cmdTable, entryPtr); @@ -1947,46 +1941,6 @@ InfoProcsCmd( } entryPtr = Tcl_NextHashEntry(&search); } - - /* - * If the effective namespace isn't the global :: namespace, and a - * specific namespace wasn't requested in the pattern, then add in all - * global :: procs that match the simple pattern. Of course, we add in - * only those procs that aren't hidden by a proc in the effective - * namespace. - */ - -#ifdef INFO_PROCS_SEARCH_GLOBAL_NS - /* - * If "info procs" worked like "info commands", returning the commands - * also seen in the global namespace, then you would include this - * code. As this could break backwards compatibility with 8.0-8.2, we - * decided not to "fix" it in 8.3, leaving the behavior slightly - * different. - */ - - if ((nsPtr != globalNsPtr) && !specificNsInPattern) { - entryPtr = Tcl_FirstHashEntry(&globalNsPtr->cmdTable, &search); - while (entryPtr != NULL) { - cmdName = (const char *)Tcl_GetHashKey(&globalNsPtr->cmdTable, entryPtr); - if ((simplePattern == NULL) - || Tcl_StringMatch(cmdName, simplePattern)) { - if (Tcl_FindHashEntry(&nsPtr->cmdTable,cmdName) == NULL) { - cmdPtr = (Command *)Tcl_GetHashValue(entryPtr); - realCmdPtr = (Command *) TclGetOriginalCommand( - (Tcl_Command) cmdPtr); - - if (TclIsProc(cmdPtr) || ((realCmdPtr != NULL) - && TclIsProc(realCmdPtr))) { - Tcl_ListObjAppendElement(interp, listPtr, - Tcl_NewStringObj(cmdName, -1)); - } - } - } - entryPtr = Tcl_NextHashEntry(&search); - } - } -#endif } Tcl_SetObjResult(interp, listPtr); diff --git a/generic/tclIO.c b/generic/tclIO.c index 852bb9b..c44329e 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -1609,18 +1609,9 @@ Tcl_CreateChannel( char *tmp; ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); - /* - * With the change of the Tcl_ChannelType structure to use a version in - * 8.3.2+, we have to make sure that our assumption that the structure - * remains a binary compatible size is true. - * - * If this assertion fails on some system, then it can be removed only if - * the user recompiles code with older channel drivers in the new system - * as well. - */ - - assert(sizeof(Tcl_ChannelTypeVersion) == sizeof(Tcl_DriverBlockModeProc *)); - assert(typePtr->typeName != NULL); + if (typePtr->typeName == NULL) { + Tcl_Panic("channel does not have a type name"); + } if (Tcl_ChannelVersion(typePtr) != TCL_CHANNEL_VERSION_5) { Tcl_Panic("channel type %s must be version TCL_CHANNEL_VERSION_5", typePtr->typeName); } -- cgit v0.12 From 595c3da75ed99a36dc318226bb945d24a139f896 Mon Sep 17 00:00:00 2001 From: bch Date: Fri, 23 Feb 2024 16:24:01 +0000 Subject: speeling --- generic/tclVar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generic/tclVar.c b/generic/tclVar.c index 125091a..1bd5107 100644 --- a/generic/tclVar.c +++ b/generic/tclVar.c @@ -6713,7 +6713,7 @@ AppendLocals( * * TclInfoConstantCmd -- * - * Called to implement the "info constant" command that wests whether a + * Called to implement the "info constant" command that tests whether a * specific variable is a constant. Handles the following syntax: * * info constant varName -- cgit v0.12 From 5f715465b1cdceb6c0d1c15b63060f127975e19a Mon Sep 17 00:00:00 2001 From: dkf Date: Sat, 2 Mar 2024 16:23:38 +0000 Subject: Add missing Tcl_NextHashEntry --- generic/tclCompCmdsSZ.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generic/tclCompCmdsSZ.c b/generic/tclCompCmdsSZ.c index d79b7b9..0281465 100644 --- a/generic/tclCompCmdsSZ.c +++ b/generic/tclCompCmdsSZ.c @@ -2563,7 +2563,7 @@ DupJumptableInfo( Tcl_InitHashTable(&newJtPtr->hashTable, TCL_STRING_KEYS); hPtr = Tcl_FirstHashEntry(&jtPtr->hashTable, &search); - while (hPtr != NULL) { + for (; hPtr ; hPtr = Tcl_NextHashEntry(&search)) { newHPtr = Tcl_CreateHashEntry(&newJtPtr->hashTable, Tcl_GetHashKey(&jtPtr->hashTable, hPtr), &isNew); Tcl_SetHashValue(newHPtr, Tcl_GetHashValue(hPtr)); -- cgit v0.12 From 2c13faac42a73078e5c6de765eff4299bfc60e9f Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 5 Mar 2024 16:12:04 +0000 Subject: b1 -> b2 (preparation for release) --- README.md | 2 +- generic/tcl.h | 4 ++-- library/init.tcl | 2 +- unix/configure | 2 +- unix/configure.ac | 2 +- unix/tcl.spec | 2 +- win/configure | 2 +- win/configure.ac | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index ba29fad..2edde12 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # README: Tcl -This is the **Tcl 9.0b1** source distribution. +This is the **Tcl 9.0b2** source distribution. You can get any source release of Tcl from [our distribution site](https://sourceforge.net/projects/tcl/files/Tcl/). diff --git a/generic/tcl.h b/generic/tcl.h index 0f53228..a4480a7 100644 --- a/generic/tcl.h +++ b/generic/tcl.h @@ -53,10 +53,10 @@ extern "C" { #if TCL_MAJOR_VERSION == 9 # define TCL_MINOR_VERSION 0 # define TCL_RELEASE_LEVEL TCL_BETA_RELEASE -# define TCL_RELEASE_SERIAL 1 +# define TCL_RELEASE_SERIAL 2 # define TCL_VERSION "9.0" -# define TCL_PATCH_LEVEL "9.0b1" +# define TCL_PATCH_LEVEL "9.0b2" #endif /* TCL_MAJOR_VERSION */ #if defined(RC_INVOKED) diff --git a/library/init.tcl b/library/init.tcl index 95081ec..6500d8e 100644 --- a/library/init.tcl +++ b/library/init.tcl @@ -15,7 +15,7 @@ # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # -package require -exact tcl 9.0b1 +package require -exact tcl 9.0b2 # Compute the auto path to use in this interpreter. # The values on the path come from several locations: diff --git a/unix/configure b/unix/configure index 946e512..fe3be30 100755 --- a/unix/configure +++ b/unix/configure @@ -2710,7 +2710,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu TCL_VERSION=9.0 TCL_MAJOR_VERSION=9 TCL_MINOR_VERSION=0 -TCL_PATCH_LEVEL="b1" +TCL_PATCH_LEVEL="b2" VERSION=${TCL_VERSION} EXTRA_INSTALL_BINARIES=${EXTRA_INSTALL_BINARIES:-"@:"} diff --git a/unix/configure.ac b/unix/configure.ac index 03d7e5a..df38377 100644 --- a/unix/configure.ac +++ b/unix/configure.ac @@ -26,7 +26,7 @@ m4_ifdef([SC_USE_CONFIG_HEADERS], [ TCL_VERSION=9.0 TCL_MAJOR_VERSION=9 TCL_MINOR_VERSION=0 -TCL_PATCH_LEVEL="b1" +TCL_PATCH_LEVEL="b2" VERSION=${TCL_VERSION} EXTRA_INSTALL_BINARIES=${EXTRA_INSTALL_BINARIES:-"@:"} diff --git a/unix/tcl.spec b/unix/tcl.spec index d56cee3..65194f6 100644 --- a/unix/tcl.spec +++ b/unix/tcl.spec @@ -4,7 +4,7 @@ Name: tcl Summary: Tcl scripting language development environment -Version: 9.0b1 +Version: 9.0b2 Release: 2 License: BSD Group: Development/Languages diff --git a/win/configure b/win/configure index a0529b0..103e114 100755 --- a/win/configure +++ b/win/configure @@ -2411,7 +2411,7 @@ SHELL=/bin/sh TCL_VERSION=9.0 TCL_MAJOR_VERSION=9 TCL_MINOR_VERSION=0 -TCL_PATCH_LEVEL="b1" +TCL_PATCH_LEVEL="b2" VER=$TCL_MAJOR_VERSION$TCL_MINOR_VERSION TCL_DDE_VERSION=1.4 diff --git a/win/configure.ac b/win/configure.ac index f40871d..9f6e21a 100644 --- a/win/configure.ac +++ b/win/configure.ac @@ -15,7 +15,7 @@ SHELL=/bin/sh TCL_VERSION=9.0 TCL_MAJOR_VERSION=9 TCL_MINOR_VERSION=0 -TCL_PATCH_LEVEL="b1" +TCL_PATCH_LEVEL="b2" VER=$TCL_MAJOR_VERSION$TCL_MINOR_VERSION TCL_DDE_VERSION=1.4 -- cgit v0.12 From d0e41d2bc3adf8da88103ffa240360b6939f07b6 Mon Sep 17 00:00:00 2001 From: sebres Date: Tue, 5 Mar 2024 16:38:57 +0000 Subject: fix for [910d67a229fe7f65]: search of `namespace unknown` handler fixed: first try to find namespace unknown handler of the namespace of executed command if available; this elementary fixes following bug (additionally prevents to call slowly global "::unknown" for known/loaded namespaces with registered unknown handler) --- generic/tclBasic.c | 29 +++++++++++++++++++++++++---- tests/namespace.test | 9 +++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/generic/tclBasic.c b/generic/tclBasic.c index 8dde621..93c3b43 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -4726,12 +4726,33 @@ TEOV_NotFound( * unknown command handler for the current * namespace (TIP 181). */ Namespace *savedNsPtr = NULL; + + int qualLen; + const char *qualName = TclGetStringFromObj(objv[0], &qualLen); currNsPtr = varFramePtr->nsPtr; - if ((currNsPtr == NULL) || (currNsPtr->unknownHandlerPtr == NULL)) { - currNsPtr = iPtr->globalNsPtr; - if (currNsPtr == NULL) { - Tcl_Panic("Tcl_EvalObjv: NULL global namespace pointer"); + if ((currNsPtr == NULL) || (currNsPtr->unknownHandlerPtr == NULL) || + (qualLen > 2 && (*qualName == ':') && (*(qualName+1) == ':')) + ) { + /* + * first try to find namespace unknown handler of the namespace + * of executed command if available: + */ + Namespace *dummyNsPtr; + const char *simpleName; + + (void) TclGetNamespaceForQualName(interp, qualName, currNsPtr, + TCL_NAMESPACE_ONLY, &currNsPtr, &dummyNsPtr, &dummyNsPtr, + &simpleName); + if ((currNsPtr == NULL) || (simpleName == NULL) || + currNsPtr->unknownHandlerPtr == NULL || + (currNsPtr->flags & (NS_DYING | NS_DEAD)) + ) { + /* fallback to the global unknown */ + currNsPtr = iPtr->globalNsPtr; + if (currNsPtr == NULL) { + Tcl_Panic("TEOV_NotFound: NULL global namespace pointer"); + } } } diff --git a/tests/namespace.test b/tests/namespace.test index 08531e4..74cd6a9 100644 --- a/tests/namespace.test +++ b/tests/namespace.test @@ -3047,6 +3047,15 @@ test namespace-52.12 {unknown: error case must not reset handler} -body { } -cleanup { namespace delete foo } -result ok +test namespace-52.13 {unknown: invocation outside of NS doesn't evade namespace unknown, bug 910d67a229fe7f65} -body { + namespace eval ::foo::bar { + proc _unknown args {list ::foo:bar:_unknown [uplevel {namespace current}] $args} + namespace unknown [namespace current]::_unknown + } + list [namespace inscope ::foo::bar {xxx}] [namespace inscope ::foo {bar::xxx}] [::foo::bar::xxx] +} -cleanup { + namespace delete ::foo +} -result {{::foo:bar:_unknown ::foo::bar xxx} {::foo:bar:_unknown ::foo bar::xxx} {::foo:bar:_unknown :: ::foo::bar::xxx}} # TIP 314 - ensembles with parameters test namespace-53.1 {ensembles: parameters} { -- cgit v0.12 From c0d1bb1fb79a061d17a4ed84b63fdb92acb0ab77 Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 6 Mar 2024 12:05:31 +0000 Subject: small amend to [910d67a229fe7f65]: additional corner case (see the test namespace-52.13) --- generic/tclBasic.c | 2 +- tests/namespace.test | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/generic/tclBasic.c b/generic/tclBasic.c index 93c3b43..ffd69c4 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -4732,7 +4732,7 @@ TEOV_NotFound( currNsPtr = varFramePtr->nsPtr; if ((currNsPtr == NULL) || (currNsPtr->unknownHandlerPtr == NULL) || - (qualLen > 2 && (*qualName == ':') && (*(qualName+1) == ':')) + (qualLen > 2 && memchr(qualName, ':', qualLen)) /* fast check for NS:: */ ) { /* * first try to find namespace unknown handler of the namespace diff --git a/tests/namespace.test b/tests/namespace.test index 74cd6a9..c8c1992 100644 --- a/tests/namespace.test +++ b/tests/namespace.test @@ -3052,10 +3052,10 @@ test namespace-52.13 {unknown: invocation outside of NS doesn't evade namespace proc _unknown args {list ::foo:bar:_unknown [uplevel {namespace current}] $args} namespace unknown [namespace current]::_unknown } - list [namespace inscope ::foo::bar {xxx}] [namespace inscope ::foo {bar::xxx}] [::foo::bar::xxx] + list [namespace inscope ::foo::bar {xxx}] [namespace inscope ::foo {bar::xxx}] [::foo::bar::xxx] [namespace inscope :: {foo::bar::xxx}] } -cleanup { namespace delete ::foo -} -result {{::foo:bar:_unknown ::foo::bar xxx} {::foo:bar:_unknown ::foo bar::xxx} {::foo:bar:_unknown :: ::foo::bar::xxx}} +} -result {{::foo:bar:_unknown ::foo::bar xxx} {::foo:bar:_unknown ::foo bar::xxx} {::foo:bar:_unknown :: ::foo::bar::xxx} {::foo:bar:_unknown :: foo::bar::xxx}} # TIP 314 - ensembles with parameters test namespace-53.1 {ensembles: parameters} { -- cgit v0.12 From 2cce3b96761ce708e8a7a4fb04b1238c81c66e1d Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 6 Mar 2024 19:22:10 +0000 Subject: better variant of fix for [910d67a229fe7f65] with improved search for NS::command (find NS even if command is not simple name), additionally it'd invoke handler of parent NS if child NS doesn't have unknown handler (see test namespace-52.14) --- generic/tclBasic.c | 14 ++++++++++++-- generic/tclInt.h | 3 +++ generic/tclNamesp.c | 20 ++++++++++++-------- tests/namespace.test | 16 ++++++++++++++++ 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/generic/tclBasic.c b/generic/tclBasic.c index ffd69c4..78685f0 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -4741,13 +4741,23 @@ TEOV_NotFound( Namespace *dummyNsPtr; const char *simpleName; + tryParentNS: (void) TclGetNamespaceForQualName(interp, qualName, currNsPtr, - TCL_NAMESPACE_ONLY, &currNsPtr, &dummyNsPtr, &dummyNsPtr, - &simpleName); + TCL_NAMESPACE_ONLY | TCL_FIND_IF_NOT_SIMPLE, &currNsPtr, + &dummyNsPtr, &dummyNsPtr, &simpleName); if ((currNsPtr == NULL) || (simpleName == NULL) || currNsPtr->unknownHandlerPtr == NULL || (currNsPtr->flags & (NS_DYING | NS_DEAD)) ) { + /* traverse to alive parent namespace containing handler */ + if (currNsPtr) { + qualName = currNsPtr->fullName; + qualLen = strlen(qualName); + if (qualLen > 2 && memchr(qualName, ':', qualLen)) { + currNsPtr = iPtr->globalNsPtr; + goto tryParentNS; + } + } /* fallback to the global unknown */ currNsPtr = iPtr->globalNsPtr; if (currNsPtr == NULL) { diff --git a/generic/tclInt.h b/generic/tclInt.h index 68c07f2..de92a7d 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -400,10 +400,13 @@ struct NamespacePathEntry { * TCL_NAMESPACE_ONLY - (see tcl.h) Look only in the context ns. * TCL_CREATE_NS_IF_UNKNOWN - Create unknown namespaces. * TCL_FIND_ONLY_NS - The name sought is a namespace name. + * TCL_FIND_IF_NOT_SIMPLE - Retrieve last namespace even if the rest of + * name is not simple name (contains ::). */ #define TCL_CREATE_NS_IF_UNKNOWN 0x800 #define TCL_FIND_ONLY_NS 0x1000 +#define TCL_FIND_IF_NOT_SIMPLE 0x2000 /* * The client data for an ensemble command. This consists of the table of diff --git a/generic/tclNamesp.c b/generic/tclNamesp.c index 290dcea..37092fe 100644 --- a/generic/tclNamesp.c +++ b/generic/tclNamesp.c @@ -2280,11 +2280,8 @@ TclGetNamespaceForQualName( if (flags & TCL_FIND_ONLY_NS) { nsName = start; } else { - *nsPtrPtr = nsPtr; - *altNsPtrPtr = altNsPtr; *simpleNamePtr = start; - Tcl_DStringFree(&buffer); - return TCL_OK; + goto done; } } else { /* @@ -2334,6 +2331,15 @@ TclGetNamespaceForQualName( } } else { /* Namespace not found and was not * created. */ + if (flags & TCL_FIND_IF_NOT_SIMPLE) { + /* + * return last found NS and not simple name relative it, + * e. g. ::A::B::C::D -> ::A::B and C::D, if + * namespace C cannot be found in ::A::B + */ + *simpleNamePtr = start; + goto done; + } nsPtr = NULL; } } @@ -2364,11 +2370,8 @@ TclGetNamespaceForQualName( */ if ((nsPtr == NULL) && (altNsPtr == NULL)) { - *nsPtrPtr = NULL; - *altNsPtrPtr = NULL; *simpleNamePtr = NULL; - Tcl_DStringFree(&buffer); - return TCL_OK; + goto done; } start = end; @@ -2398,6 +2401,7 @@ TclGetNamespaceForQualName( nsPtr = NULL; } +done: *nsPtrPtr = nsPtr; *altNsPtrPtr = altNsPtr; Tcl_DStringFree(&buffer); diff --git a/tests/namespace.test b/tests/namespace.test index c8c1992..5a8f6f4 100644 --- a/tests/namespace.test +++ b/tests/namespace.test @@ -3056,6 +3056,22 @@ test namespace-52.13 {unknown: invocation outside of NS doesn't evade namespace } -cleanup { namespace delete ::foo } -result {{::foo:bar:_unknown ::foo::bar xxx} {::foo:bar:_unknown ::foo bar::xxx} {::foo:bar:_unknown :: ::foo::bar::xxx} {::foo:bar:_unknown :: foo::bar::xxx}} +test namespace-52.14 {unknown: invocation outside of NS doesn't evade namespace unknown for command with sub-NS, bug 910d67a229fe7f65} -body { + namespace eval ::foo::bar { + proc _unknown args {list ::foo:bar:_unknown [uplevel {namespace current}] $args} + namespace unknown [namespace current]::_unknown + } + set res {} + lappend res [namespace inscope ::foo::bar {xxx::yyy}] [namespace inscope ::foo {bar::xxx::yyy}] [::foo::bar::xxx::yyy] [namespace inscope :: {foo::bar::xxx::yyy}] + # now with existsing ::foo::bar::xxx, but without unknown handler inside (only parent ::foo::bar has a handler): + namespace eval ::foo::bar::xxx {} + lappend res [namespace inscope ::foo::bar {xxx::yyy}] [namespace inscope ::foo {bar::xxx::yyy}] [::foo::bar::xxx::yyy] [namespace inscope :: {foo::bar::xxx::yyy}] +} -cleanup { + namespace delete ::foo + unset -nocomplain res +} -result [lrepeat 2 \ + {::foo:bar:_unknown ::foo::bar xxx::yyy} {::foo:bar:_unknown ::foo bar::xxx::yyy} {::foo:bar:_unknown :: ::foo::bar::xxx::yyy} {::foo:bar:_unknown :: foo::bar::xxx::yyy} +] # TIP 314 - ensembles with parameters test namespace-53.1 {ensembles: parameters} { -- cgit v0.12 From 24dc4df4b8485a1ac1ab8dd2356e2bd90b0c27d8 Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 6 Mar 2024 21:29:40 +0000 Subject: small amend: simpler traversing using ns->parentPtr --- generic/tclBasic.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/generic/tclBasic.c b/generic/tclBasic.c index 78685f0..f8baf1c 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -4741,27 +4741,21 @@ TEOV_NotFound( Namespace *dummyNsPtr; const char *simpleName; - tryParentNS: (void) TclGetNamespaceForQualName(interp, qualName, currNsPtr, TCL_NAMESPACE_ONLY | TCL_FIND_IF_NOT_SIMPLE, &currNsPtr, &dummyNsPtr, &dummyNsPtr, &simpleName); - if ((currNsPtr == NULL) || (simpleName == NULL) || + while ((currNsPtr == NULL) || (simpleName == NULL) || currNsPtr->unknownHandlerPtr == NULL || (currNsPtr->flags & (NS_DYING | NS_DEAD)) ) { /* traverse to alive parent namespace containing handler */ - if (currNsPtr) { - qualName = currNsPtr->fullName; - qualLen = strlen(qualName); - if (qualLen > 2 && memchr(qualName, ':', qualLen)) { - currNsPtr = iPtr->globalNsPtr; - goto tryParentNS; + if (!currNsPtr || !(currNsPtr = currNsPtr->parentPtr)) { + /* fallback to the global unknown */ + currNsPtr = iPtr->globalNsPtr; + if (currNsPtr == NULL) { + Tcl_Panic("TEOV_NotFound: NULL global namespace pointer"); } - } - /* fallback to the global unknown */ - currNsPtr = iPtr->globalNsPtr; - if (currNsPtr == NULL) { - Tcl_Panic("TEOV_NotFound: NULL global namespace pointer"); + break; } } } -- cgit v0.12 From 54bcb0d93278525bab7577dba763c3ee6ebd677a Mon Sep 17 00:00:00 2001 From: sebres Date: Wed, 6 Mar 2024 21:44:10 +0000 Subject: core review --- generic/tclBasic.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/generic/tclBasic.c b/generic/tclBasic.c index f8baf1c..41dbee2 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -4744,12 +4744,15 @@ TEOV_NotFound( (void) TclGetNamespaceForQualName(interp, qualName, currNsPtr, TCL_NAMESPACE_ONLY | TCL_FIND_IF_NOT_SIMPLE, &currNsPtr, &dummyNsPtr, &dummyNsPtr, &simpleName); - while ((currNsPtr == NULL) || (simpleName == NULL) || - currNsPtr->unknownHandlerPtr == NULL || + if ((currNsPtr == NULL) || (simpleName == NULL)) { + goto globNS; + } + while (currNsPtr->unknownHandlerPtr == NULL || (currNsPtr->flags & (NS_DYING | NS_DEAD)) ) { /* traverse to alive parent namespace containing handler */ - if (!currNsPtr || !(currNsPtr = currNsPtr->parentPtr)) { + if (!(currNsPtr = currNsPtr->parentPtr)) { + globNS: /* fallback to the global unknown */ currNsPtr = iPtr->globalNsPtr; if (currNsPtr == NULL) { -- cgit v0.12 From 4d2d3aed4a5255a87107172a2dce28b6015a3c23 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Fri, 8 Mar 2024 10:04:57 +0000 Subject: Remove dead code --- unix/configure | 14 +++++--------- unix/tcl.m4 | 11 +++++------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/unix/configure b/unix/configure index fe3be30..c8e5bdc 100755 --- a/unix/configure +++ b/unix/configure @@ -6402,16 +6402,12 @@ fi case $system in DragonFly-*|FreeBSD-*) - if test "${TCL_THREADS}" = "1" -then : - - # The -pthread needs to go in the LDFLAGS, not LIBS - LIBS=`echo $LIBS | sed s/-pthread//` - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - LDFLAGS="$LDFLAGS $PTHREAD_LIBS" -fi + # The -pthread needs to go in the LDFLAGS, not LIBS + LIBS=`echo $LIBS | sed s/-pthread//` + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LDFLAGS="$LDFLAGS $PTHREAD_LIBS" ;; - esac + esac if test $doRpath = yes then : diff --git a/unix/tcl.m4 b/unix/tcl.m4 index 4a9fe40..7b84923 100644 --- a/unix/tcl.m4 +++ b/unix/tcl.m4 @@ -1276,13 +1276,12 @@ AC_DEFUN([SC_CONFIG_CFLAGS], [ case $system in DragonFly-*|FreeBSD-*) - AS_IF([test "${TCL_THREADS}" = "1"], [ - # The -pthread needs to go in the LDFLAGS, not LIBS - LIBS=`echo $LIBS | sed s/-pthread//` - CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - LDFLAGS="$LDFLAGS $PTHREAD_LIBS"]) + # The -pthread needs to go in the LDFLAGS, not LIBS + LIBS=`echo $LIBS | sed s/-pthread//` + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LDFLAGS="$LDFLAGS $PTHREAD_LIBS" ;; - esac + esac AS_IF([test $doRpath = yes], [ CC_SEARCH_FLAGS='"-Wl,-rpath,${LIB_RUNTIME_DIR}"']) -- cgit v0.12 From de7fa6e02c27b87267b10ca5e635841104031593 Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 8 Mar 2024 13:40:57 +0000 Subject: namespace unknown considers also alternate search path (relative global NS), see namespace-52.14 --- generic/tclBasic.c | 27 +++++++++++++++++++++------ tests/namespace.test | 17 +++++++++++++++++ 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/generic/tclBasic.c b/generic/tclBasic.c index d87fea6..9b4161a 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -4816,20 +4816,35 @@ TEOV_NotFound( * first try to find namespace unknown handler of the namespace * of executed command if available: */ - Namespace *dummyNsPtr; + Namespace *altNsPtr, *dummyNsPtr; const char *simpleName; (void) TclGetNamespaceForQualName(interp, qualName, currNsPtr, - TCL_NAMESPACE_ONLY | TCL_FIND_IF_NOT_SIMPLE, &currNsPtr, - &dummyNsPtr, &dummyNsPtr, &simpleName); - if ((currNsPtr == NULL) || (simpleName == NULL)) { - goto globNS; + TCL_FIND_IF_NOT_SIMPLE, &currNsPtr, &altNsPtr, + &dummyNsPtr, &simpleName); + if (!simpleName) { + goto globNS; + } + if (!currNsPtr || (currNsPtr == iPtr->globalNsPtr)) { + if (!altNsPtr || (altNsPtr == iPtr->globalNsPtr)) { + goto globNS; + } + currNsPtr = altNsPtr; } while (currNsPtr->unknownHandlerPtr == NULL || (currNsPtr->flags & (NS_DYING | NS_DEAD)) ) { /* traverse to alive parent namespace containing handler */ - if (!(currNsPtr = currNsPtr->parentPtr)) { + if (!(currNsPtr = currNsPtr->parentPtr) || + (currNsPtr == iPtr->globalNsPtr) + ) { + /* continue from alternate NS if available */ + if (!altNsPtr || (altNsPtr == iPtr->globalNsPtr)) { + goto globNS; + } + currNsPtr = altNsPtr; + altNsPtr = NULL; + continue; globNS: /* fallback to the global unknown */ currNsPtr = iPtr->globalNsPtr; diff --git a/tests/namespace.test b/tests/namespace.test index 9976cf3..abe642e 100644 --- a/tests/namespace.test +++ b/tests/namespace.test @@ -3149,6 +3149,23 @@ test namespace-52.14 {unknown: invocation outside of NS doesn't evade namespace } -result [lrepeat 2 \ {::foo:bar:_unknown ::foo::bar xxx::yyy} {::foo:bar:_unknown ::foo bar::xxx::yyy} {::foo:bar:_unknown :: ::foo::bar::xxx::yyy} {::foo:bar:_unknown :: foo::bar::xxx::yyy} ] +test namespace-52.14 {unknown: it must consider alternate search path (relative global NS), bug 910d67a229fe7f65} -body { + namespace eval ::foo::bar {} + namespace eval ::xxx::yyy { + proc _unknown args {list ::xxx:yyy:_unknown [uplevel {namespace current}] $args} + namespace unknown [namespace current]::_unknown + } + set res {} + lappend res [namespace inscope ::foo::bar {xxx::yyy::cmd}] [namespace inscope ::foo {xxx::yyy::cmd}] + namespace eval ::foo::bar::xxx {} + lappend res [namespace inscope ::foo::bar {xxx::yyy::cmd}] [namespace inscope ::foo {xxx::yyy::cmd}] + namespace eval ::foo::bar::xxx::yyy {} + lappend res [namespace inscope ::foo::bar {xxx::yyy::cmd}] [namespace inscope ::foo {xxx::yyy::cmd}] +} -cleanup { + namespace delete ::foo + namespace delete ::xxx + unset -nocomplain res +} -result [lrepeat 3 {::xxx:yyy:_unknown ::foo::bar xxx::yyy::cmd} {::xxx:yyy:_unknown ::foo xxx::yyy::cmd}] # TIP 314 - ensembles with parameters test namespace-53.1 {ensembles: parameters} { -- cgit v0.12