home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tkisrc04.zip / tcl / os2 / tclVar.c < prev    next >
C/C++ Source or Header  |  1998-08-07  |  74KB  |  2,576 lines

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