]> git.zerfleddert.de Git - micropolis/blob - src/tcl/tclparse.c
Import Micropolis from http://www.donhopkins.com/home/micropolis/
[micropolis] / src / tcl / tclparse.c
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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 \f
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 }
Impressum, Datenschutz