|
|
1.1 ! root 1: /* ! 2: * tclUtil.c -- ! 3: * ! 4: * This file contains utility procedures that are used by many Tcl ! 5: * commands. ! 6: * ! 7: * Copyright 1987-1991 Regents of the University of California ! 8: * Permission to use, copy, modify, and distribute this ! 9: * software and its documentation for any purpose and without ! 10: * fee is hereby granted, provided that the above copyright ! 11: * notice appear in all copies. The University of California ! 12: * makes no representations about the suitability of this ! 13: * software for any purpose. It is provided "as is" without ! 14: * express or implied warranty. ! 15: */ ! 16: ! 17: #ifndef lint ! 18: static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclUtil.c,v 1.63 92/07/02 08:50:54 ouster Exp $ SPRITE (Berkeley)"; ! 19: #endif ! 20: ! 21: #include "tclint.h" ! 22: ! 23: /* ! 24: * The following values are used in the flags returned by Tcl_ScanElement ! 25: * and used by Tcl_ConvertElement. The value TCL_DONT_USE_BRACES is also ! 26: * defined in tcl.h; make sure its value doesn't overlap with any of the ! 27: * values below. ! 28: * ! 29: * TCL_DONT_USE_BRACES - 1 means the string mustn't be enclosed in ! 30: * braces (e.g. it contains unmatched braces, ! 31: * or ends in a backslash character, or user ! 32: * just doesn't want braces); handle all ! 33: * special characters by adding backslashes. ! 34: * USE_BRACES - 1 means the string contains a special ! 35: * character that can be handled simply by ! 36: * enclosing the entire argument in braces. ! 37: * BRACES_UNMATCHED - 1 means that braces aren't properly matched ! 38: * in the argument. ! 39: */ ! 40: ! 41: #define USE_BRACES 2 ! 42: #define BRACES_UNMATCHED 4 ! 43: ! 44: /* ! 45: * The variable below is set to NULL before invoking regexp functions ! 46: * and checked after those functions. If an error occurred then regerror ! 47: * will set the variable to point to a (static) error message. This ! 48: * mechanism unfortunately does not support multi-threading, but then ! 49: * neither does the rest of the regexp facilities. ! 50: */ ! 51: ! 52: char *tclRegexpError = NULL; ! 53: ! 54: /* ! 55: * Function prototypes for local procedures in this file: ! 56: */ ! 57: ! 58: static void SetupAppendBuffer _ANSI_ARGS_((Interp *iPtr, ! 59: int newSpace)); ! 60: ! 61: /* ! 62: *---------------------------------------------------------------------- ! 63: * ! 64: * TclFindElement -- ! 65: * ! 66: * Given a pointer into a Tcl list, locate the first (or next) ! 67: * element in the list. ! 68: * ! 69: * Results: ! 70: * The return value is normally TCL_OK, which means that the ! 71: * element was successfully located. If TCL_ERROR is returned ! 72: * it means that list didn't have proper list structure; ! 73: * interp->result contains a more detailed error message. ! 74: * ! 75: * If TCL_OK is returned, then *elementPtr will be set to point ! 76: * to the first element of list, and *nextPtr will be set to point ! 77: * to the character just after any white space following the last ! 78: * character that's part of the element. If this is the last argument ! 79: * in the list, then *nextPtr will point to the NULL character at the ! 80: * end of list. If sizePtr is non-NULL, *sizePtr is filled in with ! 81: * the number of characters in the element. If the element is in ! 82: * braces, then *elementPtr will point to the character after the ! 83: * opening brace and *sizePtr will not include either of the braces. ! 84: * If there isn't an element in the list, *sizePtr will be zero, and ! 85: * both *elementPtr and *termPtr will refer to the null character at ! 86: * the end of list. Note: this procedure does NOT collapse backslash ! 87: * sequences. ! 88: * ! 89: * Side effects: ! 90: * None. ! 91: * ! 92: *---------------------------------------------------------------------- ! 93: */ ! 94: ! 95: int ! 96: TclFindElement(interp, list, elementPtr, nextPtr, sizePtr, bracePtr) ! 97: Tcl_Interp *interp; /* Interpreter to use for error reporting. */ ! 98: register char *list; /* String containing Tcl list with zero ! 99: * or more elements (possibly in braces). */ ! 100: char **elementPtr; /* Fill in with location of first significant ! 101: * character in first element of list. */ ! 102: char **nextPtr; /* Fill in with location of character just ! 103: * after all white space following end of ! 104: * argument (i.e. next argument or end of ! 105: * list). */ ! 106: int *sizePtr; /* If non-zero, fill in with size of ! 107: * element. */ ! 108: int *bracePtr; /* If non-zero fill in with non-zero/zero ! 109: * to indicate that arg was/wasn't ! 110: * in braces. */ ! 111: { ! 112: register char *p; ! 113: int openBraces = 0; ! 114: int inQuotes = 0; ! 115: int size; ! 116: ! 117: /* ! 118: * Skim off leading white space and check for an opening brace or ! 119: * quote. Note: use of "isascii" below and elsewhere in this ! 120: * procedure is a temporary workaround (7/27/90) because Mx uses characters ! 121: * with the high-order bit set for some things. This should probably ! 122: * be changed back eventually, or all of Tcl should call isascii. ! 123: */ ! 124: ! 125: while (isascii(*list) && isspace(*list)) { ! 126: list++; ! 127: } ! 128: if (*list == '{') { ! 129: openBraces = 1; ! 130: list++; ! 131: } else if (*list == '"') { ! 132: inQuotes = 1; ! 133: list++; ! 134: } ! 135: if (bracePtr != 0) { ! 136: *bracePtr = openBraces; ! 137: } ! 138: p = list; ! 139: ! 140: /* ! 141: * Find the end of the element (either a space or a close brace or ! 142: * the end of the string). ! 143: */ ! 144: ! 145: while (1) { ! 146: switch (*p) { ! 147: ! 148: /* ! 149: * Open brace: don't treat specially unless the element is ! 150: * in braces. In this case, keep a nesting count. ! 151: */ ! 152: ! 153: case '{': ! 154: if (openBraces != 0) { ! 155: openBraces++; ! 156: } ! 157: break; ! 158: ! 159: /* ! 160: * Close brace: if element is in braces, keep nesting ! 161: * count and quit when the last close brace is seen. ! 162: */ ! 163: ! 164: case '}': ! 165: if (openBraces == 1) { ! 166: char *p2; ! 167: ! 168: size = p - list; ! 169: p++; ! 170: if ((isascii(*p) && isspace(*p)) || (*p == 0)) { ! 171: goto done; ! 172: } ! 173: for (p2 = p; (*p2 != 0) && (!isspace(*p2)) && (p2 < p+20); ! 174: p2++) { ! 175: /* null body */ ! 176: } ! 177: Tcl_ResetResult(interp); ! 178: sprintf(interp->result, ! 179: "list element in braces followed by \"%.*s\" instead of space", ! 180: p2-p, p); ! 181: return TCL_ERROR; ! 182: } else if (openBraces != 0) { ! 183: openBraces--; ! 184: } ! 185: break; ! 186: ! 187: /* ! 188: * Backslash: skip over everything up to the end of the ! 189: * backslash sequence. ! 190: */ ! 191: ! 192: case '\\': { ! 193: int size; ! 194: ! 195: (void) Tcl_Backslash(p, &size); ! 196: p += size - 1; ! 197: break; ! 198: } ! 199: ! 200: /* ! 201: * Space: ignore if element is in braces or quotes; otherwise ! 202: * terminate element. ! 203: */ ! 204: ! 205: case ' ': ! 206: case '\f': ! 207: case '\n': ! 208: case '\r': ! 209: case '\t': ! 210: case '\v': ! 211: if ((openBraces == 0) && !inQuotes) { ! 212: size = p - list; ! 213: goto done; ! 214: } ! 215: break; ! 216: ! 217: /* ! 218: * Double-quote: if element is in quotes then terminate it. ! 219: */ ! 220: ! 221: case '"': ! 222: if (inQuotes) { ! 223: char *p2; ! 224: ! 225: size = p-list; ! 226: p++; ! 227: if ((isascii(*p) && isspace(*p)) || (*p == 0)) { ! 228: goto done; ! 229: } ! 230: for (p2 = p; (*p2 != 0) && (!isspace(*p2)) && (p2 < p+20); ! 231: p2++) { ! 232: /* null body */ ! 233: } ! 234: Tcl_ResetResult(interp); ! 235: sprintf(interp->result, ! 236: "list element in quotes followed by \"%.*s\" %s", ! 237: p2-p, p, "instead of space"); ! 238: return TCL_ERROR; ! 239: } ! 240: break; ! 241: ! 242: /* ! 243: * End of list: terminate element. ! 244: */ ! 245: ! 246: case 0: ! 247: if (openBraces != 0) { ! 248: Tcl_SetResult(interp, "unmatched open brace in list", ! 249: TCL_STATIC); ! 250: return TCL_ERROR; ! 251: } else if (inQuotes) { ! 252: Tcl_SetResult(interp, "unmatched open quote in list", ! 253: TCL_STATIC); ! 254: return TCL_ERROR; ! 255: } ! 256: size = p - list; ! 257: goto done; ! 258: ! 259: } ! 260: p++; ! 261: } ! 262: ! 263: done: ! 264: while (isascii(*p) && isspace(*p)) { ! 265: p++; ! 266: } ! 267: *elementPtr = list; ! 268: *nextPtr = p; ! 269: if (sizePtr != 0) { ! 270: *sizePtr = size; ! 271: } ! 272: return TCL_OK; ! 273: } ! 274: ! 275: /* ! 276: *---------------------------------------------------------------------- ! 277: * ! 278: * TclCopyAndCollapse -- ! 279: * ! 280: * Copy a string and eliminate any backslashes that aren't in braces. ! 281: * ! 282: * Results: ! 283: * There is no return value. Count chars. get copied from src ! 284: * to dst. Along the way, if backslash sequences are found outside ! 285: * braces, the backslashes are eliminated in the copy. ! 286: * After scanning count chars. from source, a null character is ! 287: * placed at the end of dst. ! 288: * ! 289: * Side effects: ! 290: * None. ! 291: * ! 292: *---------------------------------------------------------------------- ! 293: */ ! 294: ! 295: void ! 296: TclCopyAndCollapse(count, src, dst) ! 297: int count; /* Total number of characters to copy ! 298: * from src. */ ! 299: register char *src; /* Copy from here... */ ! 300: register char *dst; /* ... to here. */ ! 301: { ! 302: register char c; ! 303: int numRead; ! 304: ! 305: for (c = *src; count > 0; src++, c = *src, count--) { ! 306: if (c == '\\') { ! 307: *dst = Tcl_Backslash(src, &numRead); ! 308: if (*dst != 0) { ! 309: dst++; ! 310: } ! 311: src += numRead-1; ! 312: count -= numRead-1; ! 313: } else { ! 314: *dst = c; ! 315: dst++; ! 316: } ! 317: } ! 318: *dst = 0; ! 319: } ! 320: ! 321: /* ! 322: *---------------------------------------------------------------------- ! 323: * ! 324: * Tcl_SplitList -- ! 325: * ! 326: * Splits a list up into its constituent fields. ! 327: * ! 328: * Results ! 329: * The return value is normally TCL_OK, which means that ! 330: * the list was successfully split up. If TCL_ERROR is ! 331: * returned, it means that "list" didn't have proper list ! 332: * structure; interp->result will contain a more detailed ! 333: * error message. ! 334: * ! 335: * *argvPtr will be filled in with the address of an array ! 336: * whose elements point to the elements of list, in order. ! 337: * *argcPtr will get filled in with the number of valid elements ! 338: * in the array. A single block of memory is dynamically allocated ! 339: * to hold both the argv array and a copy of the list (with ! 340: * backslashes and braces removed in the standard way). ! 341: * The caller must eventually free this memory by calling free() ! 342: * on *argvPtr. Note: *argvPtr and *argcPtr are only modified ! 343: * if the procedure returns normally. ! 344: * ! 345: * Side effects: ! 346: * Memory is allocated. ! 347: * ! 348: *---------------------------------------------------------------------- ! 349: */ ! 350: ! 351: int ! 352: Tcl_SplitList(interp, list, argcPtr, argvPtr) ! 353: Tcl_Interp *interp; /* Interpreter to use for error reporting. */ ! 354: char *list; /* Pointer to string with list structure. */ ! 355: int *argcPtr; /* Pointer to location to fill in with ! 356: * the number of elements in the list. */ ! 357: char ***argvPtr; /* Pointer to place to store pointer to array ! 358: * of pointers to list elements. */ ! 359: { ! 360: char **argv; ! 361: register char *p; ! 362: int size, i, result, elSize, brace; ! 363: char *element; ! 364: ! 365: /* ! 366: * Figure out how much space to allocate. There must be enough ! 367: * space for both the array of pointers and also for a copy of ! 368: * the list. To estimate the number of pointers needed, count ! 369: * the number of space characters in the list. ! 370: */ ! 371: ! 372: for (size = 1, p = list; *p != 0; p++) { ! 373: if (isspace(*p)) { ! 374: size++; ! 375: } ! 376: } ! 377: size++; /* Leave space for final NULL pointer. */ ! 378: argv = (char **) ckalloc((unsigned) ! 379: ((size * sizeof(char *)) + (p - list) + 1)); ! 380: for (i = 0, p = ((char *) argv) + size*sizeof(char *); ! 381: *list != 0; i++) { ! 382: result = TclFindElement(interp, list, &element, &list, &elSize, &brace); ! 383: if (result != TCL_OK) { ! 384: ckfree((char *) argv); ! 385: return result; ! 386: } ! 387: if (*element == 0) { ! 388: break; ! 389: } ! 390: if (i >= size) { ! 391: ckfree((char *) argv); ! 392: Tcl_SetResult(interp, "internal error in Tcl_SplitList", ! 393: TCL_STATIC); ! 394: return TCL_ERROR; ! 395: } ! 396: argv[i] = p; ! 397: if (brace) { ! 398: strncpy(p, element, elSize); ! 399: p += elSize; ! 400: *p = 0; ! 401: p++; ! 402: } else { ! 403: TclCopyAndCollapse(elSize, element, p); ! 404: p += elSize+1; ! 405: } ! 406: } ! 407: ! 408: argv[i] = NULL; ! 409: *argvPtr = argv; ! 410: *argcPtr = i; ! 411: return TCL_OK; ! 412: } ! 413: ! 414: /* ! 415: *---------------------------------------------------------------------- ! 416: * ! 417: * Tcl_ScanElement -- ! 418: * ! 419: * This procedure is a companion procedure to Tcl_ConvertElement. ! 420: * It scans a string to see what needs to be done to it (e.g. ! 421: * add backslashes or enclosing braces) to make the string into ! 422: * a valid Tcl list element. ! 423: * ! 424: * Results: ! 425: * The return value is an overestimate of the number of characters ! 426: * that will be needed by Tcl_ConvertElement to produce a valid ! 427: * list element from string. The word at *flagPtr is filled in ! 428: * with a value needed by Tcl_ConvertElement when doing the actual ! 429: * conversion. ! 430: * ! 431: * Side effects: ! 432: * None. ! 433: * ! 434: *---------------------------------------------------------------------- ! 435: */ ! 436: ! 437: int ! 438: Tcl_ScanElement(string, flagPtr) ! 439: char *string; /* String to convert to Tcl list element. */ ! 440: int *flagPtr; /* Where to store information to guide ! 441: * Tcl_ConvertElement. */ ! 442: { ! 443: int flags, nestingLevel; ! 444: register char *p; ! 445: ! 446: /* ! 447: * This procedure and Tcl_ConvertElement together do two things: ! 448: * ! 449: * 1. They produce a proper list, one that will yield back the ! 450: * argument strings when evaluated or when disassembled with ! 451: * Tcl_SplitList. This is the most important thing. ! 452: * ! 453: * 2. They try to produce legible output, which means minimizing the ! 454: * use of backslashes (using braces instead). However, there are ! 455: * some situations where backslashes must be used (e.g. an element ! 456: * like "{abc": the leading brace will have to be backslashed. For ! 457: * each element, one of three things must be done: ! 458: * ! 459: * (a) Use the element as-is (it doesn't contain anything special ! 460: * characters). This is the most desirable option. ! 461: * ! 462: * (b) Enclose the element in braces, but leave the contents alone. ! 463: * This happens if the element contains embedded space, or if it ! 464: * contains characters with special interpretation ($, [, ;, or \), ! 465: * or if it starts with a brace or double-quote, or if there are ! 466: * no characters in the element. ! 467: * ! 468: * (c) Don't enclose the element in braces, but add backslashes to ! 469: * prevent special interpretation of special characters. This is a ! 470: * last resort used when the argument would normally fall under case ! 471: * (b) but contains unmatched braces. It also occurs if the last ! 472: * character of the argument is a backslash. ! 473: * ! 474: * The procedure figures out how many bytes will be needed to store ! 475: * the result (actually, it overestimates). It also collects information ! 476: * about the element in the form of a flags word. ! 477: */ ! 478: ! 479: nestingLevel = 0; ! 480: flags = 0; ! 481: p = string; ! 482: if ((*p == '{') || (*p == '"') || (*p == 0)) { ! 483: flags |= USE_BRACES; ! 484: } ! 485: for ( ; *p != 0; p++) { ! 486: switch (*p) { ! 487: case '{': ! 488: nestingLevel++; ! 489: break; ! 490: case '}': ! 491: nestingLevel--; ! 492: if (nestingLevel < 0) { ! 493: flags |= TCL_DONT_USE_BRACES|BRACES_UNMATCHED; ! 494: } ! 495: break; ! 496: case '[': ! 497: case '$': ! 498: case ';': ! 499: case ' ': ! 500: case '\f': ! 501: case '\n': ! 502: case '\r': ! 503: case '\t': ! 504: case '\v': ! 505: flags |= USE_BRACES; ! 506: break; ! 507: case '\\': ! 508: if (p[1] == 0) { ! 509: flags = TCL_DONT_USE_BRACES; ! 510: } else { ! 511: int size; ! 512: ! 513: (void) Tcl_Backslash(p, &size); ! 514: p += size-1; ! 515: flags |= USE_BRACES; ! 516: } ! 517: break; ! 518: } ! 519: } ! 520: if (nestingLevel != 0) { ! 521: flags = TCL_DONT_USE_BRACES | BRACES_UNMATCHED; ! 522: } ! 523: *flagPtr = flags; ! 524: ! 525: /* ! 526: * Allow enough space to backslash every character plus leave ! 527: * two spaces for braces. ! 528: */ ! 529: ! 530: return 2*(p-string) + 2; ! 531: } ! 532: ! 533: /* ! 534: *---------------------------------------------------------------------- ! 535: * ! 536: * Tcl_ConvertElement -- ! 537: * ! 538: * This is a companion procedure to Tcl_ScanElement. Given the ! 539: * information produced by Tcl_ScanElement, this procedure converts ! 540: * a string to a list element equal to that string. ! 541: * ! 542: * Results: ! 543: * Information is copied to *dst in the form of a list element ! 544: * identical to src (i.e. if Tcl_SplitList is applied to dst it ! 545: * will produce a string identical to src). The return value is ! 546: * a count of the number of characters copied (not including the ! 547: * terminating NULL character). ! 548: * ! 549: * Side effects: ! 550: * None. ! 551: * ! 552: *---------------------------------------------------------------------- ! 553: */ ! 554: ! 555: int ! 556: Tcl_ConvertElement(src, dst, flags) ! 557: register char *src; /* Source information for list element. */ ! 558: char *dst; /* Place to put list-ified element. */ ! 559: int flags; /* Flags produced by Tcl_ScanElement. */ ! 560: { ! 561: register char *p = dst; ! 562: ! 563: /* ! 564: * See the comment block at the beginning of the Tcl_ScanElement ! 565: * code for details of how this works. ! 566: */ ! 567: ! 568: if ((flags & USE_BRACES) && !(flags & TCL_DONT_USE_BRACES)) { ! 569: *p = '{'; ! 570: p++; ! 571: for ( ; *src != 0; src++, p++) { ! 572: *p = *src; ! 573: } ! 574: *p = '}'; ! 575: p++; ! 576: } else if (*src == 0) { ! 577: /* ! 578: * If string is empty but can't use braces, then use special ! 579: * backslash sequence that maps to empty string. ! 580: */ ! 581: ! 582: p[0] = '\\'; ! 583: p[1] = '0'; ! 584: p += 2; ! 585: } else { ! 586: for (; *src != 0 ; src++) { ! 587: switch (*src) { ! 588: case ']': ! 589: case '[': ! 590: case '$': ! 591: case ';': ! 592: case ' ': ! 593: case '\\': ! 594: case '"': ! 595: *p = '\\'; ! 596: p++; ! 597: break; ! 598: case '{': ! 599: case '}': ! 600: if (flags & BRACES_UNMATCHED) { ! 601: *p = '\\'; ! 602: p++; ! 603: } ! 604: break; ! 605: case '\f': ! 606: *p = '\\'; ! 607: p++; ! 608: *p = 'f'; ! 609: p++; ! 610: continue; ! 611: case '\n': ! 612: *p = '\\'; ! 613: p++; ! 614: *p = 'n'; ! 615: p++; ! 616: continue; ! 617: case '\r': ! 618: *p = '\\'; ! 619: p++; ! 620: *p = 'r'; ! 621: p++; ! 622: continue; ! 623: case '\t': ! 624: *p = '\\'; ! 625: p++; ! 626: *p = 't'; ! 627: p++; ! 628: continue; ! 629: case '\v': ! 630: *p = '\\'; ! 631: p++; ! 632: *p = 'v'; ! 633: p++; ! 634: continue; ! 635: } ! 636: *p = *src; ! 637: p++; ! 638: } ! 639: } ! 640: *p = '\0'; ! 641: return p-dst; ! 642: } ! 643: ! 644: /* ! 645: *---------------------------------------------------------------------- ! 646: * ! 647: * Tcl_Merge -- ! 648: * ! 649: * Given a collection of strings, merge them together into a ! 650: * single string that has proper Tcl list structured (i.e. ! 651: * Tcl_SplitList may be used to retrieve strings equal to the ! 652: * original elements, and Tcl_Eval will parse the string back ! 653: * into its original elements). ! 654: * ! 655: * Results: ! 656: * The return value is the address of a dynamically-allocated ! 657: * string containing the merged list. ! 658: * ! 659: * Side effects: ! 660: * None. ! 661: * ! 662: *---------------------------------------------------------------------- ! 663: */ ! 664: ! 665: char * ! 666: Tcl_Merge(argc, argv) ! 667: int argc; /* How many strings to merge. */ ! 668: char **argv; /* Array of string values. */ ! 669: { ! 670: # define LOCAL_SIZE 20 ! 671: int localFlags[LOCAL_SIZE], *flagPtr; ! 672: int numChars; ! 673: char *result; ! 674: register char *dst; ! 675: int i; ! 676: ! 677: /* ! 678: * Pass 1: estimate space, gather flags. ! 679: */ ! 680: ! 681: if (argc <= LOCAL_SIZE) { ! 682: flagPtr = localFlags; ! 683: } else { ! 684: flagPtr = (int *) ckalloc((unsigned) argc*sizeof(int)); ! 685: } ! 686: numChars = 1; ! 687: for (i = 0; i < argc; i++) { ! 688: numChars += Tcl_ScanElement(argv[i], &flagPtr[i]) + 1; ! 689: } ! 690: ! 691: /* ! 692: * Pass two: copy into the result area. ! 693: */ ! 694: ! 695: result = (char *) ckalloc((unsigned) numChars); ! 696: dst = result; ! 697: for (i = 0; i < argc; i++) { ! 698: numChars = Tcl_ConvertElement(argv[i], dst, flagPtr[i]); ! 699: dst += numChars; ! 700: *dst = ' '; ! 701: dst++; ! 702: } ! 703: if (dst == result) { ! 704: *dst = 0; ! 705: } else { ! 706: dst[-1] = 0; ! 707: } ! 708: ! 709: if (flagPtr != localFlags) { ! 710: ckfree((char *) flagPtr); ! 711: } ! 712: return result; ! 713: } ! 714: ! 715: /* ! 716: *---------------------------------------------------------------------- ! 717: * ! 718: * Tcl_Concat -- ! 719: * ! 720: * Concatenate a set of strings into a single large string. ! 721: * ! 722: * Results: ! 723: * The return value is dynamically-allocated string containing ! 724: * a concatenation of all the strings in argv, with spaces between ! 725: * the original argv elements. ! 726: * ! 727: * Side effects: ! 728: * Memory is allocated for the result; the caller is responsible ! 729: * for freeing the memory. ! 730: * ! 731: *---------------------------------------------------------------------- ! 732: */ ! 733: ! 734: char * ! 735: Tcl_Concat(argc, argv) ! 736: int argc; /* Number of strings to concatenate. */ ! 737: char **argv; /* Array of strings to concatenate. */ ! 738: { ! 739: int totalSize, i; ! 740: register char *p; ! 741: char *result; ! 742: ! 743: for (totalSize = 1, i = 0; i < argc; i++) { ! 744: totalSize += strlen(argv[i]) + 1; ! 745: } ! 746: result = (char *) ckalloc((unsigned) totalSize); ! 747: if (argc == 0) { ! 748: *result = '\0'; ! 749: return result; ! 750: } ! 751: for (p = result, i = 0; i < argc; i++) { ! 752: char *element; ! 753: int length; ! 754: ! 755: /* ! 756: * Clip white space off the front and back of the string ! 757: * to generate a neater result, and ignore any empty ! 758: * elements. ! 759: */ ! 760: ! 761: element = argv[i]; ! 762: while (isspace(*element)) { ! 763: element++; ! 764: } ! 765: for (length = strlen(element); ! 766: (length > 0) && (isspace(element[length-1])); ! 767: length--) { ! 768: /* Null loop body. */ ! 769: } ! 770: if (length == 0) { ! 771: continue; ! 772: } ! 773: (void) strncpy(p, element, length); ! 774: p += length; ! 775: *p = ' '; ! 776: p++; ! 777: } ! 778: if (p != result) { ! 779: p[-1] = 0; ! 780: } else { ! 781: *p = 0; ! 782: } ! 783: return result; ! 784: } ! 785: ! 786: /* ! 787: *---------------------------------------------------------------------- ! 788: * ! 789: * Tcl_StringMatch -- ! 790: * ! 791: * See if a particular string matches a particular pattern. ! 792: * ! 793: * Results: ! 794: * The return value is 1 if string matches pattern, and ! 795: * 0 otherwise. The matching operation permits the following ! 796: * special characters in the pattern: *?\[] (see the manual ! 797: * entry for details on what these mean). ! 798: * ! 799: * Side effects: ! 800: * None. ! 801: * ! 802: *---------------------------------------------------------------------- ! 803: */ ! 804: ! 805: int ! 806: Tcl_StringMatch(string, pattern) ! 807: register char *string; /* String. */ ! 808: register char *pattern; /* Pattern, which may contain ! 809: * special characters. */ ! 810: { ! 811: char c2; ! 812: ! 813: while (1) { ! 814: /* See if we're at the end of both the pattern and the string. ! 815: * If so, we succeeded. If we're at the end of the pattern ! 816: * but not at the end of the string, we failed. ! 817: */ ! 818: ! 819: if (*pattern == 0) { ! 820: if (*string == 0) { ! 821: return 1; ! 822: } else { ! 823: return 0; ! 824: } ! 825: } ! 826: if ((*string == 0) && (*pattern != '*')) { ! 827: return 0; ! 828: } ! 829: ! 830: /* Check for a "*" as the next pattern character. It matches ! 831: * any substring. We handle this by calling ourselves ! 832: * recursively for each postfix of string, until either we ! 833: * match or we reach the end of the string. ! 834: */ ! 835: ! 836: if (*pattern == '*') { ! 837: pattern += 1; ! 838: if (*pattern == 0) { ! 839: return 1; ! 840: } ! 841: while (1) { ! 842: if (Tcl_StringMatch(string, pattern)) { ! 843: return 1; ! 844: } ! 845: if (*string == 0) { ! 846: return 0; ! 847: } ! 848: string += 1; ! 849: } ! 850: } ! 851: ! 852: /* Check for a "?" as the next pattern character. It matches ! 853: * any single character. ! 854: */ ! 855: ! 856: if (*pattern == '?') { ! 857: goto thisCharOK; ! 858: } ! 859: ! 860: /* Check for a "[" as the next pattern character. It is followed ! 861: * by a list of characters that are acceptable, or by a range ! 862: * (two characters separated by "-"). ! 863: */ ! 864: ! 865: if (*pattern == '[') { ! 866: pattern += 1; ! 867: while (1) { ! 868: if ((*pattern == ']') || (*pattern == 0)) { ! 869: return 0; ! 870: } ! 871: if (*pattern == *string) { ! 872: break; ! 873: } ! 874: if (pattern[1] == '-') { ! 875: c2 = pattern[2]; ! 876: if (c2 == 0) { ! 877: return 0; ! 878: } ! 879: if ((*pattern <= *string) && (c2 >= *string)) { ! 880: break; ! 881: } ! 882: if ((*pattern >= *string) && (c2 <= *string)) { ! 883: break; ! 884: } ! 885: pattern += 2; ! 886: } ! 887: pattern += 1; ! 888: } ! 889: while ((*pattern != ']') && (*pattern != 0)) { ! 890: pattern += 1; ! 891: } ! 892: goto thisCharOK; ! 893: } ! 894: ! 895: /* If the next pattern character is '/', just strip off the '/' ! 896: * so we do exact matching on the character that follows. ! 897: */ ! 898: ! 899: if (*pattern == '\\') { ! 900: pattern += 1; ! 901: if (*pattern == 0) { ! 902: return 0; ! 903: } ! 904: } ! 905: ! 906: /* There's no special character. Just make sure that the next ! 907: * characters of each string match. ! 908: */ ! 909: ! 910: if (*pattern != *string) { ! 911: return 0; ! 912: } ! 913: ! 914: thisCharOK: pattern += 1; ! 915: string += 1; ! 916: } ! 917: } ! 918: ! 919: /* ! 920: *---------------------------------------------------------------------- ! 921: * ! 922: * Tcl_SetResult -- ! 923: * ! 924: * Arrange for "string" to be the Tcl return value. ! 925: * ! 926: * Results: ! 927: * None. ! 928: * ! 929: * Side effects: ! 930: * interp->result is left pointing either to "string" (if "copy" is 0) ! 931: * or to a copy of string. ! 932: * ! 933: *---------------------------------------------------------------------- ! 934: */ ! 935: ! 936: void ! 937: Tcl_SetResult(interp, string, freeProc) ! 938: Tcl_Interp *interp; /* Interpreter with which to associate the ! 939: * return value. */ ! 940: char *string; /* Value to be returned. If NULL, ! 941: * the result is set to an empty string. */ ! 942: Tcl_FreeProc *freeProc; /* Gives information about the string: ! 943: * TCL_STATIC, TCL_VOLATILE, or the address ! 944: * of a Tcl_FreeProc such as free. */ ! 945: { ! 946: register Interp *iPtr = (Interp *) interp; ! 947: int length; ! 948: Tcl_FreeProc *oldFreeProc = iPtr->freeProc; ! 949: char *oldResult = iPtr->result; ! 950: ! 951: iPtr->freeProc = freeProc; ! 952: if (string == NULL) { ! 953: iPtr->resultSpace[0] = 0; ! 954: iPtr->result = iPtr->resultSpace; ! 955: iPtr->freeProc = 0; ! 956: } else if (freeProc == TCL_VOLATILE) { ! 957: length = strlen(string); ! 958: if (length > TCL_RESULT_SIZE) { ! 959: iPtr->result = (char *) ckalloc((unsigned) length+1); ! 960: iPtr->freeProc = (Tcl_FreeProc *) free; ! 961: } else { ! 962: iPtr->result = iPtr->resultSpace; ! 963: iPtr->freeProc = 0; ! 964: } ! 965: strcpy(iPtr->result, string); ! 966: } else { ! 967: iPtr->result = string; ! 968: } ! 969: ! 970: /* ! 971: * If the old result was dynamically-allocated, free it up. Do it ! 972: * here, rather than at the beginning, in case the new result value ! 973: * was part of the old result value. ! 974: */ ! 975: ! 976: if (oldFreeProc != 0) { ! 977: (*oldFreeProc)(oldResult); ! 978: } ! 979: } ! 980: ! 981: /* ! 982: *---------------------------------------------------------------------- ! 983: * ! 984: * Tcl_AppendResult -- ! 985: * ! 986: * Append a variable number of strings onto the result already ! 987: * present for an interpreter. ! 988: * ! 989: * Results: ! 990: * None. ! 991: * ! 992: * Side effects: ! 993: * The result in the interpreter given by the first argument ! 994: * is extended by the strings given by the second and following ! 995: * arguments (up to a terminating NULL argument). ! 996: * ! 997: *---------------------------------------------------------------------- ! 998: */ ! 999: ! 1000: void ! 1001: Tcl_AppendResult(Tcl_Interp *interp, ...) ! 1002: { ! 1003: va_list argList; ! 1004: register Interp *iPtr; ! 1005: char *string; ! 1006: int newSpace; ! 1007: ! 1008: /* ! 1009: * First, scan through all the arguments to see how much space is ! 1010: * needed. ! 1011: */ ! 1012: ! 1013: va_start(argList, interp); ! 1014: iPtr = (Interp *)interp; ! 1015: newSpace = 0; ! 1016: while (1) { ! 1017: string = va_arg(argList, char *); ! 1018: if (string == NULL) { ! 1019: break; ! 1020: } ! 1021: newSpace += strlen(string); ! 1022: } ! 1023: va_end(argList); ! 1024: ! 1025: /* ! 1026: * If the append buffer isn't already setup and large enough ! 1027: * to hold the new data, set it up. ! 1028: */ ! 1029: ! 1030: if ((iPtr->result != iPtr->appendResult) ! 1031: || ((newSpace + iPtr->appendUsed) >= iPtr->appendAvl)) { ! 1032: SetupAppendBuffer(iPtr, newSpace); ! 1033: } ! 1034: ! 1035: /* ! 1036: * Final step: go through all the argument strings again, copying ! 1037: * them into the buffer. ! 1038: */ ! 1039: ! 1040: va_start(argList, interp); ! 1041: while (1) { ! 1042: string = va_arg(argList, char *); ! 1043: if (string == NULL) { ! 1044: break; ! 1045: } ! 1046: strcpy(iPtr->appendResult + iPtr->appendUsed, string); ! 1047: iPtr->appendUsed += strlen(string); ! 1048: } ! 1049: va_end(argList); ! 1050: } ! 1051: ! 1052: /* ! 1053: *---------------------------------------------------------------------- ! 1054: * ! 1055: * Tcl_AppendElement -- ! 1056: * ! 1057: * Convert a string to a valid Tcl list element and append it ! 1058: * to the current result (which is ostensibly a list). ! 1059: * ! 1060: * Results: ! 1061: * None. ! 1062: * ! 1063: * Side effects: ! 1064: * The result in the interpreter given by the first argument ! 1065: * is extended with a list element converted from string. If ! 1066: * the original result wasn't empty, then a blank is added before ! 1067: * the converted list element. ! 1068: * ! 1069: *---------------------------------------------------------------------- ! 1070: */ ! 1071: ! 1072: void ! 1073: Tcl_AppendElement(interp, string, noSep) ! 1074: Tcl_Interp *interp; /* Interpreter whose result is to be ! 1075: * extended. */ ! 1076: char *string; /* String to convert to list element and ! 1077: * add to result. */ ! 1078: int noSep; /* If non-zero, then don't output a ! 1079: * space character before this element, ! 1080: * even if the element isn't the first ! 1081: * thing in the output buffer. */ ! 1082: { ! 1083: register Interp *iPtr = (Interp *) interp; ! 1084: int size, flags; ! 1085: char *dst; ! 1086: ! 1087: /* ! 1088: * See how much space is needed, and grow the append buffer if ! 1089: * needed to accommodate the list element. ! 1090: */ ! 1091: ! 1092: size = Tcl_ScanElement(string, &flags) + 1; ! 1093: if ((iPtr->result != iPtr->appendResult) ! 1094: || ((size + iPtr->appendUsed) >= iPtr->appendAvl)) { ! 1095: SetupAppendBuffer(iPtr, size+iPtr->appendUsed); ! 1096: } ! 1097: ! 1098: /* ! 1099: * Convert the string into a list element and copy it to the ! 1100: * buffer that's forming. ! 1101: */ ! 1102: ! 1103: dst = iPtr->appendResult + iPtr->appendUsed; ! 1104: if (!noSep && (iPtr->appendUsed != 0)) { ! 1105: iPtr->appendUsed++; ! 1106: *dst = ' '; ! 1107: dst++; ! 1108: } ! 1109: iPtr->appendUsed += Tcl_ConvertElement(string, dst, flags); ! 1110: } ! 1111: ! 1112: /* ! 1113: *---------------------------------------------------------------------- ! 1114: * ! 1115: * SetupAppendBuffer -- ! 1116: * ! 1117: * This procedure makes sure that there is an append buffer ! 1118: * properly initialized for interp, and that it has at least ! 1119: * enough room to accommodate newSpace new bytes of information. ! 1120: * ! 1121: * Results: ! 1122: * None. ! 1123: * ! 1124: * Side effects: ! 1125: * None. ! 1126: * ! 1127: *---------------------------------------------------------------------- ! 1128: */ ! 1129: ! 1130: static void ! 1131: SetupAppendBuffer(iPtr, newSpace) ! 1132: register Interp *iPtr; /* Interpreter whose result is being set up. */ ! 1133: int newSpace; /* Make sure that at least this many bytes ! 1134: * of new information may be added. */ ! 1135: { ! 1136: int totalSpace; ! 1137: ! 1138: /* ! 1139: * Make the append buffer larger, if that's necessary, then ! 1140: * copy the current result into the append buffer and make the ! 1141: * append buffer the official Tcl result. ! 1142: */ ! 1143: ! 1144: if (iPtr->result != iPtr->appendResult) { ! 1145: /* ! 1146: * If an oversized buffer was used recently, then free it up ! 1147: * so we go back to a smaller buffer. This avoids tying up ! 1148: * memory forever after a large operation. ! 1149: */ ! 1150: ! 1151: if (iPtr->appendAvl > 500) { ! 1152: ckfree(iPtr->appendResult); ! 1153: iPtr->appendResult = NULL; ! 1154: iPtr->appendAvl = 0; ! 1155: } ! 1156: iPtr->appendUsed = strlen(iPtr->result); ! 1157: } ! 1158: totalSpace = newSpace + iPtr->appendUsed; ! 1159: if (totalSpace >= iPtr->appendAvl) { ! 1160: char *new; ! 1161: ! 1162: if (totalSpace < 100) { ! 1163: totalSpace = 200; ! 1164: } else { ! 1165: totalSpace *= 2; ! 1166: } ! 1167: new = (char *) ckalloc((unsigned) totalSpace); ! 1168: strcpy(new, iPtr->result); ! 1169: if (iPtr->appendResult != NULL) { ! 1170: ckfree(iPtr->appendResult); ! 1171: } ! 1172: iPtr->appendResult = new; ! 1173: iPtr->appendAvl = totalSpace; ! 1174: } else if (iPtr->result != iPtr->appendResult) { ! 1175: strcpy(iPtr->appendResult, iPtr->result); ! 1176: } ! 1177: Tcl_FreeResult(iPtr); ! 1178: iPtr->result = iPtr->appendResult; ! 1179: } ! 1180: ! 1181: /* ! 1182: *---------------------------------------------------------------------- ! 1183: * ! 1184: * Tcl_ResetResult -- ! 1185: * ! 1186: * This procedure restores the result area for an interpreter ! 1187: * to its default initialized state, freeing up any memory that ! 1188: * may have been allocated for the result and clearing any ! 1189: * error information for the interpreter. ! 1190: * ! 1191: * Results: ! 1192: * None. ! 1193: * ! 1194: * Side effects: ! 1195: * None. ! 1196: * ! 1197: *---------------------------------------------------------------------- ! 1198: */ ! 1199: ! 1200: void ! 1201: Tcl_ResetResult(interp) ! 1202: Tcl_Interp *interp; /* Interpreter for which to clear result. */ ! 1203: { ! 1204: register Interp *iPtr = (Interp *) interp; ! 1205: ! 1206: Tcl_FreeResult(iPtr); ! 1207: iPtr->result = iPtr->resultSpace; ! 1208: iPtr->resultSpace[0] = 0; ! 1209: iPtr->flags &= ! 1210: ~(ERR_ALREADY_LOGGED | ERR_IN_PROGRESS | ERROR_CODE_SET); ! 1211: } ! 1212: ! 1213: /* ! 1214: *---------------------------------------------------------------------- ! 1215: * ! 1216: * Tcl_SetErrorCode -- ! 1217: * ! 1218: * This procedure is called to record machine-readable information ! 1219: * about an error that is about to be returned. ! 1220: * ! 1221: * Results: ! 1222: * None. ! 1223: * ! 1224: * Side effects: ! 1225: * The errorCode global variable is modified to hold all of the ! 1226: * arguments to this procedure, in a list form with each argument ! 1227: * becoming one element of the list. A flag is set internally ! 1228: * to remember that errorCode has been set, so the variable doesn't ! 1229: * get set automatically when the error is returned. ! 1230: * ! 1231: *---------------------------------------------------------------------- ! 1232: */ ! 1233: void ! 1234: Tcl_SetErrorCode(Tcl_Interp *interp, ...) ! 1235: { ! 1236: va_list argList; ! 1237: char *string; ! 1238: int flags; ! 1239: Interp *iPtr; ! 1240: ! 1241: /* ! 1242: * Scan through the arguments one at a time, appending them to ! 1243: * $errorCode as list elements. ! 1244: */ ! 1245: ! 1246: va_start(argList, interp); ! 1247: iPtr = (Interp *)interp; ! 1248: flags = TCL_GLOBAL_ONLY | TCL_LIST_ELEMENT; ! 1249: while (1) { ! 1250: string = va_arg(argList, char *); ! 1251: if (string == NULL) { ! 1252: break; ! 1253: } ! 1254: (void) Tcl_SetVar2((Tcl_Interp *) iPtr, "errorCode", ! 1255: (char *) NULL, string, flags); ! 1256: flags |= TCL_APPEND_VALUE; ! 1257: } ! 1258: va_end(argList); ! 1259: iPtr->flags |= ERROR_CODE_SET; ! 1260: } ! 1261: ! 1262: /* ! 1263: *---------------------------------------------------------------------- ! 1264: * ! 1265: * TclGetListIndex -- ! 1266: * ! 1267: * Parse a list index, which may be either an integer or the ! 1268: * value "end". ! 1269: * ! 1270: * Results: ! 1271: * The return value is either TCL_OK or TCL_ERROR. If it is ! 1272: * TCL_OK, then the index corresponding to string is left in ! 1273: * *indexPtr. If the return value is TCL_ERROR, then string ! 1274: * was bogus; an error message is returned in interp->result. ! 1275: * If a negative index is specified, it is rounded up to 0. ! 1276: * The index value may be larger than the size of the list ! 1277: * (this happens when "end" is specified). ! 1278: * ! 1279: * Side effects: ! 1280: * None. ! 1281: * ! 1282: *---------------------------------------------------------------------- ! 1283: */ ! 1284: ! 1285: int ! 1286: TclGetListIndex(interp, string, indexPtr) ! 1287: Tcl_Interp *interp; /* Interpreter for error reporting. */ ! 1288: char *string; /* String containing list index. */ ! 1289: int *indexPtr; /* Where to store index. */ ! 1290: { ! 1291: if (isdigit(*string) || (*string == '-')) { ! 1292: if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) { ! 1293: return TCL_ERROR; ! 1294: } ! 1295: if (*indexPtr < 0) { ! 1296: *indexPtr = 0; ! 1297: } ! 1298: } else if (strncmp(string, "end", strlen(string)) == 0) { ! 1299: *indexPtr = 1<<30; ! 1300: } else { ! 1301: Tcl_AppendResult(interp, "bad index \"", string, ! 1302: "\": must be integer or \"end\"", (char *) NULL); ! 1303: return TCL_ERROR; ! 1304: } ! 1305: return TCL_OK; ! 1306: } ! 1307: ! 1308: /* ! 1309: *---------------------------------------------------------------------- ! 1310: * ! 1311: * TclCompileRegexp -- ! 1312: * ! 1313: * Compile a regular expression into a form suitable for fast ! 1314: * matching. This procedure retains a small cache of pre-compiled ! 1315: * regular expressions in the interpreter, in order to avoid ! 1316: * compilation costs as much as possible. ! 1317: * ! 1318: * Results: ! 1319: * The return value is a pointer to the compiled form of string, ! 1320: * suitable for passing to regexec. If an error occurred while ! 1321: * compiling the pattern, then NULL is returned and an error ! 1322: * message is left in interp->result. ! 1323: * ! 1324: * Side effects: ! 1325: * The cache of compiled regexp's in interp will be modified to ! 1326: * hold information for string, if such information isn't already ! 1327: * present in the cache. ! 1328: * ! 1329: *---------------------------------------------------------------------- ! 1330: */ ! 1331: ! 1332: regexp * ! 1333: TclCompileRegexp(interp, string) ! 1334: Tcl_Interp *interp; /* For use in error reporting. */ ! 1335: char *string; /* String for which to produce ! 1336: * compiled regular expression. */ ! 1337: { ! 1338: register Interp *iPtr = (Interp *) interp; ! 1339: int i, length; ! 1340: regexp *result; ! 1341: ! 1342: length = strlen(string); ! 1343: for (i = 0; i < NUM_REGEXPS; i++) { ! 1344: if ((length == iPtr->patLengths[i]) ! 1345: && (strcmp(string, iPtr->patterns[i]) == 0)) { ! 1346: /* ! 1347: * Move the matched pattern to the first slot in the ! 1348: * cache and shift the other patterns down one position. ! 1349: */ ! 1350: ! 1351: if (i != 0) { ! 1352: int j; ! 1353: char *cachedString; ! 1354: ! 1355: cachedString = iPtr->patterns[i]; ! 1356: result = iPtr->regexps[i]; ! 1357: for (j = i-1; j >= 0; j--) { ! 1358: iPtr->patterns[j+1] = iPtr->patterns[j]; ! 1359: iPtr->patLengths[j+1] = iPtr->patLengths[j]; ! 1360: iPtr->regexps[j+1] = iPtr->regexps[j]; ! 1361: } ! 1362: iPtr->patterns[0] = cachedString; ! 1363: iPtr->patLengths[0] = length; ! 1364: iPtr->regexps[0] = result; ! 1365: } ! 1366: return iPtr->regexps[0]; ! 1367: } ! 1368: } ! 1369: ! 1370: /* ! 1371: * No match in the cache. Compile the string and add it to the ! 1372: * cache. ! 1373: */ ! 1374: ! 1375: tclRegexpError = NULL; ! 1376: result = regcomp(string); ! 1377: if (tclRegexpError != NULL) { ! 1378: Tcl_AppendResult(interp, ! 1379: "couldn't compile regular expression pattern: ", ! 1380: tclRegexpError, (char *) NULL); ! 1381: return NULL; ! 1382: } ! 1383: if (iPtr->patterns[NUM_REGEXPS-1] != NULL) { ! 1384: ckfree(iPtr->patterns[NUM_REGEXPS-1]); ! 1385: ckfree((char *) iPtr->regexps[NUM_REGEXPS-1]); ! 1386: } ! 1387: for (i = NUM_REGEXPS - 2; i >= 0; i--) { ! 1388: iPtr->patterns[i+1] = iPtr->patterns[i]; ! 1389: iPtr->patLengths[i+1] = iPtr->patLengths[i]; ! 1390: iPtr->regexps[i+1] = iPtr->regexps[i]; ! 1391: } ! 1392: iPtr->patterns[0] = (char *) ckalloc((unsigned) (length+1)); ! 1393: strcpy(iPtr->patterns[0], string); ! 1394: iPtr->patLengths[0] = length; ! 1395: iPtr->regexps[0] = result; ! 1396: return result; ! 1397: } ! 1398: ! 1399: /* ! 1400: *---------------------------------------------------------------------- ! 1401: * ! 1402: * regerror -- ! 1403: * ! 1404: * This procedure is invoked by the Henry Spencer's regexp code ! 1405: * when an error occurs. It saves the error message so it can ! 1406: * be seen by the code that called Spencer's code. ! 1407: * ! 1408: * Results: ! 1409: * None. ! 1410: * ! 1411: * Side effects: ! 1412: * The value of "string" is saved in "tclRegexpError". ! 1413: * ! 1414: *---------------------------------------------------------------------- ! 1415: */ ! 1416: ! 1417: void ! 1418: regerror(string) ! 1419: char *string; /* Error message. */ ! 1420: { ! 1421: tclRegexpError = string; ! 1422: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.