Index: generic/tclParse.c ================================================================== --- generic/tclParse.c +++ generic/tclParse.c @@ -31,10 +31,11 @@ * TYPE_COMMAND_END - Character is newline or semicolon. * TYPE_SUBS - Character begins a substitution or has other special * meaning in ParseTokens: backslash, dollar sign, or * open bracket. * TYPE_QUOTE - Character is a double quote. + * TYPE_OPEN_PAREN - Character is a left parenthesis. * TYPE_CLOSE_PAREN - Character is a right parenthesis. * TYPE_CLOSE_BRACK - Character is a right square bracket. * TYPE_BRACE - Character is a curly brace (either left or right). */ @@ -52,11 +53,11 @@ TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_SPACE, TYPE_NORMAL, TYPE_QUOTE, TYPE_NORMAL, TYPE_SUBS, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, - TYPE_NORMAL, TYPE_CLOSE_PAREN, TYPE_NORMAL, TYPE_NORMAL, + TYPE_OPEN_PAREN, TYPE_CLOSE_PAREN, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_COMMAND_END, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, @@ -1396,19 +1397,32 @@ * underscore: in this case, there is no variable name and the token is * just "$". */ if (*src == '{') { + char ch; int braceCount = 0; src++; numBytes--; tokenPtr->type = TCL_TOKEN_TEXT; tokenPtr->start = src; tokenPtr->numComponents = 0; - while (numBytes && (*src != '}')) { + ch = *src; + while (numBytes && (braceCount>0 || ch != '}')) { + switch (ch) { + case '{': braceCount++; break; + case '}': braceCount--; break; + case '\\': + /* if 2 or more left, consume 2, else consume + just the \ and let it run into the end */ + if (numBytes > 1) { + src++; numBytes--; + } + } numBytes--; src++; + ch= *src; } if (numBytes == 0) { if (parsePtr->interp != NULL) { Tcl_SetObjResult(parsePtr->interp, Tcl_NewStringObj( "missing close-brace for variable name", -1)); @@ -1460,22 +1474,30 @@ * This is a reference to an array element. Call ParseTokens * recursively to parse the element name, since it could contain * any number of substitutions. */ - if (TCL_OK != ParseTokens(src+1, numBytes-1, TYPE_CLOSE_PAREN, + if (TCL_OK != ParseTokens(src+1, numBytes-1, TYPE_BAD_ARRAY_INDEX, TCL_SUBST_ALL, parsePtr)) { goto error; } - if ((parsePtr->term == src+numBytes) || (*parsePtr->term != ')')){ + if ((parsePtr->term == src+numBytes)){ if (parsePtr->interp != NULL) { Tcl_SetObjResult(parsePtr->interp, Tcl_NewStringObj( "missing )", -1)); } parsePtr->errorType = TCL_PARSE_MISSING_PAREN; parsePtr->term = src; parsePtr->incomplete = 1; + goto error; + } else if ((*parsePtr->term != ')')){ + if (parsePtr->interp != NULL) { + Tcl_SetObjResult(parsePtr->interp, Tcl_NewStringObj( + "invalid character in array index", -1)); + } + parsePtr->errorType = TCL_PARSE_SYNTAX; + parsePtr->term = src; goto error; } src = parsePtr->term + 1; } } Index: generic/tclParse.h ================================================================== --- generic/tclParse.h +++ generic/tclParse.h @@ -9,9 +9,11 @@ #define TYPE_SUBS 0x4 #define TYPE_QUOTE 0x8 #define TYPE_CLOSE_PAREN 0x10 #define TYPE_CLOSE_BRACK 0x20 #define TYPE_BRACE 0x40 +#define TYPE_OPEN_PAREN 0x80 +#define TYPE_BAD_ARRAY_INDEX (TYPE_OPEN_PAREN|TYPE_CLOSE_PAREN|TYPE_QUOTE|TYPE_BRACE) #define CHAR_TYPE(c) tclCharTypeTable[(unsigned char)(c)] MODULE_SCOPE const char tclCharTypeTable[]; Index: tests/parse.test ================================================================== --- tests/parse.test +++ tests/parse.test @@ -599,12 +599,12 @@ } {- {} 0 text {$} 0 abcd} test parse-12.6 {Tcl_ParseVarName procedure, braced variable name} testparser { testparser {${..[]b}cd} 0 } {- {${..[]b}cd} 1 word {${..[]b}cd} 3 variable {${..[]b}} 1 text {..[]b} 0 text cd 0 {}} test parse-12.7 {Tcl_ParseVarName procedure, braced variable name} testparser { - testparser "\$\{\{\} " 0 -} {- \$\{\{\}\ 1 word \$\{\{\} 2 variable \$\{\{\} 1 text \{ 0 {}} + testparser "\$\{\{\\\\\}\} " 0 +} {- {${{\\}} } 1 word {${{\\}}} 2 variable {${{\\}}} 1 text {{\\}} 0 {}} test parse-12.8 {Tcl_ParseVarName procedure, missing close brace} testparser { list [catch {testparser "$\{abc" 0} msg] $msg $::errorInfo } {1 {missing close-brace for variable name} missing\ close-brace\ for\ variable\ name\n\ \ \ \ (remainder\ of\ script:\ \"\{abc\")\n\ \ \ \ invoked\ from\ within\n\"testparser\ \"\$\\\{abc\"\ 0\"} test parse-12.9 {Tcl_ParseVarName procedure, missing close brace} testparsevarname { list [catch {testparsevarname {${bcd}} 4 0} msg] $msg @@ -795,11 +795,11 @@ test parse-15.16 {CommandComplete procedure} { info complete {a b "c $d() d"} } 1 test parse-15.17 {CommandComplete procedure} { info complete {a b "c $dd("} -} 0 +} 1 test parse-15.18 {CommandComplete procedure} { info complete {a b "c \"} } 0 test parse-15.19 {CommandComplete procedure} { info complete {a b "c [d e f]"} Index: tests/parseExpr.test ================================================================== --- tests/parseExpr.test +++ tests/parseExpr.test @@ -917,12 +917,12 @@ expr "123456789012345678901234567890*\"foobar\$\{abcdefghijklmnopqrstuvwxyz\"" } -returnCodes error -result "missing close-brace for variable name in expression \"...8901234567890*\"foobar\$\{abcdefghijklmnopqrstuv...\"" test parseExpr-21.44 {error message} -body { expr {123456789012345678901234567890*"foo$bar(abcdefghijklmnopqrstuvwxyz"} -} -returnCodes error -result {missing ) -in expression "...8901234567890*"foo$bar(abcdefghijklmnopqrstuv..."} +} -returnCodes error -result {invalid character in array index +in expression "...8901234567890*"foo$bar(abcdefghijklmnopqrstu..."} test parseExpr-21.45 {error message} -body { expr {123456789012345678901234567890*"foo$bar([{}abcdefghijklmnopqrstuvwxyz])"} } -returnCodes error -result {extra characters after close-brace in expression "...234567890*"foo$bar([{}abcdefghijklmnopqrstuv..."} test parseExpr-21.46 {error message} -body {