]>
git.zerfleddert.de Git - micropolis/blob - src/tcl/tclcmdmz.c
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).
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.
20 static char rcsid
[] = "$Header: /user6/ouster/tcl/RCS/tclCmdMZ.c,v 1.13 92/04/23 11:07:54 ouster Exp $ SPRITE (Berkeley)";
26 * Structure used to hold information about variable traces:
30 int flags
; /* Operations for which Tcl command is
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. */
41 * Forward declarations for procedures defined in this file:
44 static char * TraceVarProc
_ANSI_ARGS_((ClientData clientData
,
45 Tcl_Interp
*interp
, char *name1
, char *name2
,
49 *----------------------------------------------------------------------
53 * This procedure is invoked to process the "regexp" Tcl command.
54 * See the user documentation for details on what it does.
57 * A standard Tcl result.
60 * See the user documentation.
62 *----------------------------------------------------------------------
67 Tcl_RegexpCmd(dummy
, interp
, argc
, argv
)
68 ClientData dummy
; /* Not used. */
69 Tcl_Interp
*interp
; /* Current interpreter. */
70 int argc
; /* Number of arguments. */
71 char **argv
; /* Argument strings. */
76 char **argPtr
, *string
;
81 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
82 " ?-nocase? exp string ?matchVar? ?subMatchVar ",
83 "subMatchVar ...?\"", (char *) NULL
);
88 while ((argc
> 0) && (argPtr
[0][0] == '-')) {
89 if (strcmp(argPtr
[0], "-indices") == 0) {
93 } else if (strcmp(argPtr
[0], "-nocase") == 0) {
104 regexpPtr
= TclCompileRegexp(interp
, argPtr
[0]);
105 if (regexpPtr
== NULL
) {
110 * Convert the string to lower case, if desired, and perform
115 register char *dst
, *src
;
117 string
= (char *) ckalloc((unsigned) (strlen(argPtr
[1]) + 1));
118 for (src
= argPtr
[1], dst
= string
; *src
!= 0; src
++, dst
++) {
120 *dst
= tolower(*src
);
129 tclRegexpError
= NULL
;
130 match
= regexec(regexpPtr
, string
);
131 if (string
!= argPtr
[1]) {
134 if (tclRegexpError
!= NULL
) {
135 Tcl_AppendResult(interp
, "error while matching pattern: ",
136 tclRegexpError
, (char *) NULL
);
140 interp
->result
= "0";
145 * If additional variable names have been specified, return
146 * index information in those variables.
150 if (argc
> NSUBEXP
) {
151 interp
->result
= "too many substring variables";
154 for (i
= 0; i
< argc
; i
++) {
155 char *result
, info
[50];
157 if (regexpPtr
->startp
[i
] == NULL
) {
159 result
= Tcl_SetVar(interp
, argPtr
[i
+2], "-1 -1", 0);
161 result
= Tcl_SetVar(interp
, argPtr
[i
+2], "", 0);
165 sprintf(info
, "%d %d", regexpPtr
->startp
[i
] - string
,
166 regexpPtr
->endp
[i
] - string
- 1);
167 result
= Tcl_SetVar(interp
, argPtr
[i
+2], info
, 0);
169 char savedChar
, *first
, *last
;
171 first
= argPtr
[1] + (regexpPtr
->startp
[i
] - string
);
172 last
= argPtr
[1] + (regexpPtr
->endp
[i
] - string
);
175 result
= Tcl_SetVar(interp
, argPtr
[i
+2], first
, 0);
179 if (result
== NULL
) {
180 Tcl_AppendResult(interp
, "couldn't set variable \"",
181 argPtr
[i
+2], "\"", (char *) NULL
);
185 interp
->result
= "1";
190 *----------------------------------------------------------------------
194 * This procedure is invoked to process the "regsub" Tcl command.
195 * See the user documentation for details on what it does.
198 * A standard Tcl result.
201 * See the user documentation.
203 *----------------------------------------------------------------------
208 Tcl_RegsubCmd(dummy
, interp
, argc
, argv
)
209 ClientData dummy
; /* Not used. */
210 Tcl_Interp
*interp
; /* Current interpreter. */
211 int argc
; /* Number of arguments. */
212 char **argv
; /* Argument strings. */
214 int noCase
= 0, all
= 0;
216 char *string
, *p
, *firstChar
, *newValue
, **argPtr
;
217 int match
, result
, flags
;
218 register char *src
, c
;
222 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
223 " ?-nocase? ?-all? exp string subSpec varName\"", (char *) NULL
);
228 while (argPtr
[0][0] == '-') {
229 if (strcmp(argPtr
[0], "-nocase") == 0) {
233 } else if (strcmp(argPtr
[0], "-all") == 0) {
244 regexpPtr
= TclCompileRegexp(interp
, argPtr
[0]);
245 if (regexpPtr
== NULL
) {
250 * Convert the string to lower case, if desired.
256 string
= (char *) ckalloc((unsigned) (strlen(argPtr
[1]) + 1));
257 for (src
= argPtr
[1], dst
= string
; *src
!= 0; src
++, dst
++) {
259 *dst
= tolower(*src
);
270 * The following loop is to handle multiple matches within the
271 * same source string; each iteration handles one match and its
272 * corresponding substitution. If "-all" hasn't been specified
273 * then the loop body only gets executed once.
277 for (p
= string
; *p
!= 0; ) {
278 tclRegexpError
= NULL
;
279 match
= regexec(regexpPtr
, p
);
280 if (tclRegexpError
!= NULL
) {
281 Tcl_AppendResult(interp
, "error while matching pattern: ",
282 tclRegexpError
, (char *) NULL
);
291 * Copy the portion of the source string before the match to the
295 src
= argPtr
[1] + (regexpPtr
->startp
[0] - string
);
298 newValue
= Tcl_SetVar(interp
, argPtr
[3], argPtr
[1] + (p
- string
),
301 flags
= TCL_APPEND_VALUE
;
302 if (newValue
== NULL
) {
304 Tcl_AppendResult(interp
, "couldn't set variable \"",
305 argPtr
[3], "\"", (char *) NULL
);
311 * Append the subSpec argument to the variable, making appropriate
312 * substitutions. This code is a bit hairy because of the backslash
313 * conventions and because the code saves up ranges of characters in
314 * subSpec to reduce the number of calls to Tcl_SetVar.
317 for (src
= firstChar
= argPtr
[2], c
= *src
; c
!= 0; src
++, c
= *src
) {
322 } else if (c
== '\\') {
324 if ((c
>= '0') && (c
<= '9')) {
326 } else if ((c
== '\\') || (c
== '&')) {
329 newValue
= Tcl_SetVar(interp
, argPtr
[3], firstChar
,
333 if (newValue
== NULL
) {
345 if (firstChar
!= src
) {
348 newValue
= Tcl_SetVar(interp
, argPtr
[3], firstChar
,
351 if (newValue
== NULL
) {
355 if ((index
< NSUBEXP
) && (regexpPtr
->startp
[index
] != NULL
)
356 && (regexpPtr
->endp
[index
] != NULL
)) {
357 char *first
, *last
, saved
;
359 first
= argPtr
[1] + (regexpPtr
->startp
[index
] - string
);
360 last
= argPtr
[1] + (regexpPtr
->endp
[index
] - string
);
363 newValue
= Tcl_SetVar(interp
, argPtr
[3], first
,
366 if (newValue
== NULL
) {
375 if (firstChar
!= src
) {
376 if (Tcl_SetVar(interp
, argPtr
[3], firstChar
,
377 TCL_APPEND_VALUE
) == NULL
) {
381 p
= regexpPtr
->endp
[0];
388 * If there were no matches at all, then return a "0" result.
392 interp
->result
= "0";
398 * Copy the portion of the source string after the last match to the
403 if (Tcl_SetVar(interp
, argPtr
[3], p
, TCL_APPEND_VALUE
) == NULL
) {
407 interp
->result
= "1";
411 if (string
!= argPtr
[1]) {
418 *----------------------------------------------------------------------
422 * This procedure is invoked to process the "rename" Tcl command.
423 * See the user documentation for details on what it does.
426 * A standard Tcl result.
429 * See the user documentation.
431 *----------------------------------------------------------------------
436 Tcl_RenameCmd(dummy
, interp
, argc
, argv
)
437 ClientData dummy
; /* Not used. */
438 Tcl_Interp
*interp
; /* Current interpreter. */
439 int argc
; /* Number of arguments. */
440 char **argv
; /* Argument strings. */
442 register Command
*cmdPtr
;
443 Interp
*iPtr
= (Interp
*) interp
;
448 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
449 " oldName newName\"", (char *) NULL
);
452 if (argv
[2][0] == '\0') {
453 if (Tcl_DeleteCommand(interp
, argv
[1]) != 0) {
454 Tcl_AppendResult(interp
, "can't delete \"", argv
[1],
455 "\": command doesn't exist", (char *) NULL
);
460 hPtr
= Tcl_FindHashEntry(&iPtr
->commandTable
, argv
[2]);
462 Tcl_AppendResult(interp
, "can't rename to \"", argv
[2],
463 "\": command already exists", (char *) NULL
);
466 hPtr
= Tcl_FindHashEntry(&iPtr
->commandTable
, argv
[1]);
468 Tcl_AppendResult(interp
, "can't rename \"", argv
[1],
469 "\": command doesn't exist", (char *) NULL
);
472 cmdPtr
= (Command
*) Tcl_GetHashValue(hPtr
);
473 Tcl_DeleteHashEntry(hPtr
);
474 hPtr
= Tcl_CreateHashEntry(&iPtr
->commandTable
, argv
[2], &new);
475 Tcl_SetHashValue(hPtr
, cmdPtr
);
480 *----------------------------------------------------------------------
484 * This procedure is invoked to process the "return" Tcl command.
485 * See the user documentation for details on what it does.
488 * A standard Tcl result.
491 * See the user documentation.
493 *----------------------------------------------------------------------
498 Tcl_ReturnCmd(dummy
, interp
, argc
, argv
)
499 ClientData dummy
; /* Not used. */
500 Tcl_Interp
*interp
; /* Current interpreter. */
501 int argc
; /* Number of arguments. */
502 char **argv
; /* Argument strings. */
505 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
506 " ?value?\"", (char *) NULL
);
510 Tcl_SetResult(interp
, argv
[1], TCL_VOLATILE
);
516 *----------------------------------------------------------------------
520 * This procedure is invoked to process the "scan" Tcl command.
521 * See the user documentation for details on what it does.
524 * A standard Tcl result.
527 * See the user documentation.
529 *----------------------------------------------------------------------
534 Tcl_ScanCmd(dummy
, interp
, argc
, argv
)
535 ClientData dummy
; /* Not used. */
536 Tcl_Interp
*interp
; /* Current interpreter. */
537 int argc
; /* Number of arguments. */
538 char **argv
; /* Argument strings. */
540 int arg1Length
; /* Number of bytes in argument to be
541 * scanned. This gives an upper limit
542 * on string field sizes. */
543 # define MAX_FIELDS 20
545 char fmt
; /* Format for field. */
546 int size
; /* How many bytes to allow for
548 char *location
; /* Where field will be stored. */
550 Field fields
[MAX_FIELDS
]; /* Info about all the fields in the
552 register Field
*curField
;
553 int numFields
= 0; /* Number of fields actually
555 int suppress
; /* Current field is assignment-
557 int totalSize
= 0; /* Number of bytes needed to store
558 * all results combined. */
559 char *results
; /* Where scanned output goes. */
560 int numScanned
; /* sscanf's result. */
562 int i
, widthSpecified
;
565 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
566 " string format ?varName varName ...?\"", (char *) NULL
);
571 * This procedure operates in four stages:
572 * 1. Scan the format string, collecting information about each field.
573 * 2. Allocate an array to hold all of the scanned fields.
574 * 3. Call sscanf to do all the dirty work, and have it store the
575 * parsed fields in the array.
576 * 4. Pick off the fields from the array and assign them to variables.
579 arg1Length
= (strlen(argv
[1]) + 4) & ~03;
580 for (fmt
= argv
[2]; *fmt
!= 0; fmt
++) {
592 while (isdigit(*fmt
)) {
599 if (numFields
== MAX_FIELDS
) {
600 interp
->result
= "too many fields to scan";
603 curField
= &fields
[numFields
];
613 curField
->size
= sizeof(int);
618 curField
->size
= arg1Length
;
622 if (widthSpecified
) {
624 "field width may not be specified in %c conversion";
628 curField
->size
= sizeof(int);
634 curField
->size
= sizeof(double);
640 curField
->size
= sizeof(float);
645 curField
->size
= arg1Length
;
648 } while (*fmt
!= ']');
652 sprintf(interp
->result
, "bad scan conversion character \"%c\"",
656 totalSize
+= curField
->size
;
659 if (numFields
!= (argc
-3)) {
661 "different numbers of variable names and field specifiers";
669 results
= (char *) ckalloc((unsigned) totalSize
);
670 for (i
= 0, totalSize
= 0, curField
= fields
;
671 i
< numFields
; i
++, curField
++) {
672 curField
->location
= results
+ totalSize
;
673 totalSize
+= curField
->size
;
680 numScanned
= sscanf(argv
[1], argv
[2],
681 fields
[0].location
, fields
[1].location
, fields
[2].location
,
682 fields
[3].location
, fields
[4].location
, fields
[5].location
,
683 fields
[6].location
, fields
[7].location
, fields
[8].location
,
684 fields
[9].location
, fields
[10].location
, fields
[11].location
,
685 fields
[12].location
, fields
[13].location
, fields
[14].location
,
686 fields
[15].location
, fields
[16].location
, fields
[17].location
,
687 fields
[18].location
, fields
[19].location
);
693 if (numScanned
< numFields
) {
694 numFields
= numScanned
;
696 for (i
= 0, curField
= fields
; i
< numFields
; i
++, curField
++) {
697 switch (curField
->fmt
) {
701 sprintf(string
, "%d", *((int *) curField
->location
));
702 if (Tcl_SetVar(interp
, argv
[i
+3], string
, 0) == NULL
) {
704 Tcl_AppendResult(interp
,
705 "couldn't set variable \"", argv
[i
+3], "\"",
707 ckfree((char *) results
);
713 sprintf(string
, "%d", *((char *) curField
->location
) & 0xff);
714 if (Tcl_SetVar(interp
, argv
[i
+3], string
, 0) == NULL
) {
720 if (Tcl_SetVar(interp
, argv
[i
+3], curField
->location
, 0)
727 sprintf(string
, "%g", *((double *) curField
->location
));
728 if (Tcl_SetVar(interp
, argv
[i
+3], string
, 0) == NULL
) {
734 sprintf(string
, "%g", *((float *) curField
->location
));
735 if (Tcl_SetVar(interp
, argv
[i
+3], string
, 0) == NULL
) {
742 sprintf(interp
->result
, "%d", numScanned
);
747 *----------------------------------------------------------------------
751 * This procedure is invoked to process the "split" Tcl command.
752 * See the user documentation for details on what it does.
755 * A standard Tcl result.
758 * See the user documentation.
760 *----------------------------------------------------------------------
765 Tcl_SplitCmd(dummy
, interp
, argc
, argv
)
766 ClientData dummy
; /* Not used. */
767 Tcl_Interp
*interp
; /* Current interpreter. */
768 int argc
; /* Number of arguments. */
769 char **argv
; /* Argument strings. */
772 register char *p
, *p2
;
776 splitChars
= " \n\t\r";
777 } else if (argc
== 3) {
778 splitChars
= argv
[2];
780 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
781 " string ?splitChars?\"", (char *) NULL
);
786 * Handle the special case of splitting on every character.
789 if (*splitChars
== 0) {
792 for (p
= argv
[1]; *p
!= 0; p
++) {
794 Tcl_AppendElement(interp
, string
, 0);
800 * Normal case: split on any of a given set of characters.
801 * Discard instances of the split characters.
804 for (p
= elementStart
= argv
[1]; *p
!= 0; p
++) {
806 for (p2
= splitChars
; *p2
!= 0; p2
++) {
809 Tcl_AppendElement(interp
, elementStart
, 0);
817 Tcl_AppendElement(interp
, elementStart
, 0);
823 *----------------------------------------------------------------------
827 * This procedure is invoked to process the "string" Tcl command.
828 * See the user documentation for details on what it does.
831 * A standard Tcl result.
834 * See the user documentation.
836 *----------------------------------------------------------------------
841 Tcl_StringCmd(dummy
, interp
, argc
, argv
)
842 ClientData dummy
; /* Not used. */
843 Tcl_Interp
*interp
; /* Current interpreter. */
844 int argc
; /* Number of arguments. */
845 char **argv
; /* Argument strings. */
851 int left
= 0, right
= 0;
854 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
855 " option arg ?arg ...?\"", (char *) NULL
);
859 length
= strlen(argv
[1]);
860 if ((c
== 'c') && (strncmp(argv
[1], "compare", length
) == 0)) {
862 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
863 " compare string1 string2\"", (char *) NULL
);
866 match
= strcmp(argv
[2], argv
[3]);
868 interp
->result
= "1";
869 } else if (match
< 0) {
870 interp
->result
= "-1";
872 interp
->result
= "0";
875 } else if ((c
== 'f') && (strncmp(argv
[1], "first", length
) == 0)) {
877 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
878 " first string1 string2\"", (char *) NULL
);
886 length
= strlen(argv
[2]);
887 for (p
= argv
[3]; *p
!= 0; p
++) {
891 if (strncmp(argv
[2], p
, length
) == 0) {
898 sprintf(interp
->result
, "%d", match
);
900 } else if ((c
== 'i') && (strncmp(argv
[1], "index", length
) == 0)) {
904 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
905 " index string charIndex\"", (char *) NULL
);
908 if (Tcl_GetInt(interp
, argv
[3], &index
) != TCL_OK
) {
911 if ((index
>= 0) && (index
< strlen(argv
[2]))) {
912 interp
->result
[0] = argv
[2][index
];
913 interp
->result
[1] = 0;
916 } else if ((c
== 'l') && (strncmp(argv
[1], "last", length
) == 0)
919 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
920 " last string1 string2\"", (char *) NULL
);
925 } else if ((c
== 'l') && (strncmp(argv
[1], "length", length
) == 0)
928 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
929 " length string\"", (char *) NULL
);
932 sprintf(interp
->result
, "%d", strlen(argv
[2]));
934 } else if ((c
== 'm') && (strncmp(argv
[1], "match", length
) == 0)) {
936 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
937 " match pattern string\"", (char *) NULL
);
940 if (Tcl_StringMatch(argv
[3], argv
[2]) != 0) {
941 interp
->result
= "1";
943 interp
->result
= "0";
946 } else if ((c
== 'r') && (strncmp(argv
[1], "range", length
) == 0)) {
947 int first
, last
, stringLength
;
950 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
951 " range string first last\"", (char *) NULL
);
954 stringLength
= strlen(argv
[2]);
955 if (Tcl_GetInt(interp
, argv
[3], &first
) != TCL_OK
) {
958 if ((*argv
[4] == 'e')
959 && (strncmp(argv
[4], "end", strlen(argv
[4])) == 0)) {
960 last
= stringLength
-1;
962 if (Tcl_GetInt(interp
, argv
[4], &last
) != TCL_OK
) {
963 Tcl_ResetResult(interp
);
964 Tcl_AppendResult(interp
,
965 "expected integer or \"end\" but got \"",
966 argv
[4], "\"", (char *) NULL
);
973 if (last
>= stringLength
) {
974 last
= stringLength
-1;
979 p
= argv
[2] + last
+ 1;
982 Tcl_SetResult(interp
, argv
[2] + first
, TCL_VOLATILE
);
986 } else if ((c
== 't') && (strncmp(argv
[1], "tolower", length
) == 0)
991 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
992 " tolower string\"", (char *) NULL
);
995 Tcl_SetResult(interp
, argv
[2], TCL_VOLATILE
);
996 for (p
= interp
->result
; *p
!= 0; p
++) {
1002 } else if ((c
== 't') && (strncmp(argv
[1], "toupper", length
) == 0)
1007 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
1008 " toupper string\"", (char *) NULL
);
1011 Tcl_SetResult(interp
, argv
[2], TCL_VOLATILE
);
1012 for (p
= interp
->result
; *p
!= 0; p
++) {
1018 } else if ((c
== 't') && (strncmp(argv
[1], "trim", length
) == 0)
1021 register char *p
, *checkPtr
;
1027 trimChars
= argv
[3];
1028 } else if (argc
== 3) {
1029 trimChars
= " \t\n\r";
1031 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
1032 " ", argv
[1], " string ?chars?\"", (char *) NULL
);
1037 for (c
= *p
; c
!= 0; p
++, c
= *p
) {
1038 for (checkPtr
= trimChars
; *checkPtr
!= c
; checkPtr
++) {
1039 if (*checkPtr
== 0) {
1046 Tcl_SetResult(interp
, p
, TCL_VOLATILE
);
1050 p
= interp
->result
+ strlen(interp
->result
) - 1;
1051 donePtr
= &interp
->result
[-1];
1052 for (c
= *p
; p
!= donePtr
; p
--, c
= *p
) {
1053 for (checkPtr
= trimChars
; *checkPtr
!= c
; checkPtr
++) {
1054 if (*checkPtr
== 0) {
1063 } else if ((c
== 't') && (strncmp(argv
[1], "trimleft", length
) == 0)
1066 argv
[1] = "trimleft";
1068 } else if ((c
== 't') && (strncmp(argv
[1], "trimright", length
) == 0)
1071 argv
[1] = "trimright";
1074 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
1075 "\": should be compare, first, index, last, length, match, ",
1076 "range, tolower, toupper, trim, trimleft, or trimright",
1083 *----------------------------------------------------------------------
1087 * This procedure is invoked to process the "trace" Tcl command.
1088 * See the user documentation for details on what it does.
1091 * A standard Tcl result.
1094 * See the user documentation.
1096 *----------------------------------------------------------------------
1101 Tcl_TraceCmd(dummy
, interp
, argc
, argv
)
1102 ClientData dummy
; /* Not used. */
1103 Tcl_Interp
*interp
; /* Current interpreter. */
1104 int argc
; /* Number of arguments. */
1105 char **argv
; /* Argument strings. */
1111 Tcl_AppendResult(interp
, "too few args: should be \"",
1112 argv
[0], " option [arg arg ...]\"", (char *) NULL
);
1116 length
= strlen(argv
[1]);
1117 if ((c
== 'a') && (strncmp(argv
[1], "variable", length
) == 0)
1121 TraceVarInfo
*tvarPtr
;
1124 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1125 argv
[0], " variable name ops command\"", (char *) NULL
);
1130 for (p
= argv
[3] ; *p
!= 0; p
++) {
1132 flags
|= TCL_TRACE_READS
;
1133 } else if (*p
== 'w') {
1134 flags
|= TCL_TRACE_WRITES
;
1135 } else if (*p
== 'u') {
1136 flags
|= TCL_TRACE_UNSETS
;
1145 length
= strlen(argv
[4]);
1146 tvarPtr
= (TraceVarInfo
*) ckalloc((unsigned)
1147 (sizeof(TraceVarInfo
) - sizeof(tvarPtr
->command
) + length
+ 1));
1148 tvarPtr
->flags
= flags
;
1149 tvarPtr
->length
= length
;
1150 flags
|= TCL_TRACE_UNSETS
;
1151 strcpy(tvarPtr
->command
, argv
[4]);
1152 if (Tcl_TraceVar(interp
, argv
[2], flags
, TraceVarProc
,
1153 (ClientData
) tvarPtr
) != TCL_OK
) {
1154 ckfree((char *) tvarPtr
);
1157 } else if ((c
== 'd') && (strncmp(argv
[1], "vdelete", length
)
1158 && (length
>= 2)) == 0) {
1161 TraceVarInfo
*tvarPtr
;
1162 ClientData clientData
;
1165 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1166 argv
[0], " vdelete name ops command\"", (char *) NULL
);
1171 for (p
= argv
[3] ; *p
!= 0; p
++) {
1173 flags
|= TCL_TRACE_READS
;
1174 } else if (*p
== 'w') {
1175 flags
|= TCL_TRACE_WRITES
;
1176 } else if (*p
== 'u') {
1177 flags
|= TCL_TRACE_UNSETS
;
1187 * Search through all of our traces on this variable to
1188 * see if there's one with the given command. If so, then
1189 * delete the first one that matches.
1192 length
= strlen(argv
[4]);
1194 while ((clientData
= Tcl_VarTraceInfo(interp
, argv
[2], 0,
1195 TraceVarProc
, clientData
)) != 0) {
1196 tvarPtr
= (TraceVarInfo
*) clientData
;
1197 if ((tvarPtr
->length
== length
) && (tvarPtr
->flags
== flags
)
1198 && (strncmp(argv
[4], tvarPtr
->command
, length
) == 0)) {
1199 Tcl_UntraceVar(interp
, argv
[2], flags
| TCL_TRACE_UNSETS
,
1200 TraceVarProc
, clientData
);
1201 ckfree((char *) tvarPtr
);
1205 } else if ((c
== 'i') && (strncmp(argv
[1], "vinfo", length
) == 0)
1207 ClientData clientData
;
1212 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1213 argv
[0], " vinfo name\"", (char *) NULL
);
1217 while ((clientData
= Tcl_VarTraceInfo(interp
, argv
[2], 0,
1218 TraceVarProc
, clientData
)) != 0) {
1219 TraceVarInfo
*tvarPtr
= (TraceVarInfo
*) clientData
;
1221 if (tvarPtr
->flags
& TCL_TRACE_READS
) {
1225 if (tvarPtr
->flags
& TCL_TRACE_WRITES
) {
1229 if (tvarPtr
->flags
& TCL_TRACE_UNSETS
) {
1234 Tcl_AppendResult(interp
, prefix
, (char *) NULL
);
1235 Tcl_AppendElement(interp
, ops
, 1);
1236 Tcl_AppendElement(interp
, tvarPtr
->command
, 0);
1237 Tcl_AppendResult(interp
, "}", (char *) NULL
);
1241 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
1242 "\": should be variable, vdelete, or vinfo",
1249 Tcl_AppendResult(interp
, "bad operations \"", argv
[3],
1250 "\": should be one or more of rwu", (char *) NULL
);
1255 *----------------------------------------------------------------------
1259 * This procedure is called to handle variable accesses that have
1260 * been traced using the "trace" command.
1263 * Normally returns NULL. If the trace command returns an error,
1264 * then this procedure returns an error string.
1267 * Depends on the command associated with the trace.
1269 *----------------------------------------------------------------------
1274 TraceVarProc(clientData
, interp
, name1
, name2
, flags
)
1275 ClientData clientData
; /* Information about the variable trace. */
1276 Tcl_Interp
*interp
; /* Interpreter containing variable. */
1277 char *name1
; /* Name of variable or array. */
1278 char *name2
; /* Name of element within array; NULL means
1279 * scalar variable is being referenced. */
1280 int flags
; /* OR-ed bits giving operation and other
1283 TraceVarInfo
*tvarPtr
= (TraceVarInfo
*) clientData
;
1285 int code
, cmdLength
, flags1
, flags2
;
1287 #define STATIC_SIZE 199
1288 char staticSpace
[STATIC_SIZE
+1];
1292 if ((tvarPtr
->flags
& flags
) && !(flags
& TCL_INTERP_DESTROYED
)) {
1295 * Generate a command to execute by appending list elements
1296 * for the two variable names and the operation. The five
1297 * extra characters are for three space, the opcode character,
1298 * and the terminating null.
1301 if (name2
== NULL
) {
1304 cmdLength
= tvarPtr
->length
+ Tcl_ScanElement(name1
, &flags1
) +
1305 Tcl_ScanElement(name2
, &flags2
) + 5;
1306 if (cmdLength
< STATIC_SIZE
) {
1307 cmdPtr
= staticSpace
;
1309 cmdPtr
= (char *) ckalloc((unsigned) cmdLength
);
1312 strcpy(p
, tvarPtr
->command
);
1313 p
+= tvarPtr
->length
;
1316 p
+= Tcl_ConvertElement(name1
, p
, flags1
);
1319 p
+= Tcl_ConvertElement(name2
, p
, flags2
);
1321 if (flags
& TCL_TRACE_READS
) {
1323 } else if (flags
& TCL_TRACE_WRITES
) {
1325 } else if (flags
& TCL_TRACE_UNSETS
) {
1331 * Execute the command. Be careful to save and restore the
1332 * result from the interpreter used for the command.
1335 dummy
.freeProc
= interp
->freeProc
;
1336 if (interp
->freeProc
== 0) {
1337 Tcl_SetResult((Tcl_Interp
*) &dummy
, interp
->result
, TCL_VOLATILE
);
1339 dummy
.result
= interp
->result
;
1341 code
= Tcl_Eval(interp
, cmdPtr
, 0, (char **) NULL
);
1342 if (cmdPtr
!= staticSpace
) {
1345 if (code
!= TCL_OK
) {
1346 result
= "access disallowed by trace command";
1347 Tcl_ResetResult(interp
); /* Must clear error state. */
1349 Tcl_FreeResult(interp
);
1350 interp
->result
= dummy
.result
;
1351 interp
->freeProc
= dummy
.freeProc
;
1353 if (flags
& TCL_TRACE_DESTROYED
) {
1354 ckfree((char *) tvarPtr
);
1360 *----------------------------------------------------------------------
1364 * This procedure is invoked to process the "while" Tcl command.
1365 * See the user documentation for details on what it does.
1368 * A standard Tcl result.
1371 * See the user documentation.
1373 *----------------------------------------------------------------------
1378 Tcl_WhileCmd(dummy
, interp
, argc
, argv
)
1379 ClientData dummy
; /* Not used. */
1380 Tcl_Interp
*interp
; /* Current interpreter. */
1381 int argc
; /* Number of arguments. */
1382 char **argv
; /* Argument strings. */
1387 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1388 argv
[0], " test command\"", (char *) NULL
);
1393 result
= Tcl_ExprBoolean(interp
, argv
[1], &value
);
1394 if (result
!= TCL_OK
) {
1400 result
= Tcl_Eval(interp
, argv
[2], 0, (char **) NULL
);
1401 if (result
== TCL_CONTINUE
) {
1403 } else if (result
!= TCL_OK
) {
1404 if (result
== TCL_ERROR
) {
1406 sprintf(msg
, "\n (\"while\" body line %d)",
1408 Tcl_AddErrorInfo(interp
, msg
);
1413 if (result
== TCL_BREAK
) {
1416 if (result
== TCL_OK
) {
1417 Tcl_ResetResult(interp
);