|
|
1.1 ! root 1: /* ! 2: * tclParse.c -- ! 3: * ! 4: * This file contains a collection of procedures that are used ! 5: * to parse Tcl commands or parts of commands (like quoted ! 6: * strings or nested sub-commands). ! 7: * ! 8: * Copyright 1991 Regents of the University of California. ! 9: * Permission to use, copy, modify, and distribute this ! 10: * software and its documentation for any purpose and without ! 11: * fee is hereby granted, provided that the above copyright ! 12: * notice appear in all copies. The University of California ! 13: * makes no representations about the suitability of this ! 14: * software for any purpose. It is provided "as is" without ! 15: * express or implied warranty. ! 16: */ ! 17: ! 18: #ifndef lint ! 19: static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclParse.c,v 1.21 92/06/08 09:32:37 ouster Exp $ SPRITE (Berkeley)"; ! 20: #endif ! 21: ! 22: #include "tclint.h" ! 23: ! 24: /* ! 25: * The following table assigns a type to each character. Only types ! 26: * meaningful to Tcl parsing are represented here. The table indexes ! 27: * all 256 characters, with the negative ones first, then the positive ! 28: * ones. ! 29: */ ! 30: ! 31: char tclTypeTable[] = { ! 32: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 33: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 34: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 35: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 36: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 37: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 38: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 39: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 40: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 41: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 42: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 43: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 44: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 45: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 46: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 47: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 48: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 49: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 50: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 51: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 52: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 53: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 54: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 55: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 56: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 57: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 58: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 59: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 60: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 61: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 62: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 63: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 64: TCL_COMMAND_END, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 65: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 66: TCL_NORMAL, TCL_SPACE, TCL_COMMAND_END, TCL_SPACE, ! 67: TCL_SPACE, TCL_SPACE, TCL_NORMAL, TCL_NORMAL, ! 68: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 69: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 70: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 71: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 72: TCL_SPACE, TCL_NORMAL, TCL_QUOTE, TCL_NORMAL, ! 73: TCL_DOLLAR, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 74: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 75: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 76: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 77: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 78: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_COMMAND_END, ! 79: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 80: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 81: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 82: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 83: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 84: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 85: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 86: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_OPEN_BRACKET, ! 87: TCL_BACKSLASH, TCL_COMMAND_END, TCL_NORMAL, TCL_NORMAL, ! 88: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 89: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 90: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 91: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 92: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 93: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, ! 94: TCL_NORMAL, TCL_NORMAL, TCL_NORMAL, TCL_OPEN_BRACE, ! 95: TCL_NORMAL, TCL_CLOSE_BRACE, TCL_NORMAL, TCL_NORMAL, ! 96: }; ! 97: ! 98: /* ! 99: * Function prototypes for procedures local to this file: ! 100: */ ! 101: ! 102: static char * QuoteEnd _ANSI_ARGS_((char *string, int term)); ! 103: static char * VarNameEnd _ANSI_ARGS_((char *string)); ! 104: ! 105: /* ! 106: *---------------------------------------------------------------------- ! 107: * ! 108: * Tcl_Backslash -- ! 109: * ! 110: * Figure out how to handle a backslash sequence. ! 111: * ! 112: * Results: ! 113: * The return value is the character that should be substituted ! 114: * in place of the backslash sequence that starts at src, or 0 ! 115: * if the backslash sequence should be replace by nothing (e.g. ! 116: * backslash followed by newline). If readPtr isn't NULL then ! 117: * it is filled in with a count of the number of characters in ! 118: * the backslash sequence. Note: if the backslash isn't followed ! 119: * by characters that are understood here, then the backslash ! 120: * sequence is only considered to be one character long, and it ! 121: * is replaced by a backslash char. ! 122: * ! 123: * Side effects: ! 124: * None. ! 125: * ! 126: *---------------------------------------------------------------------- ! 127: */ ! 128: ! 129: char ! 130: Tcl_Backslash(src, readPtr) ! 131: char *src; /* Points to the backslash character of ! 132: * a backslash sequence. */ ! 133: int *readPtr; /* Fill in with number of characters read ! 134: * from src, unless NULL. */ ! 135: { ! 136: register char *p = src+1; ! 137: char result; ! 138: int count; ! 139: ! 140: count = 2; ! 141: ! 142: switch (*p) { ! 143: case 'b': ! 144: result = '\b'; ! 145: break; ! 146: case 'e': ! 147: result = 033; ! 148: break; ! 149: case 'f': ! 150: result = '\f'; ! 151: break; ! 152: case 'n': ! 153: result = '\n'; ! 154: break; ! 155: case 'r': ! 156: result = '\r'; ! 157: break; ! 158: case 't': ! 159: result = '\t'; ! 160: break; ! 161: case 'v': ! 162: result = '\v'; ! 163: break; ! 164: case 'C': ! 165: p++; ! 166: if (isspace(*p) || (*p == 0)) { ! 167: result = 'C'; ! 168: count = 1; ! 169: break; ! 170: } ! 171: count = 3; ! 172: if (*p == 'M') { ! 173: p++; ! 174: if (isspace(*p) || (*p == 0)) { ! 175: result = 'M' & 037; ! 176: break; ! 177: } ! 178: count = 4; ! 179: result = (*p & 037) | 0200; ! 180: break; ! 181: } ! 182: count = 3; ! 183: result = *p & 037; ! 184: break; ! 185: case 'M': ! 186: p++; ! 187: if (isspace(*p) || (*p == 0)) { ! 188: result = 'M'; ! 189: count = 1; ! 190: break; ! 191: } ! 192: count = 3; ! 193: result = *p + 0200; ! 194: break; ! 195: case '}': ! 196: case '{': ! 197: case ']': ! 198: case '[': ! 199: case '$': ! 200: case ' ': ! 201: case ';': ! 202: case '"': ! 203: case '\\': ! 204: result = *p; ! 205: break; ! 206: case '\n': ! 207: result = 0; ! 208: break; ! 209: default: ! 210: if (isdigit(*p)) { ! 211: result = *p - '0'; ! 212: p++; ! 213: if (!isdigit(*p)) { ! 214: break; ! 215: } ! 216: count = 3; ! 217: result = (result << 3) + (*p - '0'); ! 218: p++; ! 219: if (!isdigit(*p)) { ! 220: break; ! 221: } ! 222: count = 4; ! 223: result = (result << 3) + (*p - '0'); ! 224: break; ! 225: } ! 226: result = '\\'; ! 227: count = 1; ! 228: break; ! 229: } ! 230: ! 231: if (readPtr != NULL) { ! 232: *readPtr = count; ! 233: } ! 234: return result; ! 235: } ! 236: ! 237: /* ! 238: *-------------------------------------------------------------- ! 239: * ! 240: * TclParseQuotes -- ! 241: * ! 242: * This procedure parses a double-quoted string such as a ! 243: * quoted Tcl command argument or a quoted value in a Tcl ! 244: * expression. This procedure is also used to parse array ! 245: * element names within parentheses, or anything else that ! 246: * needs all the substitutions that happen in quotes. ! 247: * ! 248: * Results: ! 249: * The return value is a standard Tcl result, which is ! 250: * TCL_OK unless there was an error while parsing the ! 251: * quoted string. If an error occurs then interp->result ! 252: * contains a standard error message. *TermPtr is filled ! 253: * in with the address of the character just after the ! 254: * last one successfully processed; this is usually the ! 255: * character just after the matching close-quote. The ! 256: * fully-substituted contents of the quotes are stored in ! 257: * standard fashion in *pvPtr, null-terminated with ! 258: * pvPtr->next pointing to the terminating null character. ! 259: * ! 260: * Side effects: ! 261: * The buffer space in pvPtr may be enlarged by calling its ! 262: * expandProc. ! 263: * ! 264: *-------------------------------------------------------------- ! 265: */ ! 266: ! 267: int ! 268: TclParseQuotes(interp, string, termChar, flags, termPtr, pvPtr) ! 269: Tcl_Interp *interp; /* Interpreter to use for nested command ! 270: * evaluations and error messages. */ ! 271: char *string; /* Character just after opening double- ! 272: * quote. */ ! 273: int termChar; /* Character that terminates "quoted" string ! 274: * (usually double-quote, but sometimes ! 275: * right-paren or something else). */ ! 276: int flags; /* Flags to pass to nested Tcl_Eval calls. */ ! 277: char **termPtr; /* Store address of terminating character ! 278: * here. */ ! 279: ParseValue *pvPtr; /* Information about where to place ! 280: * fully-substituted result of parse. */ ! 281: { ! 282: register char *src, *dst, c; ! 283: ! 284: src = string; ! 285: dst = pvPtr->next; ! 286: ! 287: while (1) { ! 288: if (dst == pvPtr->end) { ! 289: /* ! 290: * Target buffer space is about to run out. Make more space. ! 291: */ ! 292: ! 293: pvPtr->next = dst; ! 294: (*pvPtr->expandProc)(pvPtr, 1); ! 295: dst = pvPtr->next; ! 296: } ! 297: ! 298: c = *src; ! 299: src++; ! 300: if (c == termChar) { ! 301: *dst = '\0'; ! 302: pvPtr->next = dst; ! 303: *termPtr = src; ! 304: return TCL_OK; ! 305: } else if (CHAR_TYPE(c) == TCL_NORMAL) { ! 306: copy: ! 307: *dst = c; ! 308: dst++; ! 309: continue; ! 310: } else if (c == '$') { ! 311: int length; ! 312: char *value; ! 313: ! 314: value = Tcl_ParseVar(interp, src-1, termPtr); ! 315: if (value == NULL) { ! 316: return TCL_ERROR; ! 317: } ! 318: src = *termPtr; ! 319: length = strlen(value); ! 320: if ((pvPtr->end - dst) <= length) { ! 321: pvPtr->next = dst; ! 322: (*pvPtr->expandProc)(pvPtr, length); ! 323: dst = pvPtr->next; ! 324: } ! 325: strcpy(dst, value); ! 326: dst += length; ! 327: continue; ! 328: } else if (c == '[') { ! 329: int result; ! 330: ! 331: pvPtr->next = dst; ! 332: result = TclParseNestedCmd(interp, src, flags, termPtr, pvPtr); ! 333: if (result != TCL_OK) { ! 334: return result; ! 335: } ! 336: src = *termPtr; ! 337: dst = pvPtr->next; ! 338: continue; ! 339: } else if (c == '\\') { ! 340: int numRead; ! 341: ! 342: src--; ! 343: *dst = Tcl_Backslash(src, &numRead); ! 344: if (*dst != 0) { ! 345: dst++; ! 346: } ! 347: src += numRead; ! 348: continue; ! 349: } else if (c == '\0') { ! 350: Tcl_ResetResult(interp); ! 351: sprintf(interp->result, "missing %c", termChar); ! 352: *termPtr = string-1; ! 353: return TCL_ERROR; ! 354: } else { ! 355: goto copy; ! 356: } ! 357: } ! 358: } ! 359: ! 360: /* ! 361: *-------------------------------------------------------------- ! 362: * ! 363: * TclParseNestedCmd -- ! 364: * ! 365: * This procedure parses a nested Tcl command between ! 366: * brackets, returning the result of the command. ! 367: * ! 368: * Results: ! 369: * The return value is a standard Tcl result, which is ! 370: * TCL_OK unless there was an error while executing the ! 371: * nested command. If an error occurs then interp->result ! 372: * contains a standard error message. *TermPtr is filled ! 373: * in with the address of the character just after the ! 374: * last one processed; this is usually the character just ! 375: * after the matching close-bracket, or the null character ! 376: * at the end of the string if the close-bracket was missing ! 377: * (a missing close bracket is an error). The result returned ! 378: * by the command is stored in standard fashion in *pvPtr, ! 379: * null-terminated, with pvPtr->next pointing to the null ! 380: * character. ! 381: * ! 382: * Side effects: ! 383: * The storage space at *pvPtr may be expanded. ! 384: * ! 385: *-------------------------------------------------------------- ! 386: */ ! 387: ! 388: int ! 389: TclParseNestedCmd(interp, string, flags, termPtr, pvPtr) ! 390: Tcl_Interp *interp; /* Interpreter to use for nested command ! 391: * evaluations and error messages. */ ! 392: char *string; /* Character just after opening bracket. */ ! 393: int flags; /* Flags to pass to nested Tcl_Eval. */ ! 394: char **termPtr; /* Store address of terminating character ! 395: * here. */ ! 396: register ParseValue *pvPtr; /* Information about where to place ! 397: * result of command. */ ! 398: { ! 399: int result, length, shortfall; ! 400: Interp *iPtr = (Interp *) interp; ! 401: ! 402: result = Tcl_Eval(interp, string, flags | TCL_BRACKET_TERM, termPtr); ! 403: if (result != TCL_OK) { ! 404: /* ! 405: * The increment below results in slightly cleaner message in ! 406: * the errorInfo variable (the close-bracket will appear). ! 407: */ ! 408: ! 409: if (**termPtr == ']') { ! 410: *termPtr += 1; ! 411: } ! 412: return result; ! 413: } ! 414: (*termPtr) += 1; ! 415: length = strlen(iPtr->result); ! 416: shortfall = length + 1 - (pvPtr->end - pvPtr->next); ! 417: if (shortfall > 0) { ! 418: (*pvPtr->expandProc)(pvPtr, shortfall); ! 419: } ! 420: strcpy(pvPtr->next, iPtr->result); ! 421: pvPtr->next += length; ! 422: Tcl_FreeResult(iPtr); ! 423: iPtr->result = iPtr->resultSpace; ! 424: iPtr->resultSpace[0] = '\0'; ! 425: return TCL_OK; ! 426: } ! 427: ! 428: /* ! 429: *-------------------------------------------------------------- ! 430: * ! 431: * TclParseBraces -- ! 432: * ! 433: * This procedure scans the information between matching ! 434: * curly braces. ! 435: * ! 436: * Results: ! 437: * The return value is a standard Tcl result, which is ! 438: * TCL_OK unless there was an error while parsing string. ! 439: * If an error occurs then interp->result contains a ! 440: * standard error message. *TermPtr is filled ! 441: * in with the address of the character just after the ! 442: * last one successfully processed; this is usually the ! 443: * character just after the matching close-brace. The ! 444: * information between curly braces is stored in standard ! 445: * fashion in *pvPtr, null-terminated with pvPtr->next ! 446: * pointing to the terminating null character. ! 447: * ! 448: * Side effects: ! 449: * The storage space at *pvPtr may be expanded. ! 450: * ! 451: *-------------------------------------------------------------- ! 452: */ ! 453: ! 454: int ! 455: TclParseBraces(interp, string, termPtr, pvPtr) ! 456: Tcl_Interp *interp; /* Interpreter to use for nested command ! 457: * evaluations and error messages. */ ! 458: char *string; /* Character just after opening bracket. */ ! 459: char **termPtr; /* Store address of terminating character ! 460: * here. */ ! 461: register ParseValue *pvPtr; /* Information about where to place ! 462: * result of command. */ ! 463: { ! 464: int level; ! 465: register char *src, *dst, *end; ! 466: register char c; ! 467: ! 468: src = string; ! 469: dst = pvPtr->next; ! 470: end = pvPtr->end; ! 471: level = 1; ! 472: ! 473: /* ! 474: * Copy the characters one at a time to the result area, stopping ! 475: * when the matching close-brace is found. ! 476: */ ! 477: ! 478: while (1) { ! 479: c = *src; ! 480: src++; ! 481: if (dst == end) { ! 482: pvPtr->next = dst; ! 483: (*pvPtr->expandProc)(pvPtr, 20); ! 484: dst = pvPtr->next; ! 485: end = pvPtr->end; ! 486: } ! 487: *dst = c; ! 488: dst++; ! 489: if (CHAR_TYPE(c) == TCL_NORMAL) { ! 490: continue; ! 491: } else if (c == '{') { ! 492: level++; ! 493: } else if (c == '}') { ! 494: level--; ! 495: if (level == 0) { ! 496: dst--; /* Don't copy the last close brace. */ ! 497: break; ! 498: } ! 499: } else if (c == '\\') { ! 500: int count; ! 501: ! 502: /* ! 503: * Must always squish out backslash-newlines, even when in ! 504: * braces. This is needed so that this sequence can appear ! 505: * anywhere in a command, such as the middle of an expression. ! 506: */ ! 507: ! 508: if (*src == '\n') { ! 509: dst--; ! 510: src++; ! 511: } else { ! 512: (void) Tcl_Backslash(src-1, &count); ! 513: while (count > 1) { ! 514: if (dst == end) { ! 515: pvPtr->next = dst; ! 516: (*pvPtr->expandProc)(pvPtr, 20); ! 517: dst = pvPtr->next; ! 518: end = pvPtr->end; ! 519: } ! 520: *dst = *src; ! 521: dst++; ! 522: src++; ! 523: count--; ! 524: } ! 525: } ! 526: } else if (c == '\0') { ! 527: Tcl_SetResult(interp, "missing close-brace", TCL_STATIC); ! 528: *termPtr = string-1; ! 529: return TCL_ERROR; ! 530: } ! 531: } ! 532: ! 533: *dst = '\0'; ! 534: pvPtr->next = dst; ! 535: *termPtr = src; ! 536: return TCL_OK; ! 537: } ! 538: ! 539: /* ! 540: *-------------------------------------------------------------- ! 541: * ! 542: * TclParseWords -- ! 543: * ! 544: * This procedure parses one or more words from a command ! 545: * string and creates argv-style pointers to fully-substituted ! 546: * copies of those words. ! 547: * ! 548: * Results: ! 549: * The return value is a standard Tcl result. ! 550: * ! 551: * *argcPtr is modified to hold a count of the number of words ! 552: * successfully parsed, which may be 0. At most maxWords words ! 553: * will be parsed. If 0 <= *argcPtr < maxWords then it ! 554: * means that a command separator was seen. If *argcPtr ! 555: * is maxWords then it means that a command separator was ! 556: * not seen yet. ! 557: * ! 558: * *TermPtr is filled in with the address of the character ! 559: * just after the last one successfully processed in the ! 560: * last word. This is either the command terminator (if ! 561: * *argcPtr < maxWords), the character just after the last ! 562: * one in a word (if *argcPtr is maxWords), or the vicinity ! 563: * of an error (if the result is not TCL_OK). ! 564: * ! 565: * The pointers at *argv are filled in with pointers to the ! 566: * fully-substituted words, and the actual contents of the ! 567: * words are copied to the buffer at pvPtr. ! 568: * ! 569: * If an error occurrs then an error message is left in ! 570: * interp->result and the information at *argv, *argcPtr, ! 571: * and *pvPtr may be incomplete. ! 572: * ! 573: * Side effects: ! 574: * The buffer space in pvPtr may be enlarged by calling its ! 575: * expandProc. ! 576: * ! 577: *-------------------------------------------------------------- ! 578: */ ! 579: ! 580: int ! 581: TclParseWords(interp, string, flags, maxWords, termPtr, argcPtr, argv, pvPtr) ! 582: Tcl_Interp *interp; /* Interpreter to use for nested command ! 583: * evaluations and error messages. */ ! 584: char *string; /* First character of word. */ ! 585: int flags; /* Flags to control parsing (same values as ! 586: * passed to Tcl_Eval). */ ! 587: int maxWords; /* Maximum number of words to parse. */ ! 588: char **termPtr; /* Store address of terminating character ! 589: * here. */ ! 590: int *argcPtr; /* Filled in with actual number of words ! 591: * parsed. */ ! 592: char **argv; /* Store addresses of individual words here. */ ! 593: register ParseValue *pvPtr; /* Information about where to place ! 594: * fully-substituted word. */ ! 595: { ! 596: register char *src, *dst; ! 597: register char c; ! 598: int type, result, argc; ! 599: char *oldBuffer; /* Used to detect when pvPtr's buffer gets ! 600: * reallocated, so we can adjust all of the ! 601: * argv pointers. */ ! 602: ! 603: src = string; ! 604: oldBuffer = pvPtr->buffer; ! 605: dst = pvPtr->next; ! 606: for (argc = 0; argc < maxWords; argc++) { ! 607: argv[argc] = dst; ! 608: ! 609: /* ! 610: * Skip leading space. ! 611: */ ! 612: ! 613: skipSpace: ! 614: c = *src; ! 615: type = CHAR_TYPE(c); ! 616: while (type == TCL_SPACE) { ! 617: src++; ! 618: c = *src; ! 619: type = CHAR_TYPE(c); ! 620: } ! 621: ! 622: /* ! 623: * Handle the normal case (i.e. no leading double-quote or brace). ! 624: */ ! 625: ! 626: if (type == TCL_NORMAL) { ! 627: normalArg: ! 628: while (1) { ! 629: if (dst == pvPtr->end) { ! 630: /* ! 631: * Target buffer space is about to run out. Make ! 632: * more space. ! 633: */ ! 634: ! 635: pvPtr->next = dst; ! 636: (*pvPtr->expandProc)(pvPtr, 1); ! 637: dst = pvPtr->next; ! 638: } ! 639: ! 640: if (type == TCL_NORMAL) { ! 641: copy: ! 642: *dst = c; ! 643: dst++; ! 644: src++; ! 645: } else if (type == TCL_SPACE) { ! 646: goto wordEnd; ! 647: } else if (type == TCL_DOLLAR) { ! 648: int length; ! 649: char *value; ! 650: ! 651: value = Tcl_ParseVar(interp, src, termPtr); ! 652: if (value == NULL) { ! 653: return TCL_ERROR; ! 654: } ! 655: src = *termPtr; ! 656: length = strlen(value); ! 657: if ((pvPtr->end - dst) <= length) { ! 658: pvPtr->next = dst; ! 659: (*pvPtr->expandProc)(pvPtr, length); ! 660: dst = pvPtr->next; ! 661: } ! 662: strcpy(dst, value); ! 663: dst += length; ! 664: } else if (type == TCL_COMMAND_END) { ! 665: if ((c == ']') && !(flags & TCL_BRACKET_TERM)) { ! 666: goto copy; ! 667: } ! 668: ! 669: /* ! 670: * End of command; simulate a word-end first, so ! 671: * that the end-of-command can be processed as the ! 672: * first thing in a new word. ! 673: */ ! 674: ! 675: goto wordEnd; ! 676: } else if (type == TCL_OPEN_BRACKET) { ! 677: pvPtr->next = dst; ! 678: result = TclParseNestedCmd(interp, src+1, flags, termPtr, ! 679: pvPtr); ! 680: if (result != TCL_OK) { ! 681: return result; ! 682: } ! 683: src = *termPtr; ! 684: dst = pvPtr->next; ! 685: } else if (type == TCL_BACKSLASH) { ! 686: int numRead; ! 687: ! 688: *dst = Tcl_Backslash(src, &numRead); ! 689: if (*dst != 0) { ! 690: dst++; ! 691: } ! 692: src += numRead; ! 693: } else { ! 694: goto copy; ! 695: } ! 696: c = *src; ! 697: type = CHAR_TYPE(c); ! 698: } ! 699: } else { ! 700: ! 701: /* ! 702: * Check for the end of the command. ! 703: */ ! 704: ! 705: if (type == TCL_COMMAND_END) { ! 706: if (flags & TCL_BRACKET_TERM) { ! 707: if (c == '\0') { ! 708: Tcl_SetResult(interp, "missing close-bracket", ! 709: TCL_STATIC); ! 710: return TCL_ERROR; ! 711: } ! 712: } else { ! 713: if (c == ']') { ! 714: goto normalArg; ! 715: } ! 716: } ! 717: goto done; ! 718: } ! 719: ! 720: /* ! 721: * Now handle the special cases: open braces, double-quotes, ! 722: * and backslash-newline. ! 723: */ ! 724: ! 725: pvPtr->next = dst; ! 726: if (type == TCL_QUOTE) { ! 727: result = TclParseQuotes(interp, src+1, '"', flags, ! 728: termPtr, pvPtr); ! 729: } else if (type == TCL_OPEN_BRACE) { ! 730: result = TclParseBraces(interp, src+1, termPtr, pvPtr); ! 731: } else if ((type == TCL_BACKSLASH) && (src[1] == '\n')) { ! 732: src += 2; ! 733: goto skipSpace; ! 734: } else { ! 735: goto normalArg; ! 736: } ! 737: if (result != TCL_OK) { ! 738: return result; ! 739: } ! 740: ! 741: /* ! 742: * Back from quotes or braces; make sure that the terminating ! 743: * character was the end of the word. Have to be careful here ! 744: * to handle continuation lines (i.e. lines ending in backslash). ! 745: */ ! 746: ! 747: c = **termPtr; ! 748: if ((c == '\\') && ((*termPtr)[1] == '\n')) { ! 749: c = (*termPtr)[2]; ! 750: } ! 751: type = CHAR_TYPE(c); ! 752: if ((type != TCL_SPACE) && (type != TCL_COMMAND_END)) { ! 753: if (*src == '"') { ! 754: Tcl_SetResult(interp, "extra characters after close-quote", ! 755: TCL_STATIC); ! 756: } else { ! 757: Tcl_SetResult(interp, "extra characters after close-brace", ! 758: TCL_STATIC); ! 759: } ! 760: return TCL_ERROR; ! 761: } ! 762: src = *termPtr; ! 763: dst = pvPtr->next; ! 764: ! 765: } ! 766: ! 767: /* ! 768: * We're at the end of a word, so add a null terminator. Then ! 769: * see if the buffer was re-allocated during this word. If so, ! 770: * update all of the argv pointers. ! 771: */ ! 772: ! 773: wordEnd: ! 774: *dst = '\0'; ! 775: dst++; ! 776: if (oldBuffer != pvPtr->buffer) { ! 777: int i; ! 778: ! 779: for (i = 0; i <= argc; i++) { ! 780: argv[i] = pvPtr->buffer + (argv[i] - oldBuffer); ! 781: } ! 782: oldBuffer = pvPtr->buffer; ! 783: } ! 784: } ! 785: ! 786: done: ! 787: pvPtr->next = dst; ! 788: *termPtr = src; ! 789: *argcPtr = argc; ! 790: return TCL_OK; ! 791: } ! 792: ! 793: /* ! 794: *-------------------------------------------------------------- ! 795: * ! 796: * TclExpandParseValue -- ! 797: * ! 798: * This procedure is commonly used as the value of the ! 799: * expandProc in a ParseValue. It uses malloc to allocate ! 800: * more space for the result of a parse. ! 801: * ! 802: * Results: ! 803: * The buffer space in *pvPtr is reallocated to something ! 804: * larger, and if pvPtr->clientData is non-zero the old ! 805: * buffer is freed. Information is copied from the old ! 806: * buffer to the new one. ! 807: * ! 808: * Side effects: ! 809: * None. ! 810: * ! 811: *-------------------------------------------------------------- ! 812: */ ! 813: ! 814: void ! 815: TclExpandParseValue(pvPtr, needed) ! 816: register ParseValue *pvPtr; /* Information about buffer that ! 817: * must be expanded. If the clientData ! 818: * in the structure is non-zero, it ! 819: * means that the current buffer is ! 820: * dynamically allocated. */ ! 821: int needed; /* Minimum amount of additional space ! 822: * to allocate. */ ! 823: { ! 824: int newSpace; ! 825: char *new; ! 826: ! 827: /* ! 828: * Either double the size of the buffer or add enough new space ! 829: * to meet the demand, whichever produces a larger new buffer. ! 830: */ ! 831: ! 832: newSpace = (pvPtr->end - pvPtr->buffer) + 1; ! 833: if (newSpace < needed) { ! 834: newSpace += needed; ! 835: } else { ! 836: newSpace += newSpace; ! 837: } ! 838: new = (char *) ckalloc((unsigned) newSpace); ! 839: ! 840: /* ! 841: * Copy from old buffer to new, free old buffer if needed, and ! 842: * mark new buffer as malloc-ed. ! 843: */ ! 844: ! 845: memcpy((VOID *) new, (VOID *) pvPtr->buffer, pvPtr->next - pvPtr->buffer); ! 846: pvPtr->next = new + (pvPtr->next - pvPtr->buffer); ! 847: if (pvPtr->clientData != 0) { ! 848: ckfree(pvPtr->buffer); ! 849: } ! 850: pvPtr->buffer = new; ! 851: pvPtr->end = new + newSpace - 1; ! 852: pvPtr->clientData = (ClientData) 1; ! 853: } ! 854: ! 855: /* ! 856: *---------------------------------------------------------------------- ! 857: * ! 858: * TclWordEnd -- ! 859: * ! 860: * Given a pointer into a Tcl command, find the end of the next ! 861: * word of the command. ! 862: * ! 863: * Results: ! 864: * The return value is a pointer to the character just after the ! 865: * last one that's part of the word pointed to by "start". This ! 866: * may be the address of the NULL character at the end of the ! 867: * string. ! 868: * ! 869: * Side effects: ! 870: * None. ! 871: * ! 872: *---------------------------------------------------------------------- ! 873: */ ! 874: ! 875: char * ! 876: TclWordEnd(start, nested) ! 877: char *start; /* Beginning of a word of a Tcl command. */ ! 878: int nested; /* Zero means this is a top-level command. ! 879: * One means this is a nested command (close ! 880: * brace is a word terminator). */ ! 881: { ! 882: register char *p; ! 883: int count; ! 884: ! 885: p = start; ! 886: while (isspace(*p)) { ! 887: p++; ! 888: } ! 889: ! 890: /* ! 891: * Handle words beginning with a double-quote or a brace. ! 892: */ ! 893: ! 894: if (*p == '"') { ! 895: p = QuoteEnd(p+1, '"'); ! 896: } else if (*p == '{') { ! 897: int braces = 1; ! 898: while (braces != 0) { ! 899: p++; ! 900: while (*p == '\\') { ! 901: (void) Tcl_Backslash(p, &count); ! 902: p += count; ! 903: } ! 904: if (*p == '}') { ! 905: braces--; ! 906: } else if (*p == '{') { ! 907: braces++; ! 908: } else if (*p == 0) { ! 909: return p; ! 910: } ! 911: } ! 912: } ! 913: ! 914: /* ! 915: * Handle words that don't start with a brace or double-quote. ! 916: * This code is also invoked if the word starts with a brace or ! 917: * double-quote and there is garbage after the closing brace or ! 918: * quote. This is an error as far as Tcl_Eval is concerned, but ! 919: * for here the garbage is treated as part of the word. ! 920: */ ! 921: ! 922: while (*p != 0) { ! 923: if (*p == '[') { ! 924: p++; ! 925: while ((*p != ']') && (*p != 0)) { ! 926: p = TclWordEnd(p, 1); ! 927: } ! 928: if (*p == ']') { ! 929: p++; ! 930: } ! 931: } else if (*p == '\\') { ! 932: (void) Tcl_Backslash(p, &count); ! 933: p += count; ! 934: } else if (*p == '$') { ! 935: p = VarNameEnd(p); ! 936: } else if (*p == ';') { ! 937: /* ! 938: * Note: semi-colon terminates a word ! 939: * and also counts as a word by itself. ! 940: */ ! 941: ! 942: if (p == start) { ! 943: p++; ! 944: } ! 945: break; ! 946: } else if (isspace(*p)) { ! 947: break; ! 948: } else if ((*p == ']') && nested) { ! 949: break; ! 950: } else { ! 951: p++; ! 952: } ! 953: } ! 954: return p; ! 955: } ! 956: ! 957: /* ! 958: *---------------------------------------------------------------------- ! 959: * ! 960: * QuoteEnd -- ! 961: * ! 962: * Given a pointer to a string that obeys the parsing conventions ! 963: * for quoted things in Tcl, find the end of that quoted thing. ! 964: * The actual thing may be a quoted argument or a parenthesized ! 965: * index name. ! 966: * ! 967: * Results: ! 968: * The return value is a pointer to the character just after the ! 969: * last one that is part of the quoted string. ! 970: * ! 971: * Side effects: ! 972: * None. ! 973: * ! 974: *---------------------------------------------------------------------- ! 975: */ ! 976: ! 977: static char * ! 978: QuoteEnd(string, term) ! 979: char *string; /* Pointer to character just after opening ! 980: * "quote". */ ! 981: int term; /* This character will terminate the ! 982: * quoted string (e.g. '"' or ')'). */ ! 983: { ! 984: register char *p = string; ! 985: int count; ! 986: ! 987: while ((*p != 0) && (*p != term)) { ! 988: if (*p == '\\') { ! 989: (void) Tcl_Backslash(p, &count); ! 990: p += count; ! 991: } else if (*p == '[') { ! 992: p++; ! 993: while ((*p != ']') && (*p != 0)) { ! 994: p = TclWordEnd(p, 1); ! 995: } ! 996: if (*p == ']') { ! 997: p++; ! 998: } ! 999: } else if (*p == '$') { ! 1000: p = VarNameEnd(p); ! 1001: } else { ! 1002: p++; ! 1003: } ! 1004: } ! 1005: return p; ! 1006: } ! 1007: ! 1008: /* ! 1009: *---------------------------------------------------------------------- ! 1010: * ! 1011: * VarNameEnd -- ! 1012: * ! 1013: * Given a pointer to a variable reference using $-notation, find ! 1014: * the end of the variable name spec. ! 1015: * ! 1016: * Results: ! 1017: * The return value is a pointer to the character just after the ! 1018: * last one that is part of the variable name. ! 1019: * ! 1020: * Side effects: ! 1021: * None. ! 1022: * ! 1023: *---------------------------------------------------------------------- ! 1024: */ ! 1025: ! 1026: static char * ! 1027: VarNameEnd(string) ! 1028: char *string; /* Pointer to dollar-sign character. */ ! 1029: { ! 1030: register char *p = string+1; ! 1031: ! 1032: if (*p == '{') { ! 1033: do { ! 1034: p++; ! 1035: } while ((*p != '}') && (*p != 0)); ! 1036: } else { ! 1037: while (isalnum(*p) || (*p == '_')) { ! 1038: p++; ! 1039: } ! 1040: if ((*p == '(') && (p != string+1)) { ! 1041: p = QuoteEnd(p+1, ')'); ! 1042: } ! 1043: } ! 1044: return p; ! 1045: } ! 1046: ! 1047: /* ! 1048: *---------------------------------------------------------------------- ! 1049: * ! 1050: * Tcl_ParseVar -- ! 1051: * ! 1052: * Given a string starting with a $ sign, parse off a variable ! 1053: * name and return its value. ! 1054: * ! 1055: * Results: ! 1056: * The return value is the contents of the variable given by ! 1057: * the leading characters of string. If termPtr isn't NULL, ! 1058: * *termPtr gets filled in with the address of the character ! 1059: * just after the last one in the variable specifier. If the ! 1060: * variable doesn't exist, then the return value is NULL and ! 1061: * an error message will be left in interp->result. ! 1062: * ! 1063: * Side effects: ! 1064: * None. ! 1065: * ! 1066: *---------------------------------------------------------------------- ! 1067: */ ! 1068: ! 1069: char * ! 1070: Tcl_ParseVar(interp, string, termPtr) ! 1071: Tcl_Interp *interp; /* Context for looking up variable. */ ! 1072: register char *string; /* String containing variable name. ! 1073: * First character must be "$". */ ! 1074: char **termPtr; /* If non-NULL, points to word to fill ! 1075: * in with character just after last ! 1076: * one in the variable specifier. */ ! 1077: ! 1078: { ! 1079: char *name1, *name1End, c, *result; ! 1080: register char *name2; ! 1081: #define NUM_CHARS 200 ! 1082: char copyStorage[NUM_CHARS]; ! 1083: ParseValue pv; ! 1084: ! 1085: /* ! 1086: * There are three cases: ! 1087: * 1. The $ sign is followed by an open curly brace. Then the variable ! 1088: * name is everything up to the next close curly brace, and the ! 1089: * variable is a scalar variable. ! 1090: * 2. The $ sign is not followed by an open curly brace. Then the ! 1091: * variable name is everything up to the next character that isn't ! 1092: * a letter, digit, or underscore. If the following character is an ! 1093: * open parenthesis, then the information between parentheses is ! 1094: * the array element name, which can include any of the substitutions ! 1095: * permissible between quotes. ! 1096: * 3. The $ sign is followed by something that isn't a letter, digit, ! 1097: * or underscore: in this case, there is no variable name, and "$" ! 1098: * is returned. ! 1099: */ ! 1100: ! 1101: name2 = NULL; ! 1102: string++; ! 1103: if (*string == '{') { ! 1104: string++; ! 1105: name1 = string; ! 1106: while (*string != '}') { ! 1107: if (*string == 0) { ! 1108: Tcl_SetResult(interp, "missing close-brace for variable name", ! 1109: TCL_STATIC); ! 1110: if (termPtr != 0) { ! 1111: *termPtr = string; ! 1112: } ! 1113: return NULL; ! 1114: } ! 1115: string++; ! 1116: } ! 1117: name1End = string; ! 1118: string++; ! 1119: } else { ! 1120: name1 = string; ! 1121: while (isalnum(*string) || (*string == '_')) { ! 1122: string++; ! 1123: } ! 1124: if (string == name1) { ! 1125: if (termPtr != 0) { ! 1126: *termPtr = string; ! 1127: } ! 1128: return "$"; ! 1129: } ! 1130: name1End = string; ! 1131: if (*string == '(') { ! 1132: char *end; ! 1133: ! 1134: /* ! 1135: * Perform substitutions on the array element name, just as ! 1136: * is done for quotes. ! 1137: */ ! 1138: ! 1139: pv.buffer = pv.next = copyStorage; ! 1140: pv.end = copyStorage + NUM_CHARS - 1; ! 1141: pv.expandProc = TclExpandParseValue; ! 1142: pv.clientData = (ClientData) NULL; ! 1143: if (TclParseQuotes(interp, string+1, ')', 0, &end, &pv) ! 1144: != TCL_OK) { ! 1145: char msg[100]; ! 1146: sprintf(msg, "\n (parsing index for array \"%.*s\")", ! 1147: string-name1, name1); ! 1148: Tcl_AddErrorInfo(interp, msg); ! 1149: result = NULL; ! 1150: name2 = pv.buffer; ! 1151: if (termPtr != 0) { ! 1152: *termPtr = end; ! 1153: } ! 1154: goto done; ! 1155: } ! 1156: string = end; ! 1157: name2 = pv.buffer; ! 1158: } ! 1159: } ! 1160: if (termPtr != 0) { ! 1161: *termPtr = string; ! 1162: } ! 1163: ! 1164: c = *name1End; ! 1165: *name1End = 0; ! 1166: result = Tcl_GetVar2(interp, name1, name2, TCL_LEAVE_ERR_MSG); ! 1167: *name1End = c; ! 1168: ! 1169: done: ! 1170: if ((name2 != NULL) && (pv.buffer != copyStorage)) { ! 1171: ckfree(pv.buffer); ! 1172: } ! 1173: return result; ! 1174: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.