/*1* tclParse.c --2*3* This file contains a collection of procedures that are used4* to parse Tcl commands or parts of commands (like quoted5* strings or nested sub-commands).6*7* Copyright (c) 1987-1993 The Regents of the University of California.8* Copyright (c) 1994-1996 Sun Microsystems, Inc.9*10* See the file "license.terms" for information on usage and redistribution11* of this file, and for a DISCLAIMER OF ALL WARRANTIES.12*13* SCCS: @(#) tclParse.c 1.51 96/09/06 09:47:2914*/1516#include "tclInt.h"17#include "tclPort.h"1819/*20* The following table assigns a type to each character. Only types21* meaningful to Tcl parsing are represented here. The table is22* designed to be referenced with either signed or unsigned characters,23* so it has 384 entries. The first 128 entries correspond to negative24* character values, the next 256 correspond to positive character25* values. The last 128 entries are identical to the first 128. The26* table is always indexed with a 128-byte offset (the 128th entry27* corresponds to a 0 character value).28*/2930char tclTypeTable[] = {31/*32* Negative character values, from -128 to -1:33*/3435TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,36TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,37TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,38TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,39TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,40TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,41TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,42TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,43TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,44TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,45TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,46TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,47TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,48TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,49TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,50TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,51TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,52TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,53TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,54TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,55TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,56TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,57TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,58TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,59TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,60TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,61TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,62TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,63TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,64TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,65TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,66TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,6768/*69* Positive character values, from 0-127:70*/7172TCL_COMMAND_END, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,73TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,74TCL_NORMAL, TCL_SPACE, TCL_COMMAND_END, TCL_SPACE,75TCL_SPACE, TCL_SPACE, TCL_NORMAL, TCL_NORMAL,76TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,77TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,78TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,79TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,80TCL_SPACE, TCL_NORMAL, TCL_QUOTE, TCL_NORMAL,81TCL_DOLLAR, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,82TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,83TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,84TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,85TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,86TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_COMMAND_END,87TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,88TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,89TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,90TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,91TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,92TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,93TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,94TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_OPEN_BRACKET,95TCL_BACKSLASH, TCL_COMMAND_END, TCL_NORMAL, TCL_NORMAL,96TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,97TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,98TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,99TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,100TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,101TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,102TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_OPEN_BRACE,103TCL_NORMAL, TCL_CLOSE_BRACE, TCL_NORMAL, TCL_NORMAL,104105/*106* Large unsigned character values, from 128-255:107*/108109TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,110TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,111TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,112TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,113TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,114TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,115TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,116TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,117TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,118TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,119TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,120TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,121TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,122TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,123TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,124TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,125TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,126TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,127TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,128TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,129TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,130TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,131TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,132TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,133TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,134TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,135TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,136TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,137TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,138TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,139TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,140TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL,141};142143/*144* Function prototypes for procedures local to this file:145*/146147static char * QuoteEnd _ANSI_ARGS_((char *string, int term));148static char * ScriptEnd _ANSI_ARGS_((char *p, int nested));149static char * VarNameEnd _ANSI_ARGS_((char *string));150151/*152*----------------------------------------------------------------------153*154* Tcl_Backslash --155*156* Figure out how to handle a backslash sequence.157*158* Results:159* The return value is the character that should be substituted160* in place of the backslash sequence that starts at src. If161* readPtr isn't NULL then it is filled in with a count of the162* number of characters in the backslash sequence.163*164* Side effects:165* None.166*167*----------------------------------------------------------------------168*/169170char171Tcl_Backslash(src, readPtr)172char *src; /* Points to the backslash character of173* a backslash sequence. */174int *readPtr; /* Fill in with number of characters read175* from src, unless NULL. */176{177register char *p = src+1;178char result;179int count;180181count = 2;182183switch (*p) {184/*185* Note: in the conversions below, use absolute values (e.g.,186* 0xa) rather than symbolic values (e.g. \n) that get converted187* by the compiler. It's possible that compilers on some188* platforms will do the symbolic conversions differently, which189* could result in non-portable Tcl scripts.190*/191192case 'a':193result = 0x7;194break;195case 'b':196result = 0x8;197break;198case 'f':199result = 0xc;200break;201case 'n':202result = 0xa;203break;204case 'r':205result = 0xd;206break;207case 't':208result = 0x9;209break;210case 'v':211result = 0xb;212break;213case 'x':214if (isxdigit(UCHAR(p[1]))) {215char *end;216217result = (char) strtoul(p+1, &end, 16);218count = end - src;219} else {220count = 2;221result = 'x';222}223break;224case '\n':225do {226p++;227} while ((*p == ' ') || (*p == '\t'));228result = ' ';229count = p - src;230break;231case 0:232result = '\\';233count = 1;234break;235default:236if (isdigit(UCHAR(*p))) {237result = (char)(*p - '0');238p++;239if (!isdigit(UCHAR(*p))) {240break;241}242count = 3;243result = (char)((result << 3) + (*p - '0'));244p++;245if (!isdigit(UCHAR(*p))) {246break;247}248count = 4;249result = (char)((result << 3) + (*p - '0'));250break;251}252result = *p;253count = 2;254break;255}256257if (readPtr != NULL) {258*readPtr = count;259}260return result;261}262263/*264*--------------------------------------------------------------265*266* TclParseQuotes --267*268* This procedure parses a double-quoted string such as a269* quoted Tcl command argument or a quoted value in a Tcl270* expression. This procedure is also used to parse array271* element names within parentheses, or anything else that272* needs all the substitutions that happen in quotes.273*274* Results:275* The return value is a standard Tcl result, which is276* TCL_OK unless there was an error while parsing the277* quoted string. If an error occurs then interp->result278* contains a standard error message. *TermPtr is filled279* in with the address of the character just after the280* last one successfully processed; this is usually the281* character just after the matching close-quote. The282* fully-substituted contents of the quotes are stored in283* standard fashion in *pvPtr, null-terminated with284* pvPtr->next pointing to the terminating null character.285*286* Side effects:287* The buffer space in pvPtr may be enlarged by calling its288* expandProc.289*290*--------------------------------------------------------------291*/292293int294TclParseQuotes(interp, string, termChar, flags, termPtr, pvPtr)295Tcl_Interp *interp; /* Interpreter to use for nested command296* evaluations and error messages. */297char *string; /* Character just after opening double-298* quote. */299int termChar; /* Character that terminates "quoted" string300* (usually double-quote, but sometimes301* right-paren or something else). */302int flags; /* Flags to pass to nested Tcl_Eval calls. */303char **termPtr; /* Store address of terminating character304* here. */305ParseValue *pvPtr; /* Information about where to place306* fully-substituted result of parse. */307{308register char *src, *dst, c;309310src = string;311dst = pvPtr->next;312313while (1) {314if (dst == pvPtr->end) {315/*316* Target buffer space is about to run out. Make more space.317*/318319pvPtr->next = dst;320(*pvPtr->expandProc)(pvPtr, 1);321dst = pvPtr->next;322}323324c = *src;325src++;326if (c == termChar) {327*dst = '\0';328pvPtr->next = dst;329*termPtr = src;330return TCL_OK;331} else if (CHAR_TYPE(c) == TCL_NORMAL) {332copy:333*dst = c;334dst++;335continue;336} else if (c == '$') {337int length;338char *value;339340value = Tcl_ParseVar(interp, src-1, termPtr);341if (value == NULL) {342return TCL_ERROR;343}344src = *termPtr;345length = strlen(value);346if ((pvPtr->end - dst) <= length) {347pvPtr->next = dst;348(*pvPtr->expandProc)(pvPtr, length);349dst = pvPtr->next;350}351strcpy(dst, value);352dst += length;353continue;354} else if (c == '[') {355int result;356357pvPtr->next = dst;358result = TclParseNestedCmd(interp, src, flags, termPtr, pvPtr);359if (result != TCL_OK) {360return result;361}362src = *termPtr;363dst = pvPtr->next;364continue;365} else if (c == '\\') {366int numRead;367368src--;369*dst = Tcl_Backslash(src, &numRead);370dst++;371src += numRead;372continue;373} else if (c == '\0') {374Tcl_ResetResult(interp);375sprintf(interp->result, "missing %c", termChar);376*termPtr = string-1;377return TCL_ERROR;378} else {379goto copy;380}381}382}383384/*385*--------------------------------------------------------------386*387* TclParseNestedCmd --388*389* This procedure parses a nested Tcl command between390* brackets, returning the result of the command.391*392* Results:393* The return value is a standard Tcl result, which is394* TCL_OK unless there was an error while executing the395* nested command. If an error occurs then interp->result396* contains a standard error message. *TermPtr is filled397* in with the address of the character just after the398* last one processed; this is usually the character just399* after the matching close-bracket, or the null character400* at the end of the string if the close-bracket was missing401* (a missing close bracket is an error). The result returned402* by the command is stored in standard fashion in *pvPtr,403* null-terminated, with pvPtr->next pointing to the null404* character.405*406* Side effects:407* The storage space at *pvPtr may be expanded.408*409*--------------------------------------------------------------410*/411412int413TclParseNestedCmd(interp, string, flags, termPtr, pvPtr)414Tcl_Interp *interp; /* Interpreter to use for nested command415* evaluations and error messages. */416char *string; /* Character just after opening bracket. */417int flags; /* Flags to pass to nested Tcl_Eval. */418char **termPtr; /* Store address of terminating character419* here. */420register ParseValue *pvPtr; /* Information about where to place421* result of command. */422{423int result, length, shortfall;424Interp *iPtr = (Interp *) interp;425426iPtr->evalFlags = flags | TCL_BRACKET_TERM;427result = Tcl_Eval(interp, string);428*termPtr = iPtr->termPtr;429if (result != TCL_OK) {430/*431* The increment below results in slightly cleaner message in432* the errorInfo variable (the close-bracket will appear).433*/434435if (**termPtr == ']') {436*termPtr += 1;437}438return result;439}440(*termPtr) += 1;441length = strlen(iPtr->result);442shortfall = length + 1 - (pvPtr->end - pvPtr->next);443if (shortfall > 0) {444(*pvPtr->expandProc)(pvPtr, shortfall);445}446strcpy(pvPtr->next, iPtr->result);447pvPtr->next += length;448Tcl_FreeResult(iPtr);449iPtr->result = iPtr->resultSpace;450iPtr->resultSpace[0] = '\0';451return TCL_OK;452}453454/*455*--------------------------------------------------------------456*457* TclParseBraces --458*459* This procedure scans the information between matching460* curly braces.461*462* Results:463* The return value is a standard Tcl result, which is464* TCL_OK unless there was an error while parsing string.465* If an error occurs then interp->result contains a466* standard error message. *TermPtr is filled467* in with the address of the character just after the468* last one successfully processed; this is usually the469* character just after the matching close-brace. The470* information between curly braces is stored in standard471* fashion in *pvPtr, null-terminated with pvPtr->next472* pointing to the terminating null character.473*474* Side effects:475* The storage space at *pvPtr may be expanded.476*477*--------------------------------------------------------------478*/479480int481TclParseBraces(interp, string, termPtr, pvPtr)482Tcl_Interp *interp; /* Interpreter to use for nested command483* evaluations and error messages. */484char *string; /* Character just after opening bracket. */485char **termPtr; /* Store address of terminating character486* here. */487register ParseValue *pvPtr; /* Information about where to place488* result of command. */489{490int level;491register char *src, *dst, *end;492register char c;493494src = string;495dst = pvPtr->next;496end = pvPtr->end;497level = 1;498499/*500* Copy the characters one at a time to the result area, stopping501* when the matching close-brace is found.502*/503504while (1) {505c = *src;506src++;507if (dst == end) {508pvPtr->next = dst;509(*pvPtr->expandProc)(pvPtr, 20);510dst = pvPtr->next;511end = pvPtr->end;512}513*dst = c;514dst++;515if (CHAR_TYPE(c) == TCL_NORMAL) {516continue;517} else if (c == '{') {518level++;519} else if (c == '}') {520level--;521if (level == 0) {522dst--; /* Don't copy the last close brace. */523break;524}525} else if (c == '\\') {526int count;527528/*529* Must always squish out backslash-newlines, even when in530* braces. This is needed so that this sequence can appear531* anywhere in a command, such as the middle of an expression.532*/533534if (*src == '\n') {535dst[-1] = Tcl_Backslash(src-1, &count);536src += count - 1;537} else {538(void) Tcl_Backslash(src-1, &count);539while (count > 1) {540if (dst == end) {541pvPtr->next = dst;542(*pvPtr->expandProc)(pvPtr, 20);543dst = pvPtr->next;544end = pvPtr->end;545}546*dst = *src;547dst++;548src++;549count--;550}551}552} else if (c == '\0') {553Tcl_SetResult(interp, "missing close-brace", TCL_STATIC);554*termPtr = string-1;555return TCL_ERROR;556}557}558559*dst = '\0';560pvPtr->next = dst;561*termPtr = src;562return TCL_OK;563}564565/*566*--------------------------------------------------------------567*568* TclParseWords --569*570* This procedure parses one or more words from a command571* string and creates argv-style pointers to fully-substituted572* copies of those words.573*574* Results:575* The return value is a standard Tcl result.576*577* *argcPtr is modified to hold a count of the number of words578* successfully parsed, which may be 0. At most maxWords words579* will be parsed. If 0 <= *argcPtr < maxWords then it580* means that a command separator was seen. If *argcPtr581* is maxWords then it means that a command separator was582* not seen yet.583*584* *TermPtr is filled in with the address of the character585* just after the last one successfully processed in the586* last word. This is either the command terminator (if587* *argcPtr < maxWords), the character just after the last588* one in a word (if *argcPtr is maxWords), or the vicinity589* of an error (if the result is not TCL_OK).590*591* The pointers at *argv are filled in with pointers to the592* fully-substituted words, and the actual contents of the593* words are copied to the buffer at pvPtr.594*595* If an error occurrs then an error message is left in596* interp->result and the information at *argv, *argcPtr,597* and *pvPtr may be incomplete.598*599* Side effects:600* The buffer space in pvPtr may be enlarged by calling its601* expandProc.602*603*--------------------------------------------------------------604*/605606int607TclParseWords(interp, string, flags, maxWords, termPtr, argcPtr, argv, pvPtr)608Tcl_Interp *interp; /* Interpreter to use for nested command609* evaluations and error messages. */610char *string; /* First character of word. */611int flags; /* Flags to control parsing (same values as612* passed to Tcl_Eval). */613int maxWords; /* Maximum number of words to parse. */614char **termPtr; /* Store address of terminating character615* here. */616int *argcPtr; /* Filled in with actual number of words617* parsed. */618char **argv; /* Store addresses of individual words here. */619register ParseValue *pvPtr; /* Information about where to place620* fully-substituted word. */621{622register char *src, *dst;623register char c;624int type, result, argc;625char *oldBuffer; /* Used to detect when pvPtr's buffer gets626* reallocated, so we can adjust all of the627* argv pointers. */628629src = string;630oldBuffer = pvPtr->buffer;631dst = pvPtr->next;632for (argc = 0; argc < maxWords; argc++) {633argv[argc] = dst;634635/*636* Skip leading space.637*/638639skipSpace:640c = *src;641type = CHAR_TYPE(c);642while (type == TCL_SPACE) {643src++;644c = *src;645type = CHAR_TYPE(c);646}647648/*649* Handle the normal case (i.e. no leading double-quote or brace).650*/651652if (type == TCL_NORMAL) {653normalArg:654while (1) {655if (dst == pvPtr->end) {656/*657* Target buffer space is about to run out. Make658* more space.659*/660661pvPtr->next = dst;662(*pvPtr->expandProc)(pvPtr, 1);663dst = pvPtr->next;664}665666if (type == TCL_NORMAL) {667copy:668*dst = c;669dst++;670src++;671} else if (type == TCL_SPACE) {672goto wordEnd;673} else if (type == TCL_DOLLAR) {674int length;675char *value;676677value = Tcl_ParseVar(interp, src, termPtr);678if (value == NULL) {679return TCL_ERROR;680}681src = *termPtr;682length = strlen(value);683if ((pvPtr->end - dst) <= length) {684pvPtr->next = dst;685(*pvPtr->expandProc)(pvPtr, length);686dst = pvPtr->next;687}688strcpy(dst, value);689dst += length;690} else if (type == TCL_COMMAND_END) {691if ((c == ']') && !(flags & TCL_BRACKET_TERM)) {692goto copy;693}694695/*696* End of command; simulate a word-end first, so697* that the end-of-command can be processed as the698* first thing in a new word.699*/700701goto wordEnd;702} else if (type == TCL_OPEN_BRACKET) {703pvPtr->next = dst;704result = TclParseNestedCmd(interp, src+1, flags, termPtr,705pvPtr);706if (result != TCL_OK) {707return result;708}709src = *termPtr;710dst = pvPtr->next;711} else if (type == TCL_BACKSLASH) {712int numRead;713714*dst = Tcl_Backslash(src, &numRead);715716/*717* The following special check allows a backslash-newline718* to be treated as a word-separator, as if the backslash719* and newline had been collapsed before command parsing720* began.721*/722723if (src[1] == '\n') {724src += numRead;725goto wordEnd;726}727src += numRead;728dst++;729} else {730goto copy;731}732c = *src;733type = CHAR_TYPE(c);734}735} else {736737/*738* Check for the end of the command.739*/740741if (type == TCL_COMMAND_END) {742if (flags & TCL_BRACKET_TERM) {743if (c == '\0') {744Tcl_SetResult(interp, "missing close-bracket",745TCL_STATIC);746return TCL_ERROR;747}748} else {749if (c == ']') {750goto normalArg;751}752}753goto done;754}755756/*757* Now handle the special cases: open braces, double-quotes,758* and backslash-newline.759*/760761pvPtr->next = dst;762if (type == TCL_QUOTE) {763result = TclParseQuotes(interp, src+1, '"', flags,764termPtr, pvPtr);765} else if (type == TCL_OPEN_BRACE) {766result = TclParseBraces(interp, src+1, termPtr, pvPtr);767} else if ((type == TCL_BACKSLASH) && (src[1] == '\n')) {768/*769* This code is needed so that a backslash-newline at the770* very beginning of a word is treated as part of the white771* space between words and not as a space within the word.772*/773774src += 2;775goto skipSpace;776} else {777goto normalArg;778}779if (result != TCL_OK) {780return result;781}782783/*784* Back from quotes or braces; make sure that the terminating785* character was the end of the word.786*/787788c = **termPtr;789if ((c == '\\') && ((*termPtr)[1] == '\n')) {790/*791* Line is continued on next line; the backslash-newline792* sequence turns into space, which is OK. No need to do793* anything here.794*/795} else {796type = CHAR_TYPE(c);797if ((type != TCL_SPACE) && (type != TCL_COMMAND_END)) {798if (*src == '"') {799Tcl_SetResult(interp,800"extra characters after close-quote",801TCL_STATIC);802} else {803Tcl_SetResult(interp,804"extra characters after close-brace",805TCL_STATIC);806}807return TCL_ERROR;808}809}810src = *termPtr;811dst = pvPtr->next;812}813814/*815* We're at the end of a word, so add a null terminator. Then816* see if the buffer was re-allocated during this word. If so,817* update all of the argv pointers.818*/819820wordEnd:821*dst = '\0';822dst++;823if (oldBuffer != pvPtr->buffer) {824int i;825826for (i = 0; i <= argc; i++) {827argv[i] = pvPtr->buffer + (argv[i] - oldBuffer);828}829oldBuffer = pvPtr->buffer;830}831}832833done:834pvPtr->next = dst;835*termPtr = src;836*argcPtr = argc;837return TCL_OK;838}839840/*841*--------------------------------------------------------------842*843* TclExpandParseValue --844*845* This procedure is commonly used as the value of the846* expandProc in a ParseValue. It uses malloc to allocate847* more space for the result of a parse.848*849* Results:850* The buffer space in *pvPtr is reallocated to something851* larger, and if pvPtr->clientData is non-zero the old852* buffer is freed. Information is copied from the old853* buffer to the new one.854*855* Side effects:856* None.857*858*--------------------------------------------------------------859*/860861void862TclExpandParseValue(pvPtr, needed)863register ParseValue *pvPtr; /* Information about buffer that864* must be expanded. If the clientData865* in the structure is non-zero, it866* means that the current buffer is867* dynamically allocated. */868int needed; /* Minimum amount of additional space869* to allocate. */870{871int newSpace;872char *new;873874/*875* Either double the size of the buffer or add enough new space876* to meet the demand, whichever produces a larger new buffer.877*/878879newSpace = (pvPtr->end - pvPtr->buffer) + 1;880if (newSpace < needed) {881newSpace += needed;882} else {883newSpace += newSpace;884}885new = (char *) ckalloc((unsigned) newSpace);886887/*888* Copy from old buffer to new, free old buffer if needed, and889* mark new buffer as malloc-ed.890*/891892memcpy((VOID *) new, (VOID *) pvPtr->buffer,893(size_t) (pvPtr->next - pvPtr->buffer));894pvPtr->next = new + (pvPtr->next - pvPtr->buffer);895if (pvPtr->clientData != 0) {896ckfree(pvPtr->buffer);897}898pvPtr->buffer = new;899pvPtr->end = new + newSpace - 1;900pvPtr->clientData = (ClientData) 1;901}902903/*904*----------------------------------------------------------------------905*906* TclWordEnd --907*908* Given a pointer into a Tcl command, find the end of the next909* word of the command.910*911* Results:912* The return value is a pointer to the last character that's part913* of the word pointed to by "start". If the word doesn't end914* properly within the string then the return value is the address915* of the null character at the end of the string.916*917* Side effects:918* None.919*920*----------------------------------------------------------------------921*/922923char *924TclWordEnd(start, nested, semiPtr)925char *start; /* Beginning of a word of a Tcl command. */926int nested; /* Zero means this is a top-level command.927* One means this is a nested command (close928* bracket is a word terminator). */929int *semiPtr; /* Set to 1 if word ends with a command-930* terminating semi-colon, zero otherwise.931* If NULL then ignored. */932{933register char *p;934int count;935936if (semiPtr != NULL) {937*semiPtr = 0;938}939940/*941* Skip leading white space (backslash-newline must be treated like942* white-space, except that it better not be the last thing in the943* command).944*/945946for (p = start; ; p++) {947if (isspace(UCHAR(*p))) {948continue;949}950if ((p[0] == '\\') && (p[1] == '\n')) {951if (p[2] == 0) {952return p+2;953}954continue;955}956break;957}958959/*960* Handle words beginning with a double-quote or a brace.961*/962963if (*p == '"') {964p = QuoteEnd(p+1, '"');965if (*p == 0) {966return p;967}968p++;969} else if (*p == '{') {970int braces = 1;971while (braces != 0) {972p++;973while (*p == '\\') {974(void) Tcl_Backslash(p, &count);975p += count;976}977if (*p == '}') {978braces--;979} else if (*p == '{') {980braces++;981} else if (*p == 0) {982return p;983}984}985p++;986}987988/*989* Handle words that don't start with a brace or double-quote.990* This code is also invoked if the word starts with a brace or991* double-quote and there is garbage after the closing brace or992* quote. This is an error as far as Tcl_Eval is concerned, but993* for here the garbage is treated as part of the word.994*/995996while (1) {997if (*p == '[') {998p = ScriptEnd(p+1, 1);999if (*p == 0) {1000return p;1001}1002p++;1003} else if (*p == '\\') {1004if (p[1] == '\n') {1005/*1006* Backslash-newline: it maps to a space character1007* that is a word separator, so the word ends just before1008* the backslash.1009*/10101011return p-1;1012}1013(void) Tcl_Backslash(p, &count);1014p += count;1015} else if (*p == '$') {1016p = VarNameEnd(p);1017if (*p == 0) {1018return p;1019}1020p++;1021} else if (*p == ';') {1022/*1023* Include the semi-colon in the word that is returned.1024*/10251026if (semiPtr != NULL) {1027*semiPtr = 1;1028}1029return p;1030} else if (isspace(UCHAR(*p))) {1031return p-1;1032} else if ((*p == ']') && nested) {1033return p-1;1034} else if (*p == 0) {1035if (nested) {1036/*1037* Nested commands can't end because of the end of the1038* string.1039*/1040return p;1041}1042return p-1;1043} else {1044p++;1045}1046}1047}10481049/*1050*----------------------------------------------------------------------1051*1052* QuoteEnd --1053*1054* Given a pointer to a string that obeys the parsing conventions1055* for quoted things in Tcl, find the end of that quoted thing.1056* The actual thing may be a quoted argument or a parenthesized1057* index name.1058*1059* Results:1060* The return value is a pointer to the last character that is1061* part of the quoted string (i.e the character that's equal to1062* term). If the quoted string doesn't terminate properly then1063* the return value is a pointer to the null character at the1064* end of the string.1065*1066* Side effects:1067* None.1068*1069*----------------------------------------------------------------------1070*/10711072static char *1073QuoteEnd(string, term)1074char *string; /* Pointer to character just after opening1075* "quote". */1076int term; /* This character will terminate the1077* quoted string (e.g. '"' or ')'). */1078{1079register char *p = string;1080int count;10811082while (*p != term) {1083if (*p == '\\') {1084(void) Tcl_Backslash(p, &count);1085p += count;1086} else if (*p == '[') {1087for (p++; *p != ']'; p++) {1088p = TclWordEnd(p, 1, (int *) NULL);1089if (*p == 0) {1090return p;1091}1092}1093p++;1094} else if (*p == '$') {1095p = VarNameEnd(p);1096if (*p == 0) {1097return p;1098}1099p++;1100} else if (*p == 0) {1101return p;1102} else {1103p++;1104}1105}1106return p-1;1107}11081109/*1110*----------------------------------------------------------------------1111*1112* VarNameEnd --1113*1114* Given a pointer to a variable reference using $-notation, find1115* the end of the variable name spec.1116*1117* Results:1118* The return value is a pointer to the last character that1119* is part of the variable name. If the variable name doesn't1120* terminate properly then the return value is a pointer to the1121* null character at the end of the string.1122*1123* Side effects:1124* None.1125*1126*----------------------------------------------------------------------1127*/11281129static char *1130VarNameEnd(string)1131char *string; /* Pointer to dollar-sign character. */1132{1133register char *p = string+1;11341135if (*p == '{') {1136for (p++; (*p != '}') && (*p != 0); p++) {1137/* Empty loop body. */1138}1139return p;1140}1141while (isalnum(UCHAR(*p)) || (*p == '_')) {1142p++;1143}1144if ((*p == '(') && (p != string+1)) {1145return QuoteEnd(p+1, ')');1146}1147return p-1;1148}114911501151/*1152*----------------------------------------------------------------------1153*1154* ScriptEnd --1155*1156* Given a pointer to the beginning of a Tcl script, find the end of1157* the script.1158*1159* Results:1160* The return value is a pointer to the last character that's part1161* of the script pointed to by "p". If the command doesn't end1162* properly within the string then the return value is the address1163* of the null character at the end of the string.1164*1165* Side effects:1166* None.1167*1168*----------------------------------------------------------------------1169*/11701171static char *1172ScriptEnd(p, nested)1173char *p; /* Script to check. */1174int nested; /* Zero means this is a top-level command.1175* One means this is a nested command (the1176* last character of the script must be1177* an unquoted ]). */1178{1179int commentOK = 1;1180int length;11811182while (1) {1183while (isspace(UCHAR(*p))) {1184if (*p == '\n') {1185commentOK = 1;1186}1187p++;1188}1189if ((*p == '#') && commentOK) {1190do {1191if (*p == '\\') {1192/*1193* If the script ends with backslash-newline, then1194* this command isn't complete.1195*/11961197if ((p[1] == '\n') && (p[2] == 0)) {1198return p+2;1199}1200Tcl_Backslash(p, &length);1201p += length;1202} else {1203p++;1204}1205} while ((*p != 0) && (*p != '\n'));1206continue;1207}1208p = TclWordEnd(p, nested, &commentOK);1209if (*p == 0) {1210return p;1211}1212p++;1213if (nested) {1214if (*p == ']') {1215return p;1216}1217} else {1218if (*p == 0) {1219return p-1;1220}1221}1222}1223}12241225/*1226*----------------------------------------------------------------------1227*1228* Tcl_ParseVar --1229*1230* Given a string starting with a $ sign, parse off a variable1231* name and return its value.1232*1233* Results:1234* The return value is the contents of the variable given by1235* the leading characters of string. If termPtr isn't NULL,1236* *termPtr gets filled in with the address of the character1237* just after the last one in the variable specifier. If the1238* variable doesn't exist, then the return value is NULL and1239* an error message will be left in interp->result.1240*1241* Side effects:1242* None.1243*1244*----------------------------------------------------------------------1245*/12461247char *1248Tcl_ParseVar(interp, string, termPtr)1249Tcl_Interp *interp; /* Context for looking up variable. */1250register char *string; /* String containing variable name.1251* First character must be "$". */1252char **termPtr; /* If non-NULL, points to word to fill1253* in with character just after last1254* one in the variable specifier. */12551256{1257char *name1, *name1End, c, *result;1258register char *name2;1259#define NUM_CHARS 2001260char copyStorage[NUM_CHARS];1261ParseValue pv;12621263/*1264* There are three cases:1265* 1. The $ sign is followed by an open curly brace. Then the variable1266* name is everything up to the next close curly brace, and the1267* variable is a scalar variable.1268* 2. The $ sign is not followed by an open curly brace. Then the1269* variable name is everything up to the next character that isn't1270* a letter, digit, or underscore. If the following character is an1271* open parenthesis, then the information between parentheses is1272* the array element name, which can include any of the substitutions1273* permissible between quotes.1274* 3. The $ sign is followed by something that isn't a letter, digit,1275* or underscore: in this case, there is no variable name, and "$"1276* is returned.1277*/12781279name2 = NULL;1280string++;1281if (*string == '{') {1282string++;1283name1 = string;1284while (*string != '}') {1285if (*string == 0) {1286Tcl_SetResult(interp, "missing close-brace for variable name",1287TCL_STATIC);1288if (termPtr != 0) {1289*termPtr = string;1290}1291return NULL;1292}1293string++;1294}1295name1End = string;1296string++;1297} else {1298name1 = string;1299while (isalnum(UCHAR(*string)) || (*string == '_')) {1300string++;1301}1302if (string == name1) {1303if (termPtr != 0) {1304*termPtr = string;1305}1306return "$";1307}1308name1End = string;1309if (*string == '(') {1310char *end;13111312/*1313* Perform substitutions on the array element name, just as1314* is done for quotes.1315*/13161317pv.buffer = pv.next = copyStorage;1318pv.end = copyStorage + NUM_CHARS - 1;1319pv.expandProc = TclExpandParseValue;1320pv.clientData = (ClientData) NULL;1321if (TclParseQuotes(interp, string+1, ')', 0, &end, &pv)1322!= TCL_OK) {1323char msg[200];1324int length;13251326length = string-name1;1327if (length > 100) {1328length = 100;1329}1330sprintf(msg, "\n (parsing index for array \"%.*s\")",1331length, name1);1332Tcl_AddErrorInfo(interp, msg);1333result = NULL;1334name2 = pv.buffer;1335if (termPtr != 0) {1336*termPtr = end;1337}1338goto done;1339}1340Tcl_ResetResult(interp);1341string = end;1342name2 = pv.buffer;1343}1344}1345if (termPtr != 0) {1346*termPtr = string;1347}13481349if (((Interp *) interp)->noEval) {1350return "";1351}1352c = *name1End;1353*name1End = 0;1354result = Tcl_GetVar2(interp, name1, name2, TCL_LEAVE_ERR_MSG);1355*name1End = c;13561357done:1358if ((name2 != NULL) && (pv.buffer != copyStorage)) {1359ckfree(pv.buffer);1360}1361return result;1362}13631364/*1365*----------------------------------------------------------------------1366*1367* Tcl_CommandComplete --1368*1369* Given a partial or complete Tcl command, this procedure1370* determines whether the command is complete in the sense1371* of having matched braces and quotes and brackets.1372*1373* Results:1374* 1 is returned if the command is complete, 0 otherwise.1375*1376* Side effects:1377* None.1378*1379*----------------------------------------------------------------------1380*/13811382int1383Tcl_CommandComplete(cmd)1384char *cmd; /* Command to check. */1385{1386char *p;13871388if (*cmd == 0) {1389return 1;1390}1391p = ScriptEnd(cmd, 0);1392return (*p != 0);1393}139413951396