From 5c2bc08ea4edc13e386422d6c6f86bb65014a0a3 Mon Sep 17 00:00:00 2001 From: pooryorick Date: Wed, 26 Jan 2022 17:48:34 +0000 Subject: Add back a clarification to the documentation for [expr] that an operand is interpreted as a number wherever possible, and rework text to be more compact. --- doc/expr.n | 137 +++++++++++++++++++++++++++++++------------------------------ 1 file changed, 69 insertions(+), 68 deletions(-) diff --git a/doc/expr.n b/doc/expr.n index 43ad26f..490217c 100644 --- a/doc/expr.n +++ b/doc/expr.n @@ -17,7 +17,7 @@ expr \- Evaluate an expression .BE .SH DESCRIPTION .PP -The \fIexpr\fR command concatenates \fIarg\fRs, separated by a space, into an expression, and evaluates +Concatenates \fIarg\fRs, separated by a space, into an expression, and evaluates that expression, returning its value. The operators permitted in an expression include a subset of the operators permitted in C expressions. For those operators @@ -37,76 +37,36 @@ operands are specified. Expressions also support non-numeric operands, string comparisons, and some additional operators not found in C. .PP -When an expression evaluates to an integer, the value is the decimal form of -the integer, and when an expression evaluates to a floating-point number, the -value is the form produced by the \fB%g\fR format specifier of Tcl's -\fBformat\fR command. +When the result of expression is an integer, it is in decimal form, and when +the result is a floating-point number, it is in the form produced by the +\fB%g\fR format specifier of \fBformat\fR. .PP .VS "TIP 582" -You can use \fB#\fR at any point in the expression (except inside double -quotes or braces) to start a comment. Comments last to the end of the line or +At any point in the expression except within double quotes or braces, \fB#\fR +is the beginning of a comment, which lasts to the end of the line or the end of the expression, whichever comes first. .VE "TIP 582" .SS OPERANDS .PP An expression consists of a combination of operands, operators, parentheses and commas, possibly with whitespace between any of these elements, which is -ignored. +ignored. Each operand is intepreted as a numeric value if at all possible. .PP -An operand may be specified in any of the following ways: -.IP [1] -As a numeric value, either integer or floating-point. -.IP [2] -As a boolean value, using any form understood by \fBstring is\fR -\fBboolean\fR. -.IP [3] -As a variable, using standard \fB$\fR notation. -The value of the variable is then the value of the operand. -.IP [4] -As a string enclosed in double-quotes. -Backslash, variable, and command substitution are performed as described in -\fBTcl\fR. -.IP [5] -As a string enclosed in braces. -The operand is treated as a braced value as described in \fBTcl\fR. -.IP [6] -As a Tcl command enclosed in brackets. -Command substitution is performed as described in \fBTcl\fR. -.IP [7] -As a mathematical function such as \fBsin($x)\fR, whose arguments have any of the above -forms for operands. See \fBMATH FUNCTIONS\fR below for -a discussion of how mathematical functions are handled. -.PP -Because \fBexpr\fR parses and performs substitutions on values that have -already been parsed and substituted by \fBTcl\fR, it is usually best to enclose -expressions in braces to avoid the first round of substitutions by -\fBTcl\fR. -.PP -Below are some examples of simple expressions where the value of \fBa\fR is 3 -and the value of \fBb\fR is 6. The command on the left side of each line -produces the value on the right side. -.PP -.CS -.ta 9c -\fBexpr\fR {3.1 + $a} \fI6.1\fR -\fBexpr\fR {2 + "$a.$b"} \fI5.6\fR -\fBexpr\fR {4*[llength "6 2"]} \fI8\fR -\fBexpr\fR {{word one} < "word $a"} \fI0\fR -.CE -.PP -\fBInteger value\fR +Each operand has one of the following forms: +.RS .PP -An integer operand may be specified in decimal (the normal case, the optional -first two characters are \fB0d\fR), binary -(the first two characters are \fB0b\fR), octal -(the first two characters are \fB0o\fR), or hexadecimal -(the first two characters are \fB0x\fR) form. For -compatibility with older Tcl releases, an operand that begins with \fB0\fR is -interpreted as an octal integer even if the second character is not \fBo\fR. +.TP +A \fBnumeric value\fR .PP -\fBFloating-point value\fR +.RS +. +Either integer or floating-point. The first two characters of an integer may +also be \fB0d\fR for decimal, \fB0b\fR for binary, \fB0o\fR for octal or +\fB0x\fR for hexadicimal. For compatibility with older Tcl releases, an +operand that begins with \fB0\fR is interpreted as an octal integer even if the +second character is not \fBo\fR. .PP -A floating-point number may be specified in any of several +A floating-point number may be take any of several common decimal formats, and may use the decimal point \fB.\fR, \fBe\fR or \fBE\fR for scientific notation, and the sign characters \fB+\fR and \fB\-\fR. The @@ -116,16 +76,9 @@ and \fBNaN\fR, in any combination of case, are also recognized as floating point values. An operand that doesn't have a numeric interpretation must be quoted with either braces or with double quotes. .PP -\fBBoolean value\fR -.PP -A boolean value may be represented by any of the values \fB0\fR, \fBfalse\fR, \fBno\fR, -or \fBoff\fR and any of the values \fB1\fR, \fBtrue\fR, \fByes\fR, or \fBon\fR. -.PP -\fBDigit Separator\fR -.PP Digits in any numeric value may be separated with one or more underscore -characters, "\fB_\fR", to improve readability. These separators may only -appear between digits. The separator may not appear at the start of a +characters, "\fB_\fR". A separator may only +appear between digits, not appear at the start of a numeric value, between the leading 0 and radix specifier, or at the end of a numeric value. Here are some examples: .PP @@ -135,6 +88,54 @@ end of a numeric value. Here are some examples: \fBexpr\fR 0xffff_ffff \fI4294967295\fR \fBformat\fR 0x%x 0b1111_1110_1101_1011 \fI0xfedb\fR .CE +.RE + +.TP +A \fBboolean value\fR +. +Using any form understood by \fBstring is\fR +\fBboolean\fR. +.TP +A \fBvariable\fR +. +Using standard \fB$\fR notation. +The value of the variable is the value of the operand. +.TP +A string enclosed in \fBdouble-quotes\fR +. +Backslash, variable, and command substitution are performed according to the +rules for \fBTcl\fR. +.TP +A string enclosed in \fBbraces\fR. +The operand is treated as a braced value according to the rule for braces in +\fBTcl\fR. +.TP +A Tcl command enclosed in \fBbrackets\fR +. +Command substitution is performed as according to the command substitution rule +for \fBTcl\fR. +.TP +A mathematical function such as \fBsin($x)\fR, whose arguments have any of the above +forms for operands. See \fBMATH FUNCTIONS\fR below for +a discussion of how mathematical functions are handled. +.RE +.PP +Because \fBexpr\fR parses and performs substitutions on values that have +already been parsed and substituted by \fBTcl\fR, it is usually best to enclose +expressions in braces to avoid the first round of substitutions by +\fBTcl\fR. +.PP +Below are some examples of simple expressions where the value of \fBa\fR is 3 +and the value of \fBb\fR is 6. The command on the left side of each line +produces the value on the right side. +.PP +.CS +.ta 9c +\fBexpr\fR {3.1 + $a} \fI6.1\fR +\fBexpr\fR {2 + "$a.$b"} \fI5.6\fR +\fBexpr\fR {4*[llength {6 2}]} \fI8\fR +\fBexpr\fR {{word one} < "word $a"} \fI0\fR +.CE .PP .SS OPERATORS .PP -- cgit v0.12 From cc66f3601ff68b38489ca84cb582dbbe3ea804ef Mon Sep 17 00:00:00 2001 From: bch Date: Fri, 28 Jan 2022 23:28:24 +0000 Subject: rejig argv/argc handling in response to investigation prompted by [https://arstechnica.com/information-technology/2022/01/a-bug-lurking-for- 12-years-gives-attackers-root-on-every-major-linux-distro/|this "polkit issue"] and some experimenting w/ execve() (ab)use. Essentially port of [0e1d2702ab] and its parent; discussed at length on IRC --- generic/tclMain.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/generic/tclMain.c b/generic/tclMain.c index bb48dbb..f1b1ae2 100644 --- a/generic/tclMain.c +++ b/generic/tclMain.c @@ -288,6 +288,8 @@ Tcl_MainEx( * but before starting to execute commands. */ Tcl_Interp *interp) { + char *progname = NULL; /* may/may-not be able to use argv[0] */ + int i=0; /* argv[i] index */ Tcl_Obj *path, *resultPtr, *argvPtr, *appName; const char *encodingName = NULL; int code, exitCode = 0; @@ -296,7 +298,14 @@ Tcl_MainEx( InteractiveState is; TclpSetInitialEncodings(); - TclpFindExecutable((const char *)argv[0]); + if (0 < argc) { + progname = argv[0]; + --argc; /* consume argv[0] */ + ++i; + } + TclpFindExecutable ((const char *)progname); /* nb: this could be NULL + * w/ (eg) a malformed + * execve() */ Tcl_InitMemory(interp); @@ -318,36 +327,35 @@ Tcl_MainEx( * FILENAME */ - if ((argc > 3) && (0 == _tcscmp(TEXT("-encoding"), argv[1])) + /* mind argc is being adjusted as we proceed */ + if ((argc >= 3) && (0 == _tcscmp(TEXT("-encoding"), argv[1])) && ('-' != argv[3][0])) { Tcl_Obj *value = NewNativeObj(argv[2]); Tcl_SetStartupScript(NewNativeObj(argv[3]), Tcl_GetString(value)); Tcl_DecrRefCount(value); argc -= 3; - argv += 3; - } else if ((argc > 1) && ('-' != argv[1][0])) { + i += 3; + } else if ((argc >= 1) && ('-' != argv[1][0])) { Tcl_SetStartupScript(NewNativeObj(argv[1]), NULL); argc--; - argv++; + i++; } } path = Tcl_GetStartupScript(&encodingName); if (path == NULL) { - appName = NewNativeObj(argv[0]); + appName = NewNativeObj(progname); } else { appName = path; } Tcl_SetVar2Ex(interp, "argv0", NULL, appName, TCL_GLOBAL_ONLY); - argc--; - argv++; Tcl_SetVar2Ex(interp, "argc", NULL, Tcl_NewWideIntObj(argc), TCL_GLOBAL_ONLY); argvPtr = Tcl_NewListObj(0, NULL); while (argc--) { - Tcl_ListObjAppendElement(NULL, argvPtr, NewNativeObj(*argv++)); + Tcl_ListObjAppendElement(NULL, argvPtr, NewNativeObj(argv[i++])); } Tcl_SetVar2Ex(interp, "argv", NULL, argvPtr, TCL_GLOBAL_ONLY); -- cgit v0.12 From 95bcc538075fc86ab77313a173e2c4ce89a38f0d Mon Sep 17 00:00:00 2001 From: bch Date: Fri, 28 Jan 2022 23:52:10 +0000 Subject: take advantage of what we know re: argv guarantees [https://www.iso-9899.info/n1570.html#5.1.2.2.1|argv spec] (per @cousteau on #tcl) --- generic/tclMain.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/generic/tclMain.c b/generic/tclMain.c index f1b1ae2..be9ec4c 100644 --- a/generic/tclMain.c +++ b/generic/tclMain.c @@ -288,7 +288,6 @@ Tcl_MainEx( * but before starting to execute commands. */ Tcl_Interp *interp) { - char *progname = NULL; /* may/may-not be able to use argv[0] */ int i=0; /* argv[i] index */ Tcl_Obj *path, *resultPtr, *argvPtr, *appName; const char *encodingName = NULL; @@ -299,13 +298,12 @@ Tcl_MainEx( TclpSetInitialEncodings(); if (0 < argc) { - progname = argv[0]; - --argc; /* consume argv[0] */ + --argc; /* "consume" argv[0] */ ++i; } - TclpFindExecutable ((const char *)progname); /* nb: this could be NULL - * w/ (eg) a malformed - * execve() */ + TclpFindExecutable ((const char *)argv [0]); /* nb: this could be NULL + * w/ (eg) an empty argv + * supplied to execve() */ Tcl_InitMemory(interp); @@ -345,7 +343,7 @@ Tcl_MainEx( path = Tcl_GetStartupScript(&encodingName); if (path == NULL) { - appName = NewNativeObj(progname); + appName = NewNativeObj(argv[0]); } else { appName = path; } -- cgit v0.12 From 3311ca7d306a1bf7de8e03bac9eac81d63677899 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Tue, 1 Feb 2022 12:03:54 +0000 Subject: Code cleanup in tclMain.c and tclAppInit.c: Make them Tcl-8.7-aware, usable as more generic examples for extensions. --- generic/tclMain.c | 21 +++++++++++---------- unix/tclAppInit.c | 15 +++++++++++---- win/tclAppInit.c | 26 +++++++++++++++++--------- 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/generic/tclMain.c b/generic/tclMain.c index f0b2682..30a206f 100644 --- a/generic/tclMain.c +++ b/generic/tclMain.c @@ -95,7 +95,7 @@ typedef enum { PROMPT_CONTINUE /* Print prompt for command continuation */ } PromptType; -typedef struct InteractiveState { +typedef struct { Tcl_Channel input; /* The standard input channel from which lines * are read. */ int tty; /* Non-zero means standard input is a @@ -229,7 +229,7 @@ Tcl_SourceRCFile( const char *fileName; Tcl_Channel chan; - fileName = Tcl_GetVar(interp, "tcl_rcFileName", TCL_GLOBAL_ONLY); + fileName = Tcl_GetVar2(interp, "tcl_rcFileName", NULL, TCL_GLOBAL_ONLY); if (fileName != NULL) { Tcl_Channel c; const char *fullName; @@ -515,7 +515,7 @@ Tcl_MainEx( * error messages troubles deeper in, so lop it back off. */ - Tcl_GetStringFromObj(is.commandPtr, &length); + (void)Tcl_GetStringFromObj(is.commandPtr, &length); Tcl_SetObjLength(is.commandPtr, --length); code = Tcl_RecordAndEvalObj(interp, is.commandPtr, TCL_EVAL_GLOBAL); @@ -532,7 +532,7 @@ Tcl_MainEx( } else if (is.tty) { resultPtr = Tcl_GetObjResult(interp); Tcl_IncrRefCount(resultPtr); - Tcl_GetStringFromObj(resultPtr, &length); + (void)Tcl_GetStringFromObj(resultPtr, &length); chan = Tcl_GetStdChannel(TCL_STDOUT); if ((length > 0) && chan) { Tcl_WriteObj(chan, resultPtr); @@ -745,17 +745,18 @@ TclFullFinalizationRequested(void) *---------------------------------------------------------------------- */ - /* ARGSUSED */ static void StdinProc( ClientData clientData, /* The state of interactive cmd line */ int mask) /* Not used. */ { - int code, length; - InteractiveState *isPtr = clientData; + int code; + int length; + InteractiveState *isPtr = (InteractiveState *)clientData; Tcl_Channel chan = isPtr->input; Tcl_Obj *commandPtr = isPtr->commandPtr; Tcl_Interp *interp = isPtr->interp; + (void)mask; if (Tcl_IsShared(commandPtr)) { Tcl_DecrRefCount(commandPtr); @@ -791,7 +792,7 @@ StdinProc( goto prompt; } isPtr->prompt = PROMPT_START; - Tcl_GetStringFromObj(commandPtr, &length); + (void)Tcl_GetStringFromObj(commandPtr, &length); Tcl_SetObjLength(commandPtr, --length); /* @@ -823,7 +824,7 @@ StdinProc( chan = Tcl_GetStdChannel(TCL_STDOUT); Tcl_IncrRefCount(resultPtr); - Tcl_GetStringFromObj(resultPtr, &length); + (void)Tcl_GetStringFromObj(resultPtr, &length); if ((length > 0) && (chan != NULL)) { Tcl_WriteObj(chan, resultPtr); Tcl_WriteChars(chan, "\n", 1); @@ -925,7 +926,7 @@ static void FreeMainInterp( ClientData clientData) { - Tcl_Interp *interp = clientData; + Tcl_Interp *interp = (Tcl_Interp *)clientData; /*if (TclInExit()) return;*/ diff --git a/unix/tclAppInit.c b/unix/tclAppInit.c index 9bbc88b..552f9e4 100644 --- a/unix/tclAppInit.c +++ b/unix/tclAppInit.c @@ -15,15 +15,19 @@ #undef BUILD_tcl #undef STATIC_BUILD #include "tcl.h" +#if TCL_MAJOR_VERSION < 9 && TCL_MINOR_VERSION < 7 +# define Tcl_LibraryInitProc Tcl_PackageInitProc +# define Tcl_StaticLibrary Tcl_StaticPackage +#endif #ifdef TCL_TEST -extern Tcl_PackageInitProc Tcltest_Init; -extern Tcl_PackageInitProc Tcltest_SafeInit; +extern Tcl_LibraryInitProc Tcltest_Init; +extern Tcl_LibraryInitProc Tcltest_SafeInit; #endif /* TCL_TEST */ #ifdef TCL_XT_TEST extern void XtToolkitInitialize(void); -extern Tcl_PackageInitProc Tclxttest_Init; +extern Tcl_LibraryInitProc Tclxttest_Init; #endif /* TCL_XT_TEST */ /* @@ -79,6 +83,9 @@ main( #ifdef TCL_LOCAL_MAIN_HOOK TCL_LOCAL_MAIN_HOOK(&argc, &argv); +#elif (TCL_MAJOR_VERSION > 8 || TCL_MINOR_VERSION > 6) && (!defined(_WIN32) || defined(UNICODE)) + /* New in Tcl 8.7. This doesn't work on Windows without UNICODE */ + TclZipfs_AppHook(&argc, &argv); #endif Tcl_Main(argc, argv, TCL_LOCAL_APPINIT); @@ -122,7 +129,7 @@ Tcl_AppInit( if (Tcltest_Init(interp) == TCL_ERROR) { return TCL_ERROR; } - Tcl_StaticPackage(interp, "Tcltest", Tcltest_Init, Tcltest_SafeInit); + Tcl_StaticLibrary(interp, "Tcltest", Tcltest_Init, Tcltest_SafeInit); #endif /* TCL_TEST */ /* diff --git a/win/tclAppInit.c b/win/tclAppInit.c index 695099e..058b92a 100644 --- a/win/tclAppInit.c +++ b/win/tclAppInit.c @@ -23,16 +23,20 @@ #include #include #include +#if TCL_MAJOR_VERSION < 9 && TCL_MINOR_VERSION < 7 +# define Tcl_LibraryInitProc Tcl_PackageInitProc +# define Tcl_StaticLibrary Tcl_StaticPackage +#endif #ifdef TCL_TEST -extern Tcl_PackageInitProc Tcltest_Init; -extern Tcl_PackageInitProc Tcltest_SafeInit; +extern Tcl_LibraryInitProc Tcltest_Init; +extern Tcl_LibraryInitProc Tcltest_SafeInit; #endif /* TCL_TEST */ #if defined(STATIC_BUILD) && defined(TCL_USE_STATIC_PACKAGES) && TCL_USE_STATIC_PACKAGES -extern Tcl_PackageInitProc Registry_Init; -extern Tcl_PackageInitProc Dde_Init; -extern Tcl_PackageInitProc Dde_SafeInit; +extern Tcl_LibraryInitProc Registry_Init; +extern Tcl_LibraryInitProc Dde_Init; +extern Tcl_LibraryInitProc Dde_SafeInit; #endif #if defined(__GNUC__) || defined(TCL_BROKEN_MAINARGS) @@ -87,7 +91,7 @@ MODULE_SCOPE int TCL_LOCAL_MAIN_HOOK(int *argc, TCHAR ***argv); int main( int argc, /* Number of command-line arguments. */ - char *dummy[]) /* Not used. */ + char **argv1) /* Not used. */ { TCHAR **argv; #else @@ -111,6 +115,7 @@ _tmain( * Get our args from the c-runtime. Ignore command line. */ + (void)argv1; setargv(&argc, &argv); #endif @@ -126,6 +131,9 @@ _tmain( #ifdef TCL_LOCAL_MAIN_HOOK TCL_LOCAL_MAIN_HOOK(&argc, &argv); +#elif (TCL_MAJOR_VERSION > 8 || TCL_MINOR_VERSION > 6) && (!defined(_WIN32) || defined(UNICODE)) + /* New in Tcl 8.7. This doesn't work on Windows without UNICODE */ + TclZipfs_AppHook(&argc, &argv); #endif Tcl_Main(argc, argv, TCL_LOCAL_APPINIT); @@ -163,19 +171,19 @@ Tcl_AppInit( if (Registry_Init(interp) == TCL_ERROR) { return TCL_ERROR; } - Tcl_StaticPackage(interp, "Registry", Registry_Init, NULL); + Tcl_StaticLibrary(interp, "Registry", Registry_Init, NULL); if (Dde_Init(interp) == TCL_ERROR) { return TCL_ERROR; } - Tcl_StaticPackage(interp, "Dde", Dde_Init, Dde_SafeInit); + Tcl_StaticLibrary(interp, "Dde", Dde_Init, Dde_SafeInit); #endif #ifdef TCL_TEST if (Tcltest_Init(interp) == TCL_ERROR) { return TCL_ERROR; } - Tcl_StaticPackage(interp, "Tcltest", Tcltest_Init, Tcltest_SafeInit); + Tcl_StaticLibrary(interp, "Tcltest", Tcltest_Init, Tcltest_SafeInit); #endif /* TCL_TEST */ /* -- cgit v0.12 From 7b1d345686119335c547557b1029df64b0d3c5c5 Mon Sep 17 00:00:00 2001 From: "jan.nijtmans" Date: Wed, 2 Feb 2022 16:42:18 +0000 Subject: Change DEFAULT_PRIMARY_PROMPT from #define to static const string (saves a strlen() call) --- generic/tclMain.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/generic/tclMain.c b/generic/tclMain.c index 30a206f..3f72838 100644 --- a/generic/tclMain.c +++ b/generic/tclMain.c @@ -28,7 +28,7 @@ * The default prompt used when the user has not overridden it. */ -#define DEFAULT_PRIMARY_PROMPT "% " +static const char DEFAULT_PRIMARY_PROMPT[] = "% "; /* * This file can be compiled on Windows in UNICODE mode, as well as on all @@ -887,7 +887,7 @@ Prompt( chan = Tcl_GetStdChannel(TCL_STDOUT); if (chan != NULL) { Tcl_WriteChars(chan, DEFAULT_PRIMARY_PROMPT, - strlen(DEFAULT_PRIMARY_PROMPT)); + sizeof(DEFAULT_PRIMARY_PROMPT) - 1); } } } else { -- cgit v0.12