|
|
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.