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