summaryrefslogtreecommitdiffstats
path: root/generic/tclParse.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclParse.c')
-rw-r--r--generic/tclParse.c3245
1 files changed, 1736 insertions, 1509 deletions
diff --git a/generic/tclParse.c b/generic/tclParse.c
index de62df8..ee0d4c4 100644
--- a/generic/tclParse.c
+++ b/generic/tclParse.c
@@ -1,64 +1,49 @@
-/*
+/*
* tclParse.c --
*
- * This file contains procedures that parse Tcl scripts. They
- * do so in a general-purpose fashion that can be used for many
- * different purposes, including compilation, direct execution,
- * code analysis, etc. This file also includes a few additional
- * procedures such as Tcl_EvalObjv, Tcl_Eval, and Tcl_EvalEx, which
- * allow scripts to be evaluated directly, without compiling.
+ * This file contains functions that parse Tcl scripts. They do so in a
+ * general-purpose fashion that can be used for many different purposes,
+ * including compilation, direct execution, code analysis, etc.
*
* Copyright (c) 1997 Sun Microsystems, Inc.
* Copyright (c) 1998-2000 Ajuba Solutions.
+ * Contributions from Don Porter, NIST, 2002. (not subject to US copyright)
*
- * See the file "license.terms" for information on usage and redistribution
- * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
- *
- * RCS: @(#) $Id: tclParse.c,v 1.15 2001/05/03 21:14:57 msofer Exp $
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
-
+
#include "tclInt.h"
-#include "tclPort.h"
+#include "tclParse.h"
+#include <assert.h>
/*
- * The following table provides parsing information about each possible
- * 8-bit character. The table is designed to be referenced with either
- * signed or unsigned characters, so it has 384 entries. The first 128
- * entries correspond to negative character values, the next 256 correspond
- * to positive character values. The last 128 entries are identical to the
- * first 128. The table is always indexed with a 128-byte offset (the 128th
- * entry corresponds to a character value of 0).
- *
- * The macro CHAR_TYPE is used to index into the table and return
- * information about its character argument. The following return
- * values are defined.
- *
- * TYPE_NORMAL - All characters that don't have special significance
- * to the Tcl parser.
- * TYPE_SPACE - The character is a whitespace character other
- * than newline.
+ * The following table provides parsing information about each possible 8-bit
+ * character. The table is designed to be referenced with either signed or
+ * unsigned characters, so it has 384 entries. The first 128 entries
+ * correspond to negative character values, the next 256 correspond to
+ * positive character values. The last 128 entries are identical to the first
+ * 128. The table is always indexed with a 128-byte offset (the 128th entry
+ * corresponds to a character value of 0).
+ *
+ * The macro CHAR_TYPE is used to index into the table and return information
+ * about its character argument. The following return values are defined.
+ *
+ * TYPE_NORMAL - All characters that don't have special significance to
+ * the Tcl parser.
+ * TYPE_SPACE - The character is a whitespace character other than
+ * newline.
* TYPE_COMMAND_END - Character is newline or semicolon.
- * TYPE_SUBS - Character begins a substitution or has other
- * special meaning in ParseTokens: backslash, dollar
- * sign, open bracket, or null.
+ * 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_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).
*/
-#define TYPE_NORMAL 0
-#define TYPE_SPACE 0x1
-#define TYPE_COMMAND_END 0x2
-#define TYPE_SUBS 0x4
-#define TYPE_QUOTE 0x8
-#define TYPE_CLOSE_PAREN 0x10
-#define TYPE_CLOSE_BRACK 0x20
-#define TYPE_BRACE 0x40
-
-#define CHAR_TYPE(c) (typeTable+128)[(int)(c)]
-
-char typeTable[] = {
+const char tclCharTypeTable[] = {
/*
* Negative character values, from -128 to -1:
*/
@@ -172,98 +157,121 @@ char typeTable[] = {
};
/*
- * Prototypes for local procedures defined in this file:
+ * Prototypes for local functions defined in this file:
+ */
+
+static inline int CommandComplete(const char *script, int numBytes);
+static int ParseComment(const char *src, int numBytes,
+ Tcl_Parse *parsePtr);
+static int ParseTokens(const char *src, int numBytes, int mask,
+ int flags, Tcl_Parse *parsePtr);
+static int ParseWhiteSpace(const char *src, int numBytes,
+ int *incompletePtr, char *typePtr);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclParseInit --
+ *
+ * Initialize the fields of a Tcl_Parse struct.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The Tcl_Parse struct pointed to by parsePtr gets initialized.
+ *
+ *----------------------------------------------------------------------
*/
-static int CommandComplete _ANSI_ARGS_((char *script,
- int length));
-static int ParseTokens _ANSI_ARGS_((char *src, int mask,
- Tcl_Parse *parsePtr));
-static int EvalObjv _ANSI_ARGS_((Tcl_Interp *interp, int objc,
- Tcl_Obj *CONST objv[], char *command, int length,
- int flags));
+void
+TclParseInit(
+ Tcl_Interp *interp, /* Interpreter to use for error reporting */
+ const char *start, /* Start of string to be parsed. */
+ int numBytes, /* Total number of bytes in string. If < 0,
+ * the script consists of all bytes up to the
+ * first null character. */
+ Tcl_Parse *parsePtr) /* Points to struct to initialize */
+{
+ parsePtr->numWords = 0;
+ parsePtr->tokenPtr = parsePtr->staticTokens;
+ parsePtr->numTokens = 0;
+ parsePtr->tokensAvailable = NUM_STATIC_TOKENS;
+ parsePtr->string = start;
+ parsePtr->end = start + numBytes;
+ parsePtr->term = parsePtr->end;
+ parsePtr->interp = interp;
+ parsePtr->incomplete = 0;
+ parsePtr->errorType = TCL_PARSE_SUCCESS;
+}
/*
*----------------------------------------------------------------------
*
* Tcl_ParseCommand --
*
- * Given a string, this procedure parses the first Tcl command
- * in the string and returns information about the structure of
- * the command.
+ * Given a string, this function parses the first Tcl command in the
+ * string and returns information about the structure of the command.
*
* Results:
- * The return value is TCL_OK if the command was parsed
- * successfully and TCL_ERROR otherwise. If an error occurs
- * and interp isn't NULL then an error message is left in
- * its result. On a successful return, parsePtr is filled in
- * with information about the command that was parsed.
+ * The return value is TCL_OK if the command was parsed successfully and
+ * TCL_ERROR otherwise. If an error occurs and interp isn't NULL then an
+ * error message is left in its result. On a successful return, parsePtr
+ * is filled in with information about the command that was parsed.
*
* Side effects:
- * If there is insufficient space in parsePtr to hold all the
- * information about the command, then additional space is
- * malloc-ed. If the procedure returns TCL_OK then the caller must
- * eventually invoke Tcl_FreeParse to release any additional space
- * that was allocated.
+ * If there is insufficient space in parsePtr to hold all the information
+ * about the command, then additional space is malloc-ed. If the function
+ * returns TCL_OK then the caller must eventually invoke Tcl_FreeParse to
+ * release any additional space that was allocated.
*
*----------------------------------------------------------------------
*/
int
-Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
- Tcl_Interp *interp; /* Interpreter to use for error reporting;
- * if NULL, then no error message is
- * provided. */
- char *string; /* First character of string containing
- * one or more Tcl commands. The string
- * must be in writable memory and must
- * have one additional byte of space at
- * string[length] where we can
- * temporarily store a 0 sentinel
- * character. */
- int numBytes; /* Total number of bytes in string. If < 0,
- * the script consists of all bytes up to
- * the first null character. */
- int nested; /* Non-zero means this is a nested command:
- * close bracket should be considered
- * a command terminator. If zero, then close
+Tcl_ParseCommand(
+ Tcl_Interp *interp, /* Interpreter to use for error reporting; if
+ * NULL, then no error message is provided. */
+ const char *start, /* First character of string containing one or
+ * more Tcl commands. */
+ register int numBytes, /* Total number of bytes in string. If < 0,
+ * the script consists of all bytes up to the
+ * first null character. */
+ int nested, /* Non-zero means this is a nested command:
+ * close bracket should be considered a
+ * command terminator. If zero, then close
* bracket has no special meaning. */
- register Tcl_Parse *parsePtr;
- /* Structure to fill in with information
- * about the parsed command; any previous
- * information in the structure is
- * ignored. */
+ register Tcl_Parse *parsePtr)
+ /* Structure to fill in with information about
+ * the parsed command; any previous
+ * information in the structure is ignored. */
{
- register char *src; /* Points to current character
- * in the command. */
- int type; /* Result returned by CHAR_TYPE(*src). */
+ register const char *src; /* Points to current character in the
+ * command. */
+ char type; /* Result returned by CHAR_TYPE(*src). */
Tcl_Token *tokenPtr; /* Pointer to token being filled in. */
int wordIndex; /* Index of word token for current word. */
- char utfBytes[TCL_UTF_MAX]; /* Holds result of backslash substitution. */
- int terminators; /* CHAR_TYPE bits that indicate the end
- * of a command. */
- char *termPtr; /* Set by Tcl_ParseBraces/QuotedString to
+ int terminators; /* CHAR_TYPE bits that indicate the end of a
+ * command. */
+ const char *termPtr; /* Set by Tcl_ParseBraces/QuotedString to
* point to char after terminating one. */
- int length, savedChar;
-
+ int scanned;
+ if ((start == NULL) && (numBytes != 0)) {
+ if (interp != NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "can't parse a NULL pointer", -1));
+ }
+ return TCL_ERROR;
+ }
if (numBytes < 0) {
- numBytes = (string? strlen(string) : 0);
+ numBytes = strlen(start);
}
+ TclParseInit(interp, start, numBytes, parsePtr);
parsePtr->commentStart = NULL;
parsePtr->commentSize = 0;
parsePtr->commandStart = NULL;
parsePtr->commandSize = 0;
- parsePtr->numWords = 0;
- parsePtr->tokenPtr = parsePtr->staticTokens;
- parsePtr->numTokens = 0;
- parsePtr->tokensAvailable = NUM_STATIC_TOKENS;
- parsePtr->string = string;
- parsePtr->end = string + numBytes;
- parsePtr->term = parsePtr->end;
- parsePtr->interp = interp;
- parsePtr->incomplete = 0;
- parsePtr->errorType = TCL_PARSE_SUCCESS;
if (nested != 0) {
terminators = TYPE_COMMAND_END | TYPE_CLOSE_BRACK;
} else {
@@ -271,83 +279,33 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
}
/*
- * Temporarily overwrite the character just after the end of the
- * string with a 0 byte. This acts as a sentinel and reduces the
- * number of places where we have to check for the end of the
- * input string. The original value of the byte is restored at
- * the end of the parse.
- */
-
- savedChar = string[numBytes];
- if (savedChar != 0) {
- string[numBytes] = 0;
- }
-
- /*
* Parse any leading space and comments before the first word of the
* command.
*/
- src = string;
- while (1) {
- while ((CHAR_TYPE(*src) == TYPE_SPACE) || (*src == '\n')) {
- src++;
- }
- if ((*src == '\\') && (src[1] == '\n')) {
- /*
- * Skip backslash-newline sequence: it should be treated
- * just like white space.
- */
-
- if ((src + 2) == parsePtr->end) {
- parsePtr->incomplete = 1;
- }
- src += 2;
- continue;
- }
- if (*src != '#') {
- break;
- }
- if (parsePtr->commentStart == NULL) {
- parsePtr->commentStart = src;
- }
- while (1) {
- if (src == parsePtr->end) {
- if (nested) {
- parsePtr->incomplete = nested;
- }
- parsePtr->commentSize = src - parsePtr->commentStart;
- break;
- } else if (*src == '\\') {
- if ((src[1] == '\n') && ((src + 2) == parsePtr->end)) {
- parsePtr->incomplete = 1;
- }
- Tcl_UtfBackslash(src, &length, utfBytes);
- src += length;
- } else if (*src == '\n') {
- src++;
- parsePtr->commentSize = src - parsePtr->commentStart;
- break;
- } else {
- src++;
- }
+ scanned = ParseComment(start, numBytes, parsePtr);
+ src = (start + scanned);
+ numBytes -= scanned;
+ if (numBytes == 0) {
+ if (nested) {
+ parsePtr->incomplete = nested;
}
}
/*
- * The following loop parses the words of the command, one word
- * in each iteration through the loop.
+ * The following loop parses the words of the command, one word in each
+ * iteration through the loop.
*/
parsePtr->commandStart = src;
while (1) {
+ int expandWord = 0;
+
/*
* Create the token for the word.
*/
- if (parsePtr->numTokens == parsePtr->tokensAvailable) {
- TclExpandTokenArray(parsePtr);
- }
+ TclGrowParseTokenArray(parsePtr, 1);
wordIndex = parsePtr->numTokens;
tokenPtr = &parsePtr->tokenPtr[wordIndex];
tokenPtr->type = TCL_TOKEN_WORD;
@@ -357,19 +315,11 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
* sequence: it should be treated just like white space.
*/
- while (1) {
- type = CHAR_TYPE(*src);
- if (type == TYPE_SPACE) {
- src++;
- continue;
- } else if ((*src == '\\') && (src[1] == '\n')) {
- if ((src + 2) == parsePtr->end) {
- parsePtr->incomplete = 1;
- }
- Tcl_UtfBackslash(src, &length, utfBytes);
- src += length;
- continue;
- }
+ scanned = ParseWhiteSpace(src,numBytes, &parsePtr->incomplete, &type);
+ src += scanned;
+ numBytes -= scanned;
+ if (numBytes == 0) {
+ parsePtr->term = src;
break;
}
if ((type & terminators) != 0) {
@@ -377,103 +327,257 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
src++;
break;
}
- if (src == parsePtr->end) {
- break;
- }
tokenPtr->start = src;
parsePtr->numTokens++;
parsePtr->numWords++;
/*
- * At this point the word can have one of three forms: something
- * enclosed in quotes, something enclosed in braces, or an
- * unquoted word (anything else).
+ * At this point the word can have one of four forms: something
+ * enclosed in quotes, something enclosed in braces, and expanding
+ * word, or an unquoted word (anything else).
*/
+ parseWord:
if (*src == '"') {
- if (Tcl_ParseQuotedString(interp, src, (parsePtr->end - src),
- parsePtr, 1, &termPtr) != TCL_OK) {
+ if (Tcl_ParseQuotedString(interp, src, numBytes, parsePtr, 1,
+ &termPtr) != TCL_OK) {
goto error;
}
src = termPtr;
+ numBytes = parsePtr->end - src;
} else if (*src == '{') {
- if (Tcl_ParseBraces(interp, src, (parsePtr->end - src),
- parsePtr, 1, &termPtr) != TCL_OK) {
+ int expIdx = wordIndex + 1;
+ Tcl_Token *expPtr;
+
+ if (Tcl_ParseBraces(interp, src, numBytes, parsePtr, 1,
+ &termPtr) != TCL_OK) {
goto error;
}
src = termPtr;
+ numBytes = parsePtr->end - src;
+
+ /*
+ * Check whether the braces contained the word expansion prefix
+ * {*}
+ */
+
+ expPtr = &parsePtr->tokenPtr[expIdx];
+ if ((0 == expandWord)
+ /* Haven't seen prefix already */
+ && (1 == parsePtr->numTokens - expIdx)
+ /* Only one token */
+ && (((1 == (size_t) expPtr->size)
+ /* Same length as prefix */
+ && (expPtr->start[0] == '*')))
+ /* Is the prefix */
+ && (numBytes > 0) && (0 == ParseWhiteSpace(termPtr,
+ numBytes, &parsePtr->incomplete, &type))
+ && (type != TYPE_COMMAND_END)
+ /* Non-whitespace follows */) {
+ expandWord = 1;
+ parsePtr->numTokens--;
+ goto parseWord;
+ }
} else {
/*
- * This is an unquoted word. Call ParseTokens and let it do
- * all of the work.
+ * This is an unquoted word. Call ParseTokens and let it do all of
+ * the work.
*/
- if (ParseTokens(src, TYPE_SPACE|terminators,
- parsePtr) != TCL_OK) {
+ if (ParseTokens(src, numBytes, TYPE_SPACE|terminators,
+ TCL_SUBST_ALL, parsePtr) != TCL_OK) {
goto error;
}
src = parsePtr->term;
+ numBytes = parsePtr->end - src;
}
/*
- * Finish filling in the token for the word and check for the
- * special case of a word consisting of a single range of
- * literal text.
+ * Finish filling in the token for the word and check for the special
+ * case of a word consisting of a single range of literal text.
*/
tokenPtr = &parsePtr->tokenPtr[wordIndex];
tokenPtr->size = src - tokenPtr->start;
tokenPtr->numComponents = parsePtr->numTokens - (wordIndex + 1);
- if ((tokenPtr->numComponents == 1)
+ if (expandWord) {
+ int i, isLiteral = 1;
+
+ /*
+ * When a command includes a word that is an expanded literal; for
+ * example, {*}{1 2 3}, the parser performs that expansion
+ * immediately, generating several TCL_TOKEN_SIMPLE_WORDs instead
+ * of a single TCL_TOKEN_EXPAND_WORD that the Tcl_ParseCommand()
+ * caller might have to expand. This notably makes it simpler for
+ * those callers that wish to track line endings, such as those
+ * that implement key parts of TIP 280.
+ *
+ * First check whether the thing to be expanded is a literal,
+ * in the sense of being composed entirely of TCL_TOKEN_TEXT
+ * tokens.
+ */
+
+ for (i = 1; i <= tokenPtr->numComponents; i++) {
+ if (tokenPtr[i].type != TCL_TOKEN_TEXT) {
+ isLiteral = 0;
+ break;
+ }
+ }
+
+ if (isLiteral) {
+ int elemCount = 0, code = TCL_OK, literal = 1;
+ const char *nextElem, *listEnd, *elemStart;
+
+ /*
+ * The word to be expanded is a literal, so determine the
+ * boundaries of the literal string to be treated as a list
+ * and expanded. That literal string starts at
+ * tokenPtr[1].start, and includes all bytes up to, but not
+ * including (tokenPtr[tokenPtr->numComponents].start +
+ * tokenPtr[tokenPtr->numComponents].size)
+ */
+
+ listEnd = (tokenPtr[tokenPtr->numComponents].start +
+ tokenPtr[tokenPtr->numComponents].size);
+ nextElem = tokenPtr[1].start;
+
+ /*
+ * Step through the literal string, parsing and counting list
+ * elements.
+ */
+
+ while (nextElem < listEnd) {
+ int size;
+
+ code = TclFindElement(NULL, nextElem, listEnd - nextElem,
+ &elemStart, &nextElem, &size, &literal);
+ if ((code != TCL_OK) || !literal) {
+ break;
+ }
+ if (elemStart < listEnd) {
+ elemCount++;
+ }
+ }
+
+ if ((code != TCL_OK) || !literal) {
+ /*
+ * Some list element could not be parsed, or is not
+ * present as a literal substring of the script. The
+ * compiler cannot handle list elements that get generated
+ * by a call to TclCopyAndCollapse(). Defer the
+ * handling of this to compile/eval time, where code is
+ * already in place to report the "attempt to expand a
+ * non-list" error or expand lists that require
+ * substitution.
+ */
+
+ tokenPtr->type = TCL_TOKEN_EXPAND_WORD;
+ } else if (elemCount == 0) {
+ /*
+ * We are expanding a literal empty list. This means that
+ * the expanding word completely disappears, leaving no
+ * word generated this pass through the loop. Adjust
+ * accounting appropriately.
+ */
+
+ parsePtr->numWords--;
+ parsePtr->numTokens = wordIndex;
+ } else {
+ /*
+ * Recalculate the number of Tcl_Tokens needed to store
+ * tokens representing the expanded list.
+ */
+
+ const char *listStart;
+ int growthNeeded = wordIndex + 2*elemCount
+ - parsePtr->numTokens;
+
+ parsePtr->numWords += elemCount - 1;
+ if (growthNeeded > 0) {
+ TclGrowParseTokenArray(parsePtr, growthNeeded);
+ tokenPtr = &parsePtr->tokenPtr[wordIndex];
+ }
+ parsePtr->numTokens = wordIndex + 2*elemCount;
+
+ /*
+ * Generate a TCL_TOKEN_SIMPLE_WORD token sequence for
+ * each element of the literal list we are expanding in
+ * place. Take care with the start and size fields of each
+ * token so they point to the right literal characters in
+ * the original script to represent the right expanded
+ * word value.
+ */
+
+ listStart = nextElem = tokenPtr[1].start;
+ while (nextElem < listEnd) {
+ int quoted;
+
+ tokenPtr->type = TCL_TOKEN_SIMPLE_WORD;
+ tokenPtr->numComponents = 1;
+
+ tokenPtr++;
+ tokenPtr->type = TCL_TOKEN_TEXT;
+ tokenPtr->numComponents = 0;
+ TclFindElement(NULL, nextElem, listEnd - nextElem,
+ &(tokenPtr->start), &nextElem,
+ &(tokenPtr->size), NULL);
+
+ quoted = (tokenPtr->start[-1] == '{'
+ || tokenPtr->start[-1] == '"')
+ && tokenPtr->start > listStart;
+ tokenPtr[-1].start = tokenPtr->start - quoted;
+ tokenPtr[-1].size = tokenPtr->start + tokenPtr->size
+ - tokenPtr[-1].start + quoted;
+
+ tokenPtr++;
+ }
+ }
+ } else {
+ /*
+ * The word to be expanded is not a literal, so defer
+ * expansion to compile/eval time by marking with a
+ * TCL_TOKEN_EXPAND_WORD token.
+ */
+
+ tokenPtr->type = TCL_TOKEN_EXPAND_WORD;
+ }
+ } else if ((tokenPtr->numComponents == 1)
&& (tokenPtr[1].type == TCL_TOKEN_TEXT)) {
tokenPtr->type = TCL_TOKEN_SIMPLE_WORD;
}
/*
- * Do two additional checks: (a) make sure we're really at the
- * end of a word (there might have been garbage left after a
- * quoted or braced word), and (b) check for the end of the
- * command.
+ * Do two additional checks: (a) make sure we're really at the end of
+ * a word (there might have been garbage left after a quoted or braced
+ * word), and (b) check for the end of the command.
*/
- type = CHAR_TYPE(*src);
- if (type == TYPE_SPACE) {
- src++;
+ scanned = ParseWhiteSpace(src,numBytes, &parsePtr->incomplete, &type);
+ if (scanned) {
+ src += scanned;
+ numBytes -= scanned;
continue;
- } else {
- /*
- * Backslash-newline (and any following white space) must be
- * treated as if it were a space character.
- */
-
- if ((*src == '\\') && (src[1] == '\n')) {
- if ((src + 2) == parsePtr->end) {
- parsePtr->incomplete = 1;
- }
- Tcl_UtfBackslash(src, &length, utfBytes);
- src += length;
- continue;
- }
}
- if ((type & terminators) != 0) {
+ if (numBytes == 0) {
parsePtr->term = src;
- src++;
break;
}
- if (src == parsePtr->end) {
+ if ((type & terminators) != 0) {
+ parsePtr->term = src;
+ src++;
break;
}
- if (src[-1] == '"') {
+ if (src[-1] == '"') {
if (interp != NULL) {
- Tcl_SetResult(interp, "extra characters after close-quote",
- TCL_STATIC);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "extra characters after close-quote", -1));
}
parsePtr->errorType = TCL_PARSE_QUOTE_EXTRA;
} else {
if (interp != NULL) {
- Tcl_SetResult(interp, "extra characters after close-brace",
- TCL_STATIC);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "extra characters after close-brace", -1));
}
parsePtr->errorType = TCL_PARSE_BRACE_EXTRA;
}
@@ -481,44 +585,25 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
goto error;
}
-
parsePtr->commandSize = src - parsePtr->commandStart;
- if (savedChar != 0) {
- string[numBytes] = (char) savedChar;
- }
return TCL_OK;
- error:
- if (savedChar != 0) {
- string[numBytes] = (char) savedChar;
- }
+ error:
Tcl_FreeParse(parsePtr);
- if (parsePtr->commandStart == NULL) {
- parsePtr->commandStart = string;
- }
- parsePtr->commandSize = parsePtr->term - parsePtr->commandStart;
+ parsePtr->commandSize = parsePtr->end - parsePtr->commandStart;
return TCL_ERROR;
}
/*
*----------------------------------------------------------------------
*
- * ParseTokens --
+ * TclIsSpaceProc --
*
- * This procedure forms the heart of the Tcl parser. It parses one
- * or more tokens from a string, up to a termination point
- * specified by the caller. This procedure is used to parse
- * unquoted command words (those not in quotes or braces), words in
- * quotes, and array indices for variables.
+ * Report whether byte is in the set of whitespace characters used by
+ * Tcl to separate words in scripts or elements in lists.
*
* Results:
- * Tokens are added to parsePtr and parsePtr->term is filled in
- * with the address of the character that terminated the parse (the
- * first one whose CHAR_TYPE matched mask or the character at
- * parsePtr->end). The return value is TCL_OK if the parse
- * completed successfully and TCL_ERROR otherwise. If a parse
- * error occurs and parsePtr->interp isn't NULL, then an error
- * message is left in the interpreter's result.
+ * Returns 1, if byte is in the set, 0 otherwise.
*
* Side effects:
* None.
@@ -526,1071 +611,694 @@ Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
*----------------------------------------------------------------------
*/
-static int
-ParseTokens(src, mask, parsePtr)
- register char *src; /* First character to parse. */
- int mask; /* Specifies when to stop parsing. The
- * parse stops at the first unquoted
- * character whose CHAR_TYPE contains
- * any of the bits in mask. */
- Tcl_Parse *parsePtr; /* Information about parse in progress.
- * Updated with additional tokens and
- * termination information. */
+int
+TclIsSpaceProc(
+ char byte)
{
- int type, originalTokens, varToken;
- char utfBytes[TCL_UTF_MAX];
- Tcl_Token *tokenPtr;
- Tcl_Parse nested;
-
- /*
- * Each iteration through the following loop adds one token of
- * type TCL_TOKEN_TEXT, TCL_TOKEN_BS, TCL_TOKEN_COMMAND, or
- * TCL_TOKEN_VARIABLE to parsePtr. For TCL_TOKEN_VARIABLE tokens,
- * additional tokens are added for the parsed variable name.
- */
-
- originalTokens = parsePtr->numTokens;
- while (1) {
- if (parsePtr->numTokens == parsePtr->tokensAvailable) {
- TclExpandTokenArray(parsePtr);
- }
- tokenPtr = &parsePtr->tokenPtr[parsePtr->numTokens];
- tokenPtr->start = src;
- tokenPtr->numComponents = 0;
-
- type = CHAR_TYPE(*src);
- if (type & mask) {
- break;
- }
-
- if ((type & TYPE_SUBS) == 0) {
- /*
- * This is a simple range of characters. Scan to find the end
- * of the range.
- */
-
- while (1) {
- src++;
- if (CHAR_TYPE(*src) & (mask | TYPE_SUBS)) {
- break;
- }
- }
- tokenPtr->type = TCL_TOKEN_TEXT;
- tokenPtr->size = src - tokenPtr->start;
- parsePtr->numTokens++;
- } else if (*src == '$') {
- /*
- * This is a variable reference. Call Tcl_ParseVarName to do
- * all the dirty work of parsing the name.
- */
-
- varToken = parsePtr->numTokens;
- if (Tcl_ParseVarName(parsePtr->interp, src, parsePtr->end - src,
- parsePtr, 1) != TCL_OK) {
- return TCL_ERROR;
- }
- src += parsePtr->tokenPtr[varToken].size;
- } else if (*src == '[') {
- /*
- * Command substitution. Call Tcl_ParseCommand recursively
- * (and repeatedly) to parse the nested command(s), then
- * throw away the parse information.
- */
-
- src++;
- while (1) {
- if (Tcl_ParseCommand(parsePtr->interp, src,
- parsePtr->end - src, 1, &nested) != TCL_OK) {
- parsePtr->errorType = nested.errorType;
- parsePtr->term = nested.term;
- parsePtr->incomplete = nested.incomplete;
- return TCL_ERROR;
- }
- src = nested.commandStart + nested.commandSize;
- if (nested.tokenPtr != nested.staticTokens) {
- ckfree((char *) nested.tokenPtr);
- }
- if ((*nested.term == ']') && !nested.incomplete) {
- break;
- }
- if (src == parsePtr->end) {
- if (parsePtr->interp != NULL) {
- Tcl_SetResult(parsePtr->interp,
- "missing close-bracket", TCL_STATIC);
- }
- parsePtr->errorType = TCL_PARSE_MISSING_BRACKET;
- parsePtr->term = tokenPtr->start;
- parsePtr->incomplete = 1;
- return TCL_ERROR;
- }
- }
- tokenPtr->type = TCL_TOKEN_COMMAND;
- tokenPtr->size = src - tokenPtr->start;
- parsePtr->numTokens++;
- } else if (*src == '\\') {
- /*
- * Backslash substitution.
- */
-
- if (src[1] == '\n') {
- if ((src + 2) == parsePtr->end) {
- parsePtr->incomplete = 1;
- }
-
- /*
- * Note: backslash-newline is special in that it is
- * treated the same as a space character would be. This
- * means that it could terminate the token.
- */
-
- if (mask & TYPE_SPACE) {
- break;
- }
- }
- tokenPtr->type = TCL_TOKEN_BS;
- Tcl_UtfBackslash(src, &tokenPtr->size, utfBytes);
- parsePtr->numTokens++;
- src += tokenPtr->size;
- } else if (*src == 0) {
- /*
- * We encountered a null character. If it is the null
- * character at the end of the string, then return.
- * Otherwise generate a text token for the single
- * character.
- */
-
- if (src == parsePtr->end) {
- break;
- }
- tokenPtr->type = TCL_TOKEN_TEXT;
- tokenPtr->size = 1;
- parsePtr->numTokens++;
- src++;
- } else {
- panic("ParseTokens encountered unknown character");
- }
- }
- if (parsePtr->numTokens == originalTokens) {
- /*
- * There was nothing in this range of text. Add an empty token
- * for the empty range, so that there is always at least one
- * token added.
- */
-
- tokenPtr->type = TCL_TOKEN_TEXT;
- tokenPtr->size = 0;
- parsePtr->numTokens++;
- }
- parsePtr->term = src;
- return TCL_OK;
+ return CHAR_TYPE(byte) & (TYPE_SPACE) || byte == '\n';
}
/*
*----------------------------------------------------------------------
*
- * Tcl_FreeParse --
+ * ParseWhiteSpace --
*
- * This procedure is invoked to free any dynamic storage that may
- * have been allocated by a previous call to Tcl_ParseCommand.
+ * Scans up to numBytes bytes starting at src, consuming white space
+ * between words as defined by Tcl's parsing rules.
*
* Results:
- * None.
+ * Returns the number of bytes recognized as white space. Records at
+ * parsePtr, information about the parse. Records at typePtr the
+ * character type of the non-whitespace character that terminated the
+ * scan.
*
* Side effects:
- * If there is any dynamically allocated memory in *parsePtr,
- * it is freed.
+ * None.
*
*----------------------------------------------------------------------
*/
-void
-Tcl_FreeParse(parsePtr)
- Tcl_Parse *parsePtr; /* Structure that was filled in by a
- * previous call to Tcl_ParseCommand. */
+static int
+ParseWhiteSpace(
+ const char *src, /* First character to parse. */
+ register int numBytes, /* Max number of bytes to scan. */
+ int *incompletePtr, /* Set this boolean memory to true if parsing
+ * indicates an incomplete command. */
+ char *typePtr) /* Points to location to store character type
+ * of character that ends run of whitespace */
{
- if (parsePtr->tokenPtr != parsePtr->staticTokens) {
- ckfree((char *) parsePtr->tokenPtr);
- parsePtr->tokenPtr = parsePtr->staticTokens;
+ register char type = TYPE_NORMAL;
+ register const char *p = src;
+
+ while (1) {
+ while (numBytes && ((type = CHAR_TYPE(*p)) & TYPE_SPACE)) {
+ numBytes--;
+ p++;
+ }
+ if (numBytes && (type & TYPE_SUBS)) {
+ if (*p != '\\') {
+ break;
+ }
+ if (--numBytes == 0) {
+ break;
+ }
+ if (p[1] != '\n') {
+ break;
+ }
+ p += 2;
+ if (--numBytes == 0) {
+ *incompletePtr = 1;
+ break;
+ }
+ continue;
+ }
+ break;
}
+ *typePtr = type;
+ return (p - src);
}
/*
*----------------------------------------------------------------------
*
- * TclExpandTokenArray --
+ * TclParseAllWhiteSpace --
*
- * This procedure is invoked when the current space for tokens in
- * a Tcl_Parse structure fills up; it allocates memory to grow the
- * token array
+ * Scans up to numBytes bytes starting at src, consuming all white space
+ * including the command-terminating newline characters.
*
* Results:
- * None.
- *
- * Side effects:
- * Memory is allocated for a new larger token array; the memory
- * for the old array is freed, if it had been dynamically allocated.
+ * Returns the number of bytes recognized as white space.
*
*----------------------------------------------------------------------
*/
-void
-TclExpandTokenArray(parsePtr)
- Tcl_Parse *parsePtr; /* Parse structure whose token space
- * has overflowed. */
+int
+TclParseAllWhiteSpace(
+ const char *src, /* First character to parse. */
+ int numBytes) /* Max number of byes to scan */
{
- int newCount;
- Tcl_Token *newPtr;
+ int dummy;
+ char type;
+ const char *p = src;
- newCount = parsePtr->tokensAvailable*2;
- newPtr = (Tcl_Token *) ckalloc((unsigned) (newCount * sizeof(Tcl_Token)));
- memcpy((VOID *) newPtr, (VOID *) parsePtr->tokenPtr,
- (size_t) (parsePtr->tokensAvailable * sizeof(Tcl_Token)));
- if (parsePtr->tokenPtr != parsePtr->staticTokens) {
- ckfree((char *) parsePtr->tokenPtr);
- }
- parsePtr->tokenPtr = newPtr;
- parsePtr->tokensAvailable = newCount;
+ do {
+ int scanned = ParseWhiteSpace(p, numBytes, &dummy, &type);
+
+ p += scanned;
+ numBytes -= scanned;
+ } while (numBytes && (*p == '\n') && (p++, --numBytes));
+ return (p-src);
}
/*
*----------------------------------------------------------------------
*
- * EvalObjv --
+ * TclParseHex --
*
- * This procedure evaluates a Tcl command that has already been
- * parsed into words, with one Tcl_Obj holding each word.
+ * Scans a hexadecimal number as a Tcl_UniChar value (e.g., for parsing
+ * \x and \u escape sequences). At most numBytes bytes are scanned.
*
* Results:
- * The return value is a standard Tcl completion code such as
- * TCL_OK or TCL_ERROR. A result or error message is left in
- * interp's result. If an error occurs, this procedure does
- * NOT add any information to the errorInfo variable.
+ * The numeric value is stored in *resultPtr. Returns the number of bytes
+ * consumed.
*
- * Side effects:
- * Depends on the command.
+ * Notes:
+ * Relies on the following properties of the ASCII character set, with
+ * which UTF-8 is compatible:
+ *
+ * The digits '0' .. '9' and the letters 'A' .. 'Z' and 'a' .. 'z' occupy
+ * consecutive code points, and '0' < 'A' < 'a'.
*
*----------------------------------------------------------------------
*/
-static int
-EvalObjv(interp, objc, objv, command, length, flags)
- Tcl_Interp *interp; /* Interpreter in which to evaluate the
- * command. Also used for error
- * reporting. */
- int objc; /* Number of words in command. */
- Tcl_Obj *CONST objv[]; /* An array of pointers to objects that are
- * the words that make up the command. */
- char *command; /* Points to the beginning of the string
- * representation of the command; this
- * is used for traces. If the string
- * representation of the command is
- * unknown, an empty string should be
- * supplied. */
- int length; /* Number of bytes in command; if -1, all
- * characters up to the first null byte are
- * used. */
- int flags; /* Collection of OR-ed bits that control
- * the evaluation of the script. Only
- * TCL_EVAL_GLOBAL is currently
- * supported. */
-
+int
+TclParseHex(
+ const char *src, /* First character to parse. */
+ int numBytes, /* Max number of byes to scan */
+ int *resultPtr) /* Points to storage provided by caller where
+ * the character resulting from the
+ * conversion is to be written. */
{
- Command *cmdPtr;
- Interp *iPtr = (Interp *) interp;
- Tcl_Obj **newObjv;
- int i, code;
- Trace *tracePtr, *nextPtr;
- char **argv, *commandCopy;
- CallFrame *savedVarFramePtr; /* Saves old copy of iPtr->varFramePtr
- * in case TCL_EVAL_GLOBAL was set. */
-
- Tcl_ResetResult(interp);
- if (objc == 0) {
- return TCL_OK;
- }
-
- /*
- * If the interpreter was deleted, return an error.
- */
-
- if (iPtr->flags & DELETED) {
- Tcl_AppendToObj(Tcl_GetObjResult(interp),
- "attempt to call eval in deleted interpreter", -1);
- Tcl_SetErrorCode(interp, "CORE", "IDELETE",
- "attempt to call eval in deleted interpreter",
- (char *) NULL);
- return TCL_ERROR;
- }
-
- /*
- * Check depth of nested calls to Tcl_Eval: if this gets too large,
- * it's probably because of an infinite loop somewhere.
- */
-
- if (iPtr->numLevels >= iPtr->maxNestingDepth) {
- iPtr->result = "too many nested calls to Tcl_Eval (infinite loop?)";
- return TCL_ERROR;
- }
- iPtr->numLevels++;
-
- /*
- * On the Mac, we will never reach the default recursion limit before
- * blowing the stack. So we need to do a check here.
- */
-
- if (TclpCheckStackSpace() == 0) {
- /*NOTREACHED*/
- iPtr->numLevels--;
- iPtr->result = "too many nested calls to Tcl_Eval (infinite loop?)";
- return TCL_ERROR;
- }
-
- /*
- * Find the procedure to execute this command. If there isn't one,
- * then see if there is a command "unknown". If so, create a new
- * word array with "unknown" as the first word and the original
- * command words as arguments. Then call ourselves recursively
- * to execute it.
- */
-
- cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, objv[0]);
- if (cmdPtr == NULL) {
- newObjv = (Tcl_Obj **) ckalloc((unsigned)
- ((objc + 1) * sizeof (Tcl_Obj *)));
- for (i = objc-1; i >= 0; i--) {
- newObjv[i+1] = objv[i];
- }
- newObjv[0] = Tcl_NewStringObj("::unknown", -1);
- Tcl_IncrRefCount(newObjv[0]);
- cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, newObjv[0]);
- if (cmdPtr == NULL) {
- Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
- "invalid command name \"", Tcl_GetString(objv[0]), "\"",
- (char *) NULL);
- code = TCL_ERROR;
- } else {
- code = EvalObjv(interp, objc+1, newObjv, command, length, 0);
- }
- Tcl_DecrRefCount(newObjv[0]);
- ckfree((char *) newObjv);
- goto done;
- }
-
- /*
- * Call trace procedures if needed.
- */
+ int result = 0;
+ register const char *p = src;
- argv = NULL;
- commandCopy = command;
+ while (numBytes--) {
+ unsigned char digit = UCHAR(*p);
- for (tracePtr = iPtr->tracePtr; tracePtr != NULL; tracePtr = nextPtr) {
- nextPtr = tracePtr->nextPtr;
- if (iPtr->numLevels > tracePtr->level) {
- continue;
+ if (!isxdigit(digit) || (result > 0x10fff)) {
+ break;
}
- /*
- * This is a bit messy because we have to emulate the old trace
- * interface, which uses strings for everything.
- */
+ p++;
+ result <<= 4;
- if (argv == NULL) {
- argv = (char **) ckalloc((unsigned) (objc + 1) * sizeof(char *));
- for (i = 0; i < objc; i++) {
- argv[i] = Tcl_GetString(objv[i]);
- }
- argv[objc] = 0;
-
- if (length < 0) {
- length = strlen(command);
- } else if ((size_t)length < strlen(command)) {
- commandCopy = (char *) ckalloc((unsigned) (length + 1));
- strncpy(commandCopy, command, (size_t) length);
- commandCopy[length] = 0;
- }
+ if (digit >= 'a') {
+ result |= (10 + digit - 'a');
+ } else if (digit >= 'A') {
+ result |= (10 + digit - 'A');
+ } else {
+ result |= (digit - '0');
}
- (*tracePtr->proc)(tracePtr->clientData, interp, iPtr->numLevels,
- commandCopy, cmdPtr->proc, cmdPtr->clientData,
- objc, argv);
- }
- if (argv != NULL) {
- ckfree((char *) argv);
- }
- if (commandCopy != command) {
- ckfree((char *) commandCopy);
- }
-
- /*
- * Finally, invoke the command's Tcl_ObjCmdProc.
- */
-
- iPtr->cmdCount++;
- savedVarFramePtr = iPtr->varFramePtr;
- if (flags & TCL_EVAL_GLOBAL) {
- iPtr->varFramePtr = NULL;
- }
- code = (*cmdPtr->objProc)(cmdPtr->objClientData, interp, objc, objv);
- iPtr->varFramePtr = savedVarFramePtr;
- if (Tcl_AsyncReady()) {
- code = Tcl_AsyncInvoke(interp, code);
}
- /*
- * If the interpreter has a non-empty string result, the result
- * object is either empty or stale because some procedure set
- * interp->result directly. If so, move the string result to the
- * result object, then reset the string result.
- */
-
- if (*(iPtr->result) != 0) {
- (void) Tcl_GetObjResult(interp);
- }
-
- done:
- iPtr->numLevels--;
- return code;
+ *resultPtr = result;
+ return (p - src);
}
/*
*----------------------------------------------------------------------
*
- * Tcl_EvalObjv --
+ * TclParseBackslash --
*
- * This procedure evaluates a Tcl command that has already been
- * parsed into words, with one Tcl_Obj holding each word.
+ * Scans up to numBytes bytes starting at src, consuming a backslash
+ * sequence as defined by Tcl's parsing rules.
*
* Results:
- * The return value is a standard Tcl completion code such as
- * TCL_OK or TCL_ERROR. A result or error message is left in
- * interp's result.
+ * Records at readPtr the number of bytes making up the backslash
+ * sequence. Records at dst the UTF-8 encoded equivalent of that
+ * backslash sequence. Returns the number of bytes written to dst, at
+ * most TCL_UTF_MAX. Either readPtr or dst may be NULL, if the results
+ * are not needed, but the return value is the same either way.
*
* Side effects:
- * Depends on the command.
+ * None.
*
*----------------------------------------------------------------------
*/
int
-Tcl_EvalObjv(interp, objc, objv, flags)
- Tcl_Interp *interp; /* Interpreter in which to evaluate the
- * command. Also used for error
- * reporting. */
- int objc; /* Number of words in command. */
- Tcl_Obj *CONST objv[]; /* An array of pointers to objects that are
- * the words that make up the command. */
- int flags; /* Collection of OR-ed bits that control
- * the evaluation of the script. Only
- * TCL_EVAL_GLOBAL is currently
- * supported. */
+TclParseBackslash(
+ const char *src, /* Points to the backslash character of a a
+ * backslash sequence. */
+ int numBytes, /* Max number of bytes to scan. */
+ int *readPtr, /* NULL, or points to storage where the number
+ * of bytes scanned should be written. */
+ char *dst) /* NULL, or points to buffer where the UTF-8
+ * encoding of the backslash sequence is to be
+ * written. At most TCL_UTF_MAX bytes will be
+ * written there. */
{
- Interp *iPtr = (Interp *)interp;
- Trace *tracePtr;
- Tcl_DString cmdBuf;
- char *cmdString = "";
- int cmdLen = 0;
- int code = TCL_OK;
+ register const char *p = src+1;
+ Tcl_UniChar unichar;
+ int result;
+ int count;
+ char buf[TCL_UTF_MAX];
- for (tracePtr = iPtr->tracePtr; tracePtr; tracePtr = tracePtr->nextPtr) {
- /*
- * EvalObjv will increment numLevels so use "<" rather than "<="
- */
- if (iPtr->numLevels < tracePtr->level) {
- int i;
- /*
- * The command will be needed for an execution trace or stack trace
- * generate a command string.
- */
- cmdtraced:
- Tcl_DStringInit(&cmdBuf);
- for (i = 0; i < objc; i++) {
- Tcl_DStringAppendElement(&cmdBuf, Tcl_GetString(objv[i]));
- }
- cmdString = Tcl_DStringValue(&cmdBuf);
- cmdLen = Tcl_DStringLength(&cmdBuf);
- break;
+ if (numBytes == 0) {
+ if (readPtr != NULL) {
+ *readPtr = 0;
}
+ return 0;
}
- /*
- * Execute the command if we have not done so already
- */
- switch (code) {
- case TCL_OK:
- code = EvalObjv(interp, objc, objv, cmdString, cmdLen, flags);
- if (code == TCL_ERROR && cmdLen == 0)
- goto cmdtraced;
- break;
- case TCL_ERROR:
- Tcl_LogCommandInfo(interp, cmdString, cmdString, cmdLen);
- break;
- default:
- /*NOTREACHED*/
- break;
+ if (dst == NULL) {
+ dst = buf;
}
- if (cmdLen != 0) {
- Tcl_DStringFree(&cmdBuf);
- }
- return code;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_LogCommandInfo --
- *
- * This procedure is invoked after an error occurs in an interpreter.
- * It adds information to the "errorInfo" variable to describe the
- * command that was being executed when the error occurred.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Information about the command is added to errorInfo and the
- * line number stored internally in the interpreter is set. If this
- * is the first call to this procedure or Tcl_AddObjErrorInfo since
- * an error occurred, then old information in errorInfo is
- * deleted.
- *
- *----------------------------------------------------------------------
- */
+ if (numBytes == 1) {
+ /*
+ * Can only scan the backslash, so return it.
+ */
-void
-Tcl_LogCommandInfo(interp, script, command, length)
- Tcl_Interp *interp; /* Interpreter in which to log information. */
- char *script; /* First character in script containing
- * command (must be <= command). */
- char *command; /* First character in command that
- * generated the error. */
- int length; /* Number of bytes in command (-1 means
- * use all bytes up to first null byte). */
-{
- char buffer[200];
- register char *p;
- char *ellipsis = "";
- Interp *iPtr = (Interp *) interp;
+ result = '\\';
+ count = 1;
+ goto done;
+ }
- if (iPtr->flags & ERR_ALREADY_LOGGED) {
+ count = 2;
+ switch (*p) {
/*
- * Someone else has already logged error information for this
- * command; we shouldn't add anything more.
+ * Note: in the conversions below, use absolute values (e.g., 0xa)
+ * rather than symbolic values (e.g. \n) that get converted by the
+ * compiler. It's possible that compilers on some platforms will do
+ * the symbolic conversions differently, which could result in
+ * non-portable Tcl scripts.
*/
- return;
- }
+ case 'a':
+ result = 0x7;
+ break;
+ case 'b':
+ result = 0x8;
+ break;
+ case 'f':
+ result = 0xc;
+ break;
+ case 'n':
+ result = 0xa;
+ break;
+ case 'r':
+ result = 0xd;
+ break;
+ case 't':
+ result = 0x9;
+ break;
+ case 'v':
+ result = 0xb;
+ break;
+ case 'x':
+ count += TclParseHex(p+1, (numBytes > 3) ? 2 : numBytes-2, &result);
+ if (count == 2) {
+ /*
+ * No hexadigits -> This is just "x".
+ */
- /*
- * Compute the line number where the error occurred.
- */
+ result = 'x';
+ } else {
+ /*
+ * Keep only the last byte (2 hex digits).
+ */
+ result = (unsigned char) result;
+ }
+ break;
+ case 'u':
+ count += TclParseHex(p+1, (numBytes > 5) ? 4 : numBytes-2, &result);
+ if (count == 2) {
+ /*
+ * No hexadigits -> This is just "u".
+ */
+ result = 'u';
+ }
+ break;
+ case 'U':
+ count += TclParseHex(p+1, (numBytes > 9) ? 8 : numBytes-2, &result);
+ if (count == 2) {
+ /*
+ * No hexadigits -> This is just "U".
+ */
+ result = 'U';
+ }
+ break;
+ case '\n':
+ count--;
+ do {
+ p++;
+ count++;
+ } while ((count < numBytes) && ((*p == ' ') || (*p == '\t')));
+ result = ' ';
+ break;
+ case 0:
+ result = '\\';
+ count = 1;
+ break;
+ default:
+ /*
+ * Check for an octal number \oo?o?
+ */
- iPtr->errorLine = 1;
- for (p = script; p != command; p++) {
- if (*p == '\n') {
- iPtr->errorLine++;
+ if (isdigit(UCHAR(*p)) && (UCHAR(*p) < '8')) { /* INTL: digit */
+ result = *p - '0';
+ p++;
+ if ((numBytes == 2) || !isdigit(UCHAR(*p)) /* INTL: digit */
+ || (UCHAR(*p) >= '8')) {
+ break;
+ }
+ count = 3;
+ result = (result << 3) + (*p - '0');
+ p++;
+ if ((numBytes == 3) || !isdigit(UCHAR(*p)) /* INTL: digit */
+ || (UCHAR(*p) >= '8') || (result >= 0x20)) {
+ break;
+ }
+ count = 4;
+ result = UCHAR((result << 3) + (*p - '0'));
+ break;
}
- }
- /*
- * Create an error message to add to errorInfo, including up to a
- * maximum number of characters of the command.
- */
+ /*
+ * We have to convert here in case the user has put a backslash in
+ * front of a multi-byte utf-8 character. While this means nothing
+ * special, we shouldn't break up a correct utf-8 character. [Bug
+ * #217987] test subst-3.2
+ */
- if (length < 0) {
- length = strlen(command);
- }
- if (length > 150) {
- length = 150;
- ellipsis = "...";
+ if (Tcl_UtfCharComplete(p, numBytes - 1)) {
+ count = Tcl_UtfToUniChar(p, &unichar) + 1; /* +1 for '\' */
+ } else {
+ char utfBytes[TCL_UTF_MAX];
+
+ memcpy(utfBytes, p, (size_t) (numBytes - 1));
+ utfBytes[numBytes - 1] = '\0';
+ count = Tcl_UtfToUniChar(utfBytes, &unichar) + 1;
+ }
+ result = unichar;
+ break;
}
- if (!(iPtr->flags & ERR_IN_PROGRESS)) {
- sprintf(buffer, "\n while executing\n\"%.*s%s\"",
- length, command, ellipsis);
- } else {
- sprintf(buffer, "\n invoked from within\n\"%.*s%s\"",
- length, command, ellipsis);
+
+ done:
+ if (readPtr != NULL) {
+ *readPtr = count;
}
- Tcl_AddObjErrorInfo(interp, buffer, -1);
- iPtr->flags &= ~ERR_ALREADY_LOGGED;
+ return Tcl_UniCharToUtf(result, dst);
}
/*
*----------------------------------------------------------------------
*
- * Tcl_EvalTokens --
+ * ParseComment --
*
- * Given an array of tokens parsed from a Tcl command (e.g., the
- * tokens that make up a word or the index for an array variable)
- * this procedure evaluates the tokens and concatenates their
- * values to form a single result value.
+ * Scans up to numBytes bytes starting at src, consuming a Tcl comment as
+ * defined by Tcl's parsing rules.
*
* Results:
- * The return value is a pointer to a newly allocated Tcl_Obj
- * containing the value of the array of tokens. The reference
- * count of the returned object has been incremented. If an error
- * occurs in evaluating the tokens then a NULL value is returned
- * and an error message is left in interp's result.
+ * Records in parsePtr information about the parse. Returns the number of
+ * bytes consumed.
*
* Side effects:
- * A new object is allocated to hold the result.
+ * None.
*
*----------------------------------------------------------------------
*/
-Tcl_Obj *
-Tcl_EvalTokens(interp, tokenPtr, count)
- Tcl_Interp *interp; /* Interpreter in which to lookup
- * variables, execute nested commands,
- * and report errors. */
- Tcl_Token *tokenPtr; /* Pointer to first in an array of tokens
- * to evaluate and concatenate. */
- int count; /* Number of tokens to consider at tokenPtr.
- * Must be at least 1. */
+static int
+ParseComment(
+ const char *src, /* First character to parse. */
+ register int numBytes, /* Max number of bytes to scan. */
+ Tcl_Parse *parsePtr) /* Information about parse in progress.
+ * Updated if parsing indicates an incomplete
+ * command. */
{
- Tcl_Obj *resultPtr, *indexPtr, *valuePtr, *newPtr;
- char buffer[TCL_UTF_MAX];
-#ifdef TCL_MEM_DEBUG
-# define MAX_VAR_CHARS 5
-#else
-# define MAX_VAR_CHARS 30
-#endif
- char nameBuffer[MAX_VAR_CHARS+1];
- char *varName, *index;
- char *p = NULL; /* Initialized to avoid compiler warning. */
- int length, code;
-
- /*
- * The only tricky thing about this procedure is that it attempts to
- * avoid object creation and string copying whenever possible. For
- * example, if the value is just a nested command, then use the
- * command's result object directly.
- */
-
- resultPtr = NULL;
- for ( ; count > 0; count--, tokenPtr++) {
- valuePtr = NULL;
-
- /*
- * The switch statement below computes the next value to be
- * concat to the result, as either a range of text or an
- * object.
- */
+ register const char *p = src;
- switch (tokenPtr->type) {
- case TCL_TOKEN_TEXT:
- p = tokenPtr->start;
- length = tokenPtr->size;
- break;
+ while (numBytes) {
+ char type;
+ int scanned;
- case TCL_TOKEN_BS:
- length = Tcl_UtfBackslash(tokenPtr->start, (int *) NULL,
- buffer);
- p = buffer;
- break;
+ do {
+ scanned = ParseWhiteSpace(p, numBytes,
+ &parsePtr->incomplete, &type);
+ p += scanned;
+ numBytes -= scanned;
+ } while (numBytes && (*p == '\n') && (p++,numBytes--));
- case TCL_TOKEN_COMMAND:
- code = Tcl_EvalEx(interp, tokenPtr->start+1, tokenPtr->size-2,
- 0);
- if (code != TCL_OK) {
- goto error;
- }
- valuePtr = Tcl_GetObjResult(interp);
- break;
+ if ((numBytes == 0) || (*p != '#')) {
+ break;
+ }
+ if (parsePtr->commentStart == NULL) {
+ parsePtr->commentStart = p;
+ }
- case TCL_TOKEN_VARIABLE:
- if (tokenPtr->numComponents == 1) {
- indexPtr = NULL;
+ while (numBytes) {
+ if (*p == '\\') {
+ scanned = ParseWhiteSpace(p, numBytes, &parsePtr->incomplete,
+ &type);
+ if (scanned) {
+ p += scanned;
+ numBytes -= scanned;
} else {
- indexPtr = Tcl_EvalTokens(interp, tokenPtr+2,
- tokenPtr->numComponents - 1);
- if (indexPtr == NULL) {
- goto error;
- }
- }
-
- /*
- * We have to make a copy of the variable name in order
- * to have a null-terminated string. We can't make a
- * temporary modification to the script to null-terminate
- * the name, because a trace callback might potentially
- * reuse the script and be affected by the null character.
- */
+ /*
+ * General backslash substitution in comments isn't part
+ * of the formal spec, but test parse-15.47 and history
+ * indicate that it has been the de facto rule. Don't
+ * change it now.
+ */
- if (tokenPtr[1].size <= MAX_VAR_CHARS) {
- varName = nameBuffer;
- } else {
- varName = ckalloc((unsigned) (tokenPtr[1].size + 1));
- }
- strncpy(varName, tokenPtr[1].start, (size_t) tokenPtr[1].size);
- varName[tokenPtr[1].size] = 0;
- if (indexPtr != NULL) {
- index = TclGetString(indexPtr);
- } else {
- index = NULL;
- }
- valuePtr = Tcl_GetVar2Ex(interp, varName, index,
- TCL_LEAVE_ERR_MSG);
- if (varName != nameBuffer) {
- ckfree(varName);
+ TclParseBackslash(p, numBytes, &scanned, NULL);
+ p += scanned;
+ numBytes -= scanned;
}
- if (indexPtr != NULL) {
- Tcl_DecrRefCount(indexPtr);
- }
- if (valuePtr == NULL) {
- goto error;
- }
- count -= tokenPtr->numComponents;
- tokenPtr += tokenPtr->numComponents;
- break;
-
- default:
- panic("unexpected token type in Tcl_EvalTokens");
- }
-
- /*
- * If valuePtr isn't NULL, the next piece of text comes from that
- * object; otherwise, take length bytes starting at p.
- */
-
- if (resultPtr == NULL) {
- if (valuePtr != NULL) {
- resultPtr = valuePtr;
} else {
- resultPtr = Tcl_NewStringObj(p, length);
- }
- Tcl_IncrRefCount(resultPtr);
- } else {
- if (Tcl_IsShared(resultPtr)) {
- newPtr = Tcl_DuplicateObj(resultPtr);
- Tcl_DecrRefCount(resultPtr);
- resultPtr = newPtr;
- Tcl_IncrRefCount(resultPtr);
- }
- if (valuePtr != NULL) {
- p = Tcl_GetStringFromObj(valuePtr, &length);
+ p++;
+ numBytes--;
+ if (p[-1] == '\n') {
+ break;
+ }
}
- Tcl_AppendToObj(resultPtr, p, length);
}
+ parsePtr->commentSize = p - parsePtr->commentStart;
}
- return resultPtr;
-
- error:
- if (resultPtr != NULL) {
- Tcl_DecrRefCount(resultPtr);
- }
- return NULL;
+ return (p - src);
}
/*
*----------------------------------------------------------------------
*
- * Tcl_EvalEx --
+ * ParseTokens --
*
- * This procedure evaluates a Tcl script without using the compiler
- * or byte-code interpreter. It just parses the script, creates
- * values for each word of each command, then calls EvalObjv
- * to execute each command.
+ * This function forms the heart of the Tcl parser. It parses one or more
+ * tokens from a string, up to a termination point specified by the
+ * caller. This function is used to parse unquoted command words (those
+ * not in quotes or braces), words in quotes, and array indices for
+ * variables. No more than numBytes bytes will be scanned.
*
* Results:
- * The return value is a standard Tcl completion code such as
- * TCL_OK or TCL_ERROR. A result or error message is left in
- * interp's result.
+ * Tokens are added to parsePtr and parsePtr->term is filled in with the
+ * address of the character that terminated the parse (the first one
+ * whose CHAR_TYPE matched mask or the character at parsePtr->end). The
+ * return value is TCL_OK if the parse completed successfully and
+ * TCL_ERROR otherwise. If a parse error occurs and parsePtr->interp is
+ * not NULL, then an error message is left in the interpreter's result.
*
* Side effects:
- * Depends on the script.
+ * None.
*
*----------------------------------------------------------------------
*/
-int
-Tcl_EvalEx(interp, script, numBytes, flags)
- Tcl_Interp *interp; /* Interpreter in which to evaluate the
- * script. Also used for error reporting. */
- char *script; /* First character of script to evaluate. */
- int numBytes; /* Number of bytes in script. If < 0, the
- * script consists of all bytes up to the
- * first null character. */
- int flags; /* Collection of OR-ed bits that control
- * the evaluation of the script. Only
- * TCL_EVAL_GLOBAL is currently
- * supported. */
+static int
+ParseTokens(
+ register const char *src, /* First character to parse. */
+ register int numBytes, /* Max number of bytes to scan. */
+ int mask, /* Specifies when to stop parsing. The parse
+ * stops at the first unquoted character whose
+ * CHAR_TYPE contains any of the bits in
+ * mask. */
+ int flags, /* OR-ed bits indicating what substitutions to
+ * perform: TCL_SUBST_COMMANDS,
+ * TCL_SUBST_VARIABLES, and
+ * TCL_SUBST_BACKSLASHES */
+ Tcl_Parse *parsePtr) /* Information about parse in progress.
+ * Updated with additional tokens and
+ * termination information. */
{
- Interp *iPtr = (Interp *) interp;
- char *p, *next;
- Tcl_Parse parse;
-#define NUM_STATIC_OBJS 20
- Tcl_Obj *staticObjArray[NUM_STATIC_OBJS], **objv;
+ char type;
+ int originalTokens;
+ int noSubstCmds = !(flags & TCL_SUBST_COMMANDS);
+ int noSubstVars = !(flags & TCL_SUBST_VARIABLES);
+ int noSubstBS = !(flags & TCL_SUBST_BACKSLASHES);
Tcl_Token *tokenPtr;
- int i, code, commandLength, bytesLeft, nested;
- CallFrame *savedVarFramePtr; /* Saves old copy of iPtr->varFramePtr
- * in case TCL_EVAL_GLOBAL was set. */
/*
- * The variables below keep track of how much state has been
- * allocated while evaluating the script, so that it can be freed
- * properly if an error occurs.
+ * Each iteration through the following loop adds one token of type
+ * TCL_TOKEN_TEXT, TCL_TOKEN_BS, TCL_TOKEN_COMMAND, or TCL_TOKEN_VARIABLE
+ * to parsePtr. For TCL_TOKEN_VARIABLE tokens, additional tokens are added
+ * for the parsed variable name.
*/
- int gotParse = 0, objectsUsed = 0;
-
- if (numBytes < 0) {
- numBytes = strlen(script);
- }
- Tcl_ResetResult(interp);
-
- savedVarFramePtr = iPtr->varFramePtr;
- if (flags & TCL_EVAL_GLOBAL) {
- iPtr->varFramePtr = NULL;
- }
-
- /*
- * Each iteration through the following loop parses the next
- * command from the script and then executes it.
- */
+ originalTokens = parsePtr->numTokens;
+ while (numBytes && !((type = CHAR_TYPE(*src)) & mask)) {
+ TclGrowParseTokenArray(parsePtr, 1);
+ tokenPtr = &parsePtr->tokenPtr[parsePtr->numTokens];
+ tokenPtr->start = src;
+ tokenPtr->numComponents = 0;
- objv = staticObjArray;
- p = script;
- bytesLeft = numBytes;
- if (iPtr->evalFlags & TCL_BRACKET_TERM) {
- nested = 1;
- } else {
- nested = 0;
- }
- iPtr->evalFlags = 0;
- do {
- if (Tcl_ParseCommand(interp, p, bytesLeft, nested, &parse)
- != TCL_OK) {
- code = TCL_ERROR;
- goto error;
- }
- gotParse = 1;
- if (parse.numWords > 0) {
+ if ((type & TYPE_SUBS) == 0) {
/*
- * Generate an array of objects for the words of the command.
+ * This is a simple range of characters. Scan to find the end of
+ * the range.
*/
-
- if (parse.numWords <= NUM_STATIC_OBJS) {
- objv = staticObjArray;
- } else {
- objv = (Tcl_Obj **) ckalloc((unsigned)
- (parse.numWords * sizeof (Tcl_Obj *)));
+
+ while ((++src, --numBytes)
+ && !(CHAR_TYPE(*src) & (mask | TYPE_SUBS))) {
+ /* empty loop */
}
- for (objectsUsed = 0, tokenPtr = parse.tokenPtr;
- objectsUsed < parse.numWords;
- objectsUsed++, tokenPtr += (tokenPtr->numComponents + 1)) {
- objv[objectsUsed] = Tcl_EvalTokens(interp, tokenPtr+1,
- tokenPtr->numComponents);
- if (objv[objectsUsed] == NULL) {
- code = TCL_ERROR;
- goto error;
- }
+ tokenPtr->type = TCL_TOKEN_TEXT;
+ tokenPtr->size = src - tokenPtr->start;
+ parsePtr->numTokens++;
+ } else if (*src == '$') {
+ int varToken;
+
+ if (noSubstVars) {
+ tokenPtr->type = TCL_TOKEN_TEXT;
+ tokenPtr->size = 1;
+ parsePtr->numTokens++;
+ src++;
+ numBytes--;
+ continue;
}
-
+
/*
- * Execute the command and free the objects for its words.
+ * This is a variable reference. Call Tcl_ParseVarName to do all
+ * the dirty work of parsing the name.
*/
-
- code = EvalObjv(interp, objectsUsed, objv, p, bytesLeft, 0);
- if (code != TCL_OK) {
- goto error;
- }
- for (i = 0; i < objectsUsed; i++) {
- Tcl_DecrRefCount(objv[i]);
- }
- objectsUsed = 0;
- if (objv != staticObjArray) {
- ckfree((char *) objv);
- objv = staticObjArray;
+
+ varToken = parsePtr->numTokens;
+ if (Tcl_ParseVarName(parsePtr->interp, src, numBytes, parsePtr,
+ 1) != TCL_OK) {
+ return TCL_ERROR;
}
- }
+ src += parsePtr->tokenPtr[varToken].size;
+ numBytes -= parsePtr->tokenPtr[varToken].size;
+ } else if (*src == '[') {
+ Tcl_Parse *nestedPtr;
- /*
- * Advance to the next command in the script.
- */
+ if (noSubstCmds) {
+ tokenPtr->type = TCL_TOKEN_TEXT;
+ tokenPtr->size = 1;
+ parsePtr->numTokens++;
+ src++;
+ numBytes--;
+ continue;
+ }
- next = parse.commandStart + parse.commandSize;
- bytesLeft -= next - p;
- p = next;
- Tcl_FreeParse(&parse);
- gotParse = 0;
- if ((nested != 0) && (p > script) && (p[-1] == ']')) {
/*
- * We get here in the special case where the TCL_BRACKET_TERM
- * flag was set in the interpreter and we reached a close
- * bracket in the script. Return immediately.
+ * Command substitution. Call Tcl_ParseCommand recursively (and
+ * repeatedly) to parse the nested command(s), then throw away the
+ * parse information.
*/
- iPtr->termOffset = (p - 1) - script;
- iPtr->varFramePtr = savedVarFramePtr;
- return TCL_OK;
- }
- } while (bytesLeft > 0);
- iPtr->termOffset = p - script;
- iPtr->varFramePtr = savedVarFramePtr;
- return TCL_OK;
-
- error:
- /*
- * Generate various pieces of error information, such as the line
- * number where the error occurred and information to add to the
- * errorInfo variable. Then free resources that had been allocated
- * to the command.
- */
+ src++;
+ numBytes--;
+ nestedPtr = TclStackAlloc(parsePtr->interp, sizeof(Tcl_Parse));
+ while (1) {
+ if (Tcl_ParseCommand(parsePtr->interp, src, numBytes, 1,
+ nestedPtr) != TCL_OK) {
+ parsePtr->errorType = nestedPtr->errorType;
+ parsePtr->term = nestedPtr->term;
+ parsePtr->incomplete = nestedPtr->incomplete;
+ TclStackFree(parsePtr->interp, nestedPtr);
+ return TCL_ERROR;
+ }
+ src = nestedPtr->commandStart + nestedPtr->commandSize;
+ numBytes = parsePtr->end - src;
+ Tcl_FreeParse(nestedPtr);
- if ((code == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) {
- commandLength = parse.commandSize;
- if ((parse.commandStart + commandLength) != (script + numBytes)) {
- /*
- * The command where the error occurred didn't end at the end
- * of the script (i.e. it ended at a terminator character such
- * as ";". Reduce the length by one so that the error message
- * doesn't include the terminator character.
- */
-
- commandLength -= 1;
- }
- Tcl_LogCommandInfo(interp, script, parse.commandStart, commandLength);
- }
-
- for (i = 0; i < objectsUsed; i++) {
- Tcl_DecrRefCount(objv[i]);
- }
- if (gotParse) {
- next = parse.commandStart + parse.commandSize;
- bytesLeft -= next - p;
- p = next;
- Tcl_FreeParse(&parse);
+ /*
+ * Check for the closing ']' that ends the command
+ * substitution. It must have been the last character of the
+ * parsed command.
+ */
- if ((nested != 0) && (p > script)) {
- char *nextCmd = NULL; /* pointer to start of next command */
+ if ((nestedPtr->term < parsePtr->end)
+ && (*(nestedPtr->term) == ']')
+ && !(nestedPtr->incomplete)) {
+ break;
+ }
+ if (numBytes == 0) {
+ if (parsePtr->interp != NULL) {
+ Tcl_SetObjResult(parsePtr->interp, Tcl_NewStringObj(
+ "missing close-bracket", -1));
+ }
+ parsePtr->errorType = TCL_PARSE_MISSING_BRACKET;
+ parsePtr->term = tokenPtr->start;
+ parsePtr->incomplete = 1;
+ TclStackFree(parsePtr->interp, nestedPtr);
+ return TCL_ERROR;
+ }
+ }
+ TclStackFree(parsePtr->interp, nestedPtr);
+ tokenPtr->type = TCL_TOKEN_COMMAND;
+ tokenPtr->size = src - tokenPtr->start;
+ parsePtr->numTokens++;
+ } else if (*src == '\\') {
+ if (noSubstBS) {
+ tokenPtr->type = TCL_TOKEN_TEXT;
+ tokenPtr->size = 1;
+ parsePtr->numTokens++;
+ src++;
+ numBytes--;
+ continue;
+ }
/*
- * We get here in the special case where the TCL_BRACKET_TERM
- * flag was set in the interpreter.
- *
- * At this point, we want to find the end of the script
- * (either end of script or the closing ']').
+ * Backslash substitution.
*/
- while ((p[-1] != ']') && bytesLeft) {
- if (Tcl_ParseCommand(NULL, p, bytesLeft, nested, &parse)
- != TCL_OK) {
- /*
- * We were looking for the ']' to close the script.
- * But if we find a syntax error, it is ok to quit
- * early since in that case we no longer need to know
- * where the ']' is (if there was one). We reset the
- * pointer to the start of the command that after the
- * one causing the return. -- hobbs
- */
+ TclParseBackslash(src, numBytes, &tokenPtr->size, NULL);
- p = (nextCmd == NULL) ? parse.commandStart : nextCmd;
- break;
- }
+ if (tokenPtr->size == 1) {
+ /*
+ * Just a backslash, due to end of string.
+ */
+
+ tokenPtr->type = TCL_TOKEN_TEXT;
+ parsePtr->numTokens++;
+ src++;
+ numBytes--;
+ continue;
+ }
- if (nextCmd == NULL) {
- nextCmd = parse.commandStart;
+ if (src[1] == '\n') {
+ if (numBytes == 2) {
+ parsePtr->incomplete = 1;
}
/*
- * Advance to the next command in the script.
+ * Note: backslash-newline is special in that it is treated
+ * the same as a space character would be. This means that it
+ * could terminate the token.
*/
- next = parse.commandStart + parse.commandSize;
- bytesLeft -= next - p;
- p = next;
- Tcl_FreeParse(&parse);
+ if (mask & TYPE_SPACE) {
+ if (parsePtr->numTokens == originalTokens) {
+ goto finishToken;
+ }
+ break;
+ }
}
- iPtr->termOffset = (p - 1) - script;
+
+ tokenPtr->type = TCL_TOKEN_BS;
+ parsePtr->numTokens++;
+ src += tokenPtr->size;
+ numBytes -= tokenPtr->size;
+ } else if (*src == 0) {
+ tokenPtr->type = TCL_TOKEN_TEXT;
+ tokenPtr->size = 1;
+ parsePtr->numTokens++;
+ src++;
+ numBytes--;
} else {
- iPtr->termOffset = p - script;
- }
- }
- if (objv != staticObjArray) {
- ckfree((char *) objv);
+ Tcl_Panic("ParseTokens encountered unknown character");
+ }
}
- iPtr->varFramePtr = savedVarFramePtr;
- return code;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_Eval --
- *
- * Execute a Tcl command in a string. This procedure executes the
- * script directly, rather than compiling it to bytecodes. Before
- * the arrival of the bytecode compiler in Tcl 8.0 Tcl_Eval was
- * the main procedure used for executing Tcl commands, but nowadays
- * it isn't used much.
- *
- * Results:
- * The return value is one of the return codes defined in tcl.h
- * (such as TCL_OK), and interp's result contains a value
- * to supplement the return code. The value of the result
- * will persist only until the next call to Tcl_Eval or Tcl_EvalObj:
- * you must copy it or lose it!
- *
- * Side effects:
- * Can be almost arbitrary, depending on the commands in the script.
- *
- *----------------------------------------------------------------------
- */
-
-int
-Tcl_Eval(interp, string)
- Tcl_Interp *interp; /* Token for command interpreter (returned
- * by previous call to Tcl_CreateInterp). */
- char *string; /* Pointer to TCL command to execute. */
-{
- int code;
-
- code = Tcl_EvalEx(interp, string, -1, 0);
+ if (parsePtr->numTokens == originalTokens) {
+ /*
+ * There was nothing in this range of text. Add an empty token for the
+ * empty range, so that there is always at least one token added.
+ */
- /*
- * For backwards compatibility with old C code that predates the
- * object system in Tcl 8.0, we have to mirror the object result
- * back into the string result (some callers may expect it there).
- */
+ TclGrowParseTokenArray(parsePtr, 1);
+ tokenPtr = &parsePtr->tokenPtr[parsePtr->numTokens];
+ tokenPtr->start = src;
+ tokenPtr->numComponents = 0;
- Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)),
- TCL_VOLATILE);
- return code;
+ finishToken:
+ tokenPtr->type = TCL_TOKEN_TEXT;
+ tokenPtr->size = 0;
+ parsePtr->numTokens++;
+ }
+ parsePtr->term = src;
+ return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
- * Tcl_EvalObj, Tcl_GlobalEvalObj --
+ * Tcl_FreeParse --
*
- * These functions are deprecated but we keep them around for backwards
- * compatibility reasons.
+ * This function is invoked to free any dynamic storage that may have
+ * been allocated by a previous call to Tcl_ParseCommand.
*
* Results:
- * See the functions they call.
+ * None.
*
* Side effects:
- * See the functions they call.
+ * If there is any dynamically allocated memory in *parsePtr, it is
+ * freed.
*
*----------------------------------------------------------------------
*/
-#undef Tcl_EvalObj
-int
-Tcl_EvalObj(interp, objPtr)
- Tcl_Interp * interp;
- Tcl_Obj * objPtr;
-{
- return Tcl_EvalObjEx(interp, objPtr, 0);
-}
-
-#undef Tcl_GlobalEvalObj
-int
-Tcl_GlobalEvalObj(interp, objPtr)
- Tcl_Interp * interp;
- Tcl_Obj * objPtr;
+void
+Tcl_FreeParse(
+ Tcl_Parse *parsePtr) /* Structure that was filled in by a previous
+ * call to Tcl_ParseCommand. */
{
- return Tcl_EvalObjEx(interp, objPtr, TCL_EVAL_GLOBAL);
+ if (parsePtr->tokenPtr != parsePtr->staticTokens) {
+ ckfree(parsePtr->tokenPtr);
+ parsePtr->tokenPtr = parsePtr->staticTokens;
+ }
}
/*
@@ -1598,81 +1306,69 @@ Tcl_GlobalEvalObj(interp, objPtr)
*
* Tcl_ParseVarName --
*
- * Given a string starting with a $ sign, parse off a variable
- * name and return information about the parse.
+ * Given a string starting with a $ sign, parse off a variable name and
+ * return information about the parse. No more than numBytes bytes will
+ * be scanned.
*
* Results:
- * The return value is TCL_OK if the command was parsed
- * successfully and TCL_ERROR otherwise. If an error occurs and
- * interp isn't NULL then an error message is left in its result.
- * On a successful return, tokenPtr and numTokens fields of
- * parsePtr are filled in with information about the variable name
- * that was parsed. The "size" field of the first new token gives
- * the total number of bytes in the variable name. Other fields in
- * parsePtr are undefined.
+ * The return value is TCL_OK if the command was parsed successfully and
+ * TCL_ERROR otherwise. If an error occurs and interp isn't NULL then an
+ * error message is left in its result. On a successful return, tokenPtr
+ * and numTokens fields of parsePtr are filled in with information about
+ * the variable name that was parsed. The "size" field of the first new
+ * token gives the total number of bytes in the variable name. Other
+ * fields in parsePtr are undefined.
*
* Side effects:
- * If there is insufficient space in parsePtr to hold all the
- * information about the command, then additional space is
- * malloc-ed. If the procedure returns TCL_OK then the caller must
- * eventually invoke Tcl_FreeParse to release any additional space
- * that was allocated.
+ * If there is insufficient space in parsePtr to hold all the information
+ * about the command, then additional space is malloc-ed. If the function
+ * returns TCL_OK then the caller must eventually invoke Tcl_FreeParse to
+ * release any additional space that was allocated.
*
*----------------------------------------------------------------------
*/
int
-Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
- Tcl_Interp *interp; /* Interpreter to use for error reporting;
- * if NULL, then no error message is
- * provided. */
- char *string; /* String containing variable name. First
- * character must be "$". */
- int numBytes; /* Total number of bytes in string. If < 0,
+Tcl_ParseVarName(
+ Tcl_Interp *interp, /* Interpreter to use for error reporting; if
+ * NULL, then no error message is provided. */
+ const char *start, /* Start of variable substitution string.
+ * First character must be "$". */
+ register int numBytes, /* Total number of bytes in string. If < 0,
* the string consists of all bytes up to the
* first null character. */
- Tcl_Parse *parsePtr; /* Structure to fill in with information
- * about the variable name. */
- int append; /* Non-zero means append tokens to existing
+ Tcl_Parse *parsePtr, /* Structure to fill in with information about
+ * the variable name. */
+ int append) /* Non-zero means append tokens to existing
* information in parsePtr; zero means ignore
- * existing tokens in parsePtr and reinitialize
- * it. */
+ * existing tokens in parsePtr and
+ * reinitialize it. */
{
Tcl_Token *tokenPtr;
- char *end, *src;
+ register const char *src;
unsigned char c;
int varIndex, offset;
Tcl_UniChar ch;
unsigned array;
- if (numBytes >= 0) {
- end = string + numBytes;
- } else {
- end = string + strlen(string);
+ if ((numBytes == 0) || (start == NULL)) {
+ return TCL_ERROR;
+ }
+ if (numBytes < 0) {
+ numBytes = strlen(start);
}
if (!append) {
- parsePtr->numWords = 0;
- parsePtr->tokenPtr = parsePtr->staticTokens;
- parsePtr->numTokens = 0;
- parsePtr->tokensAvailable = NUM_STATIC_TOKENS;
- parsePtr->string = string;
- parsePtr->end = end;
- parsePtr->interp = interp;
- parsePtr->errorType = TCL_PARSE_SUCCESS;
- parsePtr->incomplete = 0;
+ TclParseInit(interp, start, numBytes, parsePtr);
}
/*
- * Generate one token for the variable, an additional token for the
- * name, plus any number of additional tokens for the index, if
- * there is one.
+ * Generate one token for the variable, an additional token for the name,
+ * plus any number of additional tokens for the index, if there is one.
*/
- src = string;
- if ((parsePtr->numTokens + 2) > parsePtr->tokensAvailable) {
- TclExpandTokenArray(parsePtr);
- }
+ src = start;
+ TclGrowParseTokenArray(parsePtr, 2);
tokenPtr = &parsePtr->tokenPtr[parsePtr->numTokens];
tokenPtr->type = TCL_TOKEN_VARIABLE;
tokenPtr->start = src;
@@ -1680,7 +1376,8 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
parsePtr->numTokens++;
tokenPtr++;
src++;
- if (src >= end) {
+ numBytes--;
+ if (numBytes == 0) {
goto justADollarSign;
}
tokenPtr->type = TCL_TOKEN_TEXT;
@@ -1689,43 +1386,41 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
/*
* The name of the variable can have three forms:
- * 1. The $ sign is followed by an open curly brace. Then
- * the variable name is everything up to the next close
- * curly brace, and the variable is a scalar variable.
- * 2. The $ sign is not followed by an open curly brace. Then
- * the variable name is everything up to the next
- * character that isn't a letter, digit, or underscore.
- * :: sequences are also considered part of the variable
- * name, in order to support namespaces. If the following
- * character is an open parenthesis, then the information
- * between parentheses is the array element name.
- * 3. The $ sign is followed by something that isn't a letter,
- * digit, or underscore: in this case, there is no variable
- * name and the token is just "$".
+ * 1. The $ sign is followed by an open curly brace. Then the variable
+ * name is everything up to the next close curly brace, and the
+ * variable is a scalar variable.
+ * 2. The $ sign is not followed by an open curly brace. Then the variable
+ * name is everything up to the next character that isn't a letter,
+ * digit, or underscore. :: sequences are also considered part of the
+ * variable name, in order to support namespaces. If the following
+ * character is an open parenthesis, then the information between
+ * parentheses is the array element name.
+ * 3. The $ sign is followed by something that isn't a letter, digit, or
+ * underscore: in this case, there is no variable name and the token is
+ * just "$".
*/
if (*src == '{') {
src++;
+ numBytes--;
tokenPtr->type = TCL_TOKEN_TEXT;
tokenPtr->start = src;
tokenPtr->numComponents = 0;
- while (1) {
- if (src == end) {
- if (interp != NULL) {
- Tcl_SetResult(interp,
- "missing close-brace for variable name",
- TCL_STATIC);
- }
- parsePtr->errorType = TCL_PARSE_MISSING_VAR_BRACE;
- parsePtr->term = tokenPtr->start-1;
- parsePtr->incomplete = 1;
- goto error;
- }
- if (*src == '}') {
- break;
- }
+
+ while (numBytes && (*src != '}')) {
+ numBytes--;
src++;
}
+ if (numBytes == 0) {
+ if (parsePtr->interp != NULL) {
+ Tcl_SetObjResult(parsePtr->interp, Tcl_NewStringObj(
+ "missing close-brace for variable name", -1));
+ }
+ parsePtr->errorType = TCL_PARSE_MISSING_VAR_BRACE;
+ parsePtr->term = tokenPtr->start-1;
+ parsePtr->incomplete = 1;
+ goto error;
+ }
tokenPtr->size = src - tokenPtr->start;
tokenPtr[-1].size = src - tokenPtr[-1].start;
parsePtr->numTokens++;
@@ -1734,17 +1429,29 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
tokenPtr->type = TCL_TOKEN_TEXT;
tokenPtr->start = src;
tokenPtr->numComponents = 0;
- while (src != end) {
- offset = Tcl_UtfToUniChar(src, &ch);
+
+ while (numBytes) {
+ if (Tcl_UtfCharComplete(src, numBytes)) {
+ offset = Tcl_UtfToUniChar(src, &ch);
+ } else {
+ char utfBytes[TCL_UTF_MAX];
+
+ memcpy(utfBytes, src, (size_t) numBytes);
+ utfBytes[numBytes] = '\0';
+ offset = Tcl_UtfToUniChar(utfBytes, &ch);
+ }
c = UCHAR(ch);
- if (isalnum(c) || (c == '_')) { /* INTL: ISO only, UCHAR. */
+ if (isalnum(c) || (c == '_')) { /* INTL: ISO only, UCHAR. */
src += offset;
+ numBytes -= offset;
continue;
}
- if ((c == ':') && (((src+1) != end) && (src[1] == ':'))) {
+ if ((c == ':') && (numBytes != 1) && (src[1] == ':')) {
src += 2;
- while ((src != end) && (*src == ':')) {
- src += 1;
+ numBytes -= 2;
+ while (numBytes && (*src == ':')) {
+ src++;
+ numBytes--;
}
continue;
}
@@ -1754,27 +1461,28 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
/*
* Support for empty array names here.
*/
- array = ((src != end) && (*src == '('));
+
+ array = (numBytes && (*src == '('));
tokenPtr->size = src - tokenPtr->start;
- if (tokenPtr->size == 0 && !array) {
+ if ((tokenPtr->size == 0) && !array) {
goto justADollarSign;
}
parsePtr->numTokens++;
if (array) {
/*
- * This is a reference to an array element. Call
- * ParseTokens recursively to parse the element name,
- * since it could contain any number of substitutions.
+ * 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 (ParseTokens(src+1, TYPE_CLOSE_PAREN, parsePtr)
- != TCL_OK) {
+ if (TCL_OK != ParseTokens(src+1, numBytes-1, TYPE_CLOSE_PAREN,
+ TCL_SUBST_ALL, parsePtr)) {
goto error;
}
- if ((parsePtr->term == end) || (*parsePtr->term != ')')) {
+ if ((parsePtr->term == src+numBytes) || (*parsePtr->term != ')')){
if (parsePtr->interp != NULL) {
- Tcl_SetResult(parsePtr->interp, "missing )",
- TCL_STATIC);
+ Tcl_SetObjResult(parsePtr->interp, Tcl_NewStringObj(
+ "missing )", -1));
}
parsePtr->errorType = TCL_PARSE_MISSING_PAREN;
parsePtr->term = src;
@@ -1790,19 +1498,19 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
return TCL_OK;
/*
- * The dollar sign isn't followed by a variable name.
- * replace the TCL_TOKEN_VARIABLE token with a
- * TCL_TOKEN_TEXT token for the dollar sign.
+ * The dollar sign isn't followed by a variable name. Replace the
+ * TCL_TOKEN_VARIABLE token with a TCL_TOKEN_TEXT token for the dollar
+ * sign.
*/
- justADollarSign:
+ justADollarSign:
tokenPtr = &parsePtr->tokenPtr[varIndex];
tokenPtr->type = TCL_TOKEN_TEXT;
tokenPtr->size = 1;
tokenPtr->numComponents = 0;
return TCL_OK;
- error:
+ error:
Tcl_FreeParse(parsePtr);
return TCL_ERROR;
}
@@ -1812,16 +1520,15 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
*
* Tcl_ParseVar --
*
- * Given a string starting with a $ sign, parse off a variable
- * name and return its value.
+ * Given a string starting with a $ sign, parse off a variable name and
+ * return its value.
*
* Results:
- * The return value is the contents of the variable given by
- * the leading characters of string. If termPtr isn't NULL,
- * *termPtr gets filled in with the address of the character
- * just after the last one in the variable specifier. If the
- * variable doesn't exist, then the return value is NULL and
- * an error message will be left in interp's result.
+ * The return value is the contents of the variable given by the leading
+ * characters of string. If termPtr isn't NULL, *termPtr gets filled in
+ * with the address of the character just after the last one in the
+ * variable specifier. If the variable doesn't exist, then the return
+ * value is NULL and an error message will be left in interp's result.
*
* Side effects:
* None.
@@ -1829,50 +1536,57 @@ Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
*----------------------------------------------------------------------
*/
-char *
-Tcl_ParseVar(interp, string, termPtr)
- Tcl_Interp *interp; /* Context for looking up variable. */
- register char *string; /* String containing variable name.
- * First character must be "$". */
- char **termPtr; /* If non-NULL, points to word to fill
- * in with character just after last
- * one in the variable specifier. */
-
+const char *
+Tcl_ParseVar(
+ Tcl_Interp *interp, /* Context for looking up variable. */
+ register const char *start, /* Start of variable substitution. First
+ * character must be "$". */
+ const char **termPtr) /* If non-NULL, points to word to fill in with
+ * character just after last one in the
+ * variable specifier. */
{
- Tcl_Parse parse;
register Tcl_Obj *objPtr;
+ int code;
+ Tcl_Parse *parsePtr = TclStackAlloc(interp, sizeof(Tcl_Parse));
- if (Tcl_ParseVarName(interp, string, -1, &parse, 0) != TCL_OK) {
+ if (Tcl_ParseVarName(interp, start, -1, parsePtr, 0) != TCL_OK) {
+ TclStackFree(interp, parsePtr);
return NULL;
}
if (termPtr != NULL) {
- *termPtr = string + parse.tokenPtr->size;
+ *termPtr = start + parsePtr->tokenPtr->size;
}
- if (parse.numTokens == 1) {
+ if (parsePtr->numTokens == 1) {
/*
* There isn't a variable name after all: the $ is just a $.
*/
+ TclStackFree(interp, parsePtr);
return "$";
}
- objPtr = Tcl_EvalTokens(interp, parse.tokenPtr, parse.numTokens);
- if (objPtr == NULL) {
+ code = TclSubstTokens(interp, parsePtr->tokenPtr, parsePtr->numTokens,
+ NULL, 1, NULL, NULL);
+ Tcl_FreeParse(parsePtr);
+ TclStackFree(interp, parsePtr);
+ if (code != TCL_OK) {
return NULL;
}
+ objPtr = Tcl_GetObjResult(interp);
/*
- * At this point we should have an object containing the value of
- * a variable. Just return the string from that object.
+ * At this point we should have an object containing the value of a
+ * variable. Just return the string from that object.
+ *
+ * Since TclSubstTokens above returned TCL_OK, we know that objPtr
+ * is shared. It is in both the interp result and the value of the
+ * variable. Returning the string relies on that to be true.
*/
-#ifdef TCL_COMPILE_DEBUG
- if (objPtr->refCount < 2) {
- panic("Tcl_ParseVar got temporary object from Tcl_EvalTokens");
- }
-#endif /*TCL_COMPILE_DEBUG*/
- TclDecrRefCount(objPtr);
+ assert( Tcl_IsShared(objPtr) );
+
+ Tcl_ResetResult(interp);
return TclGetString(objPtr);
}
@@ -1882,201 +1596,198 @@ Tcl_ParseVar(interp, string, termPtr)
* Tcl_ParseBraces --
*
* Given a string in braces such as a Tcl command argument or a string
- * value in a Tcl expression, this procedure parses the string and
- * returns information about the parse.
+ * value in a Tcl expression, this function parses the string and returns
+ * information about the parse. No more than numBytes bytes will be
+ * scanned.
*
* Results:
* The return value is TCL_OK if the string was parsed successfully and
- * TCL_ERROR otherwise. If an error occurs and interp isn't NULL then
- * an error message is left in its result. On a successful return,
- * tokenPtr and numTokens fields of parsePtr are filled in with
- * information about the string that was parsed. Other fields in
- * parsePtr are undefined. termPtr is set to point to the character
- * just after the last one in the braced string.
+ * TCL_ERROR otherwise. If an error occurs and interp isn't NULL then an
+ * error message is left in its result. On a successful return, tokenPtr
+ * and numTokens fields of parsePtr are filled in with information about
+ * the string that was parsed. Other fields in parsePtr are undefined.
+ * termPtr is set to point to the character just after the last one in
+ * the braced string.
*
* Side effects:
- * If there is insufficient space in parsePtr to hold all the
- * information about the command, then additional space is
- * malloc-ed. If the procedure returns TCL_OK then the caller must
- * eventually invoke Tcl_FreeParse to release any additional space
- * that was allocated.
+ * If there is insufficient space in parsePtr to hold all the information
+ * about the command, then additional space is malloc-ed. If the function
+ * returns TCL_OK then the caller must eventually invoke Tcl_FreeParse to
+ * release any additional space that was allocated.
*
*----------------------------------------------------------------------
*/
int
-Tcl_ParseBraces(interp, string, numBytes, parsePtr, append, termPtr)
- Tcl_Interp *interp; /* Interpreter to use for error reporting;
- * if NULL, then no error message is
- * provided. */
- char *string; /* String containing the string in braces.
- * The first character must be '{'. */
- int numBytes; /* Total number of bytes in string. If < 0,
- * the string consists of all bytes up to
- * the first null character. */
- register Tcl_Parse *parsePtr;
- /* Structure to fill in with information
- * about the string. */
- int append; /* Non-zero means append tokens to existing
- * information in parsePtr; zero means
- * ignore existing tokens in parsePtr and
+Tcl_ParseBraces(
+ Tcl_Interp *interp, /* Interpreter to use for error reporting; if
+ * NULL, then no error message is provided. */
+ const char *start, /* Start of string enclosed in braces. The
+ * first character must be {'. */
+ register int numBytes, /* Total number of bytes in string. If < 0,
+ * the string consists of all bytes up to the
+ * first null character. */
+ register Tcl_Parse *parsePtr,
+ /* Structure to fill in with information about
+ * the string. */
+ int append, /* Non-zero means append tokens to existing
+ * information in parsePtr; zero means ignore
+ * existing tokens in parsePtr and
* reinitialize it. */
- char **termPtr; /* If non-NULL, points to word in which to
- * store a pointer to the character just
- * after the terminating '}' if the parse
- * was successful. */
-
+ const char **termPtr) /* If non-NULL, points to word in which to
+ * store a pointer to the character just after
+ * the terminating '}' if the parse was
+ * successful. */
{
- char utfBytes[TCL_UTF_MAX]; /* For result of backslash substitution. */
Tcl_Token *tokenPtr;
- register char *src, *end;
+ register const char *src;
int startIndex, level, length;
- if ((numBytes >= 0) || (string == NULL)) {
- end = string + numBytes;
- } else {
- end = string + strlen(string);
+ if ((numBytes == 0) || (start == NULL)) {
+ return TCL_ERROR;
}
-
+ if (numBytes < 0) {
+ numBytes = strlen(start);
+ }
+
if (!append) {
- parsePtr->numWords = 0;
- parsePtr->tokenPtr = parsePtr->staticTokens;
- parsePtr->numTokens = 0;
- parsePtr->tokensAvailable = NUM_STATIC_TOKENS;
- parsePtr->string = string;
- parsePtr->end = end;
- parsePtr->interp = interp;
- parsePtr->errorType = TCL_PARSE_SUCCESS;
+ TclParseInit(interp, start, numBytes, parsePtr);
}
- src = string+1;
+ src = start;
startIndex = parsePtr->numTokens;
- if (parsePtr->numTokens == parsePtr->tokensAvailable) {
- TclExpandTokenArray(parsePtr);
- }
+ TclGrowParseTokenArray(parsePtr, 1);
tokenPtr = &parsePtr->tokenPtr[startIndex];
tokenPtr->type = TCL_TOKEN_TEXT;
- tokenPtr->start = src;
+ tokenPtr->start = src+1;
tokenPtr->numComponents = 0;
level = 1;
while (1) {
- while (CHAR_TYPE(*src) == TYPE_NORMAL) {
- src++;
- }
- if (*src == '}') {
- level--;
- if (level == 0) {
+ while (++src, --numBytes) {
+ if (CHAR_TYPE(*src) != TYPE_NORMAL) {
break;
}
- src++;
- } else if (*src == '{') {
+ }
+ if (numBytes == 0) {
+ goto missingBraceError;
+ }
+
+ switch (*src) {
+ case '{':
level++;
- src++;
- } else if (*src == '\\') {
- Tcl_UtfBackslash(src, &length, utfBytes);
- if (src[1] == '\n') {
+ break;
+ case '}':
+ if (--level == 0) {
+ /*
+ * Decide if we need to finish emitting a partially-finished
+ * token. There are 3 cases:
+ * {abc \newline xyz} or {xyz}
+ * - finish emitting "xyz" token
+ * {abc \newline}
+ * - don't emit token after \newline
+ * {} - finish emitting zero-sized token
+ *
+ * The last case ensures that there is a token (even if empty)
+ * that describes the braced string.
+ */
+
+ if ((src != tokenPtr->start)
+ || (parsePtr->numTokens == startIndex)) {
+ tokenPtr->size = (src - tokenPtr->start);
+ parsePtr->numTokens++;
+ }
+ if (termPtr != NULL) {
+ *termPtr = src+1;
+ }
+ return TCL_OK;
+ }
+ break;
+ case '\\':
+ TclParseBackslash(src, numBytes, &length, NULL);
+ if ((length > 1) && (src[1] == '\n')) {
/*
- * A backslash-newline sequence must be collapsed, even
- * inside braces, so we have to split the word into
- * multiple tokens so that the backslash-newline can be
- * represented explicitly.
+ * A backslash-newline sequence must be collapsed, even inside
+ * braces, so we have to split the word into multiple tokens
+ * so that the backslash-newline can be represented
+ * explicitly.
*/
-
- if ((src + 2) == end) {
+
+ if (numBytes == 2) {
parsePtr->incomplete = 1;
}
tokenPtr->size = (src - tokenPtr->start);
if (tokenPtr->size != 0) {
parsePtr->numTokens++;
}
- if ((parsePtr->numTokens+1) >= parsePtr->tokensAvailable) {
- TclExpandTokenArray(parsePtr);
- }
+ TclGrowParseTokenArray(parsePtr, 2);
tokenPtr = &parsePtr->tokenPtr[parsePtr->numTokens];
tokenPtr->type = TCL_TOKEN_BS;
tokenPtr->start = src;
tokenPtr->size = length;
tokenPtr->numComponents = 0;
parsePtr->numTokens++;
-
- src += length;
+
+ src += length - 1;
+ numBytes -= length - 1;
tokenPtr++;
tokenPtr->type = TCL_TOKEN_TEXT;
- tokenPtr->start = src;
+ tokenPtr->start = src + 1;
tokenPtr->numComponents = 0;
} else {
- src += length;
- }
- } else if (src == end) {
- int openBrace;
-
- if (interp != NULL) {
- Tcl_SetResult(interp, "missing close-brace", TCL_STATIC);
- }
- /*
- * Search the source string for a possible open
- * brace within the context of a comment. Since we
- * aren't performing a full Tcl parse, just look for
- * an open brace preceeded by a '<whitspace>#' on
- * the same line.
- */
- openBrace = 0;
- while (src > string ) {
- switch (*src) {
- case '{':
- openBrace = 1;
- break;
- case '\n':
- openBrace = 0;
- break;
- case '#':
- if ((openBrace == 1) && (isspace(UCHAR(src[-1])))) {
- if (interp != NULL) {
- Tcl_AppendResult(interp,
- ": possible unbalanced brace in comment",
- (char *) NULL);
- }
- openBrace = -1;
- break;
- }
- break;
- }
- if (openBrace == -1) {
- break;
- }
- src--;
+ src += length - 1;
+ numBytes -= length - 1;
}
- parsePtr->errorType = TCL_PARSE_MISSING_BRACE;
- parsePtr->term = string;
- parsePtr->incomplete = 1;
- goto error;
- } else {
- src++;
+ break;
}
}
+ missingBraceError:
+ parsePtr->errorType = TCL_PARSE_MISSING_BRACE;
+ parsePtr->term = start;
+ parsePtr->incomplete = 1;
+ if (parsePtr->interp == NULL) {
+ /*
+ * Skip straight to the exit code since we have no interpreter to put
+ * error message in.
+ */
+
+ goto error;
+ }
+
+ Tcl_SetObjResult(parsePtr->interp, Tcl_NewStringObj(
+ "missing close-brace", -1));
+
/*
- * Decide if we need to finish emitting a partially-finished token.
- * There are 3 cases:
- * {abc \newline xyz} or {xyz} - finish emitting "xyz" token
- * {abc \newline} - don't emit token after \newline
- * {} - finish emitting zero-sized token
- * The last case ensures that there is a token (even if empty) that
- * describes the braced string.
+ * Guess if the problem is due to comments by searching the source string
+ * for a possible open brace within the context of a comment. Since we
+ * aren't performing a full Tcl parse, just look for an open brace
+ * preceded by a '<whitespace>#' on the same line.
*/
-
- if ((src != tokenPtr->start)
- || (parsePtr->numTokens == startIndex)) {
- tokenPtr->size = (src - tokenPtr->start);
- parsePtr->numTokens++;
- }
- if (termPtr != NULL) {
- *termPtr = src+1;
+
+ {
+ register int openBrace = 0;
+
+ while (--src > start) {
+ switch (*src) {
+ case '{':
+ openBrace = 1;
+ break;
+ case '\n':
+ openBrace = 0;
+ break;
+ case '#' :
+ if (openBrace && TclIsSpaceProc(src[-1])) {
+ Tcl_AppendToObj(Tcl_GetObjResult(parsePtr->interp),
+ ": possible unbalanced brace in comment", -1);
+ goto error;
+ }
+ break;
+ }
+ }
}
- return TCL_OK;
- error:
+ error:
Tcl_FreeParse(parsePtr);
return TCL_ERROR;
}
@@ -2086,79 +1797,72 @@ Tcl_ParseBraces(interp, string, numBytes, parsePtr, append, termPtr)
*
* Tcl_ParseQuotedString --
*
- * Given a double-quoted string such as a quoted Tcl command argument
- * or a quoted value in a Tcl expression, this procedure parses the
- * string and returns information about the parse.
+ * Given a double-quoted string such as a quoted Tcl command argument or
+ * a quoted value in a Tcl expression, this function parses the string
+ * and returns information about the parse. No more than numBytes bytes
+ * will be scanned.
*
* Results:
* The return value is TCL_OK if the string was parsed successfully and
- * TCL_ERROR otherwise. If an error occurs and interp isn't NULL then
- * an error message is left in its result. On a successful return,
- * tokenPtr and numTokens fields of parsePtr are filled in with
- * information about the string that was parsed. Other fields in
- * parsePtr are undefined. termPtr is set to point to the character
- * just after the quoted string's terminating close-quote.
+ * TCL_ERROR otherwise. If an error occurs and interp isn't NULL then an
+ * error message is left in its result. On a successful return, tokenPtr
+ * and numTokens fields of parsePtr are filled in with information about
+ * the string that was parsed. Other fields in parsePtr are undefined.
+ * termPtr is set to point to the character just after the quoted
+ * string's terminating close-quote.
*
* Side effects:
- * If there is insufficient space in parsePtr to hold all the
- * information about the command, then additional space is
- * malloc-ed. If the procedure returns TCL_OK then the caller must
- * eventually invoke Tcl_FreeParse to release any additional space
- * that was allocated.
+ * If there is insufficient space in parsePtr to hold all the information
+ * about the command, then additional space is malloc-ed. If the function
+ * returns TCL_OK then the caller must eventually invoke Tcl_FreeParse to
+ * release any additional space that was allocated.
*
*----------------------------------------------------------------------
*/
int
-Tcl_ParseQuotedString(interp, string, numBytes, parsePtr, append, termPtr)
- Tcl_Interp *interp; /* Interpreter to use for error reporting;
- * if NULL, then no error message is
- * provided. */
- char *string; /* String containing the quoted string.
- * The first character must be '"'. */
- int numBytes; /* Total number of bytes in string. If < 0,
- * the string consists of all bytes up to
- * the first null character. */
- register Tcl_Parse *parsePtr;
- /* Structure to fill in with information
- * about the string. */
- int append; /* Non-zero means append tokens to existing
- * information in parsePtr; zero means
- * ignore existing tokens in parsePtr and
+Tcl_ParseQuotedString(
+ Tcl_Interp *interp, /* Interpreter to use for error reporting; if
+ * NULL, then no error message is provided. */
+ const char *start, /* Start of the quoted string. The first
+ * character must be '"'. */
+ register int numBytes, /* Total number of bytes in string. If < 0,
+ * the string consists of all bytes up to the
+ * first null character. */
+ register Tcl_Parse *parsePtr,
+ /* Structure to fill in with information about
+ * the string. */
+ int append, /* Non-zero means append tokens to existing
+ * information in parsePtr; zero means ignore
+ * existing tokens in parsePtr and
* reinitialize it. */
- char **termPtr; /* If non-NULL, points to word in which to
- * store a pointer to the character just
- * after the quoted string's terminating
- * close-quote if the parse succeeds. */
+ const char **termPtr) /* If non-NULL, points to word in which to
+ * store a pointer to the character just after
+ * the quoted string's terminating close-quote
+ * if the parse succeeds. */
{
- char *end;
-
- if ((numBytes >= 0) || (string == NULL)) {
- end = string + numBytes;
- } else {
- end = string + strlen(string);
+ if ((numBytes == 0) || (start == NULL)) {
+ return TCL_ERROR;
}
-
+ if (numBytes < 0) {
+ numBytes = strlen(start);
+ }
+
if (!append) {
- parsePtr->numWords = 0;
- parsePtr->tokenPtr = parsePtr->staticTokens;
- parsePtr->numTokens = 0;
- parsePtr->tokensAvailable = NUM_STATIC_TOKENS;
- parsePtr->string = string;
- parsePtr->end = end;
- parsePtr->interp = interp;
- parsePtr->errorType = TCL_PARSE_SUCCESS;
+ TclParseInit(interp, start, numBytes, parsePtr);
}
-
- if (ParseTokens(string+1, TYPE_QUOTE, parsePtr) != TCL_OK) {
+
+ if (TCL_OK != ParseTokens(start+1, numBytes-1, TYPE_QUOTE, TCL_SUBST_ALL,
+ parsePtr)) {
goto error;
}
if (*parsePtr->term != '"') {
- if (interp != NULL) {
- Tcl_SetResult(parsePtr->interp, "missing \"", TCL_STATIC);
+ if (parsePtr->interp != NULL) {
+ Tcl_SetObjResult(parsePtr->interp, Tcl_NewStringObj(
+ "missing \"", -1));
}
parsePtr->errorType = TCL_PARSE_MISSING_QUOTE;
- parsePtr->term = string;
+ parsePtr->term = start;
parsePtr->incomplete = 1;
goto error;
}
@@ -2167,7 +1871,7 @@ Tcl_ParseQuotedString(interp, string, numBytes, parsePtr, append, termPtr)
}
return TCL_OK;
- error:
+ error:
Tcl_FreeParse(parsePtr);
return TCL_ERROR;
}
@@ -2175,16 +1879,533 @@ Tcl_ParseQuotedString(interp, string, numBytes, parsePtr, append, termPtr)
/*
*----------------------------------------------------------------------
*
+ * TclSubstParse --
+ *
+ * Token parser used by the [subst] command. Parses the string made up of
+ * 'numBytes' bytes starting at 'bytes'. Parsing is controlled by the
+ * flags argument to provide support for the -nobackslashes, -nocommands,
+ * and -novariables options, as represented by the flag values
+ * TCL_SUBST_BACKSLASHES, TCL_SUBST_COMMANDS, TCL_SUBST_VARIABLES.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The Tcl_Parse struct '*parsePtr' is filled with parse results.
+ * The caller is expected to eventually call Tcl_FreeParse() to properly
+ * cleanup the value written there.
+ *
+ * If a parse error occurs, the Tcl_InterpState value '*statePtr' is
+ * filled with the state created by that error. When *statePtr is written
+ * to, the caller is expected to make the required calls to either
+ * Tcl_RestoreInterpState() or Tcl_DiscardInterpState() to dispose of the
+ * value written there.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclSubstParse(
+ Tcl_Interp *interp,
+ const char *bytes,
+ int numBytes,
+ int flags,
+ Tcl_Parse *parsePtr,
+ Tcl_InterpState *statePtr)
+{
+ int length = numBytes;
+ const char *p = bytes;
+
+ TclParseInit(interp, p, length, parsePtr);
+
+ /*
+ * First parse the string rep of objPtr, as if it were enclosed as a
+ * "-quoted word in a normal Tcl command. Honor flags that selectively
+ * inhibit types of substitution.
+ */
+
+ if (TCL_OK != ParseTokens(p, length, /* mask */ 0, flags, parsePtr)) {
+ /*
+ * There was a parse error. Save the interpreter state for possible
+ * error reporting later.
+ */
+
+ *statePtr = Tcl_SaveInterpState(interp, TCL_ERROR);
+
+ /*
+ * We need to re-parse to get the portion of the string we can [subst]
+ * before the parse error. Sadly, all the Tcl_Token's created by the
+ * first parse attempt are gone, freed according to the public spec
+ * for the Tcl_Parse* routines. The only clue we have is parse.term,
+ * which points to either the unmatched opener, or to characters that
+ * follow a close brace or close quote.
+ *
+ * Call ParseTokens again, working on the string up to parse.term.
+ * Keep repeating until we get a good parse on a prefix.
+ */
+
+ do {
+ parsePtr->numTokens = 0;
+ parsePtr->tokensAvailable = NUM_STATIC_TOKENS;
+ parsePtr->end = parsePtr->term;
+ parsePtr->incomplete = 0;
+ parsePtr->errorType = TCL_PARSE_SUCCESS;
+ } while (TCL_OK !=
+ ParseTokens(p, parsePtr->end - p, 0, flags, parsePtr));
+
+ /*
+ * The good parse will have to be followed by {, (, or [.
+ */
+
+ switch (*(parsePtr->term)) {
+ case '{':
+ /*
+ * Parse error was a missing } in a ${varname} variable
+ * substitution at the toplevel. We will subst everything up to
+ * that broken variable substitution before reporting the parse
+ * error. Substituting the leftover '$' will have no side-effects,
+ * so the current token stream is fine.
+ */
+ break;
+
+ case '(':
+ /*
+ * Parse error was during the parsing of the index part of an
+ * array variable substitution at the toplevel.
+ */
+
+ if (*(parsePtr->term - 1) == '$') {
+ /*
+ * Special case where removing the array index left us with
+ * just a dollar sign (array variable with name the empty
+ * string as its name), instead of with a scalar variable
+ * reference.
+ *
+ * As in the previous case, existing token stream is OK.
+ */
+ } else {
+ /*
+ * The current parse includes a successful parse of a scalar
+ * variable substitution where there should have been an array
+ * variable substitution. We remove that mistaken part of the
+ * parse before moving on. A scalar variable substitution is
+ * two tokens.
+ */
+
+ Tcl_Token *varTokenPtr =
+ parsePtr->tokenPtr + parsePtr->numTokens - 2;
+
+ if (varTokenPtr->type != TCL_TOKEN_VARIABLE) {
+ Tcl_Panic("TclSubstParse: programming error");
+ }
+ if (varTokenPtr[1].type != TCL_TOKEN_TEXT) {
+ Tcl_Panic("TclSubstParse: programming error");
+ }
+ parsePtr->numTokens -= 2;
+ }
+ break;
+ case '[':
+ /*
+ * Parse error occurred during parsing of a toplevel command
+ * substitution.
+ */
+
+ parsePtr->end = p + length;
+ p = parsePtr->term + 1;
+ length = parsePtr->end - p;
+ if (length == 0) {
+ /*
+ * No commands, just an unmatched [. As in previous cases,
+ * existing token stream is OK.
+ */
+ } else {
+ /*
+ * We want to add the parsing of as many commands as we can
+ * within that substitution until we reach the actual parse
+ * error. We'll do additional parsing to determine what length
+ * to claim for the final TCL_TOKEN_COMMAND token.
+ */
+
+ Tcl_Token *tokenPtr;
+ const char *lastTerm = parsePtr->term;
+ Tcl_Parse *nestedPtr =
+ TclStackAlloc(interp, sizeof(Tcl_Parse));
+
+ while (TCL_OK ==
+ Tcl_ParseCommand(NULL, p, length, 0, nestedPtr)) {
+ Tcl_FreeParse(nestedPtr);
+ p = nestedPtr->term + (nestedPtr->term < nestedPtr->end);
+ length = nestedPtr->end - p;
+ if ((length == 0) && (nestedPtr->term == nestedPtr->end)) {
+ /*
+ * If we run out of string, blame the missing close
+ * bracket on the last command, and do not evaluate it
+ * during substitution.
+ */
+
+ break;
+ }
+ lastTerm = nestedPtr->term;
+ }
+ TclStackFree(interp, nestedPtr);
+
+ if (lastTerm == parsePtr->term) {
+ /*
+ * Parse error in first command. No commands to subst, add
+ * no more tokens.
+ */
+ break;
+ }
+
+ /*
+ * Create a command substitution token for whatever commands
+ * got parsed.
+ */
+
+ TclGrowParseTokenArray(parsePtr, 1);
+ tokenPtr = &(parsePtr->tokenPtr[parsePtr->numTokens]);
+ tokenPtr->start = parsePtr->term;
+ tokenPtr->numComponents = 0;
+ tokenPtr->type = TCL_TOKEN_COMMAND;
+ tokenPtr->size = lastTerm - tokenPtr->start + 1;
+ parsePtr->numTokens++;
+ }
+ break;
+
+ default:
+ Tcl_Panic("bad parse in TclSubstParse: %c", p[length]);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclSubstTokens --
+ *
+ * Accepts an array of count Tcl_Token's, and creates a result value in
+ * the interp from concatenating the results of performing Tcl
+ * substitution on each Tcl_Token. Substitution is interrupted if any
+ * non-TCL_OK completion code arises.
+ *
+ * Results:
+ * The return value is a standard Tcl completion code. The result in
+ * interp is the substituted value, or an error message if TCL_ERROR is
+ * returned. If tokensLeftPtr is not NULL, then it points to an int where
+ * the number of tokens remaining to be processed is written.
+ *
+ * Side effects:
+ * Can be anything, depending on the types of substitution done.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TclSubstTokens(
+ Tcl_Interp *interp, /* Interpreter in which to lookup variables,
+ * execute nested commands, and report
+ * errors. */
+ Tcl_Token *tokenPtr, /* Pointer to first in an array of tokens to
+ * evaluate and concatenate. */
+ int count, /* Number of tokens to consider at tokenPtr.
+ * Must be at least 1. */
+ int *tokensLeftPtr, /* If not NULL, points to memory where an
+ * integer representing the number of tokens
+ * left to be substituted will be written */
+ int line, /* The line the script starts on. */
+ int *clNextOuter, /* Information about an outer context for */
+ const char *outerScript) /* continuation line data. This is set by
+ * EvalEx() to properly handle [...]-nested
+ * commands. The 'outerScript' refers to the
+ * most-outer script containing the embedded
+ * command, which is refered to by 'script'.
+ * The 'clNextOuter' refers to the current
+ * entry in the table of continuation lines in
+ * this "master script", and the character
+ * offsets are relative to the 'outerScript'
+ * as well.
+ *
+ * If outerScript == script, then this call is
+ * for words in the outer-most script or
+ * command. See Tcl_EvalEx and TclEvalObjEx
+ * for the places generating arguments for
+ * which this is true. */
+{
+ Tcl_Obj *result;
+ int code = TCL_OK;
+#define NUM_STATIC_POS 20
+ int isLiteral, maxNumCL, numCL, i, adjust;
+ int *clPosition = NULL;
+ Interp *iPtr = (Interp *) interp;
+ int inFile = iPtr->evalFlags & TCL_EVAL_FILE;
+
+ /*
+ * Each pass through this loop will substitute one token, and its
+ * components, if any. The only thing tricky here is that we go to some
+ * effort to pass Tcl_Obj's through untouched, to avoid string copying and
+ * Tcl_Obj creation if possible, to aid performance and limit shimmering.
+ *
+ * Further optimization opportunities might be to check for the equivalent
+ * of Tcl_SetObjResult(interp, Tcl_GetObjResult(interp)) and omit them.
+ */
+
+ /*
+ * For the handling of continuation lines in literals we first check if
+ * this is actually a literal. For if not we can forego the additional
+ * processing. Otherwise we pre-allocate a small table to store the
+ * locations of all continuation lines we find in this literal, if any.
+ * The table is extended if needed.
+ */
+
+ numCL = 0;
+ maxNumCL = 0;
+ isLiteral = 1;
+ for (i=0 ; i < count; i++) {
+ if ((tokenPtr[i].type != TCL_TOKEN_TEXT)
+ && (tokenPtr[i].type != TCL_TOKEN_BS)) {
+ isLiteral = 0;
+ break;
+ }
+ }
+
+ if (isLiteral) {
+ maxNumCL = NUM_STATIC_POS;
+ clPosition = ckalloc(maxNumCL * sizeof(int));
+ }
+
+ adjust = 0;
+ result = NULL;
+ for (; count>0 && code==TCL_OK ; count--, tokenPtr++) {
+ Tcl_Obj *appendObj = NULL;
+ const char *append = NULL;
+ int appendByteLength = 0;
+ char utfCharBytes[TCL_UTF_MAX];
+
+ switch (tokenPtr->type) {
+ case TCL_TOKEN_TEXT:
+ append = tokenPtr->start;
+ appendByteLength = tokenPtr->size;
+ break;
+
+ case TCL_TOKEN_BS:
+ appendByteLength = TclParseBackslash(tokenPtr->start,
+ tokenPtr->size, NULL, utfCharBytes);
+ append = utfCharBytes;
+
+ /*
+ * If the backslash sequence we found is in a literal, and
+ * represented a continuation line, we compute and store its
+ * location (as char offset to the beginning of the _result_
+ * script). We may have to extend the table of locations.
+ *
+ * Note that the continuation line information is relevant even if
+ * the word we are processing is not a literal, as it can affect
+ * nested commands. See the branch for TCL_TOKEN_COMMAND below,
+ * where the adjustment we are tracking here is taken into
+ * account. The good thing is that we do not need a table of
+ * everything, just the number of lines we have to add as
+ * correction.
+ */
+
+ if ((appendByteLength == 1) && (utfCharBytes[0] == ' ')
+ && (tokenPtr->start[1] == '\n')) {
+ if (isLiteral) {
+ int clPos;
+
+ if (result == 0) {
+ clPos = 0;
+ } else {
+ Tcl_GetStringFromObj(result, &clPos);
+ }
+
+ if (numCL >= maxNumCL) {
+ maxNumCL *= 2;
+ clPosition = ckrealloc(clPosition,
+ maxNumCL * sizeof(int));
+ }
+ clPosition[numCL] = clPos;
+ numCL++;
+ }
+ adjust++;
+ }
+ break;
+
+ case TCL_TOKEN_COMMAND: {
+ /* TIP #280: Transfer line information to nested command */
+ iPtr->numLevels++;
+ code = TclInterpReady(interp);
+ if (code == TCL_OK) {
+ /*
+ * Test cases: info-30.{6,8,9}
+ */
+
+ int theline;
+
+ TclAdvanceContinuations(&line, &clNextOuter,
+ tokenPtr->start - outerScript);
+ theline = line + adjust;
+ code = TclEvalEx(interp, tokenPtr->start+1, tokenPtr->size-2,
+ 0, theline, clNextOuter, outerScript);
+
+ TclAdvanceLines(&line, tokenPtr->start+1,
+ tokenPtr->start + tokenPtr->size - 1);
+
+ /*
+ * Restore flag reset by nested eval for future bracketed
+ * commands and their cmdframe setup
+ */
+
+ if (inFile) {
+ iPtr->evalFlags |= TCL_EVAL_FILE;
+ }
+ }
+ iPtr->numLevels--;
+ TclResetCancellation(interp, 0);
+ appendObj = Tcl_GetObjResult(interp);
+ break;
+ }
+
+ case TCL_TOKEN_VARIABLE: {
+ Tcl_Obj *arrayIndex = NULL;
+ Tcl_Obj *varName = NULL;
+
+ if (tokenPtr->numComponents > 1) {
+ /*
+ * Subst the index part of an array variable reference.
+ */
+
+ code = TclSubstTokens(interp, tokenPtr+2,
+ tokenPtr->numComponents - 1, NULL, line, NULL, NULL);
+ arrayIndex = Tcl_GetObjResult(interp);
+ Tcl_IncrRefCount(arrayIndex);
+ }
+
+ if (code == TCL_OK) {
+ varName = Tcl_NewStringObj(tokenPtr[1].start,
+ tokenPtr[1].size);
+ appendObj = Tcl_ObjGetVar2(interp, varName, arrayIndex,
+ TCL_LEAVE_ERR_MSG);
+ Tcl_DecrRefCount(varName);
+ if (appendObj == NULL) {
+ code = TCL_ERROR;
+ }
+ }
+
+ switch (code) {
+ case TCL_OK: /* Got value */
+ case TCL_ERROR: /* Already have error message */
+ case TCL_BREAK: /* Will not substitute anyway */
+ case TCL_CONTINUE: /* Will not substitute anyway */
+ break;
+ default:
+ /*
+ * All other return codes, we will subst the result from the
+ * code-throwing evaluation.
+ */
+
+ appendObj = Tcl_GetObjResult(interp);
+ }
+
+ if (arrayIndex != NULL) {
+ Tcl_DecrRefCount(arrayIndex);
+ }
+ count -= tokenPtr->numComponents;
+ tokenPtr += tokenPtr->numComponents;
+ break;
+ }
+
+ default:
+ Tcl_Panic("unexpected token type in TclSubstTokens: %d",
+ tokenPtr->type);
+ }
+
+ if ((code == TCL_BREAK) || (code == TCL_CONTINUE)) {
+ /*
+ * Inhibit substitution.
+ */
+ continue;
+ }
+
+ if (result == NULL) {
+ /*
+ * First pass through. If we have a Tcl_Obj, just use it. If not,
+ * create one from our string.
+ */
+
+ if (appendObj != NULL) {
+ result = appendObj;
+ } else {
+ result = Tcl_NewStringObj(append, appendByteLength);
+ }
+ Tcl_IncrRefCount(result);
+ } else {
+ /*
+ * Subsequent passes. Append to result.
+ */
+
+ if (Tcl_IsShared(result)) {
+ Tcl_DecrRefCount(result);
+ result = Tcl_DuplicateObj(result);
+ Tcl_IncrRefCount(result);
+ }
+ if (appendObj != NULL) {
+ Tcl_AppendObjToObj(result, appendObj);
+ } else {
+ Tcl_AppendToObj(result, append, appendByteLength);
+ }
+ }
+ }
+
+ if (code != TCL_ERROR) { /* Keep error message in result! */
+ if (result != NULL) {
+ Tcl_SetObjResult(interp, result);
+
+ /*
+ * If the code found continuation lines (which implies that this
+ * word is a literal), then we store the accumulated table of
+ * locations in the thread-global data structure for the bytecode
+ * compiler to find later, assuming that the literal is a script
+ * which will be compiled.
+ */
+
+ if (numCL) {
+ TclContinuationsEnter(result, numCL, clPosition);
+ }
+
+ /*
+ * Release the temp table we used to collect the locations of
+ * continuation lines, if any.
+ */
+
+ if (maxNumCL) {
+ ckfree(clPosition);
+ }
+ } else {
+ Tcl_ResetResult(interp);
+ }
+ }
+ if (tokensLeftPtr != NULL) {
+ *tokensLeftPtr = count;
+ }
+ if (result != NULL) {
+ Tcl_DecrRefCount(result);
+ }
+ return code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* CommandComplete --
*
- * This procedure is shared by TclCommandComplete and
- * Tcl_ObjCommandcoComplete; it does all the real work of seeing
- * whether a script is complete
+ * This function is shared by TclCommandComplete and
+ * Tcl_ObjCommandComplete; it does all the real work of seeing whether a
+ * script is complete
*
* Results:
* 1 is returned if the script is complete, 0 if there are open
- * delimiters such as " or (. 1 is also returned if there is a
- * parse error in the script other than unmatched delimiters.
+ * delimiters such as " or (. 1 is also returned if there is a parse
+ * error in the script other than unmatched delimiters.
*
* Side effects:
* None.
@@ -2192,21 +2413,20 @@ Tcl_ParseQuotedString(interp, string, numBytes, parsePtr, append, termPtr)
*----------------------------------------------------------------------
*/
-static int
-CommandComplete(script, length)
- char *script; /* Script to check. */
- int length; /* Number of bytes in script. */
+static inline int
+CommandComplete(
+ const char *script, /* Script to check. */
+ int numBytes) /* Number of bytes in script. */
{
Tcl_Parse parse;
- char *p, *end;
+ const char *p, *end;
int result;
p = script;
- end = p + length;
- while (Tcl_ParseCommand((Tcl_Interp *) NULL, p, end - p, 0, &parse)
- == TCL_OK) {
+ end = p + numBytes;
+ while (Tcl_ParseCommand(NULL, p, end - p, 0, &parse) == TCL_OK) {
p = parse.commandStart + parse.commandSize;
- if (*p == 0) {
+ if (p >= end) {
break;
}
Tcl_FreeParse(&parse);
@@ -2225,14 +2445,14 @@ CommandComplete(script, length)
*
* Tcl_CommandComplete --
*
- * Given a partial or complete Tcl script, this procedure
- * determines whether the script is complete in the sense
- * of having matched braces and quotes and brackets.
+ * Given a partial or complete Tcl script, this function determines
+ * whether the script is complete in the sense of having matched braces
+ * and quotes and brackets.
*
* Results:
- * 1 is returned if the script is complete, 0 otherwise.
- * 1 is also returned if there is a parse error in the script
- * other than unmatched delimiters.
+ * 1 is returned if the script is complete, 0 otherwise. 1 is also
+ * returned if there is a parse error in the script other than unmatched
+ * delimiters.
*
* Side effects:
* None.
@@ -2241,8 +2461,8 @@ CommandComplete(script, length)
*/
int
-Tcl_CommandComplete(script)
- char *script; /* Script to check. */
+Tcl_CommandComplete(
+ const char *script) /* Script to check. */
{
return CommandComplete(script, (int) strlen(script));
}
@@ -2252,9 +2472,9 @@ Tcl_CommandComplete(script)
*
* TclObjCommandComplete --
*
- * Given a partial or complete Tcl command in a Tcl object, this
- * procedure determines whether the command is complete in the sense of
- * having matched braces and quotes and brackets.
+ * Given a partial or complete Tcl command in a Tcl object, this function
+ * determines whether the command is complete in the sense of having
+ * matched braces and quotes and brackets.
*
* Results:
* 1 is returned if the command is complete, 0 otherwise.
@@ -2266,14 +2486,13 @@ Tcl_CommandComplete(script)
*/
int
-TclObjCommandComplete(objPtr)
- Tcl_Obj *objPtr; /* Points to object holding script
- * to check. */
+TclObjCommandComplete(
+ Tcl_Obj *objPtr) /* Points to object holding script to
+ * check. */
{
- char *script;
int length;
+ const char *script = Tcl_GetStringFromObj(objPtr, &length);
- script = Tcl_GetStringFromObj(objPtr, &length);
return CommandComplete(script, length);
}
@@ -2282,8 +2501,8 @@ TclObjCommandComplete(objPtr)
*
* TclIsLocalScalar --
*
- * Check to see if a given string is a legal scalar variable
- * name with no namespace qualifiers or substitutions.
+ * Check to see if a given string is a legal scalar variable name with no
+ * namespace qualifiers or substitutions.
*
* Results:
* Returns 1 if the variable is a local scalar.
@@ -2295,34 +2514,42 @@ TclObjCommandComplete(objPtr)
*/
int
-TclIsLocalScalar(src, len)
- CONST char *src;
- int len;
+TclIsLocalScalar(
+ const char *src,
+ int len)
{
- CONST char *p;
- CONST char *lastChar = src + (len - 1);
+ const char *p;
+ const char *lastChar = src + (len - 1);
- for (p = src; p <= lastChar; p++) {
- if ((CHAR_TYPE(*p) != TYPE_NORMAL) &&
- (CHAR_TYPE(*p) != TYPE_COMMAND_END)) {
+ for (p=src ; p<=lastChar ; p++) {
+ if ((CHAR_TYPE(*p) != TYPE_NORMAL)
+ && (CHAR_TYPE(*p) != TYPE_COMMAND_END)) {
/*
- * TCL_COMMAND_END is returned for the last character
- * of the string. By this point we know it isn't
- * an array or namespace reference.
+ * TCL_COMMAND_END is returned for the last character of the
+ * string. By this point we know it isn't an array or namespace
+ * reference.
*/
return 0;
}
- if (*p == '(') {
- if (*lastChar == ')') { /* we have an array element */
+ if (*p == '(') {
+ if (*lastChar == ')') { /* We have an array element */
return 0;
}
} else if (*p == ':') {
- if ((p != lastChar) && *(p+1) == ':') { /* qualified name */
+ if ((p != lastChar) && *(p+1) == ':') { /* qualified name */
return 0;
}
}
}
-
+
return 1;
}
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */