Annotation of micropolis/src/tcl/tclvar.c, revision 1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.