]> cvs.zerfleddert.de Git - micropolis/blob - src/tcl/tclvar.c
Fixes for compilation with gcc 15
[micropolis] / src / tcl / tclvar.c
1 /*
2 * tclVar.c --
3 *
4 * This file contains routines that implement Tcl variables
5 * (both scalars and arrays).
6 *
7 * The implementation of arrays is modelled after an initial
8 * implementation by Karl Lehenbauer, Mark Diekhans and
9 * Peter da Silva.
10 *
11 * Copyright 1987-1991 Regents of the University of California
12 * Permission to use, copy, modify, and distribute this
13 * software and its documentation for any purpose and without
14 * fee is hereby granted, provided that the above copyright
15 * notice appear in all copies. The University of California
16 * makes no representations about the suitability of this
17 * software for any purpose. It is provided "as is" without
18 * express or implied warranty.
19 */
20
21 #ifndef lint
22 static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclVar.c,v 1.27 92/05/07 09:24:59 ouster Exp $ SPRITE (Berkeley)";
23 #endif
24
25 #include "tclint.h"
26
27 /*
28 * The strings below are used to indicate what went wrong when a
29 * variable access is denied.
30 */
31
32 static char *noSuchVar = "no such variable";
33 static char *isArray = "variable is array";
34 static char *needArray = "variable isn't array";
35 static char *noSuchElement = "no such element in array";
36 static char *traceActive = "trace is active on variable";
37
38 /*
39 * Forward references to procedures defined later in this file:
40 */
41
42 static char * CallTraces _ANSI_ARGS_((Interp *iPtr, Var *arrayPtr,
43 Tcl_HashEntry *hPtr, char *name1, char *name2,
44 int flags));
45 static void DeleteSearches _ANSI_ARGS_((Var *arrayVarPtr));
46 static void DeleteArray _ANSI_ARGS_((Interp *iPtr, char *arrayName,
47 Var *varPtr, int flags));
48 static Var * NewVar _ANSI_ARGS_((int space));
49 static ArraySearch * ParseSearchId _ANSI_ARGS_((Tcl_Interp *interp,
50 Var *varPtr, char *varName, char *string));
51 static void VarErrMsg _ANSI_ARGS_((Tcl_Interp *interp,
52 char *name1, char *name2, char *operation,
53 char *reason));
54 \f
55 /*
56 *----------------------------------------------------------------------
57 *
58 * Tcl_GetVar --
59 *
60 * Return the value of a Tcl variable.
61 *
62 * Results:
63 * The return value points to the current value of varName. If
64 * the variable is not defined or can't be read because of a clash
65 * in array usage then a NULL pointer is returned and an error
66 * message is left in interp->result if the TCL_LEAVE_ERR_MSG
67 * flag is set. Note: the return value is only valid up until
68 * the next call to Tcl_SetVar or Tcl_SetVar2; if you depend on
69 * the value lasting longer than that, then make yourself a private
70 * copy.
71 *
72 * Side effects:
73 * None.
74 *
75 *----------------------------------------------------------------------
76 */
77
78 char *
79 Tcl_GetVar (
80 Tcl_Interp *interp, /* Command interpreter in which varName is
81 * to be looked up. */
82 char *varName, /* Name of a variable in interp. */
83 int flags /* OR-ed combination of TCL_GLOBAL_ONLY
84 * or TCL_LEAVE_ERR_MSG bits. */
85 )
86 {
87 register char *p;
88
89 /*
90 * If varName refers to an array (it ends with a parenthesized
91 * element name), then handle it specially.
92 */
93
94 for (p = varName; *p != '\0'; p++) {
95 if (*p == '(') {
96 char *result;
97 char *open = p;
98
99 do {
100 p++;
101 } while (*p != '\0');
102 p--;
103 if (*p != ')') {
104 goto scalar;
105 }
106 *open = '\0';
107 *p = '\0';
108 result = Tcl_GetVar2(interp, varName, open + 1, flags);
109 *open = '(';
110 *p = ')';
111 #ifdef sgi
112 strcmp("a", "b"); /* XXX SGI compiler optimizer bug */
113 #endif
114 return result;
115 }
116 }
117
118 scalar:
119 return Tcl_GetVar2(interp, varName, (char *) NULL, flags);
120 }
121 \f
122 /*
123 *----------------------------------------------------------------------
124 *
125 * Tcl_GetVar2 --
126 *
127 * Return the value of a Tcl variable, given a two-part name
128 * consisting of array name and element within array.
129 *
130 * Results:
131 * The return value points to the current value of the variable
132 * given by name1 and name2. If the specified variable doesn't
133 * exist, or if there is a clash in array usage, then NULL is
134 * returned and a message will be left in interp->result if the
135 * TCL_LEAVE_ERR_MSG flag is set. Note: the return value is
136 * only valid up until the next call to Tcl_SetVar or Tcl_SetVar2;
137 * if you depend on the value lasting longer than that, then make
138 * yourself a private copy.
139 *
140 * Side effects:
141 * None.
142 *
143 *----------------------------------------------------------------------
144 */
145
146 char *
147 Tcl_GetVar2 (
148 Tcl_Interp *interp, /* Command interpreter in which variable is
149 * to be looked up. */
150 char *name1, /* Name of array (if name2 is NULL) or
151 * name of variable. */
152 char *name2, /* If non-null, gives name of element in
153 * array. */
154 int flags /* OR-ed combination of TCL_GLOBAL_ONLY
155 * or TCL_LEAVE_ERR_MSG bits. */
156 )
157 {
158 Tcl_HashEntry *hPtr;
159 Var *varPtr;
160 Interp *iPtr = (Interp *) interp;
161 Var *arrayPtr = NULL;
162
163 /*
164 * Lookup the first name.
165 */
166
167 if ((flags & TCL_GLOBAL_ONLY) || (iPtr->varFramePtr == NULL)) {
168 hPtr = Tcl_FindHashEntry(&iPtr->globalTable, name1);
169 } else {
170 hPtr = Tcl_FindHashEntry(&iPtr->varFramePtr->varTable, name1);
171 }
172 if (hPtr == NULL) {
173 if (flags & TCL_LEAVE_ERR_MSG) {
174 VarErrMsg(interp, name1, name2, "read", noSuchVar);
175 }
176 return NULL;
177 }
178 varPtr = (Var *) Tcl_GetHashValue(hPtr);
179 if (varPtr->flags & VAR_UPVAR) {
180 hPtr = varPtr->value.upvarPtr;
181 varPtr = (Var *) Tcl_GetHashValue(hPtr);
182 }
183
184 /*
185 * If this is an array reference, then remember the traces on the array
186 * and lookup the element within the array.
187 */
188
189 if (name2 != NULL) {
190 if (varPtr->flags & VAR_UNDEFINED) {
191 if (flags & TCL_LEAVE_ERR_MSG) {
192 VarErrMsg(interp, name1, name2, "read", noSuchVar);
193 }
194 return NULL;
195 } else if (!(varPtr->flags & VAR_ARRAY)) {
196 if (flags & TCL_LEAVE_ERR_MSG) {
197 VarErrMsg(interp, name1, name2, "read", needArray);
198 }
199 return NULL;
200 }
201 arrayPtr = varPtr;
202 hPtr = Tcl_FindHashEntry(varPtr->value.tablePtr, name2);
203 if (hPtr == NULL) {
204 if (flags & TCL_LEAVE_ERR_MSG) {
205 VarErrMsg(interp, name1, name2, "read", noSuchElement);
206 }
207 return NULL;
208 }
209 varPtr = (Var *) Tcl_GetHashValue(hPtr);
210 }
211
212 /*
213 * Invoke any traces that have been set for the variable.
214 */
215
216 if ((varPtr->tracePtr != NULL)
217 || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
218 char *msg;
219
220 msg = CallTraces(iPtr, arrayPtr, hPtr, name1, name2,
221 (flags & TCL_GLOBAL_ONLY) | TCL_TRACE_READS);
222 if (msg != NULL) {
223 VarErrMsg(interp, name1, name2, "read", msg);
224 return NULL;
225 }
226
227 /*
228 * Watch out! The variable could have gotten re-allocated to
229 * a larger size. Fortunately the hash table entry will still
230 * be around.
231 */
232
233 varPtr = (Var *) Tcl_GetHashValue(hPtr);
234 }
235 if (varPtr->flags & (VAR_UNDEFINED|VAR_UPVAR|VAR_ARRAY)) {
236 if (flags & TCL_LEAVE_ERR_MSG) {
237 VarErrMsg(interp, name1, name2, "read", noSuchVar);
238 }
239 return NULL;
240 }
241 return varPtr->value.string;
242 }
243 \f
244 /*
245 *----------------------------------------------------------------------
246 *
247 * Tcl_SetVar --
248 *
249 * Change the value of a variable.
250 *
251 * Results:
252 * Returns a pointer to the malloc'ed string holding the new
253 * value of the variable. The caller should not modify this
254 * string. If the write operation was disallowed then NULL
255 * is returned; if the TCL_LEAVE_ERR_MSG flag is set, then
256 * an explanatory message will be left in interp->result.
257 *
258 * Side effects:
259 * If varName is defined as a local or global variable in interp,
260 * its value is changed to newValue. If varName isn't currently
261 * defined, then a new global variable by that name is created.
262 *
263 *----------------------------------------------------------------------
264 */
265
266 char *
267 Tcl_SetVar (
268 Tcl_Interp *interp, /* Command interpreter in which varName is
269 * to be looked up. */
270 char *varName, /* Name of a variable in interp. */
271 char *newValue, /* New value for varName. */
272 int flags /* Various flags that tell how to set value:
273 * any of TCL_GLOBAL_ONLY, TCL_APPEND_VALUE,
274 * TCL_LIST_ELEMENT, TCL_NO_SPACE, or
275 * TCL_LEAVE_ERR_MSG. */
276 )
277 {
278 register char *p;
279
280 /*
281 * If varName refers to an array (it ends with a parenthesized
282 * element name), then handle it specially.
283 */
284
285 for (p = varName; *p != '\0'; p++) {
286 if (*p == '(') {
287 char *result;
288 char *open = p;
289
290 do {
291 p++;
292 } while (*p != '\0');
293 p--;
294 if (*p != ')') {
295 goto scalar;
296 }
297 *open = '\0';
298 *p = '\0';
299 result = Tcl_SetVar2(interp, varName, open+1, newValue, flags);
300 *open = '(';
301 *p = ')';
302 return result;
303 }
304 }
305
306 scalar:
307 return Tcl_SetVar2(interp, varName, (char *) NULL, newValue, flags);
308 }
309 \f
310 /*
311 *----------------------------------------------------------------------
312 *
313 * Tcl_SetVar2 --
314 *
315 * Given a two-part variable name, which may refer either to a
316 * scalar variable or an element of an array, change the value
317 * of the variable. If the named scalar or array or element
318 * doesn't exist then create one.
319 *
320 * Results:
321 * Returns a pointer to the malloc'ed string holding the new
322 * value of the variable. The caller should not modify this
323 * string. If the write operation was disallowed because an
324 * array was expected but not found (or vice versa), then NULL
325 * is returned; if the TCL_LEAVE_ERR_MSG flag is set, then
326 * an explanatory message will be left in interp->result.
327 *
328 * Side effects:
329 * The value of the given variable is set. If either the array
330 * or the entry didn't exist then a new one is created.
331 *
332 *----------------------------------------------------------------------
333 */
334
335 char *
336 Tcl_SetVar2 (
337 Tcl_Interp *interp, /* Command interpreter in which variable is
338 * to be looked up. */
339 char *name1, /* If name2 is NULL, this is name of scalar
340 * variable. Otherwise it is name of array. */
341 char *name2, /* Name of an element within array, or NULL. */
342 char *newValue, /* New value for variable. */
343 int flags /* Various flags that tell how to set value:
344 * any of TCL_GLOBAL_ONLY, TCL_APPEND_VALUE,
345 * TCL_LIST_ELEMENT, and TCL_NO_SPACE, or
346 * TCL_LEAVE_ERR_MSG . */
347 )
348 {
349 Tcl_HashEntry *hPtr;
350 register Var *varPtr = NULL;
351 /* Initial value only used to stop compiler
352 * from complaining; not really needed. */
353 register Interp *iPtr = (Interp *) interp;
354 int length, new, listFlags;
355 Var *arrayPtr = NULL;
356
357 /*
358 * Lookup the first name.
359 */
360
361 if ((flags & TCL_GLOBAL_ONLY) || (iPtr->varFramePtr == NULL)) {
362 hPtr = Tcl_CreateHashEntry(&iPtr->globalTable, name1, &new);
363 } else {
364 hPtr = Tcl_CreateHashEntry(&iPtr->varFramePtr->varTable,
365 name1, &new);
366 }
367 if (!new) {
368 varPtr = (Var *) Tcl_GetHashValue(hPtr);
369 if (varPtr->flags & VAR_UPVAR) {
370 hPtr = varPtr->value.upvarPtr;
371 varPtr = (Var *) Tcl_GetHashValue(hPtr);
372 }
373 }
374
375 /*
376 * If this is an array reference, then create a new array (if
377 * needed), remember any traces on the array, and lookup the
378 * element within the array.
379 */
380
381 if (name2 != NULL) {
382 if (new) {
383 varPtr = NewVar(0);
384 Tcl_SetHashValue(hPtr, varPtr);
385 varPtr->flags = VAR_ARRAY;
386 varPtr->value.tablePtr = (Tcl_HashTable *)
387 ckalloc(sizeof(Tcl_HashTable));
388 Tcl_InitHashTable(varPtr->value.tablePtr, TCL_STRING_KEYS);
389 } else {
390 if (varPtr->flags & VAR_UNDEFINED) {
391 varPtr->flags = VAR_ARRAY;
392 varPtr->value.tablePtr = (Tcl_HashTable *)
393 ckalloc(sizeof(Tcl_HashTable));
394 Tcl_InitHashTable(varPtr->value.tablePtr, TCL_STRING_KEYS);
395 } else if (!(varPtr->flags & VAR_ARRAY)) {
396 if (flags & TCL_LEAVE_ERR_MSG) {
397 VarErrMsg(interp, name1, name2, "set", needArray);
398 }
399 return NULL;
400 }
401 arrayPtr = varPtr;
402 }
403 hPtr = Tcl_CreateHashEntry(varPtr->value.tablePtr, name2, &new);
404 }
405
406 /*
407 * Compute how many bytes will be needed for newValue (leave space
408 * for a separating space between list elements).
409 */
410
411 if (flags & TCL_LIST_ELEMENT) {
412 length = Tcl_ScanElement(newValue, &listFlags) + 1;
413 } else {
414 length = strlen(newValue);
415 }
416
417 /*
418 * If the variable doesn't exist then create a new one. If it
419 * does exist then clear its current value unless this is an
420 * append operation.
421 */
422
423 if (new) {
424 varPtr = NewVar(length);
425 Tcl_SetHashValue(hPtr, varPtr);
426 if ((arrayPtr != NULL) && (arrayPtr->searchPtr != NULL)) {
427 DeleteSearches(arrayPtr);
428 }
429 } else {
430 varPtr = (Var *) Tcl_GetHashValue(hPtr);
431 if (varPtr->flags & VAR_ARRAY) {
432 if (flags & TCL_LEAVE_ERR_MSG) {
433 VarErrMsg(interp, name1, name2, "set", isArray);
434 }
435 return NULL;
436 }
437 if (!(flags & TCL_APPEND_VALUE) || (varPtr->flags & VAR_UNDEFINED)) {
438 varPtr->valueLength = 0;
439 }
440 }
441
442 /*
443 * Make sure there's enough space to hold the variable's
444 * new value. If not, enlarge the variable's space.
445 */
446
447 if ((length + varPtr->valueLength) >= varPtr->valueSpace) {
448 Var *newVarPtr;
449 int newSize;
450
451 newSize = 2*varPtr->valueSpace;
452 if (newSize <= (length + varPtr->valueLength)) {
453 newSize += length;
454 }
455 newVarPtr = NewVar(newSize);
456 newVarPtr->valueLength = varPtr->valueLength;
457 newVarPtr->upvarUses = varPtr->upvarUses;
458 newVarPtr->tracePtr = varPtr->tracePtr;
459 strcpy(newVarPtr->value.string, varPtr->value.string);
460 Tcl_SetHashValue(hPtr, newVarPtr);
461 ckfree((char *) varPtr);
462 varPtr = newVarPtr;
463 }
464
465 /*
466 * Append the new value to the variable, either as a list
467 * element or as a string.
468 */
469
470 if (flags & TCL_LIST_ELEMENT) {
471 if ((varPtr->valueLength > 0) && !(flags & TCL_NO_SPACE)) {
472 varPtr->value.string[varPtr->valueLength] = ' ';
473 varPtr->valueLength++;
474 }
475 varPtr->valueLength += Tcl_ConvertElement(newValue,
476 varPtr->value.string + varPtr->valueLength, listFlags);
477 varPtr->value.string[varPtr->valueLength] = 0;
478 } else {
479 strcpy(varPtr->value.string + varPtr->valueLength, newValue);
480 varPtr->valueLength += length;
481 }
482 varPtr->flags &= ~VAR_UNDEFINED;
483
484 /*
485 * Invoke any write traces for the variable.
486 */
487
488 if ((varPtr->tracePtr != NULL)
489 || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
490 char *msg;
491
492 msg = CallTraces(iPtr, arrayPtr, hPtr, name1, name2,
493 (flags & TCL_GLOBAL_ONLY) | TCL_TRACE_WRITES);
494 if (msg != NULL) {
495 VarErrMsg(interp, name1, name2, "set", msg);
496 return NULL;
497 }
498
499 /*
500 * Watch out! The variable could have gotten re-allocated to
501 * a larger size. Fortunately the hash table entry will still
502 * be around.
503 */
504
505 varPtr = (Var *) Tcl_GetHashValue(hPtr);
506 }
507 return varPtr->value.string;
508 }
509 \f
510 /*
511 *----------------------------------------------------------------------
512 *
513 * Tcl_UnsetVar --
514 *
515 * Delete a variable, so that it may not be accessed anymore.
516 *
517 * Results:
518 * Returns 0 if the variable was successfully deleted, -1
519 * if the variable can't be unset. In the event of an error,
520 * if the TCL_LEAVE_ERR_MSG flag is set then an error message
521 * is left in interp->result.
522 *
523 * Side effects:
524 * If varName is defined as a local or global variable in interp,
525 * it is deleted.
526 *
527 *----------------------------------------------------------------------
528 */
529
530 int
531 Tcl_UnsetVar (
532 Tcl_Interp *interp, /* Command interpreter in which varName is
533 * to be looked up. */
534 char *varName, /* Name of a variable in interp. May be
535 * either a scalar name or an array name
536 * or an element in an array. */
537 int flags /* OR-ed combination of any of
538 * TCL_GLOBAL_ONLY or TCL_LEAVE_ERR_MSG. */
539 )
540 {
541 register char *p;
542 int result;
543
544 /*
545 * Figure out whether this is an array reference, then call
546 * Tcl_UnsetVar2 to do all the real work.
547 */
548
549 for (p = varName; *p != '\0'; p++) {
550 if (*p == '(') {
551 char *open = p;
552
553 do {
554 p++;
555 } while (*p != '\0');
556 p--;
557 if (*p != ')') {
558 goto scalar;
559 }
560 *open = '\0';
561 *p = '\0';
562 result = Tcl_UnsetVar2(interp, varName, open+1, flags);
563 *open = '(';
564 *p = ')';
565 return result;
566 }
567 }
568
569 scalar:
570 return Tcl_UnsetVar2(interp, varName, (char *) NULL, flags);
571 }
572 \f
573 /*
574 *----------------------------------------------------------------------
575 *
576 * Tcl_UnsetVar2 --
577 *
578 * Delete a variable, given a 2-part name.
579 *
580 * Results:
581 * Returns 0 if the variable was successfully deleted, -1
582 * if the variable can't be unset. In the event of an error,
583 * if the TCL_LEAVE_ERR_MSG flag is set then an error message
584 * is left in interp->result.
585 *
586 * Side effects:
587 * If name1 and name2 indicate a local or global variable in interp,
588 * it is deleted. If name1 is an array name and name2 is NULL, then
589 * the whole array is deleted.
590 *
591 *----------------------------------------------------------------------
592 */
593
594 int
595 Tcl_UnsetVar2 (
596 Tcl_Interp *interp, /* Command interpreter in which varName is
597 * to be looked up. */
598 char *name1, /* Name of variable or array. */
599 char *name2, /* Name of element within array or NULL. */
600 int flags /* OR-ed combination of any of
601 * TCL_GLOBAL_ONLY or TCL_LEAVE_ERR_MSG. */
602 )
603 {
604 Tcl_HashEntry *hPtr, dummyEntry;
605 Var *varPtr, dummyVar;
606 Interp *iPtr = (Interp *) interp;
607 Var *arrayPtr = NULL;
608
609 if ((flags & TCL_GLOBAL_ONLY) || (iPtr->varFramePtr == NULL)) {
610 hPtr = Tcl_FindHashEntry(&iPtr->globalTable, name1);
611 } else {
612 hPtr = Tcl_FindHashEntry(&iPtr->varFramePtr->varTable, name1);
613 }
614 if (hPtr == NULL) {
615 if (flags & TCL_LEAVE_ERR_MSG) {
616 VarErrMsg(interp, name1, name2, "unset", noSuchVar);
617 }
618 return -1;
619 }
620 varPtr = (Var *) Tcl_GetHashValue(hPtr);
621
622 /*
623 * For global variables referenced in procedures, leave the procedure's
624 * reference variable in place, but unset the global variable. Can't
625 * decrement the actual variable's use count, since we didn't delete
626 * the reference variable.
627 */
628
629 if (varPtr->flags & VAR_UPVAR) {
630 hPtr = varPtr->value.upvarPtr;
631 varPtr = (Var *) Tcl_GetHashValue(hPtr);
632 }
633
634 /*
635 * If the variable being deleted is an element of an array, then
636 * remember trace procedures on the overall array and find the
637 * element to delete.
638 */
639
640 if (name2 != NULL) {
641 if (!(varPtr->flags & VAR_ARRAY)) {
642 if (flags & TCL_LEAVE_ERR_MSG) {
643 VarErrMsg(interp, name1, name2, "unset", needArray);
644 }
645 return -1;
646 }
647 if (varPtr->searchPtr != NULL) {
648 DeleteSearches(varPtr);
649 }
650 arrayPtr = varPtr;
651 hPtr = Tcl_FindHashEntry(varPtr->value.tablePtr, name2);
652 if (hPtr == NULL) {
653 if (flags & TCL_LEAVE_ERR_MSG) {
654 VarErrMsg(interp, name1, name2, "unset", noSuchElement);
655 }
656 return -1;
657 }
658 varPtr = (Var *) Tcl_GetHashValue(hPtr);
659 }
660
661 /*
662 * If there is a trace active on this variable or if the variable
663 * is already being deleted then don't delete the variable: it
664 * isn't safe, since there are procedures higher up on the stack
665 * that will use pointers to the variable. Also don't delete an
666 * array if there are traces active on any of its elements.
667 */
668
669 if (varPtr->flags &
670 (VAR_TRACE_ACTIVE|VAR_ELEMENT_ACTIVE)) {
671 if (flags & TCL_LEAVE_ERR_MSG) {
672 VarErrMsg(interp, name1, name2, "unset", traceActive);
673 }
674 return -1;
675 }
676
677 /*
678 * The code below is tricky, because of the possibility that
679 * a trace procedure might try to access a variable being
680 * deleted. To handle this situation gracefully, copy the
681 * contents of the variable and its hash table entry to
682 * dummy variables, then clean up the actual variable so that
683 * it's been completely deleted before the traces are called.
684 * Then call the traces, and finally clean up the variable's
685 * storage using the dummy copies.
686 */
687
688 dummyVar = *varPtr;
689 Tcl_SetHashValue(&dummyEntry, &dummyVar);
690 if (varPtr->upvarUses == 0) {
691 Tcl_DeleteHashEntry(hPtr);
692 ckfree((char *) varPtr);
693 } else {
694 varPtr->flags = VAR_UNDEFINED;
695 varPtr->tracePtr = NULL;
696 }
697
698 /*
699 * Call trace procedures for the variable being deleted and delete
700 * its traces.
701 */
702
703 if ((dummyVar.tracePtr != NULL)
704 || ((arrayPtr != NULL) && (arrayPtr->tracePtr != NULL))) {
705 (void) CallTraces(iPtr, arrayPtr, &dummyEntry, name1, name2,
706 (flags & TCL_GLOBAL_ONLY) | TCL_TRACE_UNSETS);
707 while (dummyVar.tracePtr != NULL) {
708 VarTrace *tracePtr = dummyVar.tracePtr;
709 dummyVar.tracePtr = tracePtr->nextPtr;
710 ckfree((char *) tracePtr);
711 }
712 }
713
714 /*
715 * If the variable is an array, delete all of its elements. This
716 * must be done after calling the traces on the array, above (that's
717 * the way traces are defined).
718 */
719
720 if (dummyVar.flags & VAR_ARRAY) {
721 DeleteArray(iPtr, name1, &dummyVar,
722 (flags & TCL_GLOBAL_ONLY) | TCL_TRACE_UNSETS);
723 }
724 if (dummyVar.flags & VAR_UNDEFINED) {
725 if (flags & TCL_LEAVE_ERR_MSG) {
726 VarErrMsg(interp, name1, name2, "unset",
727 (name2 == NULL) ? noSuchVar : noSuchElement);
728 }
729 return -1;
730 }
731 return 0;
732 }
733 \f
734 /*
735 *----------------------------------------------------------------------
736 *
737 * Tcl_TraceVar --
738 *
739 * Arrange for reads and/or writes to a variable to cause a
740 * procedure to be invoked, which can monitor the operations
741 * and/or change their actions.
742 *
743 * Results:
744 * A standard Tcl return value.
745 *
746 * Side effects:
747 * A trace is set up on the variable given by varName, such that
748 * future references to the variable will be intermediated by
749 * proc. See the manual entry for complete details on the calling
750 * sequence for proc.
751 *
752 *----------------------------------------------------------------------
753 */
754
755 int
756 Tcl_TraceVar (
757 Tcl_Interp *interp, /* Interpreter in which variable is
758 * to be traced. */
759 char *varName, /* Name of variable; may end with "(index)"
760 * to signify an array reference. */
761 int flags, /* OR-ed collection of bits, including any
762 * of TCL_TRACE_READS, TCL_TRACE_WRITES,
763 * TCL_TRACE_UNSETS, and TCL_GLOBAL_ONLY. */
764 Tcl_VarTraceProc *proc, /* Procedure to call when specified ops are
765 * invoked upon varName. */
766 ClientData clientData /* Arbitrary argument to pass to proc. */
767 )
768 {
769 register char *p;
770
771 /*
772 * If varName refers to an array (it ends with a parenthesized
773 * element name), then handle it specially.
774 */
775
776 for (p = varName; *p != '\0'; p++) {
777 if (*p == '(') {
778 int result;
779 char *open = p;
780
781 do {
782 p++;
783 } while (*p != '\0');
784 p--;
785 if (*p != ')') {
786 goto scalar;
787 }
788 *open = '\0';
789 *p = '\0';
790 result = Tcl_TraceVar2(interp, varName, open+1, flags,
791 proc, clientData);
792 *open = '(';
793 *p = ')';
794 return result;
795 }
796 }
797
798 scalar:
799 return Tcl_TraceVar2(interp, varName, (char *) NULL, flags,
800 proc, clientData);
801 }
802 \f
803 /*
804 *----------------------------------------------------------------------
805 *
806 * Tcl_TraceVar2 --
807 *
808 * Arrange for reads and/or writes to a variable to cause a
809 * procedure to be invoked, which can monitor the operations
810 * and/or change their actions.
811 *
812 * Results:
813 * A standard Tcl return value.
814 *
815 * Side effects:
816 * A trace is set up on the variable given by name1 and name2, such
817 * that future references to the variable will be intermediated by
818 * proc. See the manual entry for complete details on the calling
819 * sequence for proc.
820 *
821 *----------------------------------------------------------------------
822 */
823
824 int
825 Tcl_TraceVar2 (
826 Tcl_Interp *interp, /* Interpreter in which variable is
827 * to be traced. */
828 char *name1, /* Name of scalar variable or array. */
829 char *name2, /* Name of element within array; NULL means
830 * trace applies to scalar variable or array
831 * as-a-whole. */
832 int flags, /* OR-ed collection of bits, including any
833 * of TCL_TRACE_READS, TCL_TRACE_WRITES,
834 * TCL_TRACE_UNSETS, and TCL_GLOBAL_ONLY. */
835 Tcl_VarTraceProc *proc, /* Procedure to call when specified ops are
836 * invoked upon varName. */
837 ClientData clientData /* Arbitrary argument to pass to proc. */
838 )
839 {
840 Tcl_HashEntry *hPtr;
841 Var *varPtr = NULL; /* Initial value only used to stop compiler
842 * from complaining; not really needed. */
843 Interp *iPtr = (Interp *) interp;
844 register VarTrace *tracePtr;
845 int new;
846
847 /*
848 * Locate the variable, making a new (undefined) one if necessary.
849 */
850
851 if ((flags & TCL_GLOBAL_ONLY) || (iPtr->varFramePtr == NULL)) {
852 hPtr = Tcl_CreateHashEntry(&iPtr->globalTable, name1, &new);
853 } else {
854 hPtr = Tcl_CreateHashEntry(&iPtr->varFramePtr->varTable, name1, &new);
855 }
856 if (!new) {
857 varPtr = (Var *) Tcl_GetHashValue(hPtr);
858 if (varPtr->flags & VAR_UPVAR) {
859 hPtr = varPtr->value.upvarPtr;
860 varPtr = (Var *) Tcl_GetHashValue(hPtr);
861 }
862 }
863
864 /*
865 * If the trace is to be on an array element, make sure that the
866 * variable is an array variable. If the variable doesn't exist
867 * then define it as an empty array. Then find the specific
868 * array element.
869 */
870
871 if (name2 != NULL) {
872 if (new) {
873 varPtr = NewVar(0);
874 Tcl_SetHashValue(hPtr, varPtr);
875 varPtr->flags = VAR_ARRAY;
876 varPtr->value.tablePtr = (Tcl_HashTable *)
877 ckalloc(sizeof(Tcl_HashTable));
878 Tcl_InitHashTable(varPtr->value.tablePtr, TCL_STRING_KEYS);
879 } else {
880 if (varPtr->flags & VAR_UNDEFINED) {
881 varPtr->flags = VAR_ARRAY;
882 varPtr->value.tablePtr = (Tcl_HashTable *)
883 ckalloc(sizeof(Tcl_HashTable));
884 Tcl_InitHashTable(varPtr->value.tablePtr, TCL_STRING_KEYS);
885 } else if (!(varPtr->flags & VAR_ARRAY)) {
886 iPtr->result = needArray;
887 return TCL_ERROR;
888 }
889 }
890 hPtr = Tcl_CreateHashEntry(varPtr->value.tablePtr, name2, &new);
891 }
892
893 if (new) {
894 if ((name2 != NULL) && (varPtr->searchPtr != NULL)) {
895 DeleteSearches(varPtr);
896 }
897 varPtr = NewVar(0);
898 varPtr->flags = VAR_UNDEFINED;
899 Tcl_SetHashValue(hPtr, varPtr);
900 } else {
901 varPtr = (Var *) Tcl_GetHashValue(hPtr);
902 }
903
904 /*
905 * Set up trace information.
906 */
907
908 tracePtr = (VarTrace *) ckalloc(sizeof(VarTrace));
909 tracePtr->traceProc = proc;
910 tracePtr->clientData = clientData;
911 tracePtr->flags = flags &
912 (TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_UNSETS);
913 tracePtr->nextPtr = varPtr->tracePtr;
914 varPtr->tracePtr = tracePtr;
915 return TCL_OK;
916 }
917 \f
918 /*
919 *----------------------------------------------------------------------
920 *
921 * Tcl_UntraceVar --
922 *
923 * Remove a previously-created trace for a variable.
924 *
925 * Results:
926 * None.
927 *
928 * Side effects:
929 * If there exists a trace for the variable given by varName
930 * with the given flags, proc, and clientData, then that trace
931 * is removed.
932 *
933 *----------------------------------------------------------------------
934 */
935
936 void
937 Tcl_UntraceVar (
938 Tcl_Interp *interp, /* Interpreter containing traced variable. */
939 char *varName, /* Name of variable; may end with "(index)"
940 * to signify an array reference. */
941 int flags, /* OR-ed collection of bits describing
942 * current trace, including any of
943 * TCL_TRACE_READS, TCL_TRACE_WRITES,
944 * TCL_TRACE_UNSETS, and TCL_GLOBAL_ONLY. */
945 Tcl_VarTraceProc *proc, /* Procedure assocated with trace. */
946 ClientData clientData /* Arbitrary argument to pass to proc. */
947 )
948 {
949 register char *p;
950
951 /*
952 * If varName refers to an array (it ends with a parenthesized
953 * element name), then handle it specially.
954 */
955
956 for (p = varName; *p != '\0'; p++) {
957 if (*p == '(') {
958 char *open = p;
959
960 do {
961 p++;
962 } while (*p != '\0');
963 p--;
964 if (*p != ')') {
965 goto scalar;
966 }
967 *open = '\0';
968 *p = '\0';
969 Tcl_UntraceVar2(interp, varName, open+1, flags, proc, clientData);
970 *open = '(';
971 *p = ')';
972 return;
973 }
974 }
975
976 scalar:
977 Tcl_UntraceVar2(interp, varName, (char *) NULL, flags, proc, clientData);
978 }
979 \f
980 /*
981 *----------------------------------------------------------------------
982 *
983 * Tcl_UntraceVar2 --
984 *
985 * Remove a previously-created trace for a variable.
986 *
987 * Results:
988 * None.
989 *
990 * Side effects:
991 * If there exists a trace for the variable given by name1
992 * and name2 with the given flags, proc, and clientData, then
993 * that trace is removed.
994 *
995 *----------------------------------------------------------------------
996 */
997
998 void
999 Tcl_UntraceVar2 (
1000 Tcl_Interp *interp, /* Interpreter containing traced variable. */
1001 char *name1, /* Name of variable or array. */
1002 char *name2, /* Name of element within array; NULL means
1003 * trace applies to scalar variable or array
1004 * as-a-whole. */
1005 int flags, /* OR-ed collection of bits describing
1006 * current trace, including any of
1007 * TCL_TRACE_READS, TCL_TRACE_WRITES,
1008 * TCL_TRACE_UNSETS, and TCL_GLOBAL_ONLY. */
1009 Tcl_VarTraceProc *proc, /* Procedure assocated with trace. */
1010 ClientData clientData /* Arbitrary argument to pass to proc. */
1011 )
1012 {
1013 register VarTrace *tracePtr;
1014 VarTrace *prevPtr;
1015 Var *varPtr;
1016 Interp *iPtr = (Interp *) interp;
1017 Tcl_HashEntry *hPtr;
1018 ActiveVarTrace *activePtr;
1019
1020 /*
1021 * First, lookup the variable.
1022 */
1023
1024 if ((flags & TCL_GLOBAL_ONLY) || (iPtr->varFramePtr == NULL)) {
1025 hPtr = Tcl_FindHashEntry(&iPtr->globalTable, name1);
1026 } else {
1027 hPtr = Tcl_FindHashEntry(&iPtr->varFramePtr->varTable, name1);
1028 }
1029 if (hPtr == NULL) {
1030 return;
1031 }
1032 varPtr = (Var *) Tcl_GetHashValue(hPtr);
1033 if (varPtr->flags & VAR_UPVAR) {
1034 hPtr = varPtr->value.upvarPtr;
1035 varPtr = (Var *) Tcl_GetHashValue(hPtr);
1036 }
1037 if (name2 != NULL) {
1038 if (!(varPtr->flags & VAR_ARRAY)) {
1039 return;
1040 }
1041 hPtr = Tcl_FindHashEntry(varPtr->value.tablePtr, name2);
1042 if (hPtr == NULL) {
1043 return;
1044 }
1045 varPtr = (Var *) Tcl_GetHashValue(hPtr);
1046 }
1047
1048 flags &= (TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS);
1049 for (tracePtr = varPtr->tracePtr, prevPtr = NULL; ;
1050 prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
1051 if (tracePtr == NULL) {
1052 return;
1053 }
1054 if ((tracePtr->traceProc == proc) && (tracePtr->flags == flags)
1055 && (tracePtr->clientData == clientData)) {
1056 break;
1057 }
1058 }
1059
1060 /*
1061 * The code below makes it possible to delete traces while traces
1062 * are active: it makes sure that the deleted trace won't be
1063 * processed by CallTraces.
1064 */
1065
1066 for (activePtr = iPtr->activeTracePtr; activePtr != NULL;
1067 activePtr = activePtr->nextPtr) {
1068 if (activePtr->nextTracePtr == tracePtr) {
1069 activePtr->nextTracePtr = tracePtr->nextPtr;
1070 }
1071 }
1072 if (prevPtr == NULL) {
1073 varPtr->tracePtr = tracePtr->nextPtr;
1074 } else {
1075 prevPtr->nextPtr = tracePtr->nextPtr;
1076 }
1077 ckfree((char *) tracePtr);
1078 }
1079 \f
1080 /*
1081 *----------------------------------------------------------------------
1082 *
1083 * Tcl_VarTraceInfo --
1084 *
1085 * Return the clientData value associated with a trace on a
1086 * variable. This procedure can also be used to step through
1087 * all of the traces on a particular variable that have the
1088 * same trace procedure.
1089 *
1090 * Results:
1091 * The return value is the clientData value associated with
1092 * a trace on the given variable. Information will only be
1093 * returned for a trace with proc as trace procedure. If
1094 * the clientData argument is NULL then the first such trace is
1095 * returned; otherwise, the next relevant one after the one
1096 * given by clientData will be returned. If the variable
1097 * doesn't exist, or if there are no (more) traces for it,
1098 * then NULL is returned.
1099 *
1100 * Side effects:
1101 * None.
1102 *
1103 *----------------------------------------------------------------------
1104 */
1105
1106 ClientData
1107 Tcl_VarTraceInfo (
1108 Tcl_Interp *interp, /* Interpreter containing variable. */
1109 char *varName, /* Name of variable; may end with "(index)"
1110 * to signify an array reference. */
1111 int flags, /* 0 or TCL_GLOBAL_ONLY. */
1112 Tcl_VarTraceProc *proc, /* Procedure assocated with trace. */
1113 ClientData prevClientData /* If non-NULL, gives last value returned
1114 * by this procedure, so this call will
1115 * return the next trace after that one.
1116 * If NULL, this call will return the
1117 * first trace. */
1118 )
1119 {
1120 register char *p;
1121
1122 /*
1123 * If varName refers to an array (it ends with a parenthesized
1124 * element name), then handle it specially.
1125 */
1126
1127 for (p = varName; *p != '\0'; p++) {
1128 if (*p == '(') {
1129 ClientData result;
1130 char *open = p;
1131
1132 do {
1133 p++;
1134 } while (*p != '\0');
1135 p--;
1136 if (*p != ')') {
1137 goto scalar;
1138 }
1139 *open = '\0';
1140 *p = '\0';
1141 result = Tcl_VarTraceInfo2(interp, varName, open+1, flags, proc,
1142 prevClientData);
1143 *open = '(';
1144 *p = ')';
1145 return result;
1146 }
1147 }
1148
1149 scalar:
1150 return Tcl_VarTraceInfo2(interp, varName, (char *) NULL, flags, proc,
1151 prevClientData);
1152 }
1153 \f
1154 /*
1155 *----------------------------------------------------------------------
1156 *
1157 * Tcl_VarTraceInfo2 --
1158 *
1159 * Same as Tcl_VarTraceInfo, except takes name in two pieces
1160 * instead of one.
1161 *
1162 * Results:
1163 * Same as Tcl_VarTraceInfo.
1164 *
1165 * Side effects:
1166 * None.
1167 *
1168 *----------------------------------------------------------------------
1169 */
1170
1171 ClientData
1172 Tcl_VarTraceInfo2 (
1173 Tcl_Interp *interp, /* Interpreter containing variable. */
1174 char *name1, /* Name of variable or array. */
1175 char *name2, /* Name of element within array; NULL means
1176 * trace applies to scalar variable or array
1177 * as-a-whole. */
1178 int flags, /* 0 or TCL_GLOBAL_ONLY. */
1179 Tcl_VarTraceProc *proc, /* Procedure assocated with trace. */
1180 ClientData prevClientData /* If non-NULL, gives last value returned
1181 * by this procedure, so this call will
1182 * return the next trace after that one.
1183 * If NULL, this call will return the
1184 * first trace. */
1185 )
1186 {
1187 register VarTrace *tracePtr;
1188 Var *varPtr;
1189 Interp *iPtr = (Interp *) interp;
1190 Tcl_HashEntry *hPtr;
1191
1192 /*
1193 * First, lookup the variable.
1194 */
1195
1196 if ((flags & TCL_GLOBAL_ONLY) || (iPtr->varFramePtr == NULL)) {
1197 hPtr = Tcl_FindHashEntry(&iPtr->globalTable, name1);
1198 } else {
1199 hPtr = Tcl_FindHashEntry(&iPtr->varFramePtr->varTable, name1);
1200 }
1201 if (hPtr == NULL) {
1202 return NULL;
1203 }
1204 varPtr = (Var *) Tcl_GetHashValue(hPtr);
1205 if (varPtr->flags & VAR_UPVAR) {
1206 hPtr = varPtr->value.upvarPtr;
1207 varPtr = (Var *) Tcl_GetHashValue(hPtr);
1208 }
1209 if (name2 != NULL) {
1210 if (!(varPtr->flags & VAR_ARRAY)) {
1211 return NULL;
1212 }
1213 hPtr = Tcl_FindHashEntry(varPtr->value.tablePtr, name2);
1214 if (hPtr == NULL) {
1215 return NULL;
1216 }
1217 varPtr = (Var *) Tcl_GetHashValue(hPtr);
1218 }
1219
1220 /*
1221 * Find the relevant trace, if any, and return its clientData.
1222 */
1223
1224 tracePtr = varPtr->tracePtr;
1225 if (prevClientData != NULL) {
1226 for ( ; tracePtr != NULL; tracePtr = tracePtr->nextPtr) {
1227 if ((tracePtr->clientData == prevClientData)
1228 && (tracePtr->traceProc == proc)) {
1229 tracePtr = tracePtr->nextPtr;
1230 break;
1231 }
1232 }
1233 }
1234 for ( ; tracePtr != NULL; tracePtr = tracePtr->nextPtr) {
1235 if (tracePtr->traceProc == proc) {
1236 return tracePtr->clientData;
1237 }
1238 }
1239 return NULL;
1240 }
1241 \f
1242 /*
1243 *----------------------------------------------------------------------
1244 *
1245 * Tcl_SetCmd --
1246 *
1247 * This procedure is invoked to process the "set" Tcl command.
1248 * See the user documentation for details on what it does.
1249 *
1250 * Results:
1251 * A standard Tcl result value.
1252 *
1253 * Side effects:
1254 * A variable's value may be changed.
1255 *
1256 *----------------------------------------------------------------------
1257 */
1258
1259 /* ARGSUSED */
1260 int
1261 Tcl_SetCmd (
1262 ClientData dummy, /* Not used. */
1263 register Tcl_Interp *interp, /* Current interpreter. */
1264 int argc, /* Number of arguments. */
1265 char **argv /* Argument strings. */
1266 )
1267 {
1268 if (argc == 2) {
1269 char *value;
1270
1271 value = Tcl_GetVar(interp, argv[1], TCL_LEAVE_ERR_MSG);
1272 if (value == NULL) {
1273 return TCL_ERROR;
1274 }
1275 interp->result = value;
1276 return TCL_OK;
1277 } else if (argc == 3) {
1278 char *result;
1279
1280 result = Tcl_SetVar(interp, argv[1], argv[2], TCL_LEAVE_ERR_MSG);
1281 if (result == NULL) {
1282 return TCL_ERROR;
1283 }
1284 interp->result = result;
1285 return TCL_OK;
1286 } else {
1287 Tcl_AppendResult(interp, "wrong # args: should be \"",
1288 argv[0], " varName ?newValue?\"", (char *) NULL);
1289 return TCL_ERROR;
1290 }
1291 }
1292 \f
1293 /*
1294 *----------------------------------------------------------------------
1295 *
1296 * Tcl_UnsetCmd --
1297 *
1298 * This procedure is invoked to process the "unset" Tcl command.
1299 * See the user documentation for details on what it does.
1300 *
1301 * Results:
1302 * A standard Tcl result value.
1303 *
1304 * Side effects:
1305 * See the user documentation.
1306 *
1307 *----------------------------------------------------------------------
1308 */
1309
1310 /* ARGSUSED */
1311 int
1312 Tcl_UnsetCmd (
1313 ClientData dummy, /* Not used. */
1314 register Tcl_Interp *interp, /* Current interpreter. */
1315 int argc, /* Number of arguments. */
1316 char **argv /* Argument strings. */
1317 )
1318 {
1319 int i;
1320
1321 if (argc < 2) {
1322 Tcl_AppendResult(interp, "wrong # args: should be \"",
1323 argv[0], " varName ?varName ...?\"", (char *) NULL);
1324 return TCL_ERROR;
1325 }
1326 for (i = 1; i < argc; i++) {
1327 if (Tcl_UnsetVar(interp, argv[i], TCL_LEAVE_ERR_MSG) != 0) {
1328 return TCL_ERROR;
1329 }
1330 }
1331 return TCL_OK;
1332 }
1333 \f
1334 /*
1335 *----------------------------------------------------------------------
1336 *
1337 * Tcl_AppendCmd --
1338 *
1339 * This procedure is invoked to process the "append" Tcl command.
1340 * See the user documentation for details on what it does.
1341 *
1342 * Results:
1343 * A standard Tcl result value.
1344 *
1345 * Side effects:
1346 * A variable's value may be changed.
1347 *
1348 *----------------------------------------------------------------------
1349 */
1350
1351 /* ARGSUSED */
1352 int
1353 Tcl_AppendCmd (
1354 ClientData dummy, /* Not used. */
1355 register Tcl_Interp *interp, /* Current interpreter. */
1356 int argc, /* Number of arguments. */
1357 char **argv /* Argument strings. */
1358 )
1359 {
1360 int i;
1361 char *result = NULL; /* (Initialization only needed to keep
1362 * the compiler from complaining) */
1363
1364 if (argc < 3) {
1365 Tcl_AppendResult(interp, "wrong # args: should be \"",
1366 argv[0], " varName value ?value ...?\"", (char *) NULL);
1367 return TCL_ERROR;
1368 }
1369
1370 for (i = 2; i < argc; i++) {
1371 result = Tcl_SetVar(interp, argv[1], argv[i],
1372 TCL_APPEND_VALUE|TCL_LEAVE_ERR_MSG);
1373 if (result == NULL) {
1374 return TCL_ERROR;
1375 }
1376 }
1377 interp->result = result;
1378 return TCL_OK;
1379 }
1380 \f
1381 /*
1382 *----------------------------------------------------------------------
1383 *
1384 * Tcl_LappendCmd --
1385 *
1386 * This procedure is invoked to process the "lappend" Tcl command.
1387 * See the user documentation for details on what it does.
1388 *
1389 * Results:
1390 * A standard Tcl result value.
1391 *
1392 * Side effects:
1393 * A variable's value may be changed.
1394 *
1395 *----------------------------------------------------------------------
1396 */
1397
1398 /* ARGSUSED */
1399 int
1400 Tcl_LappendCmd (
1401 ClientData dummy, /* Not used. */
1402 register Tcl_Interp *interp, /* Current interpreter. */
1403 int argc, /* Number of arguments. */
1404 char **argv /* Argument strings. */
1405 )
1406 {
1407 int i;
1408 char *result = NULL; /* (Initialization only needed to keep
1409 * the compiler from complaining) */
1410
1411 if (argc < 3) {
1412 Tcl_AppendResult(interp, "wrong # args: should be \"",
1413 argv[0], " varName value ?value ...?\"", (char *) NULL);
1414 return TCL_ERROR;
1415 }
1416
1417 for (i = 2; i < argc; i++) {
1418 result = Tcl_SetVar(interp, argv[1], argv[i],
1419 TCL_APPEND_VALUE|TCL_LIST_ELEMENT|TCL_LEAVE_ERR_MSG);
1420 if (result == NULL) {
1421 return TCL_ERROR;
1422 }
1423 }
1424 interp->result = result;
1425 return TCL_OK;
1426 }
1427 \f
1428 /*
1429 *----------------------------------------------------------------------
1430 *
1431 * Tcl_ArrayCmd --
1432 *
1433 * This procedure is invoked to process the "array" Tcl command.
1434 * See the user documentation for details on what it does.
1435 *
1436 * Results:
1437 * A standard Tcl result value.
1438 *
1439 * Side effects:
1440 * See the user documentation.
1441 *
1442 *----------------------------------------------------------------------
1443 */
1444
1445 /* ARGSUSED */
1446 int
1447 Tcl_ArrayCmd (
1448 ClientData dummy, /* Not used. */
1449 register Tcl_Interp *interp, /* Current interpreter. */
1450 int argc, /* Number of arguments. */
1451 char **argv /* Argument strings. */
1452 )
1453 {
1454 int length;
1455 char c;
1456 Var *varPtr;
1457 Tcl_HashEntry *hPtr;
1458 Interp *iPtr = (Interp *) interp;
1459
1460 if (argc < 3) {
1461 Tcl_AppendResult(interp, "wrong # args: should be \"",
1462 argv[0], " option arrayName ?arg ...?\"", (char *) NULL);
1463 return TCL_ERROR;
1464 }
1465
1466 /*
1467 * Locate the array variable (and it better be an array).
1468 */
1469
1470 if (iPtr->varFramePtr == NULL) {
1471 hPtr = Tcl_FindHashEntry(&iPtr->globalTable, argv[2]);
1472 } else {
1473 hPtr = Tcl_FindHashEntry(&iPtr->varFramePtr->varTable, argv[2]);
1474 }
1475 if (hPtr == NULL) {
1476 notArray:
1477 Tcl_AppendResult(interp, "\"", argv[2], "\" isn't an array",
1478 (char *) NULL);
1479 return TCL_ERROR;
1480 }
1481 varPtr = (Var *) Tcl_GetHashValue(hPtr);
1482 if (varPtr->flags & VAR_UPVAR) {
1483 varPtr = (Var *) Tcl_GetHashValue(varPtr->value.upvarPtr);
1484 }
1485 if (!(varPtr->flags & VAR_ARRAY)) {
1486 goto notArray;
1487 }
1488
1489 /*
1490 * Dispatch based on the option.
1491 */
1492
1493 c = argv[1][0];
1494 length = strlen(argv[1]);
1495 if ((c == 'a') && (strncmp(argv[1], "anymore", length) == 0)) {
1496 ArraySearch *searchPtr;
1497
1498 if (argc != 4) {
1499 Tcl_AppendResult(interp, "wrong # args: should be \"",
1500 argv[0], " anymore arrayName searchId\"", (char *) NULL);
1501 return TCL_ERROR;
1502 }
1503 searchPtr = ParseSearchId(interp, varPtr, argv[2], argv[3]);
1504 if (searchPtr == NULL) {
1505 return TCL_ERROR;
1506 }
1507 while (1) {
1508 Var *varPtr2;
1509
1510 if (searchPtr->nextEntry != NULL) {
1511 varPtr2 = (Var *) Tcl_GetHashValue(searchPtr->nextEntry);
1512 if (!(varPtr2->flags & VAR_UNDEFINED)) {
1513 break;
1514 }
1515 }
1516 searchPtr->nextEntry = Tcl_NextHashEntry(&searchPtr->search);
1517 if (searchPtr->nextEntry == NULL) {
1518 interp->result = "0";
1519 return TCL_OK;
1520 }
1521 }
1522 interp->result = "1";
1523 return TCL_OK;
1524 } else if ((c == 'd') && (strncmp(argv[1], "donesearch", length) == 0)) {
1525 ArraySearch *searchPtr, *prevPtr;
1526
1527 if (argc != 4) {
1528 Tcl_AppendResult(interp, "wrong # args: should be \"",
1529 argv[0], " donesearch arrayName searchId\"", (char *) NULL);
1530 return TCL_ERROR;
1531 }
1532 searchPtr = ParseSearchId(interp, varPtr, argv[2], argv[3]);
1533 if (searchPtr == NULL) {
1534 return TCL_ERROR;
1535 }
1536 if (varPtr->searchPtr == searchPtr) {
1537 varPtr->searchPtr = searchPtr->nextPtr;
1538 } else {
1539 for (prevPtr = varPtr->searchPtr; ; prevPtr = prevPtr->nextPtr) {
1540 if (prevPtr->nextPtr == searchPtr) {
1541 prevPtr->nextPtr = searchPtr->nextPtr;
1542 break;
1543 }
1544 }
1545 }
1546 ckfree((char *) searchPtr);
1547 } else if ((c == 'n') && (strncmp(argv[1], "names", length) == 0)
1548 && (length >= 2)) {
1549 Tcl_HashSearch search;
1550 Var *varPtr2;
1551
1552 if (argc != 3) {
1553 Tcl_AppendResult(interp, "wrong # args: should be \"",
1554 argv[0], " names arrayName\"", (char *) NULL);
1555 return TCL_ERROR;
1556 }
1557 for (hPtr = Tcl_FirstHashEntry(varPtr->value.tablePtr, &search);
1558 hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
1559 varPtr2 = (Var *) Tcl_GetHashValue(hPtr);
1560 if (varPtr2->flags & VAR_UNDEFINED) {
1561 continue;
1562 }
1563 Tcl_AppendElement(interp,
1564 Tcl_GetHashKey(varPtr->value.tablePtr, hPtr), 0);
1565 }
1566 } else if ((c == 'n') && (strncmp(argv[1], "nextelement", length) == 0)
1567 && (length >= 2)) {
1568 ArraySearch *searchPtr;
1569 Tcl_HashEntry *hPtr;
1570
1571 if (argc != 4) {
1572 Tcl_AppendResult(interp, "wrong # args: should be \"",
1573 argv[0], " nextelement arrayName searchId\"",
1574 (char *) NULL);
1575 return TCL_ERROR;
1576 }
1577 searchPtr = ParseSearchId(interp, varPtr, argv[2], argv[3]);
1578 if (searchPtr == NULL) {
1579 return TCL_ERROR;
1580 }
1581 while (1) {
1582 Var *varPtr2;
1583
1584 hPtr = searchPtr->nextEntry;
1585 if (hPtr == NULL) {
1586 hPtr = Tcl_NextHashEntry(&searchPtr->search);
1587 if (hPtr == NULL) {
1588 return TCL_OK;
1589 }
1590 } else {
1591 searchPtr->nextEntry = NULL;
1592 }
1593 varPtr2 = (Var *) Tcl_GetHashValue(hPtr);
1594 if (!(varPtr2->flags & VAR_UNDEFINED)) {
1595 break;
1596 }
1597 }
1598 interp->result = Tcl_GetHashKey(varPtr->value.tablePtr, hPtr);
1599 } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)
1600 && (length >= 2)) {
1601 Tcl_HashSearch search;
1602 Var *varPtr2;
1603 int size;
1604
1605 if (argc != 3) {
1606 Tcl_AppendResult(interp, "wrong # args: should be \"",
1607 argv[0], " size arrayName\"", (char *) NULL);
1608 return TCL_ERROR;
1609 }
1610 size = 0;
1611 for (hPtr = Tcl_FirstHashEntry(varPtr->value.tablePtr, &search);
1612 hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
1613 varPtr2 = (Var *) Tcl_GetHashValue(hPtr);
1614 if (varPtr2->flags & VAR_UNDEFINED) {
1615 continue;
1616 }
1617 size++;
1618 }
1619 sprintf(interp->result, "%d", size);
1620 } else if ((c == 's') && (strncmp(argv[1], "startsearch", length) == 0)
1621 && (length >= 2)) {
1622 ArraySearch *searchPtr;
1623
1624 if (argc != 3) {
1625 Tcl_AppendResult(interp, "wrong # args: should be \"",
1626 argv[0], " startsearch arrayName\"", (char *) NULL);
1627 return TCL_ERROR;
1628 }
1629 searchPtr = (ArraySearch *) ckalloc(sizeof(ArraySearch));
1630 if (varPtr->searchPtr == NULL) {
1631 searchPtr->id = 1;
1632 Tcl_AppendResult(interp, "s-1-", argv[2], (char *) NULL);
1633 } else {
1634 char string[20];
1635
1636 searchPtr->id = varPtr->searchPtr->id + 1;
1637 sprintf(string, "%d", searchPtr->id);
1638 Tcl_AppendResult(interp, "s-", string, "-", argv[2],
1639 (char *) NULL);
1640 }
1641 searchPtr->varPtr = varPtr;
1642 searchPtr->nextEntry = Tcl_FirstHashEntry(varPtr->value.tablePtr,
1643 &searchPtr->search);
1644 searchPtr->nextPtr = varPtr->searchPtr;
1645 varPtr->searchPtr = searchPtr;
1646 } else {
1647 Tcl_AppendResult(interp, "bad option \"", argv[1],
1648 "\": should be anymore, donesearch, names, nextelement, ",
1649 "size, or startsearch", (char *) NULL);
1650 return TCL_ERROR;
1651 }
1652 return TCL_OK;
1653 }
1654 \f
1655 /*
1656 *----------------------------------------------------------------------
1657 *
1658 * Tcl_GlobalCmd --
1659 *
1660 * This procedure is invoked to process the "global" Tcl command.
1661 * See the user documentation for details on what it does.
1662 *
1663 * Results:
1664 * A standard Tcl result value.
1665 *
1666 * Side effects:
1667 * See the user documentation.
1668 *
1669 *----------------------------------------------------------------------
1670 */
1671
1672 /* ARGSUSED */
1673 int
1674 Tcl_GlobalCmd (
1675 ClientData dummy, /* Not used. */
1676 Tcl_Interp *interp, /* Current interpreter. */
1677 int argc, /* Number of arguments. */
1678 char **argv /* Argument strings. */
1679 )
1680 {
1681 Var *varPtr, *gVarPtr;
1682 register Interp *iPtr = (Interp *) interp;
1683 Tcl_HashEntry *hPtr, *hPtr2;
1684 int new;
1685
1686 if (argc < 2) {
1687 Tcl_AppendResult((Tcl_Interp *) iPtr, "wrong # args: should be \"",
1688 argv[0], " varName ?varName ...?\"", (char *) NULL);
1689 return TCL_ERROR;
1690 }
1691 if (iPtr->varFramePtr == NULL) {
1692 return TCL_OK;
1693 }
1694
1695 for (argc--, argv++; argc > 0; argc--, argv++) {
1696 hPtr = Tcl_CreateHashEntry(&iPtr->globalTable, *argv, &new);
1697 if (new) {
1698 gVarPtr = NewVar(0);
1699 gVarPtr->flags |= VAR_UNDEFINED;
1700 Tcl_SetHashValue(hPtr, gVarPtr);
1701 } else {
1702 gVarPtr = (Var *) Tcl_GetHashValue(hPtr);
1703 }
1704 hPtr2 = Tcl_CreateHashEntry(&iPtr->varFramePtr->varTable, *argv, &new);
1705 if (!new) {
1706 Var *varPtr;
1707 varPtr = (Var *) Tcl_GetHashValue(hPtr2);
1708 if (varPtr->flags & VAR_UPVAR) {
1709 continue;
1710 } else {
1711 Tcl_AppendResult((Tcl_Interp *) iPtr, "variable \"", *argv,
1712 "\" already exists", (char *) NULL);
1713 return TCL_ERROR;
1714 }
1715 }
1716 varPtr = NewVar(0);
1717 varPtr->flags |= VAR_UPVAR;
1718 varPtr->value.upvarPtr = hPtr;
1719 gVarPtr->upvarUses++;
1720 Tcl_SetHashValue(hPtr2, varPtr);
1721 }
1722 return TCL_OK;
1723 }
1724 \f
1725 /*
1726 *----------------------------------------------------------------------
1727 *
1728 * Tcl_UpvarCmd --
1729 *
1730 * This procedure is invoked to process the "upvar" Tcl command.
1731 * See the user documentation for details on what it does.
1732 *
1733 * Results:
1734 * A standard Tcl result value.
1735 *
1736 * Side effects:
1737 * See the user documentation.
1738 *
1739 *----------------------------------------------------------------------
1740 */
1741
1742 /* ARGSUSED */
1743 int
1744 Tcl_UpvarCmd (
1745 ClientData dummy, /* Not used. */
1746 Tcl_Interp *interp, /* Current interpreter. */
1747 int argc, /* Number of arguments. */
1748 char **argv /* Argument strings. */
1749 )
1750 {
1751 register Interp *iPtr = (Interp *) interp;
1752 int result;
1753 CallFrame *framePtr;
1754 Var *varPtr = NULL;
1755 Tcl_HashTable *upVarTablePtr;
1756 Tcl_HashEntry *hPtr, *hPtr2;
1757 int new;
1758 Var *upVarPtr;
1759
1760 if (argc < 3) {
1761 upvarSyntax:
1762 Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
1763 " ?level? otherVar localVar ?otherVar localVar ...?\"",
1764 (char *) NULL);
1765 return TCL_ERROR;
1766 }
1767
1768 /*
1769 * Find the hash table containing the variable being referenced.
1770 */
1771
1772 result = TclGetFrame(interp, argv[1], &framePtr);
1773 if (result == -1) {
1774 return TCL_ERROR;
1775 }
1776 argc -= result+1;
1777 argv += result+1;
1778 if (framePtr == NULL) {
1779 upVarTablePtr = &iPtr->globalTable;
1780 } else {
1781 upVarTablePtr = &framePtr->varTable;
1782 }
1783
1784 if ((argc & 1) != 0) {
1785 goto upvarSyntax;
1786 }
1787
1788 /*
1789 * Iterate over all the pairs of (local variable, other variable)
1790 * names. For each pair, create a hash table entry in the upper
1791 * context (if the name wasn't there already), then associate it
1792 * with a new local variable.
1793 */
1794
1795 while (argc > 0) {
1796 hPtr = Tcl_CreateHashEntry(upVarTablePtr, argv[0], &new);
1797 if (new) {
1798 upVarPtr = NewVar(0);
1799 upVarPtr->flags |= VAR_UNDEFINED;
1800 Tcl_SetHashValue(hPtr, upVarPtr);
1801 } else {
1802 upVarPtr = (Var *) Tcl_GetHashValue(hPtr);
1803 if (upVarPtr->flags & VAR_UPVAR) {
1804 hPtr = upVarPtr->value.upvarPtr;
1805 upVarPtr = (Var *) Tcl_GetHashValue(hPtr);
1806 }
1807 }
1808
1809 hPtr2 = Tcl_CreateHashEntry(&iPtr->varFramePtr->varTable,
1810 argv[1], &new);
1811 if (!new) {
1812 Tcl_AppendResult((Tcl_Interp *) iPtr, "variable \"", argv[1],
1813 "\" already exists", (char *) NULL);
1814 return TCL_ERROR;
1815 }
1816 varPtr = NewVar(0);
1817 varPtr->flags |= VAR_UPVAR;
1818 varPtr->value.upvarPtr = hPtr;
1819 upVarPtr->upvarUses++;
1820 Tcl_SetHashValue(hPtr2, varPtr);
1821
1822 argc -= 2;
1823 argv += 2;
1824 }
1825 return TCL_OK;
1826 }
1827 \f
1828 /*
1829 *----------------------------------------------------------------------
1830 *
1831 * TclDeleteVars --
1832 *
1833 * This procedure is called to recycle all the storage space
1834 * associated with a table of variables. For this procedure
1835 * to work correctly, it must not be possible for any of the
1836 * variable in the table to be accessed from Tcl commands
1837 * (e.g. from trace procedures).
1838 *
1839 * Results:
1840 * None.
1841 *
1842 * Side effects:
1843 * Variables are deleted and trace procedures are invoked, if
1844 * any are declared.
1845 *
1846 *----------------------------------------------------------------------
1847 */
1848
1849 void
1850 TclDeleteVars (
1851 Interp *iPtr, /* Interpreter to which variables belong. */
1852 Tcl_HashTable *tablePtr /* Hash table containing variables to
1853 * delete. */
1854 )
1855 {
1856 Tcl_HashSearch search;
1857 Tcl_HashEntry *hPtr;
1858 register Var *varPtr;
1859 int flags, globalFlag;
1860
1861 flags = TCL_TRACE_UNSETS;
1862 if (tablePtr == &iPtr->globalTable) {
1863 flags |= TCL_INTERP_DESTROYED | TCL_GLOBAL_ONLY;
1864 }
1865 for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr != NULL;
1866 hPtr = Tcl_NextHashEntry(&search)) {
1867 varPtr = (Var *) Tcl_GetHashValue(hPtr);
1868
1869 /*
1870 * For global/upvar variables referenced in procedures, free up the
1871 * local space and then decrement the reference count on the
1872 * variable referred to. If there are no more references to the
1873 * global/upvar and it is undefined and has no traces set, then
1874 * follow on and delete the referenced variable too.
1875 */
1876
1877 globalFlag = 0;
1878 if (varPtr->flags & VAR_UPVAR) {
1879 hPtr = varPtr->value.upvarPtr;
1880 ckfree((char *) varPtr);
1881 varPtr = (Var *) Tcl_GetHashValue(hPtr);
1882 varPtr->upvarUses--;
1883 if ((varPtr->upvarUses != 0) || !(varPtr->flags & VAR_UNDEFINED)
1884 || (varPtr->tracePtr != NULL)) {
1885 continue;
1886 }
1887 globalFlag = TCL_GLOBAL_ONLY;
1888 }
1889
1890 /*
1891 * Invoke traces on the variable that is being deleted, then
1892 * free up the variable's space (no need to free the hash entry
1893 * here, unless we're dealing with a global variable: the
1894 * hash entries will be deleted automatically when the whole
1895 * table is deleted).
1896 */
1897
1898 if (varPtr->tracePtr != NULL) {
1899 (void) CallTraces(iPtr, (Var *) NULL, hPtr,
1900 Tcl_GetHashKey(tablePtr, hPtr), (char *) NULL,
1901 flags | globalFlag);
1902 while (varPtr->tracePtr != NULL) {
1903 VarTrace *tracePtr = varPtr->tracePtr;
1904 varPtr->tracePtr = tracePtr->nextPtr;
1905 ckfree((char *) tracePtr);
1906 }
1907 }
1908 if (varPtr->flags & VAR_ARRAY) {
1909 DeleteArray(iPtr, Tcl_GetHashKey(tablePtr, hPtr), varPtr,
1910 flags | globalFlag);
1911 }
1912 if (globalFlag) {
1913 Tcl_DeleteHashEntry(hPtr);
1914 }
1915 ckfree((char *) varPtr);
1916 }
1917 Tcl_DeleteHashTable(tablePtr);
1918 }
1919 \f
1920 /*
1921 *----------------------------------------------------------------------
1922 *
1923 * CallTraces --
1924 *
1925 * This procedure is invoked to find and invoke relevant
1926 * trace procedures associated with a particular operation on
1927 * a variable. This procedure invokes traces both on the
1928 * variable and on its containing array (where relevant).
1929 *
1930 * Results:
1931 * The return value is NULL if no trace procedures were invoked, or
1932 * if all the invoked trace procedures returned successfully.
1933 * The return value is non-zero if a trace procedure returned an
1934 * error (in this case no more trace procedures were invoked after
1935 * the error was returned). In this case the return value is a
1936 * pointer to a static string describing the error.
1937 *
1938 * Side effects:
1939 * Almost anything can happen, depending on trace; this procedure
1940 * itself doesn't have any side effects.
1941 *
1942 *----------------------------------------------------------------------
1943 */
1944
1945 static char *
1946 CallTraces (
1947 Interp *iPtr, /* Interpreter containing variable. */
1948 register Var *arrayPtr, /* Pointer to array variable that
1949 * contains the variable, or NULL if
1950 * the variable isn't an element of an
1951 * array. */
1952 Tcl_HashEntry *hPtr, /* Hash table entry corresponding to
1953 * variable whose traces are to be
1954 * invoked. */
1955 char *name1,
1956 char *name2, /* Variable's two-part name. */
1957 int flags /* Flags to pass to trace procedures:
1958 * indicates what's happening to
1959 * variable, plus other stuff like
1960 * TCL_GLOBAL_ONLY and
1961 * TCL_INTERP_DESTROYED. */
1962 )
1963 {
1964 Var *varPtr;
1965 register VarTrace *tracePtr;
1966 ActiveVarTrace active;
1967 char *result;
1968 int savedArrayFlags = 0; /* (Initialization not needed except
1969 * to prevent compiler warning) */
1970
1971 /*
1972 * If there are already similar trace procedures active for the
1973 * variable, don't call them again.
1974 */
1975
1976 varPtr = (Var *) Tcl_GetHashValue(hPtr);
1977 if (varPtr->flags & VAR_TRACE_ACTIVE) {
1978 return NULL;
1979 }
1980 varPtr->flags |= VAR_TRACE_ACTIVE;
1981
1982 /*
1983 * Invoke traces on the array containing the variable, if relevant.
1984 */
1985
1986 result = NULL;
1987 active.nextPtr = iPtr->activeTracePtr;
1988 iPtr->activeTracePtr = &active;
1989 if (arrayPtr != NULL) {
1990 savedArrayFlags = arrayPtr->flags;
1991 arrayPtr->flags |= VAR_ELEMENT_ACTIVE;
1992 for (tracePtr = arrayPtr->tracePtr; tracePtr != NULL;
1993 tracePtr = active.nextTracePtr) {
1994 active.nextTracePtr = tracePtr->nextPtr;
1995 if (!(tracePtr->flags & flags)) {
1996 continue;
1997 }
1998 result = (*tracePtr->traceProc)(tracePtr->clientData,
1999 (Tcl_Interp *) iPtr, name1, name2, flags);
2000 if (result != NULL) {
2001 if (flags & TCL_TRACE_UNSETS) {
2002 result = NULL;
2003 } else {
2004 goto done;
2005 }
2006 }
2007 }
2008 }
2009
2010 /*
2011 * Invoke traces on the variable itself.
2012 */
2013
2014 if (flags & TCL_TRACE_UNSETS) {
2015 flags |= TCL_TRACE_DESTROYED;
2016 }
2017 for (tracePtr = varPtr->tracePtr; tracePtr != NULL;
2018 tracePtr = active.nextTracePtr) {
2019 active.nextTracePtr = tracePtr->nextPtr;
2020 if (!(tracePtr->flags & flags)) {
2021 continue;
2022 }
2023 result = (*tracePtr->traceProc)(tracePtr->clientData,
2024 (Tcl_Interp *) iPtr, name1, name2, flags);
2025 if (result != NULL) {
2026 if (flags & TCL_TRACE_UNSETS) {
2027 result = NULL;
2028 } else {
2029 goto done;
2030 }
2031 }
2032 }
2033
2034 /*
2035 * Restore the variable's flags, remove the record of our active
2036 * traces, and then return. Remember that the variable could have
2037 * been re-allocated during the traces, but its hash entry won't
2038 * change.
2039 */
2040
2041 done:
2042 if (arrayPtr != NULL) {
2043 arrayPtr->flags = savedArrayFlags;
2044 }
2045 varPtr = (Var *) Tcl_GetHashValue(hPtr);
2046 varPtr->flags &= ~VAR_TRACE_ACTIVE;
2047 iPtr->activeTracePtr = active.nextPtr;
2048 return result;
2049 }
2050 \f
2051 /*
2052 *----------------------------------------------------------------------
2053 *
2054 * NewVar --
2055 *
2056 * Create a new variable with a given initial value.
2057 *
2058 * Results:
2059 * The return value is a pointer to the new variable structure.
2060 * The variable will not be part of any hash table yet, and its
2061 * upvarUses count is initialized to 0. Its initial value will
2062 * be empty, but "space" bytes will be available in the value
2063 * area.
2064 *
2065 * Side effects:
2066 * Storage gets allocated.
2067 *
2068 *----------------------------------------------------------------------
2069 */
2070
2071 static Var *
2072 NewVar (
2073 int space /* Minimum amount of space to allocate
2074 * for variable's value. */
2075 )
2076 {
2077 int extra;
2078 register Var *varPtr;
2079
2080 extra = space - sizeof(varPtr->value);
2081 if (extra < 0) {
2082 extra = 0;
2083 space = sizeof(varPtr->value);
2084 }
2085 varPtr = (Var *) ckalloc((unsigned) (sizeof(Var) + extra));
2086 varPtr->valueLength = 0;
2087 varPtr->valueSpace = space;
2088 varPtr->upvarUses = 0;
2089 varPtr->tracePtr = NULL;
2090 varPtr->searchPtr = NULL;
2091 varPtr->flags = 0;
2092 varPtr->value.string[0] = 0;
2093 return varPtr;
2094 }
2095 \f
2096 /*
2097 *----------------------------------------------------------------------
2098 *
2099 * ParseSearchId --
2100 *
2101 * This procedure translates from a string to a pointer to an
2102 * active array search (if there is one that matches the string).
2103 *
2104 * Results:
2105 * The return value is a pointer to the array search indicated
2106 * by string, or NULL if there isn't one. If NULL is returned,
2107 * interp->result contains an error message.
2108 *
2109 * Side effects:
2110 * None.
2111 *
2112 *----------------------------------------------------------------------
2113 */
2114
2115 static ArraySearch *
2116 ParseSearchId (
2117 Tcl_Interp *interp, /* Interpreter containing variable. */
2118 Var *varPtr, /* Array variable search is for. */
2119 char *varName, /* Name of array variable that search is
2120 * supposed to be for. */
2121 char *string /* String containing id of search. Must have
2122 * form "search-num-var" where "num" is a
2123 * decimal number and "var" is a variable
2124 * name. */
2125 )
2126 {
2127 char *end;
2128 int id;
2129 ArraySearch *searchPtr;
2130
2131 /*
2132 * Parse the id into the three parts separated by dashes.
2133 */
2134
2135 if ((string[0] != 's') || (string[1] != '-')) {
2136 syntax:
2137 Tcl_AppendResult(interp, "illegal search identifier \"", string,
2138 "\"", (char *) NULL);
2139 return NULL;
2140 }
2141 id = strtoul(string+2, &end, 10);
2142 if ((end == (string+2)) || (*end != '-')) {
2143 goto syntax;
2144 }
2145 if (strcmp(end+1, varName) != 0) {
2146 Tcl_AppendResult(interp, "search identifier \"", string,
2147 "\" isn't for variable \"", varName, "\"", (char *) NULL);
2148 return NULL;
2149 }
2150
2151 /*
2152 * Search through the list of active searches on the interpreter
2153 * to see if the desired one exists.
2154 */
2155
2156 for (searchPtr = varPtr->searchPtr; searchPtr != NULL;
2157 searchPtr = searchPtr->nextPtr) {
2158 if (searchPtr->id == id) {
2159 return searchPtr;
2160 }
2161 }
2162 Tcl_AppendResult(interp, "couldn't find search \"", string, "\"",
2163 (char *) NULL);
2164 return NULL;
2165 }
2166 \f
2167 /*
2168 *----------------------------------------------------------------------
2169 *
2170 * DeleteSearches --
2171 *
2172 * This procedure is called to free up all of the searches
2173 * associated with an array variable.
2174 *
2175 * Results:
2176 * None.
2177 *
2178 * Side effects:
2179 * Memory is released to the storage allocator.
2180 *
2181 *----------------------------------------------------------------------
2182 */
2183
2184 static void
2185 DeleteSearches (
2186 register Var *arrayVarPtr /* Variable whose searches are
2187 * to be deleted. */
2188 )
2189 {
2190 ArraySearch *searchPtr;
2191
2192 while (arrayVarPtr->searchPtr != NULL) {
2193 searchPtr = arrayVarPtr->searchPtr;
2194 arrayVarPtr->searchPtr = searchPtr->nextPtr;
2195 ckfree((char *) searchPtr);
2196 }
2197 }
2198 \f
2199 /*
2200 *----------------------------------------------------------------------
2201 *
2202 * DeleteArray --
2203 *
2204 * This procedure is called to free up everything in an array
2205 * variable. It's the caller's responsibility to make sure
2206 * that the array is no longer accessible before this procedure
2207 * is called.
2208 *
2209 * Results:
2210 * None.
2211 *
2212 * Side effects:
2213 * All storage associated with varPtr's array elements is deleted
2214 * (including the hash table). Any delete trace procedures for
2215 * array elements are invoked.
2216 *
2217 *----------------------------------------------------------------------
2218 */
2219
2220 static void
2221 DeleteArray (
2222 Interp *iPtr, /* Interpreter containing array. */
2223 char *arrayName, /* Name of array (used for trace
2224 * callbacks). */
2225 Var *varPtr, /* Pointer to variable structure. */
2226 int flags /* Flags to pass to CallTraces:
2227 * TCL_TRACE_UNSETS and sometimes
2228 * TCL_INTERP_DESTROYED and/or
2229 * TCL_GLOBAL_ONLY. */
2230 )
2231 {
2232 Tcl_HashSearch search;
2233 register Tcl_HashEntry *hPtr;
2234 register Var *elPtr;
2235
2236 DeleteSearches(varPtr);
2237 for (hPtr = Tcl_FirstHashEntry(varPtr->value.tablePtr, &search);
2238 hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
2239 elPtr = (Var *) Tcl_GetHashValue(hPtr);
2240 if (elPtr->tracePtr != NULL) {
2241 (void) CallTraces(iPtr, (Var *) NULL, hPtr, arrayName,
2242 Tcl_GetHashKey(varPtr->value.tablePtr, hPtr), flags);
2243 while (elPtr->tracePtr != NULL) {
2244 VarTrace *tracePtr = elPtr->tracePtr;
2245 elPtr->tracePtr = tracePtr->nextPtr;
2246 ckfree((char *) tracePtr);
2247 }
2248 }
2249 if (elPtr->flags & VAR_SEARCHES_POSSIBLE) {
2250 panic("DeleteArray found searches on array alement!");
2251 }
2252 ckfree((char *) elPtr);
2253 }
2254 Tcl_DeleteHashTable(varPtr->value.tablePtr);
2255 ckfree((char *) varPtr->value.tablePtr);
2256 }
2257 \f
2258 /*
2259 *----------------------------------------------------------------------
2260 *
2261 * VarErrMsg --
2262 *
2263 * Generate a reasonable error message describing why a variable
2264 * operation failed.
2265 *
2266 * Results:
2267 * None.
2268 *
2269 * Side effects:
2270 * Interp->result is reset to hold a message identifying the
2271 * variable given by name1 and name2 and describing why the
2272 * variable operation failed.
2273 *
2274 *----------------------------------------------------------------------
2275 */
2276
2277 static void
2278 VarErrMsg (
2279 Tcl_Interp *interp, /* Interpreter in which to record message. */
2280 char *name1,
2281 char *name2, /* Variable's two-part name. */
2282 char *operation, /* String describing operation that failed,
2283 * e.g. "read", "set", or "unset". */
2284 char *reason /* String describing why operation failed. */
2285 )
2286 {
2287 Tcl_ResetResult(interp);
2288 Tcl_AppendResult(interp, "can't ", operation, " \"", name1, (char *) NULL);
2289 if (name2 != NULL) {
2290 Tcl_AppendResult(interp, "(", name2, ")", (char *) NULL);
2291 }
2292 Tcl_AppendResult(interp, "\": ", reason, (char *) NULL);
2293 }
Impressum, Datenschutz