|
|
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, 1989 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: /sprite/src/lib/tcl/RCS/tclUtil.c,v 1.30 90/03/25 11:04:25 ouster Exp $ SPRITE (Berkeley)";
19: #pragma ref rcsid
20: #endif not lint
21:
22: #define _POSIX_SOURCE
23:
24: #include <ctype.h>
25: #include <stdio.h>
26: #include <stdlib.h>
27: #include <string.h>
28: #include "tcl.h"
29: #include "tclInt.h"
30:
31: /*
32: *----------------------------------------------------------------------
33: *
34: * TclFindElement --
35: *
36: * Given a pointer into a Tcl list, locate the first (or next)
37: * element in the list.
38: *
39: * Results:
40: * The return value is normally TCL_OK, which means that the
41: * element was successfully located. If TCL_ERROR is returned
42: * it means that list didn't have proper list structure;
43: * interp->result contains a more detailed error message.
44: *
45: * If TCL_OK is returned, then *elementPtr will be set to point
46: * to the first element of list, and *nextPtr will be set to point
47: * to the character just after any white space following the last
48: * character that's part of the element. If this is the last argument
49: * in the list, then *nextPtr will point to the NULL character at the
50: * end of list. If sizePtr is non-NULL, *sizePtr is filled in with
51: * the number of characters in the element. If the element is in
52: * braces, then *elementPtr will point to the character after the
53: * opening brace and *sizePtr will not include either of the braces.
54: * If there isn't an element in the list, *sizePtr will be zero, and
55: * both *elementPtr and *termPtr will refer to the null character at
56: * the end of list. Note: this procedure does NOT collapse backslash
57: * sequences.
58: *
59: * Side effects:
60: * None.
61: *
62: *----------------------------------------------------------------------
63: */
64:
65: int
66: TclFindElement(interp, list, elementPtr, nextPtr, sizePtr, bracePtr)
67: Tcl_Interp *interp; /* Interpreter to use for error reporting. */
68: register char *list; /* String containing Tcl list with zero
69: * or more elements (possibly in braces). */
70: char **elementPtr; /* Fill in with location of first significant
71: * character in first element of list. */
72: char **nextPtr; /* Fill in with location of character just
73: * after all white space following end of
74: * argument (i.e. next argument or end of
75: * list). */
76: int *sizePtr; /* If non-zero, fill in with size of
77: * element. */
78: int *bracePtr; /* If non-zero fill in with non-zero/zero
79: * to indicate that arg was/wasn't
80: * in braces. */
81: {
82: register char *p;
83: int openBraces = 0;
84: int size;
85:
86: /*
87: * Skim off leading white space and check for an opening brace.
88: */
89:
90: while (isspace(*list)) {
91: list++;
92: }
93: if (*list == '{') {
94: openBraces = 1;
95: list++;
96: }
97: if (bracePtr != 0) {
98: *bracePtr = openBraces;
99: }
100: p = list;
101:
102: /*
103: * Find the end of the element (either a space or a close brace or
104: * the end of the string).
105: */
106:
107: while (1) {
108: switch (*p) {
109:
110: /*
111: * Open brace: don't treat specially unless the element is
112: * in braces. In this case, keep a nesting count.
113: */
114:
115: case '{':
116: if (openBraces != 0) {
117: openBraces++;
118: }
119: break;
120:
121: /*
122: * Close brace: if element is in braces, keep nesting
123: * count and quit when the last close brace is seen.
124: */
125:
126: case '}':
127: if (openBraces == 1) {
128: char *p2;
129:
130: size = p - list;
131: p++;
132: if (isspace(*p) || (*p == 0)) {
133: goto done;
134: }
135: for (p2 = p; (*p2 != 0) && (!isspace(*p2)) && (p2 < p+20);
136: p2++) {
137: /* null body */
138: }
139: Tcl_Return(interp, (char *) NULL, TCL_STATIC);
140: sprintf(interp->result,
141: "list element in braces followed by \"%.*s\" instead of space",
142: p2-p, p);
143: return TCL_ERROR;
144: } else if (openBraces != 0) {
145: openBraces--;
146: }
147: break;
148:
149: /*
150: * Backslash: skip over everything up to the end of the
151: * backslash sequence.
152: */
153:
154: case '\\': {
155: int size;
156:
157: (void) Tcl_Backslash(p, &size);
158: p += size - 1;
159: break;
160: }
161:
162: /*
163: * Space: ignore if element is in braces; otherwise
164: * terminate element.
165: */
166:
167: case ' ':
168: case '\t':
169: case '\n':
170: if (openBraces == 0) {
171: size = p - list;
172: goto done;
173: }
174: break;
175:
176: /*
177: * End of list: terminate element.
178: */
179:
180: case 0:
181: if (openBraces != 0) {
182: Tcl_Return(interp, "unmatched open brace in list",
183: TCL_STATIC);
184: return TCL_ERROR;
185: }
186: size = p - list;
187: goto done;
188:
189: }
190: p++;
191: }
192:
193: done:
194: while (isspace(*p)) {
195: p++;
196: }
197: *elementPtr = list;
198: *nextPtr = p;
199: if (sizePtr != 0) {
200: *sizePtr = size;
201: }
202: return TCL_OK;
203: }
204:
205: /*
206: *----------------------------------------------------------------------
207: *
208: * TclCopyAndCollapse --
209: *
210: * Copy a string and eliminate any backslashes that aren't in braces.
211: *
212: * Results:
213: * There is no return value. Count chars. get copied from src
214: * to dst. Along the way, if backslash sequences are found outside
215: * braces, the backslashes are eliminated in the copy.
216: * After scanning count chars. from source, a null character is
217: * placed at the end of dst.
218: *
219: * Side effects:
220: * None.
221: *
222: *----------------------------------------------------------------------
223: */
224:
225: void
226: TclCopyAndCollapse(count, src, dst)
227: register char *src; /* Copy from here... */
228: register char *dst; /* ... to here. */
229: {
230: register char c;
231: int numRead;
232:
233: for (c = *src; count > 0; dst++, src++, c = *src, count--) {
234: if (c == '\\') {
235: *dst = Tcl_Backslash(src, &numRead);
236: src += numRead-1;
237: count -= numRead-1;
238: } else {
239: *dst = c;
240: }
241: }
242: *dst = 0;
243: }
244:
245: /*
246: *----------------------------------------------------------------------
247: *
248: * Tcl_Merge --
249: *
250: * Given a collection of strings, merge them together into a
251: * single string that has proper Tcl list structured (i.e.
252: * TclFindElement and TclCopyAndCollapse may be used to retrieve
253: * strings equal to the original elements, and Tcl_Eval will
254: * parse the string back into its original elements).
255: *
256: * Results:
257: * The return value is the address of a dynamically-allocated
258: * string containing the merged list.
259: *
260: * Side effects:
261: * None.
262: *
263: *----------------------------------------------------------------------
264: */
265:
266: char *
267: Tcl_Merge(argc, argv)
268: int argc; /* How many strings to merge. */
269: char **argv; /* Array of string values. */
270: {
271: /*
272: * This procedure operates in two passes. In the first pass it figures
273: * out how many bytes will be needed to store the result (actually,
274: * it overestimates slightly). The first pass also collects information
275: * about each element in the form of a flags word. If there are only
276: * a few elements, local storage gets used for the flags; if there are
277: * a lot of elements, a new array is dynamically allocated.
278: *
279: * In the second pass this procedure copies the arguments into the
280: * result string. The special cases to worry about are:
281: *
282: * 1. Argument contains embedded spaces, or starts with a brace: must
283: * add another level of braces when copying to the result.
284: *
285: * 2. Argument contains unbalanced braces: backslash all of the
286: * braces when copying to the result. In this case, don't add another
287: * level of braces (they would prevent the backslash from
288: * being removed when the argument is extracted from the list later).
289: *
290: * 3. Argument contains backslashed brace/bracket: if possible,
291: * group the argument in braces: then no special action needs to be taken
292: * with the backslashes. If the argument can't be put in braces, then
293: * add another backslash in front of the sequence, so that upon
294: * extraction the original sequence will be restored.
295: *
296: * These potential problems are the reasons why particular information
297: * is gathered during pass 1.
298: */
299: # define WANT_PARENS 1
300: # define PARENS_UNBALANCED 2
301: # define PARENTHESIZED 4
302: # define CANT_PARENTHESIZE 8
303:
304: # define LOCAL_SIZE 20
305: int localFlags[LOCAL_SIZE];
306: int *flagPtr;
307: int numChars;
308: char *result;
309: register char *src, *dst;
310: register int curFlags;
311: int i;
312:
313: /*
314: * Pass 1: estimate space, gather information.
315: */
316:
317: if (argc <= LOCAL_SIZE) {
318: flagPtr = localFlags;
319: } else {
320: flagPtr = (int *) malloc((unsigned) argc*sizeof(int));
321: }
322: numChars = 0;
323: for (i = 0; i < argc; i++) {
324: int braceCount, nestingLevel, nestedBS, whiteSpace, brackets, dollars;
325:
326: curFlags = braceCount = nestingLevel = nestedBS = whiteSpace = 0;
327: brackets = dollars = 0;
328: src = argv[i];
329: if (*src == '{') {
330: curFlags |= PARENTHESIZED|WANT_PARENS;
331: }
332: if (*src == 0) {
333: curFlags |= WANT_PARENS;
334: } else {
335: for (; ; src++) {
336: switch (*src) {
337: case '{':
338: braceCount++;
339: nestingLevel++;
340: break;
341: case '}':
342: braceCount++;
343: nestingLevel--;
344: break;
345: case ']':
346: case '[':
347: curFlags |= WANT_PARENS;
348: brackets++;
349: break;
350: case '$':
351: curFlags |= WANT_PARENS;
352: dollars++;
353: break;
354: case ' ':
355: case '\n':
356: case '\t':
357: curFlags |= WANT_PARENS;
358: whiteSpace++;
359: break;
360: case '\\':
361: src++;
362: if (*src == 0) {
363: goto elementDone;
364: } else if ((*src == '{') || (*src == '}')
365: || (*src == '[') || (*src == ']')) {
366: curFlags |= WANT_PARENS;
367: nestedBS++;
368: }
369: break;
370: case 0:
371: goto elementDone;
372: }
373: }
374: }
375: elementDone:
376: numChars += src - argv[i];
377: if (nestingLevel != 0) {
378: numChars += braceCount + nestedBS + whiteSpace
379: + brackets + dollars;
380: curFlags = CANT_PARENTHESIZE;
381: }
382: if (curFlags & WANT_PARENS) {
383: numChars += 2;
384: }
385: numChars++; /* Space to separate arguments. */
386: flagPtr[i] = curFlags;
387: }
388:
389: /*
390: * Pass two: copy into the result area.
391: */
392:
393: result = (char *) malloc((unsigned) numChars + 1);
394: dst = result;
395: for (i = 0; i < argc; i++) {
396: curFlags = flagPtr[i];
397: if (curFlags & WANT_PARENS) {
398: *dst = '{';
399: dst++;
400: }
401: for (src = argv[i]; *src != 0 ; src++) {
402: if (curFlags & CANT_PARENTHESIZE) {
403: switch (*src) {
404: case '{':
405: case '}':
406: case ']':
407: case '[':
408: case '$':
409: case ' ':
410: *dst = '\\';
411: dst++;
412: break;
413: case '\n':
414: *dst = '\\';
415: dst++;
416: *dst = 'n';
417: goto loopBottom;
418: case '\t':
419: *dst = '\\';
420: dst++;
421: *dst = 't';
422: goto loopBottom;
423: case '\\':
424: *dst = '\\';
425: dst++;
426: src++;
427: if ((*src == '{') || (*src == '}') || (*src == '[')
428: || (*src == ']')) {
429: *dst = '\\';
430: dst++;
431: } else if (*src == 0) {
432: goto pass2ElementDone;
433: }
434: break;
435: }
436: }
437: *dst = *src;
438: loopBottom:
439: dst++;
440: }
441: pass2ElementDone:
442: if (curFlags & WANT_PARENS) {
443: *dst = '}';
444: dst++;
445: }
446: *dst = ' ';
447: dst++;
448: }
449: if (dst == result) {
450: *dst = 0;
451: } else {
452: dst[-1] = 0;
453: }
454:
455: if (flagPtr != localFlags) {
456: free((char *) flagPtr);
457: }
458: return result;
459: }
460:
461: /*
462: *----------------------------------------------------------------------
463: *
464: * Tcl_Concat --
465: *
466: * Concatenate a set of strings into a single large string.
467: *
468: * Results:
469: * The return value is dynamically-allocated string containing
470: * a concatenation of all the strings in argv, with spaces between
471: * the original argv elements.
472: *
473: * Side effects:
474: * Memory is allocated for the result; the caller is responsible
475: * for freeing the memory.
476: *
477: *----------------------------------------------------------------------
478: */
479:
480: char *
481: Tcl_Concat(argc, argv)
482: int argc; /* Number of strings to concatenate. */
483: char **argv; /* Array of strings to concatenate. */
484: {
485: int totalSize, i;
486: register char *p;
487: char *result;
488:
489: for (totalSize = 1, i = 0; i < argc; i++) {
490: totalSize += strlen(argv[i]) + 1;
491: }
492: result = malloc((unsigned) totalSize);
493: for (p = result, i = 0; i < argc; i++) {
494: (void) strcpy(p, argv[i]);
495: p += strlen(argv[i]);
496: *p = ' ';
497: p++;
498: }
499: p[-1] = 0;
500: return result;
501: }
502:
503: /*
504: *----------------------------------------------------------------------
505: *
506: * Tcl_Return --
507: *
508: * Arrange for "string" to be the Tcl return value.
509: *
510: * Results:
511: * None.
512: *
513: * Side effects:
514: * interp->result is left pointing either to "string" (if "copy" is 0)
515: * or to a copy of string.
516: *
517: *----------------------------------------------------------------------
518: */
519:
520: void
521: Tcl_Return(interp, string, status)
522: Tcl_Interp *interp; /* Interpreter with which to associate the
523: * return value. */
524: char *string; /* Value to be returned. If NULL,
525: * the result is set to an empty string. */
526: int status; /* Gives information about the string:
527: * TCL_STATIC, TCL_DYNAMIC, TCL_VOLATILE.
528: * Ignored if string is NULL. */
529: {
530: register Interp *iPtr = (Interp *) interp;
531: int length;
532: int wasDynamic = iPtr->dynamic;
533: char *oldResult = iPtr->result;
534:
535: if (string == NULL) {
536: iPtr->resultSpace[0] = 0;
537: iPtr->result = iPtr->resultSpace;
538: iPtr->dynamic = 0;
539: } else if (status == TCL_STATIC) {
540: iPtr->result = string;
541: iPtr->dynamic = 0;
542: } else if (status == TCL_DYNAMIC) {
543: iPtr->result = string;
544: iPtr->dynamic = 1;
545: } else {
546: length = strlen(string);
547: if (length > TCL_RESULT_SIZE) {
548: iPtr->result = (char *) malloc((unsigned) length+1);
549: iPtr->dynamic = 1;
550: } else {
551: iPtr->result = iPtr->resultSpace;
552: iPtr->dynamic = 0;
553: }
554: strcpy(iPtr->result, string);
555: }
556:
557: /*
558: * If the old result was dynamically-allocated, free it up. Do it
559: * here, rather than at the beginning, in case the new result value
560: * was part of the old result value.
561: */
562:
563: if (wasDynamic) {
564: free(oldResult);
565: }
566: }
567:
568: /*
569: *----------------------------------------------------------------------
570: *
571: * Tcl_Backslash --
572: *
573: * Figure out how to handle a backslash sequence.
574: *
575: * Results:
576: * The return value is the character that should be substituted
577: * in place of the backslash sequence that starts at src. If
578: * readPtr isn't NULL then it is filled in with a count of the
579: * number of characters in the backslash sequence. Note: if
580: * the backslash isn't followed by characters that are understood
581: * here, then the backslash sequence is only considered to be
582: * one character long, and it is replaced by a backslash char.
583: *
584: * Side effects:
585: * None.
586: *
587: *----------------------------------------------------------------------
588: */
589:
590: char
591: Tcl_Backslash(src, readPtr)
592: char *src; /* Points to the backslash character of
593: * a backslash sequence. */
594: int *readPtr; /* Fill in with number of characters read
595: * from src, unless NULL. */
596: {
597: register char *p = src+1;
598: char result;
599: int count;
600:
601: count = 2;
602:
603: switch (*p) {
604: case 'b':
605: result = '\b';
606: break;
607: case 'e':
608: result = 033;
609: break;
610: case 'n':
611: result = '\n';
612: break;
613: case 'r':
614: result = '\r';
615: break;
616: case 't':
617: result = '\t';
618: break;
619: case 'C':
620: p++;
621: if (isspace(*p) || (*p == 0)) {
622: result = 'C';
623: count = 1;
624: break;
625: }
626: count = 3;
627: if (*p == 'M') {
628: p++;
629: if (isspace(*p) || (*p == 0)) {
630: result = 'M' & 037;
631: break;
632: }
633: count = 4;
634: result = (*p & 037) | 0200;
635: break;
636: }
637: count = 3;
638: result = *p & 037;
639: break;
640: case 'M':
641: p++;
642: if (isspace(*p) || (*p == 0)) {
643: result = 'M';
644: count = 1;
645: break;
646: }
647: count = 3;
648: result = *p + 0200;
649: break;
650: case '}':
651: case '{':
652: case ']':
653: case '[':
654: case '$':
655: case ' ':
656: case ';':
657: case '"':
658: case '\\':
659: result = *p;
660: break;
661: default:
662: if (isdigit(*p)) {
663: result = *p - '0';
664: p++;
665: if (!isdigit(*p)) {
666: break;
667: }
668: count = 3;
669: result = (result << 3) + (*p - '0');
670: p++;
671: if (!isdigit(*p)) {
672: break;
673: }
674: count = 4;
675: result = (result << 3) + (*p - '0');
676: break;
677: }
678: result = '\\';
679: count = 1;
680: break;
681: }
682:
683: if (readPtr != NULL) {
684: *readPtr = count;
685: }
686: return result;
687: }
688:
689: /*
690: *----------------------------------------------------------------------
691: *
692: * Tcl_SplitList --
693: *
694: * Splits a list up into its constituent fields.
695: *
696: * Results
697: * The return value is normally TCL_OK, which means that
698: * the list was successfully split up. If TCL_ERROR is
699: * returned, it means that "list" didn't have proper list
700: * structure; interp->result will contain a more detailed
701: * error message.
702: *
703: * *argvPtr will be filled in with the address of an array
704: * whose elements point to the elements of list, in order.
705: * *argcPtr will get filled in with the number of valid elements
706: * in the array. A single block of memory is dynamically allocated
707: * to hold both the argv array and a copy of the list (with
708: * backslashes and braces removed in the standard way).
709: * The caller must eventually free this memory by calling free()
710: * on *argvPtr. Note: *argvPtr and *argcPtr are only modified
711: * if the procedure returns normally.
712: *
713: * Side effects:
714: * Memory is allocated.
715: *
716: *----------------------------------------------------------------------
717: */
718:
719: int
720: Tcl_SplitList(interp, list, argcPtr, argvPtr)
721: Tcl_Interp *interp; /* Interpreter to use for error reporting. */
722: char *list; /* Pointer to string with list structure. */
723: int *argcPtr; /* Pointer to location to fill in with
724: * the number of elements in the list. */
725: char ***argvPtr; /* Pointer to place to store pointer to array
726: * of pointers to list elements. */
727: {
728: char **argv;
729: register char *p;
730: int size, i, result, elSize, brace;
731: char *element;
732:
733: /*
734: * Figure out how much space to allocate. There must be enough
735: * space for both the array of pointers and also for a copy of
736: * the list. To estimate the number of pointers needed, count
737: * the number of space characters in the list.
738: */
739:
740: for (size = 1, p = list; *p != 0; p++) {
741: if (isspace(*p)) {
742: size++;
743: }
744: }
745: argv = (char **) malloc((unsigned)
746: ((size * sizeof(char *)) + (p - list) + 1));
747: for (i = 0, p = ((char *) argv) + size*sizeof(char *);
748: *list != 0; i++) {
749: result = TclFindElement(interp, list, &element, &list, &elSize, &brace);
750: if (result != TCL_OK) {
751: free((char *) argv);
752: return result;
753: }
754: if (*element == 0) {
755: break;
756: }
757: if (i >= size) {
758: Tcl_Return(interp, "internal error in Tcl_SplitList", TCL_STATIC);
759: return TCL_ERROR;
760: }
761: argv[i] = p;
762: if (brace) {
763: strncpy(p, element, elSize);
764: p += elSize;
765: *p = 0;
766: p++;
767: } else {
768: TclCopyAndCollapse(elSize, element, p);
769: p += elSize+1;
770: }
771: }
772:
773: *argvPtr = argv;
774: *argcPtr = i;
775: return TCL_OK;
776: }
777:
778: /*
779: *----------------------------------------------------------------------
780: *
781: * Tcl_StringMatch --
782: *
783: * See if a particular string matches a particular pattern.
784: *
785: * Results:
786: * The return value is 1 if string matches pattern, and
787: * 0 otherwise. The matching operation permits the following
788: * special characters in the pattern: *?\[] (see the manual
789: * entry for details on what these mean).
790: *
791: * Side effects:
792: * None.
793: *
794: *----------------------------------------------------------------------
795: */
796:
797: int
798: Tcl_StringMatch(string, pattern)
799: register char *string; /* String. */
800: register char *pattern; /* Pattern, which may contain
801: * special characters. */
802: {
803: char c2;
804:
805: while (1) {
806: /* See if we're at the end of both the pattern and the string.
807: * If so, we succeeded. If we're at the end of the pattern
808: * but not at the end of the string, we failed.
809: */
810:
811: if (*pattern == 0) {
812: if (*string == 0) {
813: return 1;
814: } else {
815: return 0;
816: }
817: }
818: if ((*string == 0) && (*pattern != '*')) {
819: return 0;
820: }
821:
822: /* Check for a "*" as the next pattern character. It matches
823: * any substring. We handle this by calling ourselves
824: * recursively for each postfix of string, until either we
825: * match or we reach the end of the string.
826: */
827:
828: if (*pattern == '*') {
829: pattern += 1;
830: if (*pattern == 0) {
831: return 1;
832: }
833: while (*string != 0) {
834: if (Tcl_StringMatch(string, pattern)) {
835: return 1;
836: }
837: string += 1;
838: }
839: return 0;
840: }
841:
842: /* Check for a "?" as the next pattern character. It matches
843: * any single character.
844: */
845:
846: if (*pattern == '?') {
847: goto thisCharOK;
848: }
849:
850: /* Check for a "[" as the next pattern character. It is followed
851: * by a list of characters that are acceptable, or by a range
852: * (two characters separated by "-").
853: */
854:
855: if (*pattern == '[') {
856: pattern += 1;
857: while (1) {
858: if ((*pattern == ']') || (*pattern == 0)) {
859: return 0;
860: }
861: if (*pattern == *string) {
862: break;
863: }
864: if (pattern[1] == '-') {
865: c2 = pattern[2];
866: if (c2 == 0) {
867: return 0;
868: }
869: if ((*pattern <= *string) && (c2 >= *string)) {
870: break;
871: }
872: if ((*pattern >= *string) && (c2 <= *string)) {
873: break;
874: }
875: pattern += 2;
876: }
877: pattern += 1;
878: }
879: while ((*pattern != ']') && (*pattern != 0)) {
880: pattern += 1;
881: }
882: goto thisCharOK;
883: }
884:
885: /* If the next pattern character is '/', just strip off the '/'
886: * so we do exact matching on the character that follows.
887: */
888:
889: if (*pattern == '\\') {
890: pattern += 1;
891: if (*pattern == 0) {
892: return 0;
893: }
894: }
895:
896: /* There's no special character. Just make sure that the next
897: * characters of each string match.
898: */
899:
900: if (*pattern != *string) {
901: return 0;
902: }
903:
904: thisCharOK: pattern += 1;
905: string += 1;
906: }
907: }
908:
909: /*
910: *----------------------------------------------------------------------
911: *
912: * TclWordEnd --
913: *
914: * Given a pointer into a Tcl command, find the end of the next
915: * word of the command.
916: *
917: * Results:
918: * The return value is a pointer to the character just after the
919: * last one that's part of the word pointed to by "start". This
920: * may be the address of the NULL character at the end of the
921: * string.
922: *
923: * Side effects:
924: * None.
925: *
926: *----------------------------------------------------------------------
927: */
928:
929: char *
930: TclWordEnd(start, nested)
931: char *start; /* Beginning of a word of a Tcl command. */
932: int nested; /* Zero means this is a top-level command.
933: * One means this is a nested command (close
934: * brace is a word terminator). */
935: {
936: register char *p;
937: int count;
938:
939: p = start;
940: while (isspace(*p)) {
941: p++;
942: }
943:
944: /*
945: * Handle words beginning with a double-quote or a brace.
946: */
947:
948: if (*p == '"') {
949: while (1) {
950: p++;
951: while (*p == '\\') {
952: (void) Tcl_Backslash(p, &count);
953: p += count;
954: }
955: if (*p == '"') {
956: break;
957: }
958: }
959: } else if (*p == '{') {
960: int braces = 1;
961: while (braces != 0) {
962: p++;
963: while (*p == '\\') {
964: (void) Tcl_Backslash(p, &count);
965: p += count;
966: }
967: if (*p == '}') {
968: braces--;
969: } else if (*p == '{') {
970: braces++;
971: } else if (*p == 0) {
972: return p;
973: }
974: }
975: }
976:
977: /*
978: * Handle words that don't start with a brace or double-quote.
979: * This code is also invoked if the word starts with a brace or
980: * double-quote and there is garbage after the closing brace or
981: * quote. This is an error as far as Tcl_Eval is concerned, but
982: * for here the garbage is treated as part of the word.
983: */
984:
985: while (1) {
986:
987: /*
988: * Handle nested commands.
989: */
990:
991: while (*p == '[') {
992: p++;
993: while ((*p != ']') && (*p != 0)) {
994: p = TclWordEnd(p, 1);
995: if (*p == ';') {
996: p++;
997: }
998: }
999: if (*p == ']') {
1000: p++;
1001: }
1002: }
1003:
1004: /*
1005: * Handle backslash sequences. Backslash-newline isn't handled
1006: * by Tcl_Backslash, so it must be checked for explicitly.
1007: */
1008:
1009: while (*p == '\\') {
1010: if (p[1] == '\n') {
1011: p += 2;
1012: } else {
1013: (void) Tcl_Backslash(p, &count);
1014: p += count;
1015: }
1016: }
1017:
1018: /*
1019: * Check for end of word. Note: semi-colon terminates a word
1020: * and also counts as a word by itself.
1021: */
1022:
1023: if (*p == ';') {
1024: if (p == start) {
1025: p++;
1026: }
1027: break;
1028: }
1029: if (isspace(*p) || (*p == 0)) {
1030: break;
1031: }
1032: if ((*p == ']') && nested) {
1033: break;
1034: }
1035:
1036: p++;
1037: }
1038: return p;
1039: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.