📄 tclparse.c
字号:
if (result != TCL_OK) {
/*
* The increment below results in slightly cleaner message in
* the errorInfo variable (the close-bracket will appear).
*/
if (**termPtr == ']') {
*termPtr += 1;
}
return result;
}
(*termPtr) += 1;
length = strlen(iPtr->result);
shortfall = length + 1 - (pvPtr->end - pvPtr->next);
if (shortfall > 0) {
(*pvPtr->expandProc)(pvPtr, shortfall);
}
strcpy(pvPtr->next, iPtr->result);
pvPtr->next += length;
Tcl_FreeResult(iPtr);
iPtr->result = iPtr->resultSpace;
iPtr->resultSpace[0] = '\0';
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* TclParseBraces --
*
* This procedure scans the information between matching
* curly braces.
*
* Results:
* The return value is a standard Tcl result, which is
* TCL_OK unless there was an error while parsing string.
* If an error occurs then interp->result contains a
* standard error message. *TermPtr is filled
* in with the address of the character just after the
* last one successfully processed; this is usually the
* character just after the matching close-brace. The
* information between curly braces is stored in standard
* fashion in *pvPtr, null-terminated with pvPtr->next
* pointing to the terminating null character.
*
* Side effects:
* The storage space at *pvPtr may be expanded.
*
*--------------------------------------------------------------
*/
int TclParseBraces(Tcl_Interp *interp, char *string, char **termPtr, ParseValue *pvPtr)
// Tcl_Interp *interp; /* Interpreter to use for nested command
// * evaluations and error messages. */
//char *string; /* Character just after opening bracket. */
//char **termPtr; /* Store address of terminating character
// * here. */
//register ParseValue *pvPtr; /* Information about where to place
// * result of command. */
{
int level;
register char *src, *dst, *end;
register char c;
src = string;
dst = pvPtr->next;
end = pvPtr->end;
level = 1;
/*
* Copy the characters one at a time to the result area, stopping
* when the matching close-brace is found.
*/
while (1) {
c = *src;
src++;
if (dst == end) {
pvPtr->next = dst;
(*pvPtr->expandProc)(pvPtr, 20);
dst = pvPtr->next;
end = pvPtr->end;
}
*dst = c;
dst++;
if (CHAR_TYPE(c) == TCL_NORMAL) {
continue;
} else if (c == '{') {
level++;
} else if (c == '}') {
level--;
if (level == 0) {
dst--; /* Don't copy the last close brace. */
break;
}
} else if (c == '\\') {
int count;
/*
* Must always squish out backslash-newlines, even when in
* braces. This is needed so that this sequence can appear
* anywhere in a command, such as the middle of an expression.
*/
if (*src == '\n') {
dst--;
src++;
} else {
(void) Tcl_Backslash(src-1, &count);
while (count > 1) {
if (dst == end) {
pvPtr->next = dst;
(*pvPtr->expandProc)(pvPtr, 20);
dst = pvPtr->next;
end = pvPtr->end;
}
*dst = *src;
dst++;
src++;
count--;
}
}
} else if (c == '\0') {
Tcl_SetResult(interp, "missing close-brace", TCL_STATIC);
*termPtr = string-1;
return TCL_ERROR;
}
}
*dst = '\0';
pvPtr->next = dst;
*termPtr = src;
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* TclParseWords --
*
* This procedure parses one or more words from a command
* string and creates argv-style pointers to fully-substituted
* copies of those words.
*
* Results:
* The return value is a standard Tcl result.
*
* *argcPtr is modified to hold a count of the number of words
* successfully parsed, which may be 0. At most maxWords words
* will be parsed. If 0 <= *argcPtr < maxWords then it
* means that a command separator was seen. If *argcPtr
* is maxWords then it means that a command separator was
* not seen yet.
*
* *TermPtr is filled in with the address of the character
* just after the last one successfully processed in the
* last word. This is either the command terminator (if
* *argcPtr < maxWords), the character just after the last
* one in a word (if *argcPtr is maxWords), or the vicinity
* of an error (if the result is not TCL_OK).
*
* The pointers at *argv are filled in with pointers to the
* fully-substituted words, and the actual contents of the
* words are copied to the buffer at pvPtr.
*
* If an error occurrs then an error message is left in
* interp->result and the information at *argv, *argcPtr,
* and *pvPtr may be incomplete.
*
* Side effects:
* The buffer space in pvPtr may be enlarged by calling its
* expandProc.
*
*--------------------------------------------------------------
*/
int TclParseWords(Tcl_Interp *interp, char *string, int flags, int maxWords,
char **termPtr, int *argcPtr, char **argv, ParseValue *pvPtr)
// Tcl_Interp *interp; /* Interpreter to use for nested command
// * evaluations and error messages. */
//char *string; /* First character of word. */
//int flags; /* Flags to control parsing (same values as
// * passed to Tcl_Eval). */
//int maxWords; /* Maximum number of words to parse. */
//char **termPtr; /* Store address of terminating character
// * here. */
//int *argcPtr; /* Filled in with actual number of words
// * parsed. */
//char **argv; /* Store addresses of individual words here. */
//register ParseValue *pvPtr; /* Information about where to place
// * fully-substituted word. */
{
register char *src, *dst;
register char c;
int type, result, argc;
char *oldBuffer; /* Used to detect when pvPtr's buffer gets
* reallocated, so we can adjust all of the
* argv pointers. */
src = string;
oldBuffer = pvPtr->buffer;
dst = pvPtr->next;
for (argc = 0; argc < maxWords; argc++) {
argv[argc] = dst;
/*
* Skip leading space.
*/
skipSpace:
c = *src;
type = CHAR_TYPE(c);
while (type == TCL_SPACE) {
src++;
c = *src;
type = CHAR_TYPE(c);
}
/*
* Handle the normal case (i.e. no leading double-quote or brace).
*/
if (type == TCL_NORMAL) {
normalArg:
while (1) {
if (dst == pvPtr->end) {
/*
* Target buffer space is about to run out. Make
* more space.
*/
pvPtr->next = dst;
(*pvPtr->expandProc)(pvPtr, 1);
dst = pvPtr->next;
}
if (type == TCL_NORMAL) {
copy:
*dst = c;
dst++;
src++;
} else if (type == TCL_SPACE) {
goto wordEnd;
} else if (type == TCL_DOLLAR) {
int length;
char *value;
value = Tcl_ParseVar(interp, src, termPtr);
if (value == NULL) {
return TCL_ERROR;
}
src = *termPtr;
length = strlen(value);
if ((pvPtr->end - dst) <= length) {
pvPtr->next = dst;
(*pvPtr->expandProc)(pvPtr, length);
dst = pvPtr->next;
}
strcpy(dst, value);
dst += length;
} else if (type == TCL_COMMAND_END) {
if ((c == ']') && !(flags & TCL_BRACKET_TERM)) {
goto copy;
}
/*
* End of command; simulate a word-end first, so
* that the end-of-command can be processed as the
* first thing in a new word.
*/
goto wordEnd;
} else if (type == TCL_OPEN_BRACKET) {
pvPtr->next = dst;
result = TclParseNestedCmd(interp, src+1, flags, termPtr,
pvPtr);
if (result != TCL_OK) {
return result;
}
src = *termPtr;
dst = pvPtr->next;
} else if (type == TCL_BACKSLASH) {
int numRead;
*dst = Tcl_Backslash(src, &numRead);
if (*dst != 0) {
dst++;
}
src += numRead;
} else {
goto copy;
}
c = *src;
type = CHAR_TYPE(c);
}
} else {
/*
* Check for the end of the command.
*/
if (type == TCL_COMMAND_END) {
if (flags & TCL_BRACKET_TERM) {
if (c == '\0') {
Tcl_SetResult(interp, "missing close-bracket",
TCL_STATIC);
return TCL_ERROR;
}
} else {
if (c == ']') {
goto normalArg;
}
}
goto done;
}
/*
* Now handle the special cases: open braces, double-quotes,
* and backslash-newline.
*/
pvPtr->next = dst;
if (type == TCL_QUOTE) {
result = TclParseQuotes(interp, src+1, '"', flags,
termPtr, pvPtr);
} else if (type == TCL_OPEN_BRACE) {
result = TclParseBraces(interp, src+1, termPtr, pvPtr);
} else if ((type == TCL_BACKSLASH) && (src[1] == '\n')) {
src += 2;
goto skipSpace;
} else {
goto normalArg;
}
if (result != TCL_OK) {
return result;
}
/*
* Back from quotes or braces; make sure that the terminating
* character was the end of the word. Have to be careful here
* to handle continuation lines (i.e. lines ending in backslash).
*/
c = **termPtr;
if ((c == '\\') && ((*termPtr)[1] == '\n')) {
c = (*termPtr)[2];
}
type = CHAR_TYPE(c);
if ((type != TCL_SPACE) && (type != TCL_COMMAND_END)) {
if (*src == '"') {
Tcl_SetResult(interp, "extra characters after close-quote",
TCL_STATIC);
} else {
Tcl_SetResult(interp, "extra characters after close-brace",
TCL_STATIC);
}
return TCL_ERROR;
}
src = *termPtr;
dst = pvPtr->next;
}
/*
* We're at the end of a word, so add a null terminator. Then
* see if the buffer was re-allocated during this word. If so,
* update all of the argv pointers.
*/
wordEnd:
*dst = '\0';
dst++;
if (oldBuffer != pvPtr->buffer) {
int i;
for (i = 0; i <= argc; i++) {
argv[i] = pvPtr->buffer + (argv[i] - oldBuffer);
}
oldBuffer = pvPtr->buffer;
}
}
done:
pvPtr->next = dst;
*termPtr = src;
*argcPtr = argc;
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* TclExpandParseValue --
*
* This procedure is commonly used as the value of the
* expandProc in a ParseValue. It uses malloc to allocate
* more space for the result of a parse.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -