]>
cvs.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 *----------------------------------------------------------------------
68 ClientData dummy
, /* Not used. */
69 Tcl_Interp
*interp
, /* Current interpreter. */
70 int argc
, /* Number of arguments. */
71 char **argv
/* Argument strings. */
77 char **argPtr
, *string
;
82 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
83 " ?-nocase? exp string ?matchVar? ?subMatchVar ",
84 "subMatchVar ...?\"", (char *) NULL
);
89 while ((argc
> 0) && (argPtr
[0][0] == '-')) {
90 if (strcmp(argPtr
[0], "-indices") == 0) {
94 } else if (strcmp(argPtr
[0], "-nocase") == 0) {
105 regexpPtr
= TclCompileRegexp(interp
, argPtr
[0]);
106 if (regexpPtr
== NULL
) {
111 * Convert the string to lower case, if desired, and perform
116 register char *dst
, *src
;
118 string
= (char *) ckalloc((unsigned) (strlen(argPtr
[1]) + 1));
119 for (src
= argPtr
[1], dst
= string
; *src
!= 0; src
++, dst
++) {
121 *dst
= tolower(*src
);
130 tclRegexpError
= NULL
;
131 match
= regexec(regexpPtr
, string
);
132 if (string
!= argPtr
[1]) {
135 if (tclRegexpError
!= NULL
) {
136 Tcl_AppendResult(interp
, "error while matching pattern: ",
137 tclRegexpError
, (char *) NULL
);
141 interp
->result
= "0";
146 * If additional variable names have been specified, return
147 * index information in those variables.
151 if (argc
> NSUBEXP
) {
152 interp
->result
= "too many substring variables";
155 for (i
= 0; i
< argc
; i
++) {
156 char *result
, info
[50];
158 if (regexpPtr
->startp
[i
] == NULL
) {
160 result
= Tcl_SetVar(interp
, argPtr
[i
+2], "-1 -1", 0);
162 result
= Tcl_SetVar(interp
, argPtr
[i
+2], "", 0);
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);
170 char savedChar
, *first
, *last
;
172 first
= argPtr
[1] + (regexpPtr
->startp
[i
] - string
);
173 last
= argPtr
[1] + (regexpPtr
->endp
[i
] - string
);
176 result
= Tcl_SetVar(interp
, argPtr
[i
+2], first
, 0);
180 if (result
== NULL
) {
181 Tcl_AppendResult(interp
, "couldn't set variable \"",
182 argPtr
[i
+2], "\"", (char *) NULL
);
186 interp
->result
= "1";
191 *----------------------------------------------------------------------
195 * This procedure is invoked to process the "regsub" Tcl command.
196 * See the user documentation for details on what it does.
199 * A standard Tcl result.
202 * See the user documentation.
204 *----------------------------------------------------------------------
210 ClientData dummy
, /* Not used. */
211 Tcl_Interp
*interp
, /* Current interpreter. */
212 int argc
, /* Number of arguments. */
213 char **argv
/* Argument strings. */
216 int noCase
= 0, all
= 0;
218 char *string
, *p
, *firstChar
, *newValue
, **argPtr
;
219 int match
, result
, flags
;
220 register char *src
, c
;
224 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
225 " ?-nocase? ?-all? exp string subSpec varName\"", (char *) NULL
);
230 while (argPtr
[0][0] == '-') {
231 if (strcmp(argPtr
[0], "-nocase") == 0) {
235 } else if (strcmp(argPtr
[0], "-all") == 0) {
246 regexpPtr
= TclCompileRegexp(interp
, argPtr
[0]);
247 if (regexpPtr
== NULL
) {
252 * Convert the string to lower case, if desired.
258 string
= (char *) ckalloc((unsigned) (strlen(argPtr
[1]) + 1));
259 for (src
= argPtr
[1], dst
= string
; *src
!= 0; src
++, dst
++) {
261 *dst
= tolower(*src
);
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.
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
);
293 * Copy the portion of the source string before the match to the
297 src
= argPtr
[1] + (regexpPtr
->startp
[0] - string
);
300 newValue
= Tcl_SetVar(interp
, argPtr
[3], argPtr
[1] + (p
- string
),
303 flags
= TCL_APPEND_VALUE
;
304 if (newValue
== NULL
) {
306 Tcl_AppendResult(interp
, "couldn't set variable \"",
307 argPtr
[3], "\"", (char *) NULL
);
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.
319 for (src
= firstChar
= argPtr
[2], c
= *src
; c
!= 0; src
++, c
= *src
) {
324 } else if (c
== '\\') {
326 if ((c
>= '0') && (c
<= '9')) {
328 } else if ((c
== '\\') || (c
== '&')) {
331 newValue
= Tcl_SetVar(interp
, argPtr
[3], firstChar
,
335 if (newValue
== NULL
) {
347 if (firstChar
!= src
) {
350 newValue
= Tcl_SetVar(interp
, argPtr
[3], firstChar
,
353 if (newValue
== NULL
) {
357 if ((index
< NSUBEXP
) && (regexpPtr
->startp
[index
] != NULL
)
358 && (regexpPtr
->endp
[index
] != NULL
)) {
359 char *first
, *last
, saved
;
361 first
= argPtr
[1] + (regexpPtr
->startp
[index
] - string
);
362 last
= argPtr
[1] + (regexpPtr
->endp
[index
] - string
);
365 newValue
= Tcl_SetVar(interp
, argPtr
[3], first
,
368 if (newValue
== NULL
) {
377 if (firstChar
!= src
) {
378 if (Tcl_SetVar(interp
, argPtr
[3], firstChar
,
379 TCL_APPEND_VALUE
) == NULL
) {
383 p
= regexpPtr
->endp
[0];
390 * If there were no matches at all, then return a "0" result.
394 interp
->result
= "0";
400 * Copy the portion of the source string after the last match to the
405 if (Tcl_SetVar(interp
, argPtr
[3], p
, TCL_APPEND_VALUE
) == NULL
) {
409 interp
->result
= "1";
413 if (string
!= argPtr
[1]) {
420 *----------------------------------------------------------------------
424 * This procedure is invoked to process the "rename" Tcl command.
425 * See the user documentation for details on what it does.
428 * A standard Tcl result.
431 * See the user documentation.
433 *----------------------------------------------------------------------
439 ClientData dummy
, /* Not used. */
440 Tcl_Interp
*interp
, /* Current interpreter. */
441 int argc
, /* Number of arguments. */
442 char **argv
/* Argument strings. */
445 register Command
*cmdPtr
;
446 Interp
*iPtr
= (Interp
*) interp
;
451 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
452 " oldName newName\"", (char *) NULL
);
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
);
463 hPtr
= Tcl_FindHashEntry(&iPtr
->commandTable
, argv
[2]);
465 Tcl_AppendResult(interp
, "can't rename to \"", argv
[2],
466 "\": command already exists", (char *) NULL
);
469 hPtr
= Tcl_FindHashEntry(&iPtr
->commandTable
, argv
[1]);
471 Tcl_AppendResult(interp
, "can't rename \"", argv
[1],
472 "\": command doesn't exist", (char *) NULL
);
475 cmdPtr
= (Command
*) Tcl_GetHashValue(hPtr
);
476 Tcl_DeleteHashEntry(hPtr
);
477 hPtr
= Tcl_CreateHashEntry(&iPtr
->commandTable
, argv
[2], &new);
478 Tcl_SetHashValue(hPtr
, cmdPtr
);
483 *----------------------------------------------------------------------
487 * This procedure is invoked to process the "return" Tcl command.
488 * See the user documentation for details on what it does.
491 * A standard Tcl result.
494 * See the user documentation.
496 *----------------------------------------------------------------------
502 ClientData dummy
, /* Not used. */
503 Tcl_Interp
*interp
, /* Current interpreter. */
504 int argc
, /* Number of arguments. */
505 char **argv
/* Argument strings. */
509 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
510 " ?value?\"", (char *) NULL
);
514 Tcl_SetResult(interp
, argv
[1], TCL_VOLATILE
);
520 *----------------------------------------------------------------------
524 * This procedure is invoked to process the "scan" Tcl command.
525 * See the user documentation for details on what it does.
528 * A standard Tcl result.
531 * See the user documentation.
533 *----------------------------------------------------------------------
539 ClientData dummy
, /* Not used. */
540 Tcl_Interp
*interp
, /* Current interpreter. */
541 int argc
, /* Number of arguments. */
542 char **argv
/* Argument strings. */
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
550 char fmt
; /* Format for field. */
551 int size
; /* How many bytes to allow for
553 char *location
; /* Where field will be stored. */
555 Field fields
[MAX_FIELDS
]; /* Info about all the fields in the
557 register Field
*curField
;
558 int numFields
= 0; /* Number of fields actually
560 int suppress
; /* Current field is assignment-
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. */
567 int i
, widthSpecified
;
570 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
571 " string format ?varName varName ...?\"", (char *) NULL
);
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.
584 arg1Length
= (strlen(argv
[1]) + 4) & ~03;
585 for (fmt
= argv
[2]; *fmt
!= 0; fmt
++) {
597 while (isdigit(*fmt
)) {
604 if (numFields
== MAX_FIELDS
) {
605 interp
->result
= "too many fields to scan";
608 curField
= &fields
[numFields
];
618 curField
->size
= sizeof(int);
623 curField
->size
= arg1Length
;
627 if (widthSpecified
) {
629 "field width may not be specified in %c conversion";
633 curField
->size
= sizeof(int);
639 curField
->size
= sizeof(double);
645 curField
->size
= sizeof(float);
650 curField
->size
= arg1Length
;
653 } while (*fmt
!= ']');
657 sprintf(interp
->result
, "bad scan conversion character \"%c\"",
661 totalSize
+= curField
->size
;
664 if (numFields
!= (argc
-3)) {
666 "different numbers of variable names and field specifiers";
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
;
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
);
698 if (numScanned
< numFields
) {
699 numFields
= numScanned
;
701 for (i
= 0, curField
= fields
; i
< numFields
; i
++, curField
++) {
702 switch (curField
->fmt
) {
706 sprintf(string
, "%d", *((int *) curField
->location
));
707 if (Tcl_SetVar(interp
, argv
[i
+3], string
, 0) == NULL
) {
709 Tcl_AppendResult(interp
,
710 "couldn't set variable \"", argv
[i
+3], "\"",
712 ckfree((char *) results
);
718 sprintf(string
, "%d", *((char *) curField
->location
) & 0xff);
719 if (Tcl_SetVar(interp
, argv
[i
+3], string
, 0) == NULL
) {
725 if (Tcl_SetVar(interp
, argv
[i
+3], curField
->location
, 0)
732 sprintf(string
, "%g", *((double *) curField
->location
));
733 if (Tcl_SetVar(interp
, argv
[i
+3], string
, 0) == NULL
) {
739 sprintf(string
, "%g", *((float *) curField
->location
));
740 if (Tcl_SetVar(interp
, argv
[i
+3], string
, 0) == NULL
) {
747 sprintf(interp
->result
, "%d", numScanned
);
752 *----------------------------------------------------------------------
756 * This procedure is invoked to process the "split" Tcl command.
757 * See the user documentation for details on what it does.
760 * A standard Tcl result.
763 * See the user documentation.
765 *----------------------------------------------------------------------
771 ClientData dummy
, /* Not used. */
772 Tcl_Interp
*interp
, /* Current interpreter. */
773 int argc
, /* Number of arguments. */
774 char **argv
/* Argument strings. */
778 register char *p
, *p2
;
782 splitChars
= " \n\t\r";
783 } else if (argc
== 3) {
784 splitChars
= argv
[2];
786 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
787 " string ?splitChars?\"", (char *) NULL
);
792 * Handle the special case of splitting on every character.
795 if (*splitChars
== 0) {
798 for (p
= argv
[1]; *p
!= 0; p
++) {
800 Tcl_AppendElement(interp
, string
, 0);
806 * Normal case: split on any of a given set of characters.
807 * Discard instances of the split characters.
810 for (p
= elementStart
= argv
[1]; *p
!= 0; p
++) {
812 for (p2
= splitChars
; *p2
!= 0; p2
++) {
815 Tcl_AppendElement(interp
, elementStart
, 0);
823 Tcl_AppendElement(interp
, elementStart
, 0);
829 *----------------------------------------------------------------------
833 * This procedure is invoked to process the "string" Tcl command.
834 * See the user documentation for details on what it does.
837 * A standard Tcl result.
840 * See the user documentation.
842 *----------------------------------------------------------------------
848 ClientData dummy
, /* Not used. */
849 Tcl_Interp
*interp
, /* Current interpreter. */
850 int argc
, /* Number of arguments. */
851 char **argv
/* Argument strings. */
858 int left
= 0, right
= 0;
861 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
862 " option arg ?arg ...?\"", (char *) NULL
);
866 length
= strlen(argv
[1]);
867 if ((c
== 'c') && (strncmp(argv
[1], "compare", length
) == 0)) {
869 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
870 " compare string1 string2\"", (char *) NULL
);
873 match
= strcmp(argv
[2], argv
[3]);
875 interp
->result
= "1";
876 } else if (match
< 0) {
877 interp
->result
= "-1";
879 interp
->result
= "0";
882 } else if ((c
== 'f') && (strncmp(argv
[1], "first", length
) == 0)) {
884 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
885 " first string1 string2\"", (char *) NULL
);
893 length
= strlen(argv
[2]);
894 for (p
= argv
[3]; *p
!= 0; p
++) {
898 if (strncmp(argv
[2], p
, length
) == 0) {
905 sprintf(interp
->result
, "%d", match
);
907 } else if ((c
== 'i') && (strncmp(argv
[1], "index", length
) == 0)) {
911 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
912 " index string charIndex\"", (char *) NULL
);
915 if (Tcl_GetInt(interp
, argv
[3], &index
) != TCL_OK
) {
918 if ((index
>= 0) && (index
< strlen(argv
[2]))) {
919 interp
->result
[0] = argv
[2][index
];
920 interp
->result
[1] = 0;
923 } else if ((c
== 'l') && (strncmp(argv
[1], "last", length
) == 0)
926 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
927 " last string1 string2\"", (char *) NULL
);
932 } else if ((c
== 'l') && (strncmp(argv
[1], "length", length
) == 0)
935 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
936 " length string\"", (char *) NULL
);
939 sprintf(interp
->result
, "%d", strlen(argv
[2]));
941 } else if ((c
== 'm') && (strncmp(argv
[1], "match", length
) == 0)) {
943 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
944 " match pattern string\"", (char *) NULL
);
947 if (Tcl_StringMatch(argv
[3], argv
[2]) != 0) {
948 interp
->result
= "1";
950 interp
->result
= "0";
953 } else if ((c
== 'r') && (strncmp(argv
[1], "range", length
) == 0)) {
954 int first
, last
, stringLength
;
957 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
958 " range string first last\"", (char *) NULL
);
961 stringLength
= strlen(argv
[2]);
962 if (Tcl_GetInt(interp
, argv
[3], &first
) != TCL_OK
) {
965 if ((*argv
[4] == 'e')
966 && (strncmp(argv
[4], "end", strlen(argv
[4])) == 0)) {
967 last
= stringLength
-1;
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
);
980 if (last
>= stringLength
) {
981 last
= stringLength
-1;
986 p
= argv
[2] + last
+ 1;
989 Tcl_SetResult(interp
, argv
[2] + first
, TCL_VOLATILE
);
993 } else if ((c
== 't') && (strncmp(argv
[1], "tolower", length
) == 0)
998 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
999 " tolower string\"", (char *) NULL
);
1002 Tcl_SetResult(interp
, argv
[2], TCL_VOLATILE
);
1003 for (p
= interp
->result
; *p
!= 0; p
++) {
1009 } else if ((c
== 't') && (strncmp(argv
[1], "toupper", length
) == 0)
1014 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
1015 " toupper string\"", (char *) NULL
);
1018 Tcl_SetResult(interp
, argv
[2], TCL_VOLATILE
);
1019 for (p
= interp
->result
; *p
!= 0; p
++) {
1025 } else if ((c
== 't') && (strncmp(argv
[1], "trim", length
) == 0)
1028 register char *p
, *checkPtr
;
1034 trimChars
= argv
[3];
1035 } else if (argc
== 3) {
1036 trimChars
= " \t\n\r";
1038 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
1039 " ", argv
[1], " string ?chars?\"", (char *) NULL
);
1044 for (c
= *p
; c
!= 0; p
++, c
= *p
) {
1045 for (checkPtr
= trimChars
; *checkPtr
!= c
; checkPtr
++) {
1046 if (*checkPtr
== 0) {
1053 Tcl_SetResult(interp
, p
, TCL_VOLATILE
);
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) {
1070 } else if ((c
== 't') && (strncmp(argv
[1], "trimleft", length
) == 0)
1073 argv
[1] = "trimleft";
1075 } else if ((c
== 't') && (strncmp(argv
[1], "trimright", length
) == 0)
1078 argv
[1] = "trimright";
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",
1090 *----------------------------------------------------------------------
1094 * This procedure is invoked to process the "trace" Tcl command.
1095 * See the user documentation for details on what it does.
1098 * A standard Tcl result.
1101 * See the user documentation.
1103 *----------------------------------------------------------------------
1109 ClientData dummy
, /* Not used. */
1110 Tcl_Interp
*interp
, /* Current interpreter. */
1111 int argc
, /* Number of arguments. */
1112 char **argv
/* Argument strings. */
1119 Tcl_AppendResult(interp
, "too few args: should be \"",
1120 argv
[0], " option [arg arg ...]\"", (char *) NULL
);
1124 length
= strlen(argv
[1]);
1125 if ((c
== 'a') && (strncmp(argv
[1], "variable", length
) == 0)
1129 TraceVarInfo
*tvarPtr
;
1132 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1133 argv
[0], " variable name ops command\"", (char *) NULL
);
1138 for (p
= argv
[3] ; *p
!= 0; p
++) {
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
;
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
);
1165 } else if ((c
== 'd') && (strncmp(argv
[1], "vdelete", length
)
1166 && (length
>= 2)) == 0) {
1169 TraceVarInfo
*tvarPtr
;
1170 ClientData clientData
;
1173 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1174 argv
[0], " vdelete name ops command\"", (char *) NULL
);
1179 for (p
= argv
[3] ; *p
!= 0; p
++) {
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
;
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.
1200 length
= strlen(argv
[4]);
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
);
1213 } else if ((c
== 'i') && (strncmp(argv
[1], "vinfo", length
) == 0)
1215 ClientData clientData
;
1220 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1221 argv
[0], " vinfo name\"", (char *) NULL
);
1225 while ((clientData
= Tcl_VarTraceInfo(interp
, argv
[2], 0,
1226 TraceVarProc
, clientData
)) != 0) {
1227 TraceVarInfo
*tvarPtr
= (TraceVarInfo
*) clientData
;
1229 if (tvarPtr
->flags
& TCL_TRACE_READS
) {
1233 if (tvarPtr
->flags
& TCL_TRACE_WRITES
) {
1237 if (tvarPtr
->flags
& TCL_TRACE_UNSETS
) {
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
);
1249 Tcl_AppendResult(interp
, "bad option \"", argv
[1],
1250 "\": should be variable, vdelete, or vinfo",
1257 Tcl_AppendResult(interp
, "bad operations \"", argv
[3],
1258 "\": should be one or more of rwu", (char *) NULL
);
1263 *----------------------------------------------------------------------
1267 * This procedure is called to handle variable accesses that have
1268 * been traced using the "trace" command.
1271 * Normally returns NULL. If the trace command returns an error,
1272 * then this procedure returns an error string.
1275 * Depends on the command associated with the trace.
1277 *----------------------------------------------------------------------
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
1292 TraceVarInfo
*tvarPtr
= (TraceVarInfo
*) clientData
;
1294 int code
, cmdLength
, flags1
, flags2
;
1296 #define STATIC_SIZE 199
1297 char staticSpace
[STATIC_SIZE
+1];
1301 if ((tvarPtr
->flags
& flags
) && !(flags
& TCL_INTERP_DESTROYED
)) {
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.
1310 if (name2
== NULL
) {
1313 cmdLength
= tvarPtr
->length
+ Tcl_ScanElement(name1
, &flags1
) +
1314 Tcl_ScanElement(name2
, &flags2
) + 5;
1315 if (cmdLength
< STATIC_SIZE
) {
1316 cmdPtr
= staticSpace
;
1318 cmdPtr
= (char *) ckalloc((unsigned) cmdLength
);
1321 strcpy(p
, tvarPtr
->command
);
1322 p
+= tvarPtr
->length
;
1325 p
+= Tcl_ConvertElement(name1
, p
, flags1
);
1328 p
+= Tcl_ConvertElement(name2
, p
, flags2
);
1330 if (flags
& TCL_TRACE_READS
) {
1332 } else if (flags
& TCL_TRACE_WRITES
) {
1334 } else if (flags
& TCL_TRACE_UNSETS
) {
1340 * Execute the command. Be careful to save and restore the
1341 * result from the interpreter used for the command.
1344 dummy
.freeProc
= interp
->freeProc
;
1345 if (interp
->freeProc
== 0) {
1346 Tcl_SetResult((Tcl_Interp
*) &dummy
, interp
->result
, TCL_VOLATILE
);
1348 dummy
.result
= interp
->result
;
1350 code
= Tcl_Eval(interp
, cmdPtr
, 0, (char **) NULL
);
1351 if (cmdPtr
!= staticSpace
) {
1354 if (code
!= TCL_OK
) {
1355 result
= "access disallowed by trace command";
1356 Tcl_ResetResult(interp
); /* Must clear error state. */
1358 Tcl_FreeResult(interp
);
1359 interp
->result
= dummy
.result
;
1360 interp
->freeProc
= dummy
.freeProc
;
1362 if (flags
& TCL_TRACE_DESTROYED
) {
1363 ckfree((char *) tvarPtr
);
1369 *----------------------------------------------------------------------
1373 * This procedure is invoked to process the "while" Tcl command.
1374 * See the user documentation for details on what it does.
1377 * A standard Tcl result.
1380 * See the user documentation.
1382 *----------------------------------------------------------------------
1388 ClientData dummy
, /* Not used. */
1389 Tcl_Interp
*interp
, /* Current interpreter. */
1390 int argc
, /* Number of arguments. */
1391 char **argv
/* Argument strings. */
1397 Tcl_AppendResult(interp
, "wrong # args: should be \"",
1398 argv
[0], " test command\"", (char *) NULL
);
1403 result
= Tcl_ExprBoolean(interp
, argv
[1], &value
);
1404 if (result
!= TCL_OK
) {
1410 result
= Tcl_Eval(interp
, argv
[2], 0, (char **) NULL
);
1411 if (result
== TCL_CONTINUE
) {
1413 } else if (result
!= TCL_OK
) {
1414 if (result
== TCL_ERROR
) {
1416 sprintf(msg
, "\n (\"while\" body line %d)",
1418 Tcl_AddErrorInfo(interp
, msg
);
1423 if (result
== TCL_BREAK
) {
1426 if (result
== TCL_OK
) {
1427 Tcl_ResetResult(interp
);