]> cvs.zerfleddert.de Git - micropolis/blob - src/tcl/tclcmdmz.c
Fixes for compilation with gcc 15
[micropolis] / src / tcl / tclcmdmz.c
1 /*
2 * tclCmdMZ.c --
3 *
4 * This file contains the top-level command routines for most of
5 * the Tcl built-in commands whose names begin with the letters
6 * M to Z. It contains only commands in the generic core (i.e.
7 * those that don't depend much upon UNIX facilities).
8 *
9 * Copyright 1987-1991 Regents of the University of California
10 * Permission to use, copy, modify, and distribute this
11 * software and its documentation for any purpose and without
12 * fee is hereby granted, provided that the above copyright
13 * notice appear in all copies. The University of California
14 * makes no representations about the suitability of this
15 * software for any purpose. It is provided "as is" without
16 * express or implied warranty.
17 */
18
19 #ifndef lint
20 static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclCmdMZ.c,v 1.13 92/04/23 11:07:54 ouster Exp $ SPRITE (Berkeley)";
21 #endif
22
23 #include "tclint.h"
24
25 /*
26 * Structure used to hold information about variable traces:
27 */
28
29 typedef struct {
30 int flags; /* Operations for which Tcl command is
31 * to be invoked. */
32 int length; /* Number of non-NULL chars. in command. */
33 char command[4]; /* Space for Tcl command to invoke. Actual
34 * size will be as large as necessary to
35 * hold command. This field must be the
36 * last in the structure, so that it can
37 * be larger than 4 bytes. */
38 } TraceVarInfo;
39
40 /*
41 * Forward declarations for procedures defined in this file:
42 */
43
44 static char * TraceVarProc _ANSI_ARGS_((ClientData clientData,
45 Tcl_Interp *interp, char *name1, char *name2,
46 int flags));
47 \f
48 /*
49 *----------------------------------------------------------------------
50 *
51 * Tcl_RegexpCmd --
52 *
53 * This procedure is invoked to process the "regexp" Tcl command.
54 * See the user documentation for details on what it does.
55 *
56 * Results:
57 * A standard Tcl result.
58 *
59 * Side effects:
60 * See the user documentation.
61 *
62 *----------------------------------------------------------------------
63 */
64
65 /* ARGSUSED */
66 int
67 Tcl_RegexpCmd (
68 ClientData dummy, /* Not used. */
69 Tcl_Interp *interp, /* Current interpreter. */
70 int argc, /* Number of arguments. */
71 char **argv /* Argument strings. */
72 )
73 {
74 int noCase = 0;
75 int indices = 0;
76 regexp *regexpPtr;
77 char **argPtr, *string;
78 int match, i;
79
80 if (argc < 3) {
81 wrongNumArgs:
82 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
83 " ?-nocase? exp string ?matchVar? ?subMatchVar ",
84 "subMatchVar ...?\"", (char *) NULL);
85 return TCL_ERROR;
86 }
87 argPtr = argv+1;
88 argc--;
89 while ((argc > 0) && (argPtr[0][0] == '-')) {
90 if (strcmp(argPtr[0], "-indices") == 0) {
91 argPtr++;
92 argc--;
93 indices = 1;
94 } else if (strcmp(argPtr[0], "-nocase") == 0) {
95 argPtr++;
96 argc--;
97 noCase = 1;
98 } else {
99 break;
100 }
101 }
102 if (argc < 2) {
103 goto wrongNumArgs;
104 }
105 regexpPtr = TclCompileRegexp(interp, argPtr[0]);
106 if (regexpPtr == NULL) {
107 return TCL_ERROR;
108 }
109
110 /*
111 * Convert the string to lower case, if desired, and perform
112 * the match.
113 */
114
115 if (noCase) {
116 register char *dst, *src;
117
118 string = (char *) ckalloc((unsigned) (strlen(argPtr[1]) + 1));
119 for (src = argPtr[1], dst = string; *src != 0; src++, dst++) {
120 if (isupper(*src)) {
121 *dst = tolower(*src);
122 } else {
123 *dst = *src;
124 }
125 }
126 *dst = 0;
127 } else {
128 string = argPtr[1];
129 }
130 tclRegexpError = NULL;
131 match = regexec(regexpPtr, string);
132 if (string != argPtr[1]) {
133 ckfree(string);
134 }
135 if (tclRegexpError != NULL) {
136 Tcl_AppendResult(interp, "error while matching pattern: ",
137 tclRegexpError, (char *) NULL);
138 return TCL_ERROR;
139 }
140 if (!match) {
141 interp->result = "0";
142 return TCL_OK;
143 }
144
145 /*
146 * If additional variable names have been specified, return
147 * index information in those variables.
148 */
149
150 argc -= 2;
151 if (argc > NSUBEXP) {
152 interp->result = "too many substring variables";
153 return TCL_ERROR;
154 }
155 for (i = 0; i < argc; i++) {
156 char *result, info[50];
157
158 if (regexpPtr->startp[i] == NULL) {
159 if (indices) {
160 result = Tcl_SetVar(interp, argPtr[i+2], "-1 -1", 0);
161 } else {
162 result = Tcl_SetVar(interp, argPtr[i+2], "", 0);
163 }
164 } else {
165 if (indices) {
166 sprintf(info, "%d %d", regexpPtr->startp[i] - string,
167 regexpPtr->endp[i] - string - 1);
168 result = Tcl_SetVar(interp, argPtr[i+2], info, 0);
169 } else {
170 char savedChar, *first, *last;
171
172 first = argPtr[1] + (regexpPtr->startp[i] - string);
173 last = argPtr[1] + (regexpPtr->endp[i] - string);
174 savedChar = *last;
175 *last = 0;
176 result = Tcl_SetVar(interp, argPtr[i+2], first, 0);
177 *last = savedChar;
178 }
179 }
180 if (result == NULL) {
181 Tcl_AppendResult(interp, "couldn't set variable \"",
182 argPtr[i+2], "\"", (char *) NULL);
183 return TCL_ERROR;
184 }
185 }
186 interp->result = "1";
187 return TCL_OK;
188 }
189 \f
190 /*
191 *----------------------------------------------------------------------
192 *
193 * Tcl_RegsubCmd --
194 *
195 * This procedure is invoked to process the "regsub" Tcl command.
196 * See the user documentation for details on what it does.
197 *
198 * Results:
199 * A standard Tcl result.
200 *
201 * Side effects:
202 * See the user documentation.
203 *
204 *----------------------------------------------------------------------
205 */
206
207 /* ARGSUSED */
208 int
209 Tcl_RegsubCmd (
210 ClientData dummy, /* Not used. */
211 Tcl_Interp *interp, /* Current interpreter. */
212 int argc, /* Number of arguments. */
213 char **argv /* Argument strings. */
214 )
215 {
216 int noCase = 0, all = 0;
217 regexp *regexpPtr;
218 char *string, *p, *firstChar, *newValue, **argPtr;
219 int match, result, flags;
220 register char *src, c;
221
222 if (argc < 5) {
223 wrongNumArgs:
224 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
225 " ?-nocase? ?-all? exp string subSpec varName\"", (char *) NULL);
226 return TCL_ERROR;
227 }
228 argPtr = argv+1;
229 argc--;
230 while (argPtr[0][0] == '-') {
231 if (strcmp(argPtr[0], "-nocase") == 0) {
232 argPtr++;
233 argc--;
234 noCase = 1;
235 } else if (strcmp(argPtr[0], "-all") == 0) {
236 argPtr++;
237 argc--;
238 all = 1;
239 } else {
240 break;
241 }
242 }
243 if (argc != 4) {
244 goto wrongNumArgs;
245 }
246 regexpPtr = TclCompileRegexp(interp, argPtr[0]);
247 if (regexpPtr == NULL) {
248 return TCL_ERROR;
249 }
250
251 /*
252 * Convert the string to lower case, if desired.
253 */
254
255 if (noCase) {
256 register char *dst;
257
258 string = (char *) ckalloc((unsigned) (strlen(argPtr[1]) + 1));
259 for (src = argPtr[1], dst = string; *src != 0; src++, dst++) {
260 if (isupper(*src)) {
261 *dst = tolower(*src);
262 } else {
263 *dst = *src;
264 }
265 }
266 *dst = 0;
267 } else {
268 string = argPtr[1];
269 }
270
271 /*
272 * The following loop is to handle multiple matches within the
273 * same source string; each iteration handles one match and its
274 * corresponding substitution. If "-all" hasn't been specified
275 * then the loop body only gets executed once.
276 */
277
278 flags = 0;
279 for (p = string; *p != 0; ) {
280 tclRegexpError = NULL;
281 match = regexec(regexpPtr, p);
282 if (tclRegexpError != NULL) {
283 Tcl_AppendResult(interp, "error while matching pattern: ",
284 tclRegexpError, (char *) NULL);
285 result = TCL_ERROR;
286 goto done;
287 }
288 if (!match) {
289 break;
290 }
291
292 /*
293 * Copy the portion of the source string before the match to the
294 * result variable.
295 */
296
297 src = argPtr[1] + (regexpPtr->startp[0] - string);
298 c = *src;
299 *src = 0;
300 newValue = Tcl_SetVar(interp, argPtr[3], argPtr[1] + (p - string),
301 flags);
302 *src = c;
303 flags = TCL_APPEND_VALUE;
304 if (newValue == NULL) {
305 cantSet:
306 Tcl_AppendResult(interp, "couldn't set variable \"",
307 argPtr[3], "\"", (char *) NULL);
308 result = TCL_ERROR;
309 goto done;
310 }
311
312 /*
313 * Append the subSpec argument to the variable, making appropriate
314 * substitutions. This code is a bit hairy because of the backslash
315 * conventions and because the code saves up ranges of characters in
316 * subSpec to reduce the number of calls to Tcl_SetVar.
317 */
318
319 for (src = firstChar = argPtr[2], c = *src; c != 0; src++, c = *src) {
320 int index;
321
322 if (c == '&') {
323 index = 0;
324 } else if (c == '\\') {
325 c = src[1];
326 if ((c >= '0') && (c <= '9')) {
327 index = c - '0';
328 } else if ((c == '\\') || (c == '&')) {
329 *src = c;
330 src[1] = 0;
331 newValue = Tcl_SetVar(interp, argPtr[3], firstChar,
332 TCL_APPEND_VALUE);
333 *src = '\\';
334 src[1] = c;
335 if (newValue == NULL) {
336 goto cantSet;
337 }
338 firstChar = src+2;
339 src++;
340 continue;
341 } else {
342 continue;
343 }
344 } else {
345 continue;
346 }
347 if (firstChar != src) {
348 c = *src;
349 *src = 0;
350 newValue = Tcl_SetVar(interp, argPtr[3], firstChar,
351 TCL_APPEND_VALUE);
352 *src = c;
353 if (newValue == NULL) {
354 goto cantSet;
355 }
356 }
357 if ((index < NSUBEXP) && (regexpPtr->startp[index] != NULL)
358 && (regexpPtr->endp[index] != NULL)) {
359 char *first, *last, saved;
360
361 first = argPtr[1] + (regexpPtr->startp[index] - string);
362 last = argPtr[1] + (regexpPtr->endp[index] - string);
363 saved = *last;
364 *last = 0;
365 newValue = Tcl_SetVar(interp, argPtr[3], first,
366 TCL_APPEND_VALUE);
367 *last = saved;
368 if (newValue == NULL) {
369 goto cantSet;
370 }
371 }
372 if (*src == '\\') {
373 src++;
374 }
375 firstChar = src+1;
376 }
377 if (firstChar != src) {
378 if (Tcl_SetVar(interp, argPtr[3], firstChar,
379 TCL_APPEND_VALUE) == NULL) {
380 goto cantSet;
381 }
382 }
383 p = regexpPtr->endp[0];
384 if (!all) {
385 break;
386 }
387 }
388
389 /*
390 * If there were no matches at all, then return a "0" result.
391 */
392
393 if (p == string) {
394 interp->result = "0";
395 result = TCL_OK;
396 goto done;
397 }
398
399 /*
400 * Copy the portion of the source string after the last match to the
401 * result variable.
402 */
403
404 if (*p != 0) {
405 if (Tcl_SetVar(interp, argPtr[3], p, TCL_APPEND_VALUE) == NULL) {
406 goto cantSet;
407 }
408 }
409 interp->result = "1";
410 result = TCL_OK;
411
412 done:
413 if (string != argPtr[1]) {
414 ckfree(string);
415 }
416 return result;
417 }
418 \f
419 /*
420 *----------------------------------------------------------------------
421 *
422 * Tcl_RenameCmd --
423 *
424 * This procedure is invoked to process the "rename" Tcl command.
425 * See the user documentation for details on what it does.
426 *
427 * Results:
428 * A standard Tcl result.
429 *
430 * Side effects:
431 * See the user documentation.
432 *
433 *----------------------------------------------------------------------
434 */
435
436 /* ARGSUSED */
437 int
438 Tcl_RenameCmd (
439 ClientData dummy, /* Not used. */
440 Tcl_Interp *interp, /* Current interpreter. */
441 int argc, /* Number of arguments. */
442 char **argv /* Argument strings. */
443 )
444 {
445 register Command *cmdPtr;
446 Interp *iPtr = (Interp *) interp;
447 Tcl_HashEntry *hPtr;
448 int new;
449
450 if (argc != 3) {
451 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
452 " oldName newName\"", (char *) NULL);
453 return TCL_ERROR;
454 }
455 if (argv[2][0] == '\0') {
456 if (Tcl_DeleteCommand(interp, argv[1]) != 0) {
457 Tcl_AppendResult(interp, "can't delete \"", argv[1],
458 "\": command doesn't exist", (char *) NULL);
459 return TCL_ERROR;
460 }
461 return TCL_OK;
462 }
463 hPtr = Tcl_FindHashEntry(&iPtr->commandTable, argv[2]);
464 if (hPtr != NULL) {
465 Tcl_AppendResult(interp, "can't rename to \"", argv[2],
466 "\": command already exists", (char *) NULL);
467 return TCL_ERROR;
468 }
469 hPtr = Tcl_FindHashEntry(&iPtr->commandTable, argv[1]);
470 if (hPtr == NULL) {
471 Tcl_AppendResult(interp, "can't rename \"", argv[1],
472 "\": command doesn't exist", (char *) NULL);
473 return TCL_ERROR;
474 }
475 cmdPtr = (Command *) Tcl_GetHashValue(hPtr);
476 Tcl_DeleteHashEntry(hPtr);
477 hPtr = Tcl_CreateHashEntry(&iPtr->commandTable, argv[2], &new);
478 Tcl_SetHashValue(hPtr, cmdPtr);
479 return TCL_OK;
480 }
481 \f
482 /*
483 *----------------------------------------------------------------------
484 *
485 * Tcl_ReturnCmd --
486 *
487 * This procedure is invoked to process the "return" Tcl command.
488 * See the user documentation for details on what it does.
489 *
490 * Results:
491 * A standard Tcl result.
492 *
493 * Side effects:
494 * See the user documentation.
495 *
496 *----------------------------------------------------------------------
497 */
498
499 /* ARGSUSED */
500 int
501 Tcl_ReturnCmd (
502 ClientData dummy, /* Not used. */
503 Tcl_Interp *interp, /* Current interpreter. */
504 int argc, /* Number of arguments. */
505 char **argv /* Argument strings. */
506 )
507 {
508 if (argc > 2) {
509 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
510 " ?value?\"", (char *) NULL);
511 return TCL_ERROR;
512 }
513 if (argc == 2) {
514 Tcl_SetResult(interp, argv[1], TCL_VOLATILE);
515 }
516 return TCL_RETURN;
517 }
518 \f
519 /*
520 *----------------------------------------------------------------------
521 *
522 * Tcl_ScanCmd --
523 *
524 * This procedure is invoked to process the "scan" Tcl command.
525 * See the user documentation for details on what it does.
526 *
527 * Results:
528 * A standard Tcl result.
529 *
530 * Side effects:
531 * See the user documentation.
532 *
533 *----------------------------------------------------------------------
534 */
535
536 /* ARGSUSED */
537 int
538 Tcl_ScanCmd (
539 ClientData dummy, /* Not used. */
540 Tcl_Interp *interp, /* Current interpreter. */
541 int argc, /* Number of arguments. */
542 char **argv /* Argument strings. */
543 )
544 {
545 int arg1Length; /* Number of bytes in argument to be
546 * scanned. This gives an upper limit
547 * on string field sizes. */
548 # define MAX_FIELDS 20
549 typedef struct {
550 char fmt; /* Format for field. */
551 int size; /* How many bytes to allow for
552 * field. */
553 char *location; /* Where field will be stored. */
554 } Field;
555 Field fields[MAX_FIELDS]; /* Info about all the fields in the
556 * format string. */
557 register Field *curField;
558 int numFields = 0; /* Number of fields actually
559 * specified. */
560 int suppress; /* Current field is assignment-
561 * suppressed. */
562 int totalSize = 0; /* Number of bytes needed to store
563 * all results combined. */
564 char *results; /* Where scanned output goes. */
565 int numScanned; /* sscanf's result. */
566 register char *fmt;
567 int i, widthSpecified;
568
569 if (argc < 3) {
570 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
571 " string format ?varName varName ...?\"", (char *) NULL);
572 return TCL_ERROR;
573 }
574
575 /*
576 * This procedure operates in four stages:
577 * 1. Scan the format string, collecting information about each field.
578 * 2. Allocate an array to hold all of the scanned fields.
579 * 3. Call sscanf to do all the dirty work, and have it store the
580 * parsed fields in the array.
581 * 4. Pick off the fields from the array and assign them to variables.
582 */
583
584 arg1Length = (strlen(argv[1]) + 4) & ~03;
585 for (fmt = argv[2]; *fmt != 0; fmt++) {
586 if (*fmt != '%') {
587 continue;
588 }
589 fmt++;
590 if (*fmt == '*') {
591 suppress = 1;
592 fmt++;
593 } else {
594 suppress = 0;
595 }
596 widthSpecified = 0;
597 while (isdigit(*fmt)) {
598 widthSpecified = 1;
599 fmt++;
600 }
601 if (suppress) {
602 continue;
603 }
604 if (numFields == MAX_FIELDS) {
605 interp->result = "too many fields to scan";
606 return TCL_ERROR;
607 }
608 curField = &fields[numFields];
609 numFields++;
610 switch (*fmt) {
611 case 'D':
612 case 'O':
613 case 'X':
614 case 'd':
615 case 'o':
616 case 'x':
617 curField->fmt = 'd';
618 curField->size = sizeof(int);
619 break;
620
621 case 's':
622 curField->fmt = 's';
623 curField->size = arg1Length;
624 break;
625
626 case 'c':
627 if (widthSpecified) {
628 interp->result =
629 "field width may not be specified in %c conversion";
630 return TCL_ERROR;
631 }
632 curField->fmt = 'c';
633 curField->size = sizeof(int);
634 break;
635
636 case 'E':
637 case 'F':
638 curField->fmt = 'F';
639 curField->size = sizeof(double);
640 break;
641
642 case 'e':
643 case 'f':
644 curField->fmt = 'f';
645 curField->size = sizeof(float);
646 break;
647
648 case '[':
649 curField->fmt = 's';
650 curField->size = arg1Length;
651 do {
652 fmt++;
653 } while (*fmt != ']');
654 break;
655
656 default:
657 sprintf(interp->result, "bad scan conversion character \"%c\"",
658 *fmt);
659 return TCL_ERROR;
660 }
661 totalSize += curField->size;
662 }
663
664 if (numFields != (argc-3)) {
665 interp->result =
666 "different numbers of variable names and field specifiers";
667 return TCL_ERROR;
668 }
669
670 /*
671 * Step 2:
672 */
673
674 results = (char *) ckalloc((unsigned) totalSize);
675 for (i = 0, totalSize = 0, curField = fields;
676 i < numFields; i++, curField++) {
677 curField->location = results + totalSize;
678 totalSize += curField->size;
679 }
680
681 /*
682 * Step 3:
683 */
684
685 numScanned = sscanf(argv[1], argv[2],
686 fields[0].location, fields[1].location, fields[2].location,
687 fields[3].location, fields[4].location, fields[5].location,
688 fields[6].location, fields[7].location, fields[8].location,
689 fields[9].location, fields[10].location, fields[11].location,
690 fields[12].location, fields[13].location, fields[14].location,
691 fields[15].location, fields[16].location, fields[17].location,
692 fields[18].location, fields[19].location);
693
694 /*
695 * Step 4:
696 */
697
698 if (numScanned < numFields) {
699 numFields = numScanned;
700 }
701 for (i = 0, curField = fields; i < numFields; i++, curField++) {
702 switch (curField->fmt) {
703 char string[120];
704
705 case 'd':
706 sprintf(string, "%d", *((int *) curField->location));
707 if (Tcl_SetVar(interp, argv[i+3], string, 0) == NULL) {
708 storeError:
709 Tcl_AppendResult(interp,
710 "couldn't set variable \"", argv[i+3], "\"",
711 (char *) NULL);
712 ckfree((char *) results);
713 return TCL_ERROR;
714 }
715 break;
716
717 case 'c':
718 sprintf(string, "%d", *((char *) curField->location) & 0xff);
719 if (Tcl_SetVar(interp, argv[i+3], string, 0) == NULL) {
720 goto storeError;
721 }
722 break;
723
724 case 's':
725 if (Tcl_SetVar(interp, argv[i+3], curField->location, 0)
726 == NULL) {
727 goto storeError;
728 }
729 break;
730
731 case 'F':
732 sprintf(string, "%g", *((double *) curField->location));
733 if (Tcl_SetVar(interp, argv[i+3], string, 0) == NULL) {
734 goto storeError;
735 }
736 break;
737
738 case 'f':
739 sprintf(string, "%g", *((float *) curField->location));
740 if (Tcl_SetVar(interp, argv[i+3], string, 0) == NULL) {
741 goto storeError;
742 }
743 break;
744 }
745 }
746 ckfree(results);
747 sprintf(interp->result, "%d", numScanned);
748 return TCL_OK;
749 }
750 \f
751 /*
752 *----------------------------------------------------------------------
753 *
754 * Tcl_SplitCmd --
755 *
756 * This procedure is invoked to process the "split" Tcl command.
757 * See the user documentation for details on what it does.
758 *
759 * Results:
760 * A standard Tcl result.
761 *
762 * Side effects:
763 * See the user documentation.
764 *
765 *----------------------------------------------------------------------
766 */
767
768 /* ARGSUSED */
769 int
770 Tcl_SplitCmd (
771 ClientData dummy, /* Not used. */
772 Tcl_Interp *interp, /* Current interpreter. */
773 int argc, /* Number of arguments. */
774 char **argv /* Argument strings. */
775 )
776 {
777 char *splitChars;
778 register char *p, *p2;
779 char *elementStart;
780
781 if (argc == 2) {
782 splitChars = " \n\t\r";
783 } else if (argc == 3) {
784 splitChars = argv[2];
785 } else {
786 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
787 " string ?splitChars?\"", (char *) NULL);
788 return TCL_ERROR;
789 }
790
791 /*
792 * Handle the special case of splitting on every character.
793 */
794
795 if (*splitChars == 0) {
796 char string[2];
797 string[1] = 0;
798 for (p = argv[1]; *p != 0; p++) {
799 string[0] = *p;
800 Tcl_AppendElement(interp, string, 0);
801 }
802 return TCL_OK;
803 }
804
805 /*
806 * Normal case: split on any of a given set of characters.
807 * Discard instances of the split characters.
808 */
809
810 for (p = elementStart = argv[1]; *p != 0; p++) {
811 char c = *p;
812 for (p2 = splitChars; *p2 != 0; p2++) {
813 if (*p2 == c) {
814 *p = 0;
815 Tcl_AppendElement(interp, elementStart, 0);
816 *p = c;
817 elementStart = p+1;
818 break;
819 }
820 }
821 }
822 if (p != argv[1]) {
823 Tcl_AppendElement(interp, elementStart, 0);
824 }
825 return TCL_OK;
826 }
827 \f
828 /*
829 *----------------------------------------------------------------------
830 *
831 * Tcl_StringCmd --
832 *
833 * This procedure is invoked to process the "string" Tcl command.
834 * See the user documentation for details on what it does.
835 *
836 * Results:
837 * A standard Tcl result.
838 *
839 * Side effects:
840 * See the user documentation.
841 *
842 *----------------------------------------------------------------------
843 */
844
845 /* ARGSUSED */
846 int
847 Tcl_StringCmd (
848 ClientData dummy, /* Not used. */
849 Tcl_Interp *interp, /* Current interpreter. */
850 int argc, /* Number of arguments. */
851 char **argv /* Argument strings. */
852 )
853 {
854 int length;
855 register char *p, c;
856 int match;
857 int first;
858 int left = 0, right = 0;
859
860 if (argc < 2) {
861 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
862 " option arg ?arg ...?\"", (char *) NULL);
863 return TCL_ERROR;
864 }
865 c = argv[1][0];
866 length = strlen(argv[1]);
867 if ((c == 'c') && (strncmp(argv[1], "compare", length) == 0)) {
868 if (argc != 4) {
869 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
870 " compare string1 string2\"", (char *) NULL);
871 return TCL_ERROR;
872 }
873 match = strcmp(argv[2], argv[3]);
874 if (match > 0) {
875 interp->result = "1";
876 } else if (match < 0) {
877 interp->result = "-1";
878 } else {
879 interp->result = "0";
880 }
881 return TCL_OK;
882 } else if ((c == 'f') && (strncmp(argv[1], "first", length) == 0)) {
883 if (argc != 4) {
884 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
885 " first string1 string2\"", (char *) NULL);
886 return TCL_ERROR;
887 }
888 first = 1;
889
890 firstLast:
891 match = -1;
892 c = *argv[2];
893 length = strlen(argv[2]);
894 for (p = argv[3]; *p != 0; p++) {
895 if (*p != c) {
896 continue;
897 }
898 if (strncmp(argv[2], p, length) == 0) {
899 match = p-argv[3];
900 if (first) {
901 break;
902 }
903 }
904 }
905 sprintf(interp->result, "%d", match);
906 return TCL_OK;
907 } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)) {
908 int index;
909
910 if (argc != 4) {
911 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
912 " index string charIndex\"", (char *) NULL);
913 return TCL_ERROR;
914 }
915 if (Tcl_GetInt(interp, argv[3], &index) != TCL_OK) {
916 return TCL_ERROR;
917 }
918 if ((index >= 0) && (index < strlen(argv[2]))) {
919 interp->result[0] = argv[2][index];
920 interp->result[1] = 0;
921 }
922 return TCL_OK;
923 } else if ((c == 'l') && (strncmp(argv[1], "last", length) == 0)
924 && (length >= 2)) {
925 if (argc != 4) {
926 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
927 " last string1 string2\"", (char *) NULL);
928 return TCL_ERROR;
929 }
930 first = 0;
931 goto firstLast;
932 } else if ((c == 'l') && (strncmp(argv[1], "length", length) == 0)
933 && (length >= 2)) {
934 if (argc != 3) {
935 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
936 " length string\"", (char *) NULL);
937 return TCL_ERROR;
938 }
939 sprintf(interp->result, "%d", strlen(argv[2]));
940 return TCL_OK;
941 } else if ((c == 'm') && (strncmp(argv[1], "match", length) == 0)) {
942 if (argc != 4) {
943 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
944 " match pattern string\"", (char *) NULL);
945 return TCL_ERROR;
946 }
947 if (Tcl_StringMatch(argv[3], argv[2]) != 0) {
948 interp->result = "1";
949 } else {
950 interp->result = "0";
951 }
952 return TCL_OK;
953 } else if ((c == 'r') && (strncmp(argv[1], "range", length) == 0)) {
954 int first, last, stringLength;
955
956 if (argc != 5) {
957 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
958 " range string first last\"", (char *) NULL);
959 return TCL_ERROR;
960 }
961 stringLength = strlen(argv[2]);
962 if (Tcl_GetInt(interp, argv[3], &first) != TCL_OK) {
963 return TCL_ERROR;
964 }
965 if ((*argv[4] == 'e')
966 && (strncmp(argv[4], "end", strlen(argv[4])) == 0)) {
967 last = stringLength-1;
968 } else {
969 if (Tcl_GetInt(interp, argv[4], &last) != TCL_OK) {
970 Tcl_ResetResult(interp);
971 Tcl_AppendResult(interp,
972 "expected integer or \"end\" but got \"",
973 argv[4], "\"", (char *) NULL);
974 return TCL_ERROR;
975 }
976 }
977 if (first < 0) {
978 first = 0;
979 }
980 if (last >= stringLength) {
981 last = stringLength-1;
982 }
983 if (last >= first) {
984 char saved, *p;
985
986 p = argv[2] + last + 1;
987 saved = *p;
988 *p = 0;
989 Tcl_SetResult(interp, argv[2] + first, TCL_VOLATILE);
990 *p = saved;
991 }
992 return TCL_OK;
993 } else if ((c == 't') && (strncmp(argv[1], "tolower", length) == 0)
994 && (length >= 3)) {
995 register char *p;
996
997 if (argc != 3) {
998 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
999 " tolower string\"", (char *) NULL);
1000 return TCL_ERROR;
1001 }
1002 Tcl_SetResult(interp, argv[2], TCL_VOLATILE);
1003 for (p = interp->result; *p != 0; p++) {
1004 if (isupper(*p)) {
1005 *p = tolower(*p);
1006 }
1007 }
1008 return TCL_OK;
1009 } else if ((c == 't') && (strncmp(argv[1], "toupper", length) == 0)
1010 && (length >= 3)) {
1011 register char *p;
1012
1013 if (argc != 3) {
1014 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1015 " toupper string\"", (char *) NULL);
1016 return TCL_ERROR;
1017 }
1018 Tcl_SetResult(interp, argv[2], TCL_VOLATILE);
1019 for (p = interp->result; *p != 0; p++) {
1020 if (islower(*p)) {
1021 *p = toupper(*p);
1022 }
1023 }
1024 return TCL_OK;
1025 } else if ((c == 't') && (strncmp(argv[1], "trim", length) == 0)
1026 && (length == 4)) {
1027 char *trimChars;
1028 register char *p, *checkPtr;
1029
1030 left = right = 1;
1031
1032 trim:
1033 if (argc == 4) {
1034 trimChars = argv[3];
1035 } else if (argc == 3) {
1036 trimChars = " \t\n\r";
1037 } else {
1038 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1039 " ", argv[1], " string ?chars?\"", (char *) NULL);
1040 return TCL_ERROR;
1041 }
1042 p = argv[2];
1043 if (left) {
1044 for (c = *p; c != 0; p++, c = *p) {
1045 for (checkPtr = trimChars; *checkPtr != c; checkPtr++) {
1046 if (*checkPtr == 0) {
1047 goto doneLeft;
1048 }
1049 }
1050 }
1051 }
1052 doneLeft:
1053 Tcl_SetResult(interp, p, TCL_VOLATILE);
1054 if (right) {
1055 char *donePtr;
1056
1057 p = interp->result + strlen(interp->result) - 1;
1058 donePtr = &interp->result[-1];
1059 for (c = *p; p != donePtr; p--, c = *p) {
1060 for (checkPtr = trimChars; *checkPtr != c; checkPtr++) {
1061 if (*checkPtr == 0) {
1062 goto doneRight;
1063 }
1064 }
1065 }
1066 doneRight:
1067 p[1] = 0;
1068 }
1069 return TCL_OK;
1070 } else if ((c == 't') && (strncmp(argv[1], "trimleft", length) == 0)
1071 && (length > 4)) {
1072 left = 1;
1073 argv[1] = "trimleft";
1074 goto trim;
1075 } else if ((c == 't') && (strncmp(argv[1], "trimright", length) == 0)
1076 && (length > 4)) {
1077 right = 1;
1078 argv[1] = "trimright";
1079 goto trim;
1080 } else {
1081 Tcl_AppendResult(interp, "bad option \"", argv[1],
1082 "\": should be compare, first, index, last, length, match, ",
1083 "range, tolower, toupper, trim, trimleft, or trimright",
1084 (char *) NULL);
1085 return TCL_ERROR;
1086 }
1087 }
1088 \f
1089 /*
1090 *----------------------------------------------------------------------
1091 *
1092 * Tcl_TraceCmd --
1093 *
1094 * This procedure is invoked to process the "trace" Tcl command.
1095 * See the user documentation for details on what it does.
1096 *
1097 * Results:
1098 * A standard Tcl result.
1099 *
1100 * Side effects:
1101 * See the user documentation.
1102 *
1103 *----------------------------------------------------------------------
1104 */
1105
1106 /* ARGSUSED */
1107 int
1108 Tcl_TraceCmd (
1109 ClientData dummy, /* Not used. */
1110 Tcl_Interp *interp, /* Current interpreter. */
1111 int argc, /* Number of arguments. */
1112 char **argv /* Argument strings. */
1113 )
1114 {
1115 char c;
1116 int length;
1117
1118 if (argc < 2) {
1119 Tcl_AppendResult(interp, "too few args: should be \"",
1120 argv[0], " option [arg arg ...]\"", (char *) NULL);
1121 return TCL_ERROR;
1122 }
1123 c = argv[1][1];
1124 length = strlen(argv[1]);
1125 if ((c == 'a') && (strncmp(argv[1], "variable", length) == 0)
1126 && (length >= 2)) {
1127 char *p;
1128 int flags, length;
1129 TraceVarInfo *tvarPtr;
1130
1131 if (argc != 5) {
1132 Tcl_AppendResult(interp, "wrong # args: should be \"",
1133 argv[0], " variable name ops command\"", (char *) NULL);
1134 return TCL_ERROR;
1135 }
1136
1137 flags = 0;
1138 for (p = argv[3] ; *p != 0; p++) {
1139 if (*p == 'r') {
1140 flags |= TCL_TRACE_READS;
1141 } else if (*p == 'w') {
1142 flags |= TCL_TRACE_WRITES;
1143 } else if (*p == 'u') {
1144 flags |= TCL_TRACE_UNSETS;
1145 } else {
1146 goto badOps;
1147 }
1148 }
1149 if (flags == 0) {
1150 goto badOps;
1151 }
1152
1153 length = strlen(argv[4]);
1154 tvarPtr = (TraceVarInfo *) ckalloc((unsigned)
1155 (sizeof(TraceVarInfo) - sizeof(tvarPtr->command) + length + 1));
1156 tvarPtr->flags = flags;
1157 tvarPtr->length = length;
1158 flags |= TCL_TRACE_UNSETS;
1159 strcpy(tvarPtr->command, argv[4]);
1160 if (Tcl_TraceVar(interp, argv[2], flags, TraceVarProc,
1161 (ClientData) tvarPtr) != TCL_OK) {
1162 ckfree((char *) tvarPtr);
1163 return TCL_ERROR;
1164 }
1165 } else if ((c == 'd') && (strncmp(argv[1], "vdelete", length)
1166 && (length >= 2)) == 0) {
1167 char *p;
1168 int flags, length;
1169 TraceVarInfo *tvarPtr;
1170 ClientData clientData;
1171
1172 if (argc != 5) {
1173 Tcl_AppendResult(interp, "wrong # args: should be \"",
1174 argv[0], " vdelete name ops command\"", (char *) NULL);
1175 return TCL_ERROR;
1176 }
1177
1178 flags = 0;
1179 for (p = argv[3] ; *p != 0; p++) {
1180 if (*p == 'r') {
1181 flags |= TCL_TRACE_READS;
1182 } else if (*p == 'w') {
1183 flags |= TCL_TRACE_WRITES;
1184 } else if (*p == 'u') {
1185 flags |= TCL_TRACE_UNSETS;
1186 } else {
1187 goto badOps;
1188 }
1189 }
1190 if (flags == 0) {
1191 goto badOps;
1192 }
1193
1194 /*
1195 * Search through all of our traces on this variable to
1196 * see if there's one with the given command. If so, then
1197 * delete the first one that matches.
1198 */
1199
1200 length = strlen(argv[4]);
1201 clientData = 0;
1202 while ((clientData = Tcl_VarTraceInfo(interp, argv[2], 0,
1203 TraceVarProc, clientData)) != 0) {
1204 tvarPtr = (TraceVarInfo *) clientData;
1205 if ((tvarPtr->length == length) && (tvarPtr->flags == flags)
1206 && (strncmp(argv[4], tvarPtr->command, length) == 0)) {
1207 Tcl_UntraceVar(interp, argv[2], flags | TCL_TRACE_UNSETS,
1208 TraceVarProc, clientData);
1209 ckfree((char *) tvarPtr);
1210 break;
1211 }
1212 }
1213 } else if ((c == 'i') && (strncmp(argv[1], "vinfo", length) == 0)
1214 && (length >= 2)) {
1215 ClientData clientData;
1216 char ops[4], *p;
1217 char *prefix = "{";
1218
1219 if (argc != 3) {
1220 Tcl_AppendResult(interp, "wrong # args: should be \"",
1221 argv[0], " vinfo name\"", (char *) NULL);
1222 return TCL_ERROR;
1223 }
1224 clientData = 0;
1225 while ((clientData = Tcl_VarTraceInfo(interp, argv[2], 0,
1226 TraceVarProc, clientData)) != 0) {
1227 TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
1228 p = ops;
1229 if (tvarPtr->flags & TCL_TRACE_READS) {
1230 *p = 'r';
1231 p++;
1232 }
1233 if (tvarPtr->flags & TCL_TRACE_WRITES) {
1234 *p = 'w';
1235 p++;
1236 }
1237 if (tvarPtr->flags & TCL_TRACE_UNSETS) {
1238 *p = 'u';
1239 p++;
1240 }
1241 *p = '\0';
1242 Tcl_AppendResult(interp, prefix, (char *) NULL);
1243 Tcl_AppendElement(interp, ops, 1);
1244 Tcl_AppendElement(interp, tvarPtr->command, 0);
1245 Tcl_AppendResult(interp, "}", (char *) NULL);
1246 prefix = " {";
1247 }
1248 } else {
1249 Tcl_AppendResult(interp, "bad option \"", argv[1],
1250 "\": should be variable, vdelete, or vinfo",
1251 (char *) NULL);
1252 return TCL_ERROR;
1253 }
1254 return TCL_OK;
1255
1256 badOps:
1257 Tcl_AppendResult(interp, "bad operations \"", argv[3],
1258 "\": should be one or more of rwu", (char *) NULL);
1259 return TCL_ERROR;
1260 }
1261 \f
1262 /*
1263 *----------------------------------------------------------------------
1264 *
1265 * TraceVarProc --
1266 *
1267 * This procedure is called to handle variable accesses that have
1268 * been traced using the "trace" command.
1269 *
1270 * Results:
1271 * Normally returns NULL. If the trace command returns an error,
1272 * then this procedure returns an error string.
1273 *
1274 * Side effects:
1275 * Depends on the command associated with the trace.
1276 *
1277 *----------------------------------------------------------------------
1278 */
1279
1280 /* ARGSUSED */
1281 static char *
1282 TraceVarProc (
1283 ClientData clientData, /* Information about the variable trace. */
1284 Tcl_Interp *interp, /* Interpreter containing variable. */
1285 char *name1, /* Name of variable or array. */
1286 char *name2, /* Name of element within array; NULL means
1287 * scalar variable is being referenced. */
1288 int flags /* OR-ed bits giving operation and other
1289 * information. */
1290 )
1291 {
1292 TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
1293 char *result;
1294 int code, cmdLength, flags1, flags2;
1295 Interp dummy;
1296 #define STATIC_SIZE 199
1297 char staticSpace[STATIC_SIZE+1];
1298 char *cmdPtr, *p;
1299
1300 result = NULL;
1301 if ((tvarPtr->flags & flags) && !(flags & TCL_INTERP_DESTROYED)) {
1302
1303 /*
1304 * Generate a command to execute by appending list elements
1305 * for the two variable names and the operation. The five
1306 * extra characters are for three space, the opcode character,
1307 * and the terminating null.
1308 */
1309
1310 if (name2 == NULL) {
1311 name2 = "";
1312 }
1313 cmdLength = tvarPtr->length + Tcl_ScanElement(name1, &flags1) +
1314 Tcl_ScanElement(name2, &flags2) + 5;
1315 if (cmdLength < STATIC_SIZE) {
1316 cmdPtr = staticSpace;
1317 } else {
1318 cmdPtr = (char *) ckalloc((unsigned) cmdLength);
1319 }
1320 p = cmdPtr;
1321 strcpy(p, tvarPtr->command);
1322 p += tvarPtr->length;
1323 *p = ' ';
1324 p++;
1325 p += Tcl_ConvertElement(name1, p, flags1);
1326 *p = ' ';
1327 p++;
1328 p += Tcl_ConvertElement(name2, p, flags2);
1329 *p = ' ';
1330 if (flags & TCL_TRACE_READS) {
1331 p[1] = 'r';
1332 } else if (flags & TCL_TRACE_WRITES) {
1333 p[1] = 'w';
1334 } else if (flags & TCL_TRACE_UNSETS) {
1335 p[1] = 'u';
1336 }
1337 p[2] = '\0';
1338
1339 /*
1340 * Execute the command. Be careful to save and restore the
1341 * result from the interpreter used for the command.
1342 */
1343
1344 dummy.freeProc = interp->freeProc;
1345 if (interp->freeProc == 0) {
1346 Tcl_SetResult((Tcl_Interp *) &dummy, interp->result, TCL_VOLATILE);
1347 } else {
1348 dummy.result = interp->result;
1349 }
1350 code = Tcl_Eval(interp, cmdPtr, 0, (char **) NULL);
1351 if (cmdPtr != staticSpace) {
1352 ckfree(cmdPtr);
1353 }
1354 if (code != TCL_OK) {
1355 result = "access disallowed by trace command";
1356 Tcl_ResetResult(interp); /* Must clear error state. */
1357 }
1358 Tcl_FreeResult(interp);
1359 interp->result = dummy.result;
1360 interp->freeProc = dummy.freeProc;
1361 }
1362 if (flags & TCL_TRACE_DESTROYED) {
1363 ckfree((char *) tvarPtr);
1364 }
1365 return result;
1366 }
1367 \f
1368 /*
1369 *----------------------------------------------------------------------
1370 *
1371 * Tcl_WhileCmd --
1372 *
1373 * This procedure is invoked to process the "while" Tcl command.
1374 * See the user documentation for details on what it does.
1375 *
1376 * Results:
1377 * A standard Tcl result.
1378 *
1379 * Side effects:
1380 * See the user documentation.
1381 *
1382 *----------------------------------------------------------------------
1383 */
1384
1385 /* ARGSUSED */
1386 int
1387 Tcl_WhileCmd (
1388 ClientData dummy, /* Not used. */
1389 Tcl_Interp *interp, /* Current interpreter. */
1390 int argc, /* Number of arguments. */
1391 char **argv /* Argument strings. */
1392 )
1393 {
1394 int result, value;
1395
1396 if (argc != 3) {
1397 Tcl_AppendResult(interp, "wrong # args: should be \"",
1398 argv[0], " test command\"", (char *) NULL);
1399 return TCL_ERROR;
1400 }
1401
1402 while (1) {
1403 result = Tcl_ExprBoolean(interp, argv[1], &value);
1404 if (result != TCL_OK) {
1405 return result;
1406 }
1407 if (!value) {
1408 break;
1409 }
1410 result = Tcl_Eval(interp, argv[2], 0, (char **) NULL);
1411 if (result == TCL_CONTINUE) {
1412 result = TCL_OK;
1413 } else if (result != TCL_OK) {
1414 if (result == TCL_ERROR) {
1415 char msg[60];
1416 sprintf(msg, "\n (\"while\" body line %d)",
1417 interp->errorLine);
1418 Tcl_AddErrorInfo(interp, msg);
1419 }
1420 break;
1421 }
1422 }
1423 if (result == TCL_BREAK) {
1424 result = TCL_OK;
1425 }
1426 if (result == TCL_OK) {
1427 Tcl_ResetResult(interp);
1428 }
1429 return result;
1430 }
Impressum, Datenschutz