#include "tclInt.h"
#include "tclPort.h"
#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) (charTypeTable+128)[(int)(c)]
static CONST char charTypeTable[] = {
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_SUBS, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_SPACE, TYPE_COMMAND_END, TYPE_SPACE,
TYPE_SPACE, TYPE_SPACE, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_SPACE, TYPE_NORMAL, TYPE_QUOTE, TYPE_NORMAL,
TYPE_SUBS, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_CLOSE_PAREN, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_COMMAND_END,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_SUBS,
TYPE_SUBS, TYPE_CLOSE_BRACK, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_BRACE,
TYPE_NORMAL, TYPE_BRACE, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
};
static int CommandComplete _ANSI_ARGS_((CONST char *script,
int numBytes));
static int ParseComment _ANSI_ARGS_((CONST char *src, int numBytes,
Tcl_Parse *parsePtr));
static int ParseTokens _ANSI_ARGS_((CONST char *src, int numBytes,
int mask, Tcl_Parse *parsePtr));
int
Tcl_ParseCommand(interp, string, numBytes, nested, parsePtr)
Tcl_Interp *interp;
CONST char *string;
register int numBytes;
int nested;
register Tcl_Parse *parsePtr;
{
register CONST char *src;
char type;
Tcl_Token *tokenPtr;
int wordIndex;
int terminators;
CONST char *termPtr;
int scanned;
if ((string == NULL) && (numBytes>0)) {
if (interp != NULL) {
Tcl_SetResult(interp, "can't parse a NULL pointer", TCL_STATIC);
}
return TCL_ERROR;
}
if (numBytes < 0) {
numBytes = strlen(string);
}
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 {
terminators = TYPE_COMMAND_END;
}
scanned = ParseComment(string, numBytes, parsePtr);
src = (string + scanned); numBytes -= scanned;
if (numBytes == 0) {
if (nested) {
parsePtr->incomplete = nested;
}
}
parsePtr->commandStart = src;
while (1) {
if (parsePtr->numTokens == parsePtr->tokensAvailable) {
TclExpandTokenArray(parsePtr);
}
wordIndex = parsePtr->numTokens;
tokenPtr = &parsePtr->tokenPtr[wordIndex];
tokenPtr->type = TCL_TOKEN_WORD;
scanned = TclParseWhiteSpace(src, numBytes, parsePtr, &type);
src += scanned; numBytes -= scanned;
if (numBytes == 0) {
break;
}
if ((type & terminators) != 0) {
parsePtr->term = src;
src++;
break;
}
tokenPtr->start = src;
parsePtr->numTokens++;
parsePtr->numWords++;
if (*src == '"') {
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, numBytes,
parsePtr, 1, &termPtr) != TCL_OK) {
goto error;
}
src = termPtr; numBytes = parsePtr->end - src;
} else {
if (ParseTokens(src, numBytes, TYPE_SPACE|terminators,
parsePtr) != TCL_OK) {
goto error;
}
src = parsePtr->term; numBytes = parsePtr->end - src;
}
tokenPtr = &parsePtr->tokenPtr[wordIndex];
tokenPtr->size = src - tokenPtr->start;
tokenPtr->numComponents = parsePtr->numTokens - (wordIndex + 1);
if ((tokenPtr->numComponents == 1)
&& (tokenPtr[1].type == TCL_TOKEN_TEXT)) {
tokenPtr->type = TCL_TOKEN_SIMPLE_WORD;
}
scanned = TclParseWhiteSpace(src, numBytes, parsePtr, &type);
if (scanned) {
src += scanned; numBytes -= scanned;
continue;
}
if (numBytes == 0) {
break;
}
if ((type & terminators) != 0) {
parsePtr->term = src;
src++;
break;
}
if (src[-1] == '"') {
if (interp != NULL) {
Tcl_SetResult(interp, "extra characters after close-quote",
TCL_STATIC);
}
parsePtr->errorType = TCL_PARSE_QUOTE_EXTRA;
} else {
if (interp != NULL) {
Tcl_SetResult(interp, "extra characters after close-brace",
TCL_STATIC);
}
parsePtr->errorType = TCL_PARSE_BRACE_EXTRA;
}
parsePtr->term = src;
goto error;
}
parsePtr->commandSize = src - parsePtr->commandStart;
return TCL_OK;
error:
Tcl_FreeParse(parsePtr);
if (parsePtr->commandStart == NULL) {
parsePtr->commandStart = string;
}
parsePtr->commandSize = parsePtr->term - parsePtr->commandStart;
return TCL_ERROR;
}
int
TclParseWhiteSpace(src, numBytes, parsePtr, typePtr)
CONST char *src;
register int numBytes;
Tcl_Parse *parsePtr;
char *typePtr;
{
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) {
parsePtr->incomplete = 1;
break;
}
continue;
}
break;
}
*typePtr = type;
return (p - src);
}
int
TclParseHex(src, numBytes, resultPtr)
CONST char *src;
int numBytes;
Tcl_UniChar *resultPtr;
{
Tcl_UniChar result = 0;
register CONST char *p = src;
while (numBytes--) {
unsigned char digit = UCHAR(*p);
if (!isxdigit(digit))
break;
++p;
result <<= 4;
if (digit >= 'a') {
result |= (10 + digit - 'a');
} else if (digit >= 'A') {
result |= (10 + digit - 'A');
} else {
result |= (digit - '0');
}
}
*resultPtr = result;
return (p - src);
}
int
TclParseBackslash(src, numBytes, readPtr, dst)
CONST char * src;
int numBytes;
int *readPtr;
char *dst;
{
register CONST char *p = src+1;
Tcl_UniChar result;
int count;
char buf[TCL_UTF_MAX];
if (numBytes == 0) {
if (readPtr != NULL) {
*readPtr = 0;
}
return 0;
}
if (dst == NULL) {
dst = buf;
}
if (numBytes == 1) {
result = '\\';
count = 1;
goto done;
}
count = 2;
switch (*p) {
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-1, &result);
if (count == 2) {
result = 'x';
} else {
result = (unsigned char) result;
}
break;
case 'u':
count += TclParseHex(p+1, (numBytes > 5) ? 4 : numBytes-1, &result);
if (count == 2) {
result = 'u';
}
break;
case '\n':
count--;
do {
p++; count++;
} while ((count < numBytes) && ((*p == ' ') || (*p == '\t')));
result = ' ';
break;
case 0:
result = '\\';
count = 1;
break;
default:
if (isdigit(UCHAR(*p)) && (UCHAR(*p) < '8')) {
result = (unsigned char)(*p - '0');
p++;
if ((numBytes == 2) || !isdigit(UCHAR(*p))
|| (UCHAR(*p) >= '8')) {
break;
}
count = 3;
result = (unsigned char)((result << 3) + (*p - '0'));
p++;
if ((numBytes == 3) || !isdigit(UCHAR(*p))
|| (UCHAR(*p) >= '8')) {
break;
}
count = 4;
result = (unsigned char)((result << 3) + (*p - '0'));
break;
}
if (Tcl_UtfCharComplete(p, numBytes - 1)) {
count = Tcl_UtfToUniChar(p, &result) + 1;
} else {
char utfBytes[TCL_UTF_MAX];
memcpy(utfBytes, p, (size_t) (numBytes - 1));
utfBytes[numBytes - 1] = '\0';
count = Tcl_UtfToUniChar(utfBytes, &result) + 1;
}
break;
}
done:
if (readPtr != NULL) {
*readPtr = count;
}
return Tcl_UniCharToUtf((int) result, dst);
}
static int
ParseComment(src, numBytes, parsePtr)
CONST char *src;
register int numBytes;
Tcl_Parse *parsePtr;
{
register CONST char *p = src;
while (numBytes) {
char type;
int scanned;
do {
scanned = TclParseWhiteSpace(p, numBytes, parsePtr, &type);
p += scanned; numBytes -= scanned;
} while (numBytes && (*p == '\n') && (p++,numBytes--));
if ((numBytes == 0) || (*p != '#')) {
break;
}
if (parsePtr->commentStart == NULL) {
parsePtr->commentStart = p;
}
while (numBytes) {
if (*p == '\\') {
scanned = TclParseWhiteSpace(p, numBytes, parsePtr, &type);
if (scanned) {
p += scanned; numBytes -= scanned;
} else {
TclParseBackslash(p, numBytes, &scanned, NULL);
p += scanned; numBytes -= scanned;
}
} else {
p++; numBytes--;
if (p[-1] == '\n') {
break;
}
}
}
parsePtr->commentSize = p - parsePtr->commentStart;
}
return (p - src);
}
static int
ParseTokens(src, numBytes, mask, parsePtr)
register CONST char *src;
register int numBytes;
int mask;
Tcl_Parse *parsePtr;
{
char type;
int originalTokens, varToken;
Tcl_Token *tokenPtr;
Tcl_Parse nested;
originalTokens = parsePtr->numTokens;
while (numBytes && !((type = CHAR_TYPE(*src)) & mask)) {
if (parsePtr->numTokens == parsePtr->tokensAvailable) {
TclExpandTokenArray(parsePtr);
}
tokenPtr = &parsePtr->tokenPtr[parsePtr->numTokens];
tokenPtr->start = src;
tokenPtr->numComponents = 0;
if ((type & TYPE_SUBS) == 0) {
while ((++src, --numBytes)
&& !(CHAR_TYPE(*src) & (mask | TYPE_SUBS))) {
}
tokenPtr->type = TCL_TOKEN_TEXT;
tokenPtr->size = src - tokenPtr->start;
parsePtr->numTokens++;
} else if (*src == '$') {
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 == '[') {
src++; numBytes--;
while (1) {
if (Tcl_ParseCommand(parsePtr->interp, src,
numBytes, 1, &nested) != TCL_OK) {
parsePtr->errorType = nested.errorType;
parsePtr->term = nested.term;
parsePtr->incomplete = nested.incomplete;
return TCL_ERROR;
}
src = nested.commandStart + nested.commandSize;
numBytes = parsePtr->end - src;
if (nested.tokenPtr != nested.staticTokens) {
ckfree((char *) nested.tokenPtr);
}
if ((*nested.term == ']') && !nested.incomplete) {
break;
}
if (numBytes == 0) {
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 == '\\') {
TclParseBackslash(src, numBytes, &tokenPtr->size, NULL);
if (tokenPtr->size == 1) {
tokenPtr->type = TCL_TOKEN_TEXT;
parsePtr->numTokens++;
src++; numBytes--;
continue;
}
if (src[1] == '\n') {
if (numBytes == 2) {
parsePtr->incomplete = 1;
}
if (mask & TYPE_SPACE) {
if (parsePtr->numTokens == originalTokens) {
goto finishToken;
}
break;
}
}
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 {
panic("ParseTokens encountered unknown character");
}
}
if (parsePtr->numTokens == originalTokens) {
if (parsePtr->numTokens == parsePtr->tokensAvailable) {
TclExpandTokenArray(parsePtr);
}
tokenPtr = &parsePtr->tokenPtr[parsePtr->numTokens];
tokenPtr->start = src;
tokenPtr->numComponents = 0;
finishToken:
tokenPtr->type = TCL_TOKEN_TEXT;
tokenPtr->size = 0;
parsePtr->numTokens++;
}
parsePtr->term = src;
return TCL_OK;
}
void
Tcl_FreeParse(parsePtr)
Tcl_Parse *parsePtr;
{
if (parsePtr->tokenPtr != parsePtr->staticTokens) {
ckfree((char *) parsePtr->tokenPtr);
parsePtr->tokenPtr = parsePtr->staticTokens;
}
}
void
TclExpandTokenArray(parsePtr)
Tcl_Parse *parsePtr;
{
int newCount;
Tcl_Token *newPtr;
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;
}
int
Tcl_ParseVarName(interp, string, numBytes, parsePtr, append)
Tcl_Interp *interp;
CONST char *string;
register int numBytes;
Tcl_Parse *parsePtr;
int append;
{
Tcl_Token *tokenPtr;
register CONST char *src;
unsigned char c;
int varIndex, offset;
Tcl_UniChar ch;
unsigned array;
if ((numBytes == 0) || (string == NULL)) {
return TCL_ERROR;
}
if (numBytes < 0) {
numBytes = strlen(string);
}
if (!append) {
parsePtr->numWords = 0;
parsePtr->tokenPtr = parsePtr->staticTokens;
parsePtr->numTokens = 0;
parsePtr->tokensAvailable = NUM_STATIC_TOKENS;
parsePtr->string = string;
parsePtr->end = (string + numBytes);
parsePtr->interp = interp;
parsePtr->errorType = TCL_PARSE_SUCCESS;
parsePtr->incomplete = 0;
}
src = string;
if ((parsePtr->numTokens + 2) > parsePtr->tokensAvailable) {
TclExpandTokenArray(parsePtr);
}
tokenPtr = &parsePtr->tokenPtr[parsePtr->numTokens];
tokenPtr->type = TCL_TOKEN_VARIABLE;
tokenPtr->start = src;
varIndex = parsePtr->numTokens;
parsePtr->numTokens++;
tokenPtr++;
src++; numBytes--;
if (numBytes == 0) {
goto justADollarSign;
}
tokenPtr->type = TCL_TOKEN_TEXT;
tokenPtr->start = src;
tokenPtr->numComponents = 0;
if (*src == '{') {
src++; numBytes--;
tokenPtr->type = TCL_TOKEN_TEXT;
tokenPtr->start = src;
tokenPtr->numComponents = 0;
while (numBytes && (*src != '}')) {
numBytes--; src++;
}
if (numBytes == 0) {
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;
}
tokenPtr->size = src - tokenPtr->start;
tokenPtr[-1].size = src - tokenPtr[-1].start;
parsePtr->numTokens++;
src++;
} else {
tokenPtr->type = TCL_TOKEN_TEXT;
tokenPtr->start = src;
tokenPtr->numComponents = 0;
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 == '_')) {
src += offset; numBytes -= offset;
continue;
}
if ((c == ':') && (numBytes != 1) && (src[1] == ':')) {
src += 2; numBytes -= 2;
while (numBytes && (*src == ':')) {
src++; numBytes--;
}
continue;
}
break;
}
array = (numBytes && (*src == '('));
tokenPtr->size = src - tokenPtr->start;
if ((tokenPtr->size == 0) && !array) {
goto justADollarSign;
}
parsePtr->numTokens++;
if (array) {
if (ParseTokens(src+1, numBytes-1, TYPE_CLOSE_PAREN, parsePtr)
!= TCL_OK) {
goto error;
}
if ((parsePtr->term == (src + numBytes))
|| (*parsePtr->term != ')')) {
if (parsePtr->interp != NULL) {
Tcl_SetResult(parsePtr->interp, "missing )",
TCL_STATIC);
}
parsePtr->errorType = TCL_PARSE_MISSING_PAREN;
parsePtr->term = src;
parsePtr->incomplete = 1;
goto error;
}
src = parsePtr->term + 1;
}
}
tokenPtr = &parsePtr->tokenPtr[varIndex];
tokenPtr->size = src - tokenPtr->start;
tokenPtr->numComponents = parsePtr->numTokens - (varIndex + 1);
return TCL_OK;
justADollarSign:
tokenPtr = &parsePtr->tokenPtr[varIndex];
tokenPtr->type = TCL_TOKEN_TEXT;
tokenPtr->size = 1;
tokenPtr->numComponents = 0;
return TCL_OK;
error:
Tcl_FreeParse(parsePtr);
return TCL_ERROR;
}
CONST char *
Tcl_ParseVar(interp, string, termPtr)
Tcl_Interp *interp;
register CONST char *string;
CONST char **termPtr;
{
Tcl_Parse parse;
register Tcl_Obj *objPtr;
int code;
if (Tcl_ParseVarName(interp, string, -1, &parse, 0) != TCL_OK) {
return NULL;
}
if (termPtr != NULL) {
*termPtr = string + parse.tokenPtr->size;
}
if (parse.numTokens == 1) {
return "$";
}
code = Tcl_EvalTokensStandard(interp, parse.tokenPtr, parse.numTokens);
if (code != TCL_OK) {
return NULL;
}
objPtr = Tcl_GetObjResult(interp);
if (!Tcl_IsShared(objPtr)) {
Tcl_IncrRefCount(objPtr);
}
Tcl_ResetResult(interp);
return TclGetString(objPtr);
}
int
Tcl_ParseBraces(interp, string, numBytes, parsePtr, append, termPtr)
Tcl_Interp *interp;
CONST char *string;
register int numBytes;
register Tcl_Parse *parsePtr;
int append;
CONST char **termPtr;
{
Tcl_Token *tokenPtr;
register CONST char *src;
int startIndex, level, length;
if ((numBytes == 0) || (string == NULL)) {
return TCL_ERROR;
}
if (numBytes < 0) {
numBytes = strlen(string);
}
if (!append) {
parsePtr->numWords = 0;
parsePtr->tokenPtr = parsePtr->staticTokens;
parsePtr->numTokens = 0;
parsePtr->tokensAvailable = NUM_STATIC_TOKENS;
parsePtr->string = string;
parsePtr->end = (string + numBytes);
parsePtr->interp = interp;
parsePtr->errorType = TCL_PARSE_SUCCESS;
}
src = string;
startIndex = parsePtr->numTokens;
if (parsePtr->numTokens == parsePtr->tokensAvailable) {
TclExpandTokenArray(parsePtr);
}
tokenPtr = &parsePtr->tokenPtr[startIndex];
tokenPtr->type = TCL_TOKEN_TEXT;
tokenPtr->start = src+1;
tokenPtr->numComponents = 0;
level = 1;
while (1) {
while (++src, --numBytes) {
if (CHAR_TYPE(*src) != TYPE_NORMAL) {
break;
}
}
if (numBytes == 0) {
register int openBrace = 0;
parsePtr->errorType = TCL_PARSE_MISSING_BRACE;
parsePtr->term = string;
parsePtr->incomplete = 1;
if (interp == NULL) {
goto error;
}
Tcl_SetResult(interp, "missing close-brace", TCL_STATIC);
for (; src > string; src--) {
switch (*src) {
case '{':
openBrace = 1;
break;
case '\n':
openBrace = 0;
break;
case '#' :
if (openBrace && (isspace(UCHAR(src[-1])))) {
Tcl_AppendResult(interp,
": possible unbalanced brace in comment",
(char *) NULL);
goto error;
}
break;
}
}
error:
Tcl_FreeParse(parsePtr);
return TCL_ERROR;
}
switch (*src) {
case '{':
level++;
break;
case '}':
if (--level == 0) {
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')) {
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);
}
tokenPtr = &parsePtr->tokenPtr[parsePtr->numTokens];
tokenPtr->type = TCL_TOKEN_BS;
tokenPtr->start = src;
tokenPtr->size = length;
tokenPtr->numComponents = 0;
parsePtr->numTokens++;
src += length - 1;
numBytes -= length - 1;
tokenPtr++;
tokenPtr->type = TCL_TOKEN_TEXT;
tokenPtr->start = src + 1;
tokenPtr->numComponents = 0;
} else {
src += length - 1;
numBytes -= length - 1;
}
break;
}
}
}
int
Tcl_ParseQuotedString(interp, string, numBytes, parsePtr, append, termPtr)
Tcl_Interp *interp;
CONST char *string;
register int numBytes;
register Tcl_Parse *parsePtr;
int append;
CONST char **termPtr;
{
if ((numBytes == 0) || (string == NULL)) {
return TCL_ERROR;
}
if (numBytes < 0) {
numBytes = strlen(string);
}
if (!append) {
parsePtr->numWords = 0;
parsePtr->tokenPtr = parsePtr->staticTokens;
parsePtr->numTokens = 0;
parsePtr->tokensAvailable = NUM_STATIC_TOKENS;
parsePtr->string = string;
parsePtr->end = (string + numBytes);
parsePtr->interp = interp;
parsePtr->errorType = TCL_PARSE_SUCCESS;
}
if (ParseTokens(string+1, numBytes-1, TYPE_QUOTE, parsePtr) != TCL_OK) {
goto error;
}
if (*parsePtr->term != '"') {
if (interp != NULL) {
Tcl_SetResult(parsePtr->interp, "missing \"", TCL_STATIC);
}
parsePtr->errorType = TCL_PARSE_MISSING_QUOTE;
parsePtr->term = string;
parsePtr->incomplete = 1;
goto error;
}
if (termPtr != NULL) {
*termPtr = (parsePtr->term + 1);
}
return TCL_OK;
error:
Tcl_FreeParse(parsePtr);
return TCL_ERROR;
}
static int
CommandComplete(script, numBytes)
CONST char *script;
int numBytes;
{
Tcl_Parse parse;
CONST char *p, *end;
int result;
p = script;
end = p + numBytes;
while (Tcl_ParseCommand((Tcl_Interp *) NULL, p, end - p, 0, &parse)
== TCL_OK) {
p = parse.commandStart + parse.commandSize;
if (*p == 0) {
break;
}
Tcl_FreeParse(&parse);
}
if (parse.incomplete) {
result = 0;
} else {
result = 1;
}
Tcl_FreeParse(&parse);
return result;
}
int
Tcl_CommandComplete(script)
CONST char *script;
{
return CommandComplete(script, (int) strlen(script));
}
int
TclObjCommandComplete(objPtr)
Tcl_Obj *objPtr;
{
CONST char *script;
int length;
script = Tcl_GetStringFromObj(objPtr, &length);
return CommandComplete(script, length);
}
int
TclIsLocalScalar(src, len)
CONST char *src;
int len;
{
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)) {
return 0;
}
if (*p == '(') {
if (*lastChar == ')') {
return 0;
}
} else if (*p == ':') {
if ((p != lastChar) && *(p+1) == ':') {
return 0;
}
}
}
return 1;
}