tclDictObj.c

Go to the documentation of this file.
00001 /*
00002  * tclDictObj.c --
00003  *
00004  *      This file contains functions that implement the Tcl dict object type
00005  *      and its accessor command.
00006  *
00007  * Copyright (c) 2002 by Donal K. Fellows.
00008  *
00009  * See the file "license.terms" for information on usage and redistribution of
00010  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00011  *
00012  * RCS: @(#) $Id: tclDictObj.c,v 1.56 2007/12/13 15:23:16 dgp Exp $
00013  */
00014 
00015 #include "tclInt.h"
00016 #include "tommath.h"
00017 
00018 /*
00019  * Forward declaration.
00020  */
00021 struct Dict;
00022 
00023 /*
00024  * Prototypes for functions defined later in this file:
00025  */
00026 
00027 static void             DeleteDict(struct Dict *dict);
00028 static int              DictAppendCmd(ClientData dummy, Tcl_Interp *interp,
00029                             int objc, Tcl_Obj *const *objv);
00030 static int              DictCreateCmd(ClientData dummy, Tcl_Interp *interp,
00031                             int objc, Tcl_Obj *const *objv);
00032 static int              DictExistsCmd(ClientData dummy, Tcl_Interp *interp,
00033                             int objc, Tcl_Obj *const *objv);
00034 static int              DictFilterCmd(ClientData dummy, Tcl_Interp *interp,
00035                             int objc, Tcl_Obj *const *objv);
00036 static int              DictForCmd(ClientData dummy, Tcl_Interp *interp,
00037                             int objc, Tcl_Obj *const *objv);
00038 static int              DictGetCmd(ClientData dummy, Tcl_Interp *interp,
00039                             int objc, Tcl_Obj *const *objv);
00040 static int              DictIncrCmd(ClientData dummy, Tcl_Interp *interp,
00041                             int objc, Tcl_Obj *const *objv);
00042 static int              DictInfoCmd(ClientData dummy, Tcl_Interp *interp,
00043                             int objc, Tcl_Obj *const *objv);
00044 static int              DictKeysCmd(ClientData dummy, Tcl_Interp *interp,
00045                             int objc, Tcl_Obj *const *objv);
00046 static int              DictLappendCmd(ClientData dummy, Tcl_Interp *interp,
00047                             int objc, Tcl_Obj *const *objv);
00048 static int              DictMergeCmd(ClientData dummy, Tcl_Interp *interp,
00049                             int objc, Tcl_Obj *const *objv);
00050 static int              DictRemoveCmd(ClientData dummy, Tcl_Interp *interp,
00051                             int objc, Tcl_Obj *const *objv);
00052 static int              DictReplaceCmd(ClientData dummy, Tcl_Interp *interp,
00053                             int objc, Tcl_Obj *const *objv);
00054 static int              DictSetCmd(ClientData dummy, Tcl_Interp *interp,
00055                             int objc, Tcl_Obj *const *objv);
00056 static int              DictSizeCmd(ClientData dummy, Tcl_Interp *interp,
00057                             int objc, Tcl_Obj *const *objv);
00058 static int              DictUnsetCmd(ClientData dummy, Tcl_Interp *interp,
00059                             int objc, Tcl_Obj *const *objv);
00060 static int              DictUpdateCmd(ClientData dummy, Tcl_Interp *interp,
00061                             int objc, Tcl_Obj *const *objv);
00062 static int              DictValuesCmd(ClientData dummy, Tcl_Interp *interp,
00063                             int objc, Tcl_Obj *const *objv);
00064 static int              DictWithCmd(ClientData dummy, Tcl_Interp *interp,
00065                             int objc, Tcl_Obj *const *objv);
00066 static void             DupDictInternalRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr);
00067 static void             FreeDictInternalRep(Tcl_Obj *dictPtr);
00068 static void             InvalidateDictChain(Tcl_Obj *dictObj);
00069 static int              SetDictFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
00070 static void             UpdateStringOfDict(Tcl_Obj *dictPtr);
00071 static Tcl_HashEntry *  AllocChainEntry(Tcl_HashTable *tablePtr,void *keyPtr);
00072 static inline void      InitChainTable(struct Dict *dict);
00073 static inline void      DeleteChainTable(struct Dict *dict);
00074 static inline Tcl_HashEntry *CreateChainEntry(struct Dict *dict,
00075                             Tcl_Obj *keyPtr, int *newPtr);
00076 static inline int       DeleteChainEntry(struct Dict *dict, Tcl_Obj *keyPtr);
00077 
00078 /*
00079  * Table of dict subcommand names and implementations.
00080  */
00081 
00082 static const EnsembleImplMap implementationMap[] = {
00083     {"append",  DictAppendCmd,  TclCompileDictAppendCmd },
00084     {"create",  DictCreateCmd,  NULL },
00085     {"exists",  DictExistsCmd,  NULL },
00086     {"filter",  DictFilterCmd,  NULL },
00087     {"for",     DictForCmd,     TclCompileDictForCmd },
00088     {"get",     DictGetCmd,     TclCompileDictGetCmd },
00089     {"incr",    DictIncrCmd,    TclCompileDictIncrCmd },
00090     {"info",    DictInfoCmd,    NULL },
00091     {"keys",    DictKeysCmd,    NULL },
00092     {"lappend", DictLappendCmd, TclCompileDictLappendCmd },
00093     {"merge",   DictMergeCmd,   NULL },
00094     {"remove",  DictRemoveCmd,  NULL },
00095     {"replace", DictReplaceCmd, NULL },
00096     {"set",     DictSetCmd,     TclCompileDictSetCmd },
00097     {"size",    DictSizeCmd,    NULL },
00098     {"unset",   DictUnsetCmd,   NULL },
00099     {"update",  DictUpdateCmd,  TclCompileDictUpdateCmd },
00100     {"values",  DictValuesCmd,  NULL },
00101     {"with",    DictWithCmd,    NULL },
00102     {NULL}
00103 };
00104 
00105 /*
00106  * Internal representation of the entries in the hash table that backs a
00107  * dictionary.
00108  */
00109 
00110 typedef struct ChainEntry {
00111     Tcl_HashEntry entry;
00112     struct ChainEntry *prevPtr;
00113     struct ChainEntry *nextPtr;
00114 } ChainEntry;
00115 
00116 /*
00117  * Internal representation of a dictionary.
00118  *
00119  * The internal representation of a dictionary object is a hash table (with
00120  * Tcl_Objs for both keys and values), a reference count and epoch number for
00121  * detecting concurrent modifications of the dictionary, and a pointer to the
00122  * parent object (used when invalidating string reps of pathed dictionary
00123  * trees) which is NULL in normal use. The fact that hash tables know (with
00124  * appropriate initialisation) already about objects makes key management /so/
00125  * much easier!
00126  *
00127  * Reference counts are used to enable safe iteration across hashes while
00128  * allowing the type of the containing object to be modified.
00129  */
00130 
00131 typedef struct Dict {
00132     Tcl_HashTable table;        /* Object hash table to store mapping in. */
00133     ChainEntry *entryChainHead; /* Linked list of all entries in the
00134                                  * dictionary. Used for doing traversal of the
00135                                  * entries in the order that they are
00136                                  * created. */
00137     ChainEntry *entryChainTail; /* Other end of linked list of all entries in
00138                                  * the dictionary. Used for doing traversal of
00139                                  * the entries in the order that they are
00140                                  * created. */
00141     int epoch;                  /* Epoch counter */
00142     int refcount;               /* Reference counter (see above) */
00143     Tcl_Obj *chain;             /* Linked list used for invalidating the
00144                                  * string representations of updated nested
00145                                  * dictionaries. */
00146 } Dict;
00147 
00148 /*
00149  * The structure below defines the dictionary object type by means of
00150  * functions that can be invoked by generic object code.
00151  */
00152 
00153 Tcl_ObjType tclDictType = {
00154     "dict",
00155     FreeDictInternalRep,                /* freeIntRepProc */
00156     DupDictInternalRep,                 /* dupIntRepProc */
00157     UpdateStringOfDict,                 /* updateStringProc */
00158     SetDictFromAny                      /* setFromAnyProc */
00159 };
00160 
00161 /*
00162  * The type of the specially adapted version of the Tcl_Obj*-containing hash
00163  * table defined in the tclObj.c code. This version differs in that it
00164  * allocates a bit more space in each hash entry in order to hold the pointers
00165  * used to keep the hash entries in a linked list.
00166  *
00167  * Note that this type of hash table is *only* suitable for direct use in
00168  * *this* file. Everything else should use the dict iterator API.
00169  */
00170 
00171 static Tcl_HashKeyType chainHashType = {
00172     TCL_HASH_KEY_TYPE_VERSION,
00173     0,
00174     TclHashObjKey,
00175     TclCompareObjKeys,
00176     AllocChainEntry,
00177     TclFreeObjEntry
00178 };
00179 
00180 /***** START OF FUNCTIONS IMPLEMENTING DICT CORE API *****/
00181 
00182 /*
00183  *----------------------------------------------------------------------
00184  *
00185  * AllocChainEntry --
00186  *
00187  *      Allocate space for a Tcl_HashEntry containing the Tcl_Obj * key, and
00188  *      which has a bit of extra space afterwards for storing pointers to the
00189  *      rest of the chain of entries (the extra pointers are left NULL).
00190  *
00191  * Results:
00192  *      The return value is a pointer to the created entry.
00193  *
00194  * Side effects:
00195  *      Increments the reference count on the object.
00196  *
00197  *----------------------------------------------------------------------
00198  */
00199 
00200 static Tcl_HashEntry *
00201 AllocChainEntry(
00202     Tcl_HashTable *tablePtr,
00203     void *keyPtr)
00204 {
00205     Tcl_Obj *objPtr = keyPtr;
00206     ChainEntry *cPtr;
00207 
00208     cPtr = (ChainEntry *) ckalloc(sizeof(ChainEntry));
00209     cPtr->entry.key.oneWordValue = (char *) objPtr;
00210     Tcl_IncrRefCount(objPtr);
00211     cPtr->entry.clientData = NULL;
00212     cPtr->prevPtr = cPtr->nextPtr = NULL;
00213 
00214     return &cPtr->entry;
00215 }
00216 
00217 /*
00218  * Helper functions that disguise most of the details relating to how the
00219  * linked list of hash entries is managed. In particular, these manage the
00220  * creation of the table and initializing of the chain, the deletion of the
00221  * table and chain, the adding of an entry to the chain, and the removal of an
00222  * entry from the chain.
00223  */
00224 
00225 static inline void
00226 InitChainTable(
00227     Dict *dict)
00228 {
00229     Tcl_InitCustomHashTable(&dict->table, TCL_CUSTOM_PTR_KEYS,
00230             &chainHashType);
00231     dict->entryChainHead = dict->entryChainTail = NULL;
00232 }
00233 
00234 static inline void
00235 DeleteChainTable(
00236     Dict *dict)
00237 {
00238     ChainEntry *cPtr;
00239 
00240     for (cPtr=dict->entryChainHead ; cPtr!=NULL ; cPtr=cPtr->nextPtr) {
00241         Tcl_Obj *valuePtr = Tcl_GetHashValue(&cPtr->entry);
00242 
00243         TclDecrRefCount(valuePtr);
00244     }
00245     Tcl_DeleteHashTable(&dict->table);
00246 }
00247 
00248 static inline Tcl_HashEntry *
00249 CreateChainEntry(
00250     Dict *dict,
00251     Tcl_Obj *keyPtr,
00252     int *newPtr)
00253 {
00254     ChainEntry *cPtr = (ChainEntry *)
00255             Tcl_CreateHashEntry(&dict->table, (char *) keyPtr, newPtr);
00256 
00257     /*
00258      * If this is a new entry in the hash table, stitch it into the chain.
00259      */
00260 
00261     if (*newPtr) {
00262         cPtr->nextPtr = NULL;
00263         if (dict->entryChainHead == NULL) {
00264             cPtr->prevPtr = NULL;
00265             dict->entryChainHead = cPtr;
00266             dict->entryChainTail = cPtr;
00267         } else {
00268             cPtr->prevPtr = dict->entryChainTail;
00269             dict->entryChainTail->nextPtr = cPtr;
00270             dict->entryChainTail = cPtr;
00271         }
00272     }
00273 
00274     return &cPtr->entry;
00275 }
00276 
00277 static inline int
00278 DeleteChainEntry(
00279     Dict *dict,
00280     Tcl_Obj *keyPtr)
00281 {
00282     ChainEntry *cPtr = (ChainEntry *)
00283             Tcl_FindHashEntry(&dict->table, (char *) keyPtr);
00284 
00285     if (cPtr == NULL) {
00286         return 0;
00287     } else {
00288         Tcl_Obj *valuePtr = Tcl_GetHashValue(&cPtr->entry);
00289         TclDecrRefCount(valuePtr);
00290     }
00291 
00292     /*
00293      * Unstitch from the chain.
00294      */
00295 
00296     if (cPtr->nextPtr) {
00297         cPtr->nextPtr->prevPtr = cPtr->prevPtr;
00298     } else {
00299         dict->entryChainTail = cPtr->prevPtr;
00300     }
00301     if (cPtr->prevPtr) {
00302         cPtr->prevPtr->nextPtr = cPtr->nextPtr;
00303     } else {
00304         dict->entryChainHead = cPtr->nextPtr;
00305     }
00306 
00307     Tcl_DeleteHashEntry(&cPtr->entry);
00308     return 1;
00309 }
00310 
00311 /*
00312  *----------------------------------------------------------------------
00313  *
00314  * DupDictInternalRep --
00315  *
00316  *      Initialize the internal representation of a dictionary Tcl_Obj to a
00317  *      copy of the internal representation of an existing dictionary object.
00318  *
00319  * Results:
00320  *      None.
00321  *
00322  * Side effects:
00323  *      "srcPtr"s dictionary internal rep pointer should not be NULL and we
00324  *      assume it is not NULL. We set "copyPtr"s internal rep to a pointer to
00325  *      a newly allocated dictionary rep that, in turn, points to "srcPtr"s
00326  *      key and value objects. Those objects are not actually copied but are
00327  *      shared between "srcPtr" and "copyPtr". The ref count of each key and
00328  *      value object is incremented.
00329  *
00330  *----------------------------------------------------------------------
00331  */
00332 
00333 static void
00334 DupDictInternalRep(
00335     Tcl_Obj *srcPtr,
00336     Tcl_Obj *copyPtr)
00337 {
00338     Dict *oldDict = srcPtr->internalRep.otherValuePtr;
00339     Dict *newDict = (Dict *) ckalloc(sizeof(Dict));
00340     ChainEntry *cPtr;
00341 
00342     /*
00343      * Copy values across from the old hash table.
00344      */
00345 
00346     InitChainTable(newDict);
00347     for (cPtr=oldDict->entryChainHead ; cPtr!=NULL ; cPtr=cPtr->nextPtr) {
00348         void *key = Tcl_GetHashKey(&oldDict->table, &cPtr->entry);
00349         Tcl_Obj *valuePtr = Tcl_GetHashValue(&cPtr->entry);
00350         int n;
00351         Tcl_HashEntry *hPtr = CreateChainEntry(newDict, key, &n);
00352 
00353         /*
00354          * Fill in the contents.
00355          */
00356 
00357         Tcl_SetHashValue(hPtr, (ClientData) valuePtr);
00358         Tcl_IncrRefCount(valuePtr);
00359     }
00360 
00361     /*
00362      * Initialise other fields.
00363      */
00364 
00365     newDict->epoch = 0;
00366     newDict->chain = NULL;
00367     newDict->refcount = 1;
00368 
00369     /*
00370      * Store in the object.
00371      */
00372 
00373     copyPtr->internalRep.otherValuePtr = newDict;
00374     copyPtr->typePtr = &tclDictType;
00375 }
00376 
00377 /*
00378  *----------------------------------------------------------------------
00379  *
00380  * FreeDictInternalRep --
00381  *
00382  *      Deallocate the storage associated with a dictionary object's internal
00383  *      representation.
00384  *
00385  * Results:
00386  *      None
00387  *
00388  * Side effects:
00389  *      Frees the memory holding the dictionary's internal hash table unless
00390  *      it is locked by an iteration going over it.
00391  *
00392  *----------------------------------------------------------------------
00393  */
00394 
00395 static void
00396 FreeDictInternalRep(
00397     Tcl_Obj *dictPtr)
00398 {
00399     Dict *dict = dictPtr->internalRep.otherValuePtr;
00400 
00401     --dict->refcount;
00402     if (dict->refcount <= 0) {
00403         DeleteDict(dict);
00404     }
00405 
00406     dictPtr->internalRep.otherValuePtr = NULL;  /* Belt and braces! */
00407 }
00408 
00409 /*
00410  *----------------------------------------------------------------------
00411  *
00412  * DeleteDict --
00413  *
00414  *      Delete the structure that is used to implement a dictionary's internal
00415  *      representation. Called when either the dictionary object loses its
00416  *      internal representation or when the last iteration over the dictionary
00417  *      completes.
00418  *
00419  * Results:
00420  *      None
00421  *
00422  * Side effects:
00423  *      Decrements the reference count of all key and value objects in the
00424  *      dictionary, which may free them.
00425  *
00426  *----------------------------------------------------------------------
00427  */
00428 
00429 static void
00430 DeleteDict(
00431     Dict *dict)
00432 {
00433     DeleteChainTable(dict);
00434     ckfree((char *) dict);
00435 }
00436 
00437 /*
00438  *----------------------------------------------------------------------
00439  *
00440  * UpdateStringOfDict --
00441  *
00442  *      Update the string representation for a dictionary object. Note: This
00443  *      function does not invalidate an existing old string rep so storage
00444  *      will be lost if this has not already been done. This code is based on
00445  *      UpdateStringOfList in tclListObj.c
00446  *
00447  * Results:
00448  *      None.
00449  *
00450  * Side effects:
00451  *      The object's string is set to a valid string that results from the
00452  *      dict-to-string conversion. This string will be empty if the dictionary
00453  *      has no key/value pairs. The dictionary internal representation should
00454  *      not be NULL and we assume it is not NULL.
00455  *
00456  *----------------------------------------------------------------------
00457  */
00458 
00459 static void
00460 UpdateStringOfDict(
00461     Tcl_Obj *dictPtr)
00462 {
00463 #define LOCAL_SIZE 20
00464     int localFlags[LOCAL_SIZE], *flagPtr;
00465     Dict *dict = dictPtr->internalRep.otherValuePtr;
00466     ChainEntry *cPtr;
00467     Tcl_Obj *keyPtr, *valuePtr;
00468     int numElems, i, length;
00469     char *elem, *dst;
00470 
00471     /*
00472      * This field is the most useful one in the whole hash structure, and it
00473      * is not exposed by any API function...
00474      */
00475 
00476     numElems = dict->table.numEntries * 2;
00477 
00478     /*
00479      * Pass 1: estimate space, gather flags.
00480      */
00481 
00482     if (numElems <= LOCAL_SIZE) {
00483         flagPtr = localFlags;
00484     } else {
00485         flagPtr = (int *) ckalloc((unsigned) numElems*sizeof(int));
00486     }
00487     dictPtr->length = 1;
00488     for (i=0,cPtr=dict->entryChainHead; i<numElems; i+=2,cPtr=cPtr->nextPtr) {
00489         /*
00490          * Assume that cPtr is never NULL since we know the number of array
00491          * elements already.
00492          */
00493 
00494         keyPtr = (Tcl_Obj *) Tcl_GetHashKey(&dict->table, &cPtr->entry);
00495         elem = TclGetStringFromObj(keyPtr, &length);
00496         dictPtr->length += Tcl_ScanCountedElement(elem, length,
00497                 &flagPtr[i]) + 1;
00498 
00499         valuePtr = Tcl_GetHashValue(&cPtr->entry);
00500         elem = TclGetStringFromObj(valuePtr, &length);
00501         dictPtr->length += Tcl_ScanCountedElement(elem, length,
00502                 &flagPtr[i+1]) + 1;
00503     }
00504 
00505     /*
00506      * Pass 2: copy into string rep buffer.
00507      */
00508 
00509     dictPtr->bytes = ckalloc((unsigned) dictPtr->length);
00510     dst = dictPtr->bytes;
00511     for (i=0,cPtr=dict->entryChainHead; i<numElems; i+=2,cPtr=cPtr->nextPtr) {
00512         keyPtr = (Tcl_Obj *) Tcl_GetHashKey(&dict->table, &cPtr->entry);
00513         elem = TclGetStringFromObj(keyPtr, &length);
00514         dst += Tcl_ConvertCountedElement(elem, length, dst,
00515                 flagPtr[i] | (i==0 ? 0 : TCL_DONT_QUOTE_HASH));
00516         *(dst++) = ' ';
00517 
00518         valuePtr = Tcl_GetHashValue(&cPtr->entry);
00519         elem = TclGetStringFromObj(valuePtr, &length);
00520         dst += Tcl_ConvertCountedElement(elem, length, dst,
00521                 flagPtr[i+1] | TCL_DONT_QUOTE_HASH);
00522         *(dst++) = ' ';
00523     }
00524     if (flagPtr != localFlags) {
00525         ckfree((char *) flagPtr);
00526     }
00527     if (dst == dictPtr->bytes) {
00528         *dst = 0;
00529     } else {
00530         *(--dst) = 0;
00531     }
00532     dictPtr->length = dst - dictPtr->bytes;
00533 }
00534 
00535 /*
00536  *----------------------------------------------------------------------
00537  *
00538  * SetDictFromAny --
00539  *
00540  *      Convert a non-dictionary object into a dictionary object. This code is
00541  *      very closely related to SetListFromAny in tclListObj.c but does not
00542  *      actually guarantee that a dictionary object will have a string rep (as
00543  *      conversions from lists are handled with a special case.)
00544  *
00545  * Results:
00546  *      A standard Tcl result.
00547  *
00548  * Side effects:
00549  *      If the string can be converted, it loses any old internal
00550  *      representation that it had and gains a dictionary's internalRep.
00551  *
00552  *----------------------------------------------------------------------
00553  */
00554 
00555 static int
00556 SetDictFromAny(
00557     Tcl_Interp *interp,
00558     Tcl_Obj *objPtr)
00559 {
00560     char *string, *s;
00561     const char *elemStart, *nextElem;
00562     int lenRemain, length, elemSize, hasBrace, result, isNew;
00563     char *limit;                /* Points just after string's last byte. */
00564     register const char *p;
00565     register Tcl_Obj *keyPtr, *valuePtr;
00566     Dict *dict;
00567     Tcl_HashEntry *hPtr;
00568 
00569     /*
00570      * Since lists and dictionaries have very closely-related string
00571      * representations (i.e. the same parsing code) we can safely special-case
00572      * the conversion from lists to dictionaries.
00573      */
00574 
00575     if (objPtr->typePtr == &tclListType) {
00576         int objc, i;
00577         Tcl_Obj **objv;
00578 
00579         if (TclListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
00580             return TCL_ERROR;
00581         }
00582         if (objc & 1) {
00583             if (interp != NULL) {
00584                 Tcl_SetResult(interp, "missing value to go with key",
00585                         TCL_STATIC);
00586             }
00587             return TCL_ERROR;
00588         }
00589 
00590         /*
00591          * If the list is shared its string rep must not be lost so it still
00592          * is the same list.
00593          */
00594 
00595         if (Tcl_IsShared(objPtr)) {
00596             (void) TclGetString(objPtr);
00597         }
00598 
00599         /*
00600          * Build the hash of key/value pairs.
00601          */
00602 
00603         dict = (Dict *) ckalloc(sizeof(Dict));
00604         InitChainTable(dict);
00605         for (i=0 ; i<objc ; i+=2) {
00606             /*
00607              * Store key and value in the hash table we're building.
00608              */
00609 
00610             hPtr = CreateChainEntry(dict, objv[i], &isNew);
00611             if (!isNew) {
00612                 Tcl_Obj *discardedValue = Tcl_GetHashValue(hPtr);
00613 
00614                 TclDecrRefCount(discardedValue);
00615             }
00616             Tcl_SetHashValue(hPtr, objv[i+1]);
00617             Tcl_IncrRefCount(objv[i+1]); /* Since hash now holds ref to it */
00618         }
00619 
00620         /*
00621          * Share type-setting code with the string-conversion case.
00622          */
00623 
00624         goto installHash;
00625     }
00626 
00627     /*
00628      * Get the string representation. Make it up-to-date if necessary.
00629      */
00630 
00631     string = TclGetStringFromObj(objPtr, &length);
00632     limit = (string + length);
00633 
00634     /*
00635      * Allocate a new HashTable that has objects for keys and objects for
00636      * values.
00637      */
00638 
00639     dict = (Dict *) ckalloc(sizeof(Dict));
00640     InitChainTable(dict);
00641     for (p = string, lenRemain = length;
00642             lenRemain > 0;
00643             p = nextElem, lenRemain = (limit - nextElem)) {
00644         result = TclFindElement(interp, p, lenRemain,
00645                 &elemStart, &nextElem, &elemSize, &hasBrace);
00646         if (result != TCL_OK) {
00647             goto errorExit;
00648         }
00649         if (elemStart >= limit) {
00650             break;
00651         }
00652 
00653         /*
00654          * Allocate a Tcl object for the element and initialize it from the
00655          * "elemSize" bytes starting at "elemStart".
00656          */
00657 
00658         s = ckalloc((unsigned) elemSize + 1);
00659         if (hasBrace) {
00660             memcpy(s, elemStart, (size_t) elemSize);
00661             s[elemSize] = 0;
00662         } else {
00663             elemSize = TclCopyAndCollapse(elemSize, elemStart, s);
00664         }
00665 
00666         TclNewObj(keyPtr);
00667         keyPtr->bytes = s;
00668         keyPtr->length = elemSize;
00669 
00670         p = nextElem;
00671         lenRemain = (limit - nextElem);
00672         if (lenRemain <= 0) {
00673             goto missingKey;
00674         }
00675 
00676         result = TclFindElement(interp, p, lenRemain,
00677                 &elemStart, &nextElem, &elemSize, &hasBrace);
00678         if (result != TCL_OK) {
00679             TclDecrRefCount(keyPtr);
00680             goto errorExit;
00681         }
00682         if (elemStart >= limit) {
00683             goto missingKey;
00684         }
00685 
00686         /*
00687          * Allocate a Tcl object for the element and initialize it from the
00688          * "elemSize" bytes starting at "elemStart".
00689          */
00690 
00691         s = ckalloc((unsigned) elemSize + 1);
00692         if (hasBrace) {
00693             memcpy((void *) s, (void *) elemStart, (size_t) elemSize);
00694             s[elemSize] = 0;
00695         } else {
00696             elemSize = TclCopyAndCollapse(elemSize, elemStart, s);
00697         }
00698 
00699         TclNewObj(valuePtr);
00700         valuePtr->bytes = s;
00701         valuePtr->length = elemSize;
00702 
00703         /*
00704          * Store key and value in the hash table we're building.
00705          */
00706 
00707         hPtr = CreateChainEntry(dict, keyPtr, &isNew);
00708         if (!isNew) {
00709             Tcl_Obj *discardedValue = Tcl_GetHashValue(hPtr);
00710 
00711             TclDecrRefCount(keyPtr);
00712             TclDecrRefCount(discardedValue);
00713         }
00714         Tcl_SetHashValue(hPtr, valuePtr);
00715         Tcl_IncrRefCount(valuePtr); /* since hash now holds ref to it */
00716     }
00717 
00718   installHash:
00719     /*
00720      * Free the old internalRep before setting the new one. We do this as late
00721      * as possible to allow the conversion code, in particular
00722      * Tcl_GetStringFromObj, to use that old internalRep.
00723      */
00724 
00725     TclFreeIntRep(objPtr);
00726     dict->epoch = 0;
00727     dict->chain = NULL;
00728     dict->refcount = 1;
00729     objPtr->internalRep.otherValuePtr = dict;
00730     objPtr->typePtr = &tclDictType;
00731     return TCL_OK;
00732 
00733   missingKey:
00734     if (interp != NULL) {
00735         Tcl_SetResult(interp, "missing value to go with key", TCL_STATIC);
00736     }
00737     TclDecrRefCount(keyPtr);
00738     result = TCL_ERROR;
00739 
00740   errorExit:
00741     DeleteChainTable(dict);
00742     ckfree((char *) dict);
00743     return result;
00744 }
00745 
00746 /*
00747  *----------------------------------------------------------------------
00748  *
00749  * TclTraceDictPath --
00750  *
00751  *      Trace through a tree of dictionaries using the array of keys given. If
00752  *      the flags argument has the DICT_PATH_UPDATE flag is set, a
00753  *      backward-pointing chain of dictionaries is also built (in the Dict's
00754  *      chain field) and the chained dictionaries are made into unshared
00755  *      dictionaries (if they aren't already.)
00756  *
00757  * Results:
00758  *      The object at the end of the path, or NULL if there was an error. Note
00759  *      that this it is an error for an intermediate dictionary on the path to
00760  *      not exist. If the flags argument has the DICT_PATH_EXISTS set, a
00761  *      non-existent path gives a DICT_PATH_NON_EXISTENT result.
00762  *
00763  * Side effects:
00764  *      If the flags argument is zero or DICT_PATH_EXISTS, there are no side
00765  *      effects (other than potential conversion of objects to dictionaries.)
00766  *      If the flags argument is DICT_PATH_UPDATE, the following additional
00767  *      side effects occur. Shared dictionaries along the path are converted
00768  *      into unshared objects, and a backward-pointing chain is built using
00769  *      the chain fields of the dictionaries (for easy invalidation of string
00770  *      representations using InvalidateDictChain). If the flags argument has
00771  *      the DICT_PATH_CREATE bits set (and not the DICT_PATH_EXISTS bit),
00772  *      non-existant keys will be inserted with a value of an empty
00773  *      dictionary, resulting in the path being built.
00774  *
00775  *----------------------------------------------------------------------
00776  */
00777 
00778 Tcl_Obj *
00779 TclTraceDictPath(
00780     Tcl_Interp *interp,
00781     Tcl_Obj *dictPtr,
00782     int keyc,
00783     Tcl_Obj *const keyv[],
00784     int flags)
00785 {
00786     Dict *dict, *newDict;
00787     int i;
00788 
00789     if (dictPtr->typePtr != &tclDictType) {
00790         if (SetDictFromAny(interp, dictPtr) != TCL_OK) {
00791             return NULL;
00792         }
00793     }
00794     dict = dictPtr->internalRep.otherValuePtr;
00795     if (flags & DICT_PATH_UPDATE) {
00796         dict->chain = NULL;
00797     }
00798 
00799     for (i=0 ; i<keyc ; i++) {
00800         Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&dict->table, (char *)keyv[i]);
00801         Tcl_Obj *tmpObj;
00802 
00803         if (hPtr == NULL) {
00804             int isNew;                  /* Dummy */
00805 
00806             if (flags & DICT_PATH_EXISTS) {
00807                 return DICT_PATH_NON_EXISTENT;
00808             }
00809             if ((flags & DICT_PATH_CREATE) != DICT_PATH_CREATE) {
00810                 if (interp != NULL) {
00811                     Tcl_ResetResult(interp);
00812                     Tcl_AppendResult(interp, "key \"", TclGetString(keyv[i]),
00813                             "\" not known in dictionary", NULL);
00814                     Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "DICT",
00815                             TclGetString(keyv[i]), NULL);
00816                 }
00817                 return NULL;
00818             }
00819 
00820             /*
00821              * The next line should always set isNew to 1.
00822              */
00823 
00824             hPtr = CreateChainEntry(dict, keyv[i], &isNew);
00825             tmpObj = Tcl_NewDictObj();
00826             Tcl_IncrRefCount(tmpObj);
00827             Tcl_SetHashValue(hPtr, tmpObj);
00828         } else {
00829             tmpObj = Tcl_GetHashValue(hPtr);
00830             if (tmpObj->typePtr != &tclDictType) {
00831                 if (SetDictFromAny(interp, tmpObj) != TCL_OK) {
00832                     return NULL;
00833                 }
00834             }
00835         }
00836 
00837         newDict = tmpObj->internalRep.otherValuePtr;
00838         if (flags & DICT_PATH_UPDATE) {
00839             if (Tcl_IsShared(tmpObj)) {
00840                 TclDecrRefCount(tmpObj);
00841                 tmpObj = Tcl_DuplicateObj(tmpObj);
00842                 Tcl_IncrRefCount(tmpObj);
00843                 Tcl_SetHashValue(hPtr, (ClientData) tmpObj);
00844                 dict->epoch++;
00845                 newDict = tmpObj->internalRep.otherValuePtr;
00846             }
00847 
00848             newDict->chain = dictPtr;
00849         }
00850         dict = newDict;
00851         dictPtr = tmpObj;
00852     }
00853     return dictPtr;
00854 }
00855 
00856 /*
00857  *----------------------------------------------------------------------
00858  *
00859  * InvalidateDictChain --
00860  *
00861  *      Go through a dictionary chain (built by an updating invokation of
00862  *      TclTraceDictPath) and invalidate the string representations of all the
00863  *      dictionaries on the chain.
00864  *
00865  * Results:
00866  *      None
00867  *
00868  * Side effects:
00869  *      String reps are invalidated and epoch counters (for detecting illegal
00870  *      concurrent modifications) are updated through the chain of updated
00871  *      dictionaries.
00872  *
00873  *----------------------------------------------------------------------
00874  */
00875 
00876 static void
00877 InvalidateDictChain(
00878     Tcl_Obj *dictObj)
00879 {
00880     Dict *dict = dictObj->internalRep.otherValuePtr;
00881 
00882     do {
00883         Tcl_InvalidateStringRep(dictObj);
00884         dict->epoch++;
00885         dictObj = dict->chain;
00886         if (dictObj == NULL) {
00887             break;
00888         }
00889         dict->chain = NULL;
00890         dict = dictObj->internalRep.otherValuePtr;
00891     } while (dict != NULL);
00892 }
00893 
00894 /*
00895  *----------------------------------------------------------------------
00896  *
00897  * Tcl_DictObjPut --
00898  *
00899  *      Add a key,value pair to a dictionary, or update the value for a key if
00900  *      that key already has a mapping in the dictionary.
00901  *
00902  * Results:
00903  *      A standard Tcl result.
00904  *
00905  * Side effects:
00906  *      The object pointed to by dictPtr is converted to a dictionary if it is
00907  *      not already one, and any string representation that it has is
00908  *      invalidated.
00909  *
00910  *----------------------------------------------------------------------
00911  */
00912 
00913 int
00914 Tcl_DictObjPut(
00915     Tcl_Interp *interp,
00916     Tcl_Obj *dictPtr,
00917     Tcl_Obj *keyPtr,
00918     Tcl_Obj *valuePtr)
00919 {
00920     Dict *dict;
00921     Tcl_HashEntry *hPtr;
00922     int isNew;
00923 
00924     if (Tcl_IsShared(dictPtr)) {
00925         Tcl_Panic("%s called with shared object", "Tcl_DictObjPut");
00926     }
00927 
00928     if (dictPtr->typePtr != &tclDictType) {
00929         int result = SetDictFromAny(interp, dictPtr);
00930 
00931         if (result != TCL_OK) {
00932             return result;
00933         }
00934     }
00935 
00936     if (dictPtr->bytes != NULL) {
00937         Tcl_InvalidateStringRep(dictPtr);
00938     }
00939     dict = dictPtr->internalRep.otherValuePtr;
00940     hPtr = CreateChainEntry(dict, keyPtr, &isNew);
00941     Tcl_IncrRefCount(valuePtr);
00942     if (!isNew) {
00943         Tcl_Obj *oldValuePtr = Tcl_GetHashValue(hPtr);
00944 
00945         TclDecrRefCount(oldValuePtr);
00946     }
00947     Tcl_SetHashValue(hPtr, valuePtr);
00948     dict->epoch++;
00949     return TCL_OK;
00950 }
00951 
00952 /*
00953  *----------------------------------------------------------------------
00954  *
00955  * Tcl_DictObjGet --
00956  *
00957  *      Given a key, get its value from the dictionary (or NULL if key is not
00958  *      found in dictionary.)
00959  *
00960  * Results:
00961  *      A standard Tcl result. The variable pointed to by valuePtrPtr is
00962  *      updated with the value for the key. Note that it is not an error for
00963  *      the key to have no mapping in the dictionary.
00964  *
00965  * Side effects:
00966  *      The object pointed to by dictPtr is converted to a dictionary if it is
00967  *      not already one.
00968  *
00969  *----------------------------------------------------------------------
00970  */
00971 
00972 int
00973 Tcl_DictObjGet(
00974     Tcl_Interp *interp,
00975     Tcl_Obj *dictPtr,
00976     Tcl_Obj *keyPtr,
00977     Tcl_Obj **valuePtrPtr)
00978 {
00979     Dict *dict;
00980     Tcl_HashEntry *hPtr;
00981 
00982     if (dictPtr->typePtr != &tclDictType) {
00983         int result = SetDictFromAny(interp, dictPtr);
00984         if (result != TCL_OK) {
00985             return result;
00986         }
00987     }
00988 
00989     dict = dictPtr->internalRep.otherValuePtr;
00990     hPtr = Tcl_FindHashEntry(&dict->table, (char *) keyPtr);
00991     if (hPtr == NULL) {
00992         *valuePtrPtr = NULL;
00993     } else {
00994         *valuePtrPtr = Tcl_GetHashValue(hPtr);
00995     }
00996     return TCL_OK;
00997 }
00998 
00999 /*
01000  *----------------------------------------------------------------------
01001  *
01002  * Tcl_DictObjRemove --
01003  *
01004  *      Remove the key,value pair with the given key from the dictionary; the
01005  *      key does not need to be present in the dictionary.
01006  *
01007  * Results:
01008  *      A standard Tcl result.
01009  *
01010  * Side effects:
01011  *      The object pointed to by dictPtr is converted to a dictionary if it is
01012  *      not already one, and any string representation that it has is
01013  *      invalidated.
01014  *
01015  *----------------------------------------------------------------------
01016  */
01017 
01018 int
01019 Tcl_DictObjRemove(
01020     Tcl_Interp *interp,
01021     Tcl_Obj *dictPtr,
01022     Tcl_Obj *keyPtr)
01023 {
01024     Dict *dict;
01025 
01026     if (Tcl_IsShared(dictPtr)) {
01027         Tcl_Panic("%s called with shared object", "Tcl_DictObjRemove");
01028     }
01029 
01030     if (dictPtr->typePtr != &tclDictType) {
01031         int result = SetDictFromAny(interp, dictPtr);
01032         if (result != TCL_OK) {
01033             return result;
01034         }
01035     }
01036 
01037     if (dictPtr->bytes != NULL) {
01038         Tcl_InvalidateStringRep(dictPtr);
01039     }
01040     dict = dictPtr->internalRep.otherValuePtr;
01041     if (DeleteChainEntry(dict, keyPtr)) {
01042         dict->epoch++;
01043     }
01044     return TCL_OK;
01045 }
01046 
01047 /*
01048  *----------------------------------------------------------------------
01049  *
01050  * Tcl_DictObjSize --
01051  *
01052  *      How many key,value pairs are there in the dictionary?
01053  *
01054  * Results:
01055  *      A standard Tcl result. Updates the variable pointed to by sizePtr with
01056  *      the number of key,value pairs in the dictionary.
01057  *
01058  * Side effects:
01059  *      The dictPtr object is converted to a dictionary type if it is not a
01060  *      dictionary already.
01061  *
01062  *----------------------------------------------------------------------
01063  */
01064 
01065 int
01066 Tcl_DictObjSize(
01067     Tcl_Interp *interp,
01068     Tcl_Obj *dictPtr,
01069     int *sizePtr)
01070 {
01071     Dict *dict;
01072 
01073     if (dictPtr->typePtr != &tclDictType) {
01074         int result = SetDictFromAny(interp, dictPtr);
01075         if (result != TCL_OK) {
01076             return result;
01077         }
01078     }
01079 
01080     dict = dictPtr->internalRep.otherValuePtr;
01081     *sizePtr = dict->table.numEntries;
01082     return TCL_OK;
01083 }
01084 
01085 /*
01086  *----------------------------------------------------------------------
01087  *
01088  * Tcl_DictObjFirst --
01089  *
01090  *      Start a traversal of the dictionary. Caller must supply the search
01091  *      context, pointers for returning key and value, and a pointer to allow
01092  *      indication of whether the dictionary has been traversed (i.e. the
01093  *      dictionary is empty). The order of traversal is undefined.
01094  *
01095  * Results:
01096  *      A standard Tcl result. Updates the variables pointed to by keyPtrPtr,
01097  *      valuePtrPtr and donePtr. Either of keyPtrPtr and valuePtrPtr may be
01098  *      NULL, in which case the key/value is not made available to the caller.
01099  *
01100  * Side effects:
01101  *      The dictPtr object is converted to a dictionary type if it is not a
01102  *      dictionary already. The search context is initialised if the search
01103  *      has not finished. The dictionary's internal rep is Tcl_Preserve()d if
01104  *      the dictionary has at least one element.
01105  *
01106  *----------------------------------------------------------------------
01107  */
01108 
01109 int
01110 Tcl_DictObjFirst(
01111     Tcl_Interp *interp,         /* For error messages, or NULL if no error
01112                                  * messages desired. */
01113     Tcl_Obj *dictPtr,           /* Dictionary to traverse. */
01114     Tcl_DictSearch *searchPtr,  /* Pointer to a dict search context. */
01115     Tcl_Obj **keyPtrPtr,        /* Pointer to a variable to have the first key
01116                                  * written into, or NULL. */
01117     Tcl_Obj **valuePtrPtr,      /* Pointer to a variable to have the first
01118                                  * value written into, or NULL.*/
01119     int *donePtr)               /* Pointer to a variable which will have a 1
01120                                  * written into when there are no further
01121                                  * values in the dictionary, or a 0
01122                                  * otherwise. */
01123 {
01124     Dict *dict;
01125     ChainEntry *cPtr;
01126 
01127     if (dictPtr->typePtr != &tclDictType) {
01128         int result = SetDictFromAny(interp, dictPtr);
01129 
01130         if (result != TCL_OK) {
01131             return result;
01132         }
01133     }
01134 
01135     dict = dictPtr->internalRep.otherValuePtr;
01136     cPtr = dict->entryChainHead;
01137     if (cPtr == NULL) {
01138         searchPtr->epoch = -1;
01139         *donePtr = 1;
01140     } else {
01141         *donePtr = 0;
01142         searchPtr->dictionaryPtr = (Tcl_Dict) dict;
01143         searchPtr->epoch = dict->epoch;
01144         searchPtr->next = cPtr->nextPtr;
01145         dict->refcount++;
01146         if (keyPtrPtr != NULL) {
01147             *keyPtrPtr = (Tcl_Obj *) Tcl_GetHashKey(&dict->table,
01148                     &cPtr->entry);
01149         }
01150         if (valuePtrPtr != NULL) {
01151             *valuePtrPtr = Tcl_GetHashValue(&cPtr->entry);
01152         }
01153     }
01154     return TCL_OK;
01155 }
01156 
01157 /*
01158  *----------------------------------------------------------------------
01159  *
01160  * Tcl_DictObjNext --
01161  *
01162  *      Continue a traversal of a dictionary previously started with
01163  *      Tcl_DictObjFirst. This function is safe against concurrent
01164  *      modification of the underlying object (including type shimmering),
01165  *      treating such situations as if the search has terminated, though it is
01166  *      up to the caller to ensure that the object itself is not disposed
01167  *      until the search has finished. It is _not_ safe against modifications
01168  *      from other threads.
01169  *
01170  * Results:
01171  *      Updates the variables pointed to by keyPtrPtr, valuePtrPtr and
01172  *      donePtr. Either of keyPtrPtr and valuePtrPtr may be NULL, in which
01173  *      case the key/value is not made available to the caller.
01174  *
01175  * Side effects:
01176  *      Removes a reference to the dictionary's internal rep if the search
01177  *      terminates.
01178  *
01179  *----------------------------------------------------------------------
01180  */
01181 
01182 void
01183 Tcl_DictObjNext(
01184     Tcl_DictSearch *searchPtr,  /* Pointer to a hash search context. */
01185     Tcl_Obj **keyPtrPtr,        /* Pointer to a variable to have the first key
01186                                  * written into, or NULL. */
01187     Tcl_Obj **valuePtrPtr,      /* Pointer to a variable to have the first
01188                                  * value written into, or NULL.*/
01189     int *donePtr)               /* Pointer to a variable which will have a 1
01190                                  * written into when there are no further
01191                                  * values in the dictionary, or a 0
01192                                  * otherwise. */
01193 {
01194     ChainEntry *cPtr;
01195 
01196     /*
01197      * If the searh is done; we do no work.
01198      */
01199 
01200     if (searchPtr->epoch == -1) {
01201         *donePtr = 1;
01202         return;
01203     }
01204 
01205     /*
01206      * Bail out if the dictionary has had any elements added, modified or
01207      * removed. This *shouldn't* happen, but...
01208      */
01209 
01210     if (((Dict *)searchPtr->dictionaryPtr)->epoch != searchPtr->epoch) {
01211         Tcl_Panic("concurrent dictionary modification and search");
01212     }
01213 
01214     cPtr = searchPtr->next;
01215     if (cPtr == NULL) {
01216         Tcl_DictObjDone(searchPtr);
01217         *donePtr = 1;
01218         return;
01219     }
01220 
01221     searchPtr->next = cPtr->nextPtr;
01222     *donePtr = 0;
01223     if (keyPtrPtr != NULL) {
01224         *keyPtrPtr = (Tcl_Obj *) Tcl_GetHashKey(
01225                 &((Dict *)searchPtr->dictionaryPtr)->table, &cPtr->entry);
01226     }
01227     if (valuePtrPtr != NULL) {
01228         *valuePtrPtr = Tcl_GetHashValue(&cPtr->entry);
01229     }
01230 }
01231 
01232 /*
01233  *----------------------------------------------------------------------
01234  *
01235  * Tcl_DictObjDone --
01236  *
01237  *      Call this if you want to stop a search before you reach the end of the
01238  *      dictionary (e.g. because of abnormal termination of the search). It
01239  *      need not be used if the search reaches its natural end (i.e. if either
01240  *      Tcl_DictObjFirst or Tcl_DictObjNext sets its donePtr variable to 1).
01241  *
01242  * Results:
01243  *      None.
01244  *
01245  * Side effects:
01246  *      Removes a reference to the dictionary's internal rep.
01247  *
01248  *----------------------------------------------------------------------
01249  */
01250 
01251 void
01252 Tcl_DictObjDone(
01253     Tcl_DictSearch *searchPtr)          /* Pointer to a hash search context. */
01254 {
01255     Dict *dict;
01256 
01257     if (searchPtr->epoch != -1) {
01258         searchPtr->epoch = -1;
01259         dict = (Dict *) searchPtr->dictionaryPtr;
01260         dict->refcount--;
01261         if (dict->refcount <= 0) {
01262             DeleteDict(dict);
01263         }
01264     }
01265 }
01266 
01267 /*
01268  *----------------------------------------------------------------------
01269  *
01270  * Tcl_DictObjPutKeyList --
01271  *
01272  *      Add a key...key,value pair to a dictionary tree. The main dictionary
01273  *      value must not be shared, though sub-dictionaries may be. All
01274  *      intermediate dictionaries on the path must exist.
01275  *
01276  * Results:
01277  *      A standard Tcl result. Note that in the error case, a message is left
01278  *      in interp unless that is NULL.
01279  *
01280  * Side effects:
01281  *      If the dictionary and any of its sub-dictionaries on the path have
01282  *      string representations, these are invalidated.
01283  *
01284  *----------------------------------------------------------------------
01285  */
01286 
01287 int
01288 Tcl_DictObjPutKeyList(
01289     Tcl_Interp *interp,
01290     Tcl_Obj *dictPtr,
01291     int keyc,
01292     Tcl_Obj *const keyv[],
01293     Tcl_Obj *valuePtr)
01294 {
01295     Dict *dict;
01296     Tcl_HashEntry *hPtr;
01297     int isNew;
01298 
01299     if (Tcl_IsShared(dictPtr)) {
01300         Tcl_Panic("%s called with shared object", "Tcl_DictObjPutKeyList");
01301     }
01302     if (keyc < 1) {
01303         Tcl_Panic("%s called with empty key list", "Tcl_DictObjPutKeyList");
01304     }
01305 
01306     dictPtr = TclTraceDictPath(interp, dictPtr, keyc-1,keyv, DICT_PATH_CREATE);
01307     if (dictPtr == NULL) {
01308         return TCL_ERROR;
01309     }
01310 
01311     dict = dictPtr->internalRep.otherValuePtr;
01312     hPtr = CreateChainEntry(dict, keyv[keyc-1], &isNew);
01313     Tcl_IncrRefCount(valuePtr);
01314     if (!isNew) {
01315         Tcl_Obj *oldValuePtr = Tcl_GetHashValue(hPtr);
01316         TclDecrRefCount(oldValuePtr);
01317     }
01318     Tcl_SetHashValue(hPtr, valuePtr);
01319     InvalidateDictChain(dictPtr);
01320 
01321     return TCL_OK;
01322 }
01323 
01324 /*
01325  *----------------------------------------------------------------------
01326  *
01327  * Tcl_DictObjRemoveKeyList --
01328  *
01329  *      Remove a key...key,value pair from a dictionary tree (the value
01330  *      removed is implicit in the key path). The main dictionary value must
01331  *      not be shared, though sub-dictionaries may be. It is not an error if
01332  *      there is no value associated with the given key list, but all
01333  *      intermediate dictionaries on the key path must exist.
01334  *
01335  * Results:
01336  *      A standard Tcl result. Note that in the error case, a message is left
01337  *      in interp unless that is NULL.
01338  *
01339  * Side effects:
01340  *      If the dictionary and any of its sub-dictionaries on the key path have
01341  *      string representations, these are invalidated.
01342  *
01343  *----------------------------------------------------------------------
01344  */
01345 
01346 int
01347 Tcl_DictObjRemoveKeyList(
01348     Tcl_Interp *interp,
01349     Tcl_Obj *dictPtr,
01350     int keyc,
01351     Tcl_Obj *const keyv[])
01352 {
01353     Dict *dict;
01354 
01355     if (Tcl_IsShared(dictPtr)) {
01356         Tcl_Panic("%s called with shared object", "Tcl_DictObjRemoveKeyList");
01357     }
01358     if (keyc < 1) {
01359         Tcl_Panic("%s called with empty key list", "Tcl_DictObjRemoveKeyList");
01360     }
01361 
01362     dictPtr = TclTraceDictPath(interp, dictPtr, keyc-1,keyv, DICT_PATH_UPDATE);
01363     if (dictPtr == NULL) {
01364         return TCL_ERROR;
01365     }
01366 
01367     dict = dictPtr->internalRep.otherValuePtr;
01368     DeleteChainEntry(dict, keyv[keyc-1]);
01369     InvalidateDictChain(dictPtr);
01370     return TCL_OK;
01371 }
01372 
01373 /*
01374  *----------------------------------------------------------------------
01375  *
01376  * Tcl_NewDictObj --
01377  *
01378  *      This function is normally called when not debugging: i.e., when
01379  *      TCL_MEM_DEBUG is not defined. It creates a new dict object without any
01380  *      content.
01381  *
01382  *      When TCL_MEM_DEBUG is defined, this function just returns the result
01383  *      of calling the debugging version Tcl_DbNewDictObj.
01384  *
01385  * Results:
01386  *      A new dict object is returned; it has no keys defined in it. The new
01387  *      object's string representation is left NULL, and the ref count of the
01388  *      object is 0.
01389  *
01390  * Side Effects:
01391  *      None.
01392  *
01393  *----------------------------------------------------------------------
01394  */
01395 
01396 Tcl_Obj *
01397 Tcl_NewDictObj(void)
01398 {
01399 #ifdef TCL_MEM_DEBUG
01400     return Tcl_DbNewDictObj("unknown", 0);
01401 #else /* !TCL_MEM_DEBUG */
01402 
01403     Tcl_Obj *dictPtr;
01404     Dict *dict;
01405 
01406     TclNewObj(dictPtr);
01407     Tcl_InvalidateStringRep(dictPtr);
01408     dict = (Dict *) ckalloc(sizeof(Dict));
01409     InitChainTable(dict);
01410     dict->epoch = 0;
01411     dict->chain = NULL;
01412     dict->refcount = 1;
01413     dictPtr->internalRep.otherValuePtr = dict;
01414     dictPtr->typePtr = &tclDictType;
01415     return dictPtr;
01416 #endif
01417 }
01418 
01419 /*
01420  *----------------------------------------------------------------------
01421  *
01422  * Tcl_DbNewDictObj --
01423  *
01424  *      This function is normally called when debugging: i.e., when
01425  *      TCL_MEM_DEBUG is defined. It creates new dict objects. It is the same
01426  *      as the Tcl_NewDictObj function above except that it calls
01427  *      Tcl_DbCkalloc directly with the file name and line number from its
01428  *      caller. This simplifies debugging since then the [memory active]
01429  *      command will report the correct file name and line number when
01430  *      reporting objects that haven't been freed.
01431  *
01432  *      When TCL_MEM_DEBUG is not defined, this function just returns the
01433  *      result of calling Tcl_NewDictObj.
01434  *
01435  * Results:
01436  *      A new dict object is returned; it has no keys defined in it. The new
01437  *      object's string representation is left NULL, and the ref count of the
01438  *      object is 0.
01439  *
01440  * Side Effects:
01441  *      None.
01442  *
01443  *----------------------------------------------------------------------
01444  */
01445 
01446 Tcl_Obj *
01447 Tcl_DbNewDictObj(
01448     const char *file,
01449     int line)
01450 {
01451 #ifdef TCL_MEM_DEBUG
01452     Tcl_Obj *dictPtr;
01453     Dict *dict;
01454 
01455     TclDbNewObj(dictPtr, file, line);
01456     Tcl_InvalidateStringRep(dictPtr);
01457     dict = (Dict *) ckalloc(sizeof(Dict));
01458     InitChainTable(dict);
01459     dict->epoch = 0;
01460     dict->chain = NULL;
01461     dict->refcount = 1;
01462     dictPtr->internalRep.otherValuePtr = dict;
01463     dictPtr->typePtr = &tclDictType;
01464     return dictPtr;
01465 #else /* !TCL_MEM_DEBUG */
01466     return Tcl_NewDictObj();
01467 #endif
01468 }
01469 
01470 /***** START OF FUNCTIONS IMPLEMENTING TCL COMMANDS *****/
01471 
01472 /*
01473  *----------------------------------------------------------------------
01474  *
01475  * DictCreateCmd --
01476  *
01477  *      This function implements the "dict create" Tcl command. See the user
01478  *      documentation for details on what it does, and TIP#111 for the formal
01479  *      specification.
01480  *
01481  * Results:
01482  *      A standard Tcl result.
01483  *
01484  * Side effects:
01485  *      See the user documentation.
01486  *
01487  *----------------------------------------------------------------------
01488  */
01489 
01490 static int
01491 DictCreateCmd(
01492     ClientData dummy,
01493     Tcl_Interp *interp,
01494     int objc,
01495     Tcl_Obj *const *objv)
01496 {
01497     Tcl_Obj *dictObj;
01498     int i;
01499 
01500     /*
01501      * Must have an even number of arguments; note that number of preceding
01502      * arguments (i.e. "dict create" is also even, which makes this much
01503      * easier.)
01504      */
01505 
01506     if ((objc & 1) == 0) {
01507         Tcl_WrongNumArgs(interp, 1, objv, "?key value ...?");
01508         return TCL_ERROR;
01509     }
01510 
01511     dictObj = Tcl_NewDictObj();
01512     for (i=1 ; i<objc ; i+=2) {
01513         /*
01514          * The next command is assumed to never fail...
01515          */
01516         Tcl_DictObjPut(interp, dictObj, objv[i], objv[i+1]);
01517     }
01518     Tcl_SetObjResult(interp, dictObj);
01519     return TCL_OK;
01520 }
01521 
01522 /*
01523  *----------------------------------------------------------------------
01524  *
01525  * DictGetCmd --
01526  *
01527  *      This function implements the "dict get" Tcl command. See the user
01528  *      documentation for details on what it does, and TIP#111 for the formal
01529  *      specification.
01530  *
01531  * Results:
01532  *      A standard Tcl result.
01533  *
01534  * Side effects:
01535  *      See the user documentation.
01536  *
01537  *----------------------------------------------------------------------
01538  */
01539 
01540 static int
01541 DictGetCmd(
01542     ClientData dummy,
01543     Tcl_Interp *interp,
01544     int objc,
01545     Tcl_Obj *const *objv)
01546 {
01547     Tcl_Obj *dictPtr, *valuePtr = NULL;
01548     int result;
01549 
01550     if (objc < 2) {
01551         Tcl_WrongNumArgs(interp, 1, objv, "dictionary ?key key ...?");
01552         return TCL_ERROR;
01553     }
01554 
01555     /*
01556      * Test for the special case of no keys, which returns a *list* of all
01557      * key,value pairs. We produce a copy here because that makes subsequent
01558      * list handling more efficient.
01559      */
01560 
01561     if (objc == 2) {
01562         Tcl_Obj *keyPtr, *listPtr;
01563         Tcl_DictSearch search;
01564         int done;
01565 
01566         result = Tcl_DictObjFirst(interp, objv[1], &search,
01567                 &keyPtr, &valuePtr, &done);
01568         if (result != TCL_OK) {
01569             return result;
01570         }
01571         listPtr = Tcl_NewListObj(0, NULL);
01572         while (!done) {
01573             /*
01574              * Assume these won't fail as we have complete control over the
01575              * types of things here.
01576              */
01577 
01578             Tcl_ListObjAppendElement(interp, listPtr, keyPtr);
01579             Tcl_ListObjAppendElement(interp, listPtr, valuePtr);
01580 
01581             Tcl_DictObjNext(&search, &keyPtr, &valuePtr, &done);
01582         }
01583         Tcl_SetObjResult(interp, listPtr);
01584         return TCL_OK;
01585     }
01586 
01587     /*
01588      * Loop through the list of keys, looking up the key at the current index
01589      * in the current dictionary each time. Once we've done the lookup, we set
01590      * the current dictionary to be the value we looked up (in case the value
01591      * was not the last one and we are going through a chain of searches.)
01592      * Note that this loop always executes at least once.
01593      */
01594 
01595     dictPtr = TclTraceDictPath(interp, objv[1], objc-3,objv+2, DICT_PATH_READ);
01596     if (dictPtr == NULL) {
01597         return TCL_ERROR;
01598     }
01599     result = Tcl_DictObjGet(interp, dictPtr, objv[objc-1], &valuePtr);
01600     if (result != TCL_OK) {
01601         return result;
01602     }
01603     if (valuePtr == NULL) {
01604         Tcl_ResetResult(interp);
01605         Tcl_AppendResult(interp, "key \"", TclGetString(objv[objc-1]),
01606                 "\" not known in dictionary", NULL);
01607         return TCL_ERROR;
01608     }
01609     Tcl_SetObjResult(interp, valuePtr);
01610     return TCL_OK;
01611 }
01612 
01613 /*
01614  *----------------------------------------------------------------------
01615  *
01616  * DictReplaceCmd --
01617  *
01618  *      This function implements the "dict replace" Tcl command. See the user
01619  *      documentation for details on what it does, and TIP#111 for the formal
01620  *      specification.
01621  *
01622  * Results:
01623  *      A standard Tcl result.
01624  *
01625  * Side effects:
01626  *      See the user documentation.
01627  *
01628  *----------------------------------------------------------------------
01629  */
01630 
01631 static int
01632 DictReplaceCmd(
01633     ClientData dummy,
01634     Tcl_Interp *interp,
01635     int objc,
01636     Tcl_Obj *const *objv)
01637 {
01638     Tcl_Obj *dictPtr;
01639     int i, result;
01640     int allocatedDict = 0;
01641 
01642     if ((objc < 2) || (objc & 1)) {
01643         Tcl_WrongNumArgs(interp, 1, objv, "dictionary ?key value ...?");
01644         return TCL_ERROR;
01645     }
01646 
01647     dictPtr = objv[1];
01648     if (Tcl_IsShared(dictPtr)) {
01649         dictPtr = Tcl_DuplicateObj(dictPtr);
01650         allocatedDict = 1;
01651     }
01652     for (i=2 ; i<objc ; i+=2) {
01653         result = Tcl_DictObjPut(interp, dictPtr, objv[i], objv[i+1]);
01654         if (result != TCL_OK) {
01655             if (allocatedDict) {
01656                 TclDecrRefCount(dictPtr);
01657             }
01658             return TCL_ERROR;
01659         }
01660     }
01661     Tcl_SetObjResult(interp, dictPtr);
01662     return TCL_OK;
01663 }
01664 
01665 /*
01666  *----------------------------------------------------------------------
01667  *
01668  * DictRemoveCmd --
01669  *
01670  *      This function implements the "dict remove" Tcl command. See the user
01671  *      documentation for details on what it does, and TIP#111 for the formal
01672  *      specification.
01673  *
01674  * Results:
01675  *      A standard Tcl result.
01676  *
01677  * Side effects:
01678  *      See the user documentation.
01679  *
01680  *----------------------------------------------------------------------
01681  */
01682 
01683 static int
01684 DictRemoveCmd(
01685     ClientData dummy,
01686     Tcl_Interp *interp,
01687     int objc,
01688     Tcl_Obj *const *objv)
01689 {
01690     Tcl_Obj *dictPtr;
01691     int i, result;
01692     int allocatedDict = 0;
01693 
01694     if (objc < 2) {
01695         Tcl_WrongNumArgs(interp, 1, objv, "dictionary ?key ...?");
01696         return TCL_ERROR;
01697     }
01698 
01699     dictPtr = objv[1];
01700     if (Tcl_IsShared(dictPtr)) {
01701         dictPtr = Tcl_DuplicateObj(dictPtr);
01702         allocatedDict = 1;
01703     }
01704     for (i=2 ; i<objc ; i++) {
01705         result = Tcl_DictObjRemove(interp, dictPtr, objv[i]);
01706         if (result != TCL_OK) {
01707             if (allocatedDict) {
01708                 TclDecrRefCount(dictPtr);
01709             }
01710             return TCL_ERROR;
01711         }
01712     }
01713     Tcl_SetObjResult(interp, dictPtr);
01714     return TCL_OK;
01715 }
01716 
01717 /*
01718  *----------------------------------------------------------------------
01719  *
01720  * DictMergeCmd --
01721  *
01722  *      This function implements the "dict merge" Tcl command. See the user
01723  *      documentation for details on what it does, and TIP#163 for the formal
01724  *      specification.
01725  *
01726  * Results:
01727  *      A standard Tcl result.
01728  *
01729  * Side effects:
01730  *      See the user documentation.
01731  *
01732  *----------------------------------------------------------------------
01733  */
01734 
01735 static int
01736 DictMergeCmd(
01737     ClientData dummy,
01738     Tcl_Interp *interp,
01739     int objc,
01740     Tcl_Obj *const *objv)
01741 {
01742     Tcl_Obj *targetObj, *keyObj, *valueObj;
01743     int allocatedDict = 0;
01744     int i, done;
01745     Tcl_DictSearch search;
01746 
01747     if (objc == 1) {
01748         /*
01749          * No dictionary arguments; return default (empty value).
01750          */
01751 
01752         return TCL_OK;
01753     }
01754 
01755     /*
01756      * Make sure first argument is a dictionary.
01757      */
01758 
01759     targetObj = objv[1];
01760     if (targetObj->typePtr != &tclDictType) {
01761         if (SetDictFromAny(interp, targetObj) != TCL_OK) {
01762             return TCL_ERROR;
01763         }
01764     }
01765 
01766     if (objc == 2) {
01767         /*
01768          * Single argument, return it.
01769          */
01770 
01771         Tcl_SetObjResult(interp, objv[1]);
01772         return TCL_OK;
01773     }
01774 
01775     /*
01776      * Normal behaviour: combining two (or more) dictionaries.
01777      */
01778 
01779     if (Tcl_IsShared(targetObj)) {
01780         targetObj = Tcl_DuplicateObj(targetObj);
01781         allocatedDict = 1;
01782     }
01783     for (i=2 ; i<objc ; i++) {
01784         if (Tcl_DictObjFirst(interp, objv[i], &search, &keyObj, &valueObj,
01785                 &done) != TCL_OK) {
01786             if (allocatedDict) {
01787                 TclDecrRefCount(targetObj);
01788             }
01789             return TCL_ERROR;
01790         }
01791         while (!done) {
01792             /*
01793              * Next line can't fail; already know we have a dictionary in
01794              * targetObj.
01795              */
01796 
01797             Tcl_DictObjPut(NULL, targetObj, keyObj, valueObj);
01798             Tcl_DictObjNext(&search, &keyObj, &valueObj, &done);
01799         }
01800         Tcl_DictObjDone(&search);
01801     }
01802     Tcl_SetObjResult(interp, targetObj);
01803     return TCL_OK;
01804 }
01805 
01806 /*
01807  *----------------------------------------------------------------------
01808  *
01809  * DictKeysCmd --
01810  *
01811  *      This function implements the "dict keys" Tcl command. See the user
01812  *      documentation for details on what it does, and TIP#111 for the formal
01813  *      specification.
01814  *
01815  * Results:
01816  *      A standard Tcl result.
01817  *
01818  * Side effects:
01819  *      See the user documentation.
01820  *
01821  *----------------------------------------------------------------------
01822  */
01823 
01824 static int
01825 DictKeysCmd(
01826     ClientData dummy,
01827     Tcl_Interp *interp,
01828     int objc,
01829     Tcl_Obj *const *objv)
01830 {
01831     Tcl_Obj *listPtr;
01832     char *pattern = NULL;
01833 
01834     if (objc!=2 && objc!=3) {
01835         Tcl_WrongNumArgs(interp, 1, objv, "dictionary ?pattern?");
01836         return TCL_ERROR;
01837     }
01838 
01839     /*
01840      * A direct check that we have a dictionary. We don't start the iteration
01841      * yet because that might allocate memory or set locks that we do not
01842      * need. [Bug 1705778, leak K04]
01843      */
01844 
01845     if (objv[1]->typePtr != &tclDictType) {
01846         int result = SetDictFromAny(interp, objv[1]);
01847 
01848         if (result != TCL_OK) {
01849             return result;
01850         }
01851     }
01852 
01853     if (objc == 3) {
01854         pattern = TclGetString(objv[2]);
01855     }
01856     listPtr = Tcl_NewListObj(0, NULL);
01857     if ((pattern != NULL) && TclMatchIsTrivial(pattern)) {
01858         Tcl_Obj *valuePtr = NULL;
01859 
01860         Tcl_DictObjGet(interp, objv[1], objv[2], &valuePtr);
01861         if (valuePtr != NULL) {
01862             Tcl_ListObjAppendElement(NULL, listPtr, objv[2]);
01863         }
01864     } else {
01865         Tcl_DictSearch search;
01866         Tcl_Obj *keyPtr;
01867         int done;
01868 
01869         /*
01870          * At this point, we know we have a dictionary (or at least something
01871          * that can be represented; it could theoretically have shimmered away
01872          * when the pattern was fetched, but that shouldn't be damaging) so we
01873          * can start the iteration process without checking for failures.
01874          */
01875 
01876         Tcl_DictObjFirst(NULL, objv[1], &search, &keyPtr, NULL, &done);
01877         for (; !done ; Tcl_DictObjNext(&search, &keyPtr, NULL, &done)) {
01878             if (!pattern || Tcl_StringMatch(TclGetString(keyPtr), pattern)) {
01879                 Tcl_ListObjAppendElement(NULL, listPtr, keyPtr);
01880             }
01881         }
01882         Tcl_DictObjDone(&search);
01883     }
01884 
01885     Tcl_SetObjResult(interp, listPtr);
01886     return TCL_OK;
01887 }
01888 
01889 /*
01890  *----------------------------------------------------------------------
01891  *
01892  * DictValuesCmd --
01893  *
01894  *      This function implements the "dict values" Tcl command. See the user
01895  *      documentation for details on what it does, and TIP#111 for the formal
01896  *      specification.
01897  *
01898  * Results:
01899  *      A standard Tcl result.
01900  *
01901  * Side effects:
01902  *      See the user documentation.
01903  *
01904  *----------------------------------------------------------------------
01905  */
01906 
01907 static int
01908 DictValuesCmd(
01909     ClientData dummy,
01910     Tcl_Interp *interp,
01911     int objc,
01912     Tcl_Obj *const *objv)
01913 {
01914     Tcl_Obj *valuePtr, *listPtr;
01915     Tcl_DictSearch search;
01916     int done;
01917     char *pattern;
01918 
01919     if (objc!=2 && objc!=3) {
01920         Tcl_WrongNumArgs(interp, 1, objv, "dictionary ?pattern?");
01921         return TCL_ERROR;
01922     }
01923 
01924     if (Tcl_DictObjFirst(interp, objv[1], &search, NULL, &valuePtr,
01925             &done) != TCL_OK) {
01926         return TCL_ERROR;
01927     }
01928     if (objc == 3) {
01929         pattern = TclGetString(objv[2]);
01930     } else {
01931         pattern = NULL;
01932     }
01933     listPtr = Tcl_NewListObj(0, NULL);
01934     for (; !done ; Tcl_DictObjNext(&search, NULL, &valuePtr, &done)) {
01935         if (pattern==NULL || Tcl_StringMatch(TclGetString(valuePtr),pattern)) {
01936             /*
01937              * Assume this operation always succeeds.
01938              */
01939 
01940             Tcl_ListObjAppendElement(interp, listPtr, valuePtr);
01941         }
01942     }
01943     Tcl_DictObjDone(&search);
01944 
01945     Tcl_SetObjResult(interp, listPtr);
01946     return TCL_OK;
01947 }
01948 
01949 /*
01950  *----------------------------------------------------------------------
01951  *
01952  * DictSizeCmd --
01953  *
01954  *      This function implements the "dict size" Tcl command. See the user
01955  *      documentation for details on what it does, and TIP#111 for the formal
01956  *      specification.
01957  *
01958  * Results:
01959  *      A standard Tcl result.
01960  *
01961  * Side effects:
01962  *      See the user documentation.
01963  *
01964  *----------------------------------------------------------------------
01965  */
01966 
01967 static int
01968 DictSizeCmd(
01969     ClientData dummy,
01970     Tcl_Interp *interp,
01971     int objc,
01972     Tcl_Obj *const *objv)
01973 {
01974     int result, size;
01975 
01976     if (objc != 2) {
01977         Tcl_WrongNumArgs(interp, 1, objv, "dictionary");
01978         return TCL_ERROR;
01979     }
01980     result = Tcl_DictObjSize(interp, objv[1], &size);
01981     if (result == TCL_OK) {
01982         Tcl_SetObjResult(interp, Tcl_NewIntObj(size));
01983     }
01984     return result;
01985 }
01986 
01987 /*
01988  *----------------------------------------------------------------------
01989  *
01990  * DictExistsCmd --
01991  *
01992  *      This function implements the "dict exists" Tcl command. See the user
01993  *      documentation for details on what it does, and TIP#111 for the formal
01994  *      specification.
01995  *
01996  * Results:
01997  *      A standard Tcl result.
01998  *
01999  * Side effects:
02000  *      See the user documentation.
02001  *
02002  *----------------------------------------------------------------------
02003  */
02004 
02005 static int
02006 DictExistsCmd(
02007     ClientData dummy,
02008     Tcl_Interp *interp,
02009     int objc,
02010     Tcl_Obj *const *objv)
02011 {
02012     Tcl_Obj *dictPtr, *valuePtr;
02013     int result;
02014 
02015     if (objc < 3) {
02016         Tcl_WrongNumArgs(interp, 1, objv, "dictionary key ?key ...?");
02017         return TCL_ERROR;
02018     }
02019 
02020     dictPtr = TclTraceDictPath(interp, objv[1], objc-3, objv+2,
02021             DICT_PATH_EXISTS);
02022     if (dictPtr == NULL) {
02023         return TCL_ERROR;
02024     }
02025     if (dictPtr == DICT_PATH_NON_EXISTENT) {
02026         Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0));
02027         return TCL_OK;
02028     }
02029     result = Tcl_DictObjGet(interp, dictPtr, objv[objc-1], &valuePtr);
02030     if (result != TCL_OK) {
02031         return result;
02032     }
02033     Tcl_SetObjResult(interp, Tcl_NewBooleanObj(valuePtr != NULL));
02034     return TCL_OK;
02035 }
02036 
02037 /*
02038  *----------------------------------------------------------------------
02039  *
02040  * DictInfoCmd --
02041  *
02042  *      This function implements the "dict info" Tcl command. See the user
02043  *      documentation for details on what it does, and TIP#111 for the formal
02044  *      specification.
02045  *
02046  * Results:
02047  *      A standard Tcl result.
02048  *
02049  * Side effects:
02050  *      See the user documentation.
02051  *
02052  *----------------------------------------------------------------------
02053  */
02054 
02055 static int
02056 DictInfoCmd(
02057     ClientData dummy,
02058     Tcl_Interp *interp,
02059     int objc,
02060     Tcl_Obj *const *objv)
02061 {
02062     Tcl_Obj *dictPtr;
02063     Dict *dict;
02064 
02065     if (objc != 2) {
02066         Tcl_WrongNumArgs(interp, 1, objv, "dictionary");
02067         return TCL_ERROR;
02068     }
02069 
02070     dictPtr = objv[1];
02071     if (dictPtr->typePtr != &tclDictType) {
02072         int result = SetDictFromAny(interp, dictPtr);
02073         if (result != TCL_OK) {
02074             return result;
02075         }
02076     }
02077     dict = dictPtr->internalRep.otherValuePtr;
02078 
02079     /*
02080      * This next cast is actually OK.
02081      */
02082 
02083     Tcl_SetResult(interp, (char *) Tcl_HashStats(&dict->table), TCL_DYNAMIC);
02084     return TCL_OK;
02085 }
02086 
02087 /*
02088  *----------------------------------------------------------------------
02089  *
02090  * DictIncrCmd --
02091  *
02092  *      This function implements the "dict incr" Tcl command. See the user
02093  *      documentation for details on what it does, and TIP#111 for the formal
02094  *      specification.
02095  *
02096  * Results:
02097  *      A standard Tcl result.
02098  *
02099  * Side effects:
02100  *      See the user documentation.
02101  *
02102  *----------------------------------------------------------------------
02103  */
02104 
02105 static int
02106 DictIncrCmd(
02107     ClientData dummy,
02108     Tcl_Interp *interp,
02109     int objc,
02110     Tcl_Obj *const *objv)
02111 {
02112     int code = TCL_OK;
02113     Tcl_Obj *dictPtr, *valuePtr = NULL;
02114 
02115     if (objc < 3 || objc > 4) {
02116         Tcl_WrongNumArgs(interp, 1, objv, "varName key ?increment?");
02117         return TCL_ERROR;
02118     }
02119 
02120     dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0);
02121     if (dictPtr == NULL) {
02122         /*
02123          * Variable didn't yet exist. Create new dictionary value.
02124          */
02125 
02126         dictPtr = Tcl_NewDictObj();
02127     } else if (Tcl_DictObjGet(interp, dictPtr, objv[2], &valuePtr) != TCL_OK) {
02128         /*
02129          * Variable contents are not a dict, report error.
02130          */
02131 
02132         return TCL_ERROR;
02133     }
02134     if (Tcl_IsShared(dictPtr)) {
02135         /*
02136          * A little internals surgery to avoid copying a string rep that will
02137          * soon be no good.
02138          */
02139 
02140         char *saved = dictPtr->bytes;
02141 
02142         dictPtr->bytes = NULL;
02143         dictPtr = Tcl_DuplicateObj(dictPtr);
02144         dictPtr->bytes = saved;
02145     }
02146     if (valuePtr == NULL) {
02147         /*
02148          * Key not in dictionary. Create new key with increment as value.
02149          */
02150 
02151         if (objc == 4) {
02152             /*
02153              * Verify increment is an integer.
02154              */
02155 
02156             mp_int increment;
02157 
02158             code = Tcl_GetBignumFromObj(interp, objv[3], &increment);
02159             if (code != TCL_OK) {
02160                 Tcl_AddErrorInfo(interp, "\n    (reading increment)");
02161             } else {
02162                 Tcl_DictObjPut(interp, dictPtr, objv[2], objv[3]);
02163             }
02164         } else {
02165             Tcl_DictObjPut(interp, dictPtr, objv[2], Tcl_NewIntObj(1));
02166         }
02167     } else {
02168         /*
02169          * Key in dictionary. Increment its value with minimum dup.
02170          */
02171 
02172         if (Tcl_IsShared(valuePtr)) {
02173             valuePtr = Tcl_DuplicateObj(valuePtr);
02174             Tcl_DictObjPut(interp, dictPtr, objv[2], valuePtr);
02175         }
02176         if (objc == 4) {
02177             code = TclIncrObj(interp, valuePtr, objv[3]);
02178         } else {
02179             Tcl_Obj *incrPtr = Tcl_NewIntObj(1);
02180 
02181             Tcl_IncrRefCount(incrPtr);
02182             code = TclIncrObj(interp, valuePtr, incrPtr);
02183             Tcl_DecrRefCount(incrPtr);
02184         }
02185     }
02186     if (code == TCL_OK) {
02187         Tcl_InvalidateStringRep(dictPtr);
02188         valuePtr = Tcl_ObjSetVar2(interp, objv[1], NULL,
02189                 dictPtr, TCL_LEAVE_ERR_MSG);
02190         if (valuePtr == NULL) {
02191             code = TCL_ERROR;
02192         } else {
02193             Tcl_SetObjResult(interp, valuePtr);
02194         }
02195     } else if (dictPtr->refCount == 0) {
02196         Tcl_DecrRefCount(dictPtr);
02197     }
02198     return code;
02199 }
02200 
02201 /*
02202  *----------------------------------------------------------------------
02203  *
02204  * DictLappendCmd --
02205  *
02206  *      This function implements the "dict lappend" Tcl command. See the user
02207  *      documentation for details on what it does, and TIP#111 for the formal
02208  *      specification.
02209  *
02210  * Results:
02211  *      A standard Tcl result.
02212  *
02213  * Side effects:
02214  *      See the user documentation.
02215  *
02216  *----------------------------------------------------------------------
02217  */
02218 
02219 static int
02220 DictLappendCmd(
02221     ClientData dummy,
02222     Tcl_Interp *interp,
02223     int objc,
02224     Tcl_Obj *const *objv)
02225 {
02226     Tcl_Obj *dictPtr, *valuePtr, *resultPtr;
02227     int i, allocatedDict = 0, allocatedValue = 0;
02228 
02229     if (objc < 3) {
02230         Tcl_WrongNumArgs(interp, 1, objv, "varName key ?value ...?");
02231         return TCL_ERROR;
02232     }
02233 
02234     dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0);
02235     if (dictPtr == NULL) {
02236         allocatedDict = 1;
02237         dictPtr = Tcl_NewDictObj();
02238     } else if (Tcl_IsShared(dictPtr)) {
02239         allocatedDict = 1;
02240         dictPtr = Tcl_DuplicateObj(dictPtr);
02241     }
02242 
02243     if (Tcl_DictObjGet(interp, dictPtr, objv[2], &valuePtr) != TCL_OK) {
02244         if (allocatedDict) {
02245             TclDecrRefCount(dictPtr);
02246         }
02247         return TCL_ERROR;
02248     }
02249 
02250     if (valuePtr == NULL) {
02251         valuePtr = Tcl_NewListObj(objc-3, objv+3);
02252         allocatedValue = 1;
02253     } else {
02254         if (Tcl_IsShared(valuePtr)) {
02255             allocatedValue = 1;
02256             valuePtr = Tcl_DuplicateObj(valuePtr);
02257         }
02258 
02259         for (i=3 ; i<objc ; i++) {
02260             if (Tcl_ListObjAppendElement(interp, valuePtr,
02261                     objv[i]) != TCL_OK) {
02262                 if (allocatedValue) {
02263                     TclDecrRefCount(valuePtr);
02264                 }
02265                 if (allocatedDict) {
02266                     TclDecrRefCount(dictPtr);
02267                 }
02268                 return TCL_ERROR;
02269             }
02270         }
02271     }
02272 
02273     if (allocatedValue) {
02274         Tcl_DictObjPut(interp, dictPtr, objv[2], valuePtr);
02275     } else if (dictPtr->bytes != NULL) {
02276         Tcl_InvalidateStringRep(dictPtr);
02277     }
02278 
02279     resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr,
02280             TCL_LEAVE_ERR_MSG);
02281     if (resultPtr == NULL) {
02282         return TCL_ERROR;
02283     }
02284     Tcl_SetObjResult(interp, resultPtr);
02285     return TCL_OK;
02286 }
02287 
02288 /*
02289  *----------------------------------------------------------------------
02290  *
02291  * DictAppendCmd --
02292  *
02293  *      This function implements the "dict append" Tcl command. See the user
02294  *      documentation for details on what it does, and TIP#111 for the formal
02295  *      specification.
02296  *
02297  * Results:
02298  *      A standard Tcl result.
02299  *
02300  * Side effects:
02301  *      See the user documentation.
02302  *
02303  *----------------------------------------------------------------------
02304  */
02305 
02306 static int
02307 DictAppendCmd(
02308     ClientData dummy,
02309     Tcl_Interp *interp,
02310     int objc,
02311     Tcl_Obj *const *objv)
02312 {
02313     Tcl_Obj *dictPtr, *valuePtr, *resultPtr;
02314     int i, allocatedDict = 0;
02315 
02316     if (objc < 3) {
02317         Tcl_WrongNumArgs(interp, 1, objv, "varName key ?value ...?");
02318         return TCL_ERROR;
02319     }
02320 
02321     dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0);
02322     if (dictPtr == NULL) {
02323         allocatedDict = 1;
02324         dictPtr = Tcl_NewDictObj();
02325     } else if (Tcl_IsShared(dictPtr)) {
02326         allocatedDict = 1;
02327         dictPtr = Tcl_DuplicateObj(dictPtr);
02328     }
02329 
02330     if (Tcl_DictObjGet(interp, dictPtr, objv[2], &valuePtr) != TCL_OK) {
02331         if (allocatedDict) {
02332             TclDecrRefCount(dictPtr);
02333         }
02334         return TCL_ERROR;
02335     }
02336 
02337     if (valuePtr == NULL) {
02338         TclNewObj(valuePtr);
02339     } else {
02340         if (Tcl_IsShared(valuePtr)) {
02341             valuePtr = Tcl_DuplicateObj(valuePtr);
02342         }
02343     }
02344 
02345     for (i=3 ; i<objc ; i++) {
02346         Tcl_AppendObjToObj(valuePtr, objv[i]);
02347     }
02348 
02349     Tcl_DictObjPut(interp, dictPtr, objv[2], valuePtr);
02350 
02351     resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr,
02352             TCL_LEAVE_ERR_MSG);
02353     if (resultPtr == NULL) {
02354         return TCL_ERROR;
02355     }
02356     Tcl_SetObjResult(interp, resultPtr);
02357     return TCL_OK;
02358 }
02359 
02360 /*
02361  *----------------------------------------------------------------------
02362  *
02363  * DictForCmd --
02364  *
02365  *      This function implements the "dict for" Tcl command. See the user
02366  *      documentation for details on what it does, and TIP#111 for the formal
02367  *      specification.
02368  *
02369  * Results:
02370  *      A standard Tcl result.
02371  *
02372  * Side effects:
02373  *      See the user documentation.
02374  *
02375  *----------------------------------------------------------------------
02376  */
02377 
02378 static int
02379 DictForCmd(
02380     ClientData dummy,
02381     Tcl_Interp *interp,
02382     int objc,
02383     Tcl_Obj *const *objv)
02384 {
02385     Interp *iPtr = (Interp *) interp;
02386     Tcl_Obj *scriptObj, *keyVarObj, *valueVarObj;
02387     Tcl_Obj **varv, *keyObj, *valueObj;
02388     Tcl_DictSearch search;
02389     int varc, done, result;
02390 
02391     if (objc != 4) {
02392         Tcl_WrongNumArgs(interp, 1, objv,
02393                 "{keyVar valueVar} dictionary script");
02394         return TCL_ERROR;
02395     }
02396 
02397     if (TclListObjGetElements(interp, objv[1], &varc, &varv) != TCL_OK) {
02398         return TCL_ERROR;
02399     }
02400     if (varc != 2) {
02401         Tcl_SetResult(interp, "must have exactly two variable names",
02402                 TCL_STATIC);
02403         return TCL_ERROR;
02404     }
02405     keyVarObj = varv[0];
02406     valueVarObj = varv[1];
02407     scriptObj = objv[3];
02408 
02409     if (Tcl_DictObjFirst(interp, objv[2], &search, &keyObj, &valueObj,
02410             &done) != TCL_OK) {
02411         return TCL_ERROR;
02412     }
02413 
02414     /*
02415      * Make sure that these objects (which we need throughout the body of the
02416      * loop) don't vanish. Note that the dictionary internal rep is locked
02417      * internally so that updates, shimmering, etc are not a problem.
02418      */
02419 
02420     Tcl_IncrRefCount(keyVarObj);
02421     Tcl_IncrRefCount(valueVarObj);
02422     Tcl_IncrRefCount(scriptObj);
02423 
02424     result = TCL_OK;
02425     while (!done) {
02426         /*
02427          * Stop the value from getting hit in any way by any traces on the key
02428          * variable.
02429          */
02430 
02431         Tcl_IncrRefCount(valueObj);
02432         if (Tcl_ObjSetVar2(interp, keyVarObj, NULL, keyObj, 0) == NULL) {
02433             Tcl_ResetResult(interp);
02434             Tcl_AppendResult(interp, "couldn't set key variable: \"",
02435                     TclGetString(keyVarObj), "\"", NULL);
02436             TclDecrRefCount(valueObj);
02437             result = TCL_ERROR;
02438             break;
02439         }
02440         TclDecrRefCount(valueObj);
02441         if (Tcl_ObjSetVar2(interp, valueVarObj, NULL, valueObj, 0) == NULL) {
02442             Tcl_ResetResult(interp);
02443             Tcl_AppendResult(interp, "couldn't set value variable: \"",
02444                     TclGetString(valueVarObj), "\"", NULL);
02445             result = TCL_ERROR;
02446             break;
02447         }
02448 
02449         /*
02450          * TIP #280. Make invoking context available to loop body.
02451          */
02452 
02453         result = TclEvalObjEx(interp, scriptObj, 0, iPtr->cmdFramePtr, 3);
02454         if (result == TCL_CONTINUE) {
02455             result = TCL_OK;
02456         } else if (result != TCL_OK) {
02457             if (result == TCL_BREAK) {
02458                 result = TCL_OK;
02459             } else if (result == TCL_ERROR) {
02460                 Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
02461                         "\n    (\"dict for\" body line %d)",
02462                         interp->errorLine));
02463             }
02464             break;
02465         }
02466 
02467         Tcl_DictObjNext(&search, &keyObj, &valueObj, &done);
02468     }
02469 
02470     /*
02471      * Stop holding a reference to these objects.
02472      */
02473 
02474     TclDecrRefCount(keyVarObj);
02475     TclDecrRefCount(valueVarObj);
02476     TclDecrRefCount(scriptObj);
02477 
02478     Tcl_DictObjDone(&search);
02479     if (result == TCL_OK) {
02480         Tcl_ResetResult(interp);
02481     }
02482     return result;
02483 }
02484 
02485 /*
02486  *----------------------------------------------------------------------
02487  *
02488  * DictSetCmd --
02489  *
02490  *      This function implements the "dict set" Tcl command. See the user
02491  *      documentation for details on what it does, and TIP#111 for the formal
02492  *      specification.
02493  *
02494  * Results:
02495  *      A standard Tcl result.
02496  *
02497  * Side effects:
02498  *      See the user documentation.
02499  *
02500  *----------------------------------------------------------------------
02501  */
02502 
02503 static int
02504 DictSetCmd(
02505     ClientData dummy,
02506     Tcl_Interp *interp,
02507     int objc,
02508     Tcl_Obj *const *objv)
02509 {
02510     Tcl_Obj *dictPtr, *resultPtr;
02511     int result, allocatedDict = 0;
02512 
02513     if (objc < 4) {
02514         Tcl_WrongNumArgs(interp, 1, objv, "varName key ?key ...? value");
02515         return TCL_ERROR;
02516     }
02517 
02518     dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0);
02519     if (dictPtr == NULL) {
02520         allocatedDict = 1;
02521         dictPtr = Tcl_NewDictObj();
02522     } else if (Tcl_IsShared(dictPtr)) {
02523         allocatedDict = 1;
02524         dictPtr = Tcl_DuplicateObj(dictPtr);
02525     }
02526 
02527     result = Tcl_DictObjPutKeyList(interp, dictPtr, objc-3, objv+2,
02528             objv[objc-1]);
02529     if (result != TCL_OK) {
02530         if (allocatedDict) {
02531             TclDecrRefCount(dictPtr);
02532         }
02533         return TCL_ERROR;
02534     }
02535 
02536     resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr,
02537             TCL_LEAVE_ERR_MSG);
02538     if (resultPtr == NULL) {
02539         return TCL_ERROR;
02540     }
02541     Tcl_SetObjResult(interp, resultPtr);
02542     return TCL_OK;
02543 }
02544 
02545 /*
02546  *----------------------------------------------------------------------
02547  *
02548  * DictUnsetCmd --
02549  *
02550  *      This function implements the "dict unset" Tcl command. See the user
02551  *      documentation for details on what it does, and TIP#111 for the formal
02552  *      specification.
02553  *
02554  * Results:
02555  *      A standard Tcl result.
02556  *
02557  * Side effects:
02558  *      See the user documentation.
02559  *
02560  *----------------------------------------------------------------------
02561  */
02562 
02563 static int
02564 DictUnsetCmd(
02565     ClientData dummy,
02566     Tcl_Interp *interp,
02567     int objc,
02568     Tcl_Obj *const *objv)
02569 {
02570     Tcl_Obj *dictPtr, *resultPtr;
02571     int result, allocatedDict = 0;
02572 
02573     if (objc < 3) {
02574         Tcl_WrongNumArgs(interp, 1, objv, "varName key ?key ...?");
02575         return TCL_ERROR;
02576     }
02577 
02578     dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0);
02579     if (dictPtr == NULL) {
02580         allocatedDict = 1;
02581         dictPtr = Tcl_NewDictObj();
02582     } else if (Tcl_IsShared(dictPtr)) {
02583         allocatedDict = 1;
02584         dictPtr = Tcl_DuplicateObj(dictPtr);
02585     }
02586 
02587     result = Tcl_DictObjRemoveKeyList(interp, dictPtr, objc-2, objv+2);
02588     if (result != TCL_OK) {
02589         if (allocatedDict) {
02590             TclDecrRefCount(dictPtr);
02591         }
02592         return TCL_ERROR;
02593     }
02594 
02595     resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr,
02596             TCL_LEAVE_ERR_MSG);
02597     if (resultPtr == NULL) {
02598         return TCL_ERROR;
02599     }
02600     Tcl_SetObjResult(interp, resultPtr);
02601     return TCL_OK;
02602 }
02603 
02604 /*
02605  *----------------------------------------------------------------------
02606  *
02607  * DictFilterCmd --
02608  *
02609  *      This function implements the "dict filter" Tcl command. See the user
02610  *      documentation for details on what it does, and TIP#111 for the formal
02611  *      specification.
02612  *
02613  * Results:
02614  *      A standard Tcl result.
02615  *
02616  * Side effects:
02617  *      See the user documentation.
02618  *
02619  *----------------------------------------------------------------------
02620  */
02621 
02622 static int
02623 DictFilterCmd(
02624     ClientData dummy,
02625     Tcl_Interp *interp,
02626     int objc,
02627     Tcl_Obj *const *objv)
02628 {
02629     Interp *iPtr = (Interp *) interp;
02630     static const char *filters[] = {
02631         "key", "script", "value", NULL
02632     };
02633     enum FilterTypes {
02634         FILTER_KEYS, FILTER_SCRIPT, FILTER_VALUES
02635     };
02636     Tcl_Obj *scriptObj, *keyVarObj, *valueVarObj;
02637     Tcl_Obj **varv, *keyObj, *valueObj, *resultObj, *boolObj;
02638     Tcl_DictSearch search;
02639     int index, varc, done, result, satisfied;
02640     char *pattern;
02641 
02642     if (objc < 3) {
02643         Tcl_WrongNumArgs(interp, 1, objv, "dictionary filterType ...");
02644         return TCL_ERROR;
02645     }
02646     if (Tcl_GetIndexFromObj(interp, objv[2], filters, "filterType",
02647              0, &index) != TCL_OK) {
02648         return TCL_ERROR;
02649     }
02650 
02651     switch ((enum FilterTypes) index) {
02652     case FILTER_KEYS:
02653         if (objc != 4) {
02654             Tcl_WrongNumArgs(interp, 1, objv, "dictionary key globPattern");
02655             return TCL_ERROR;
02656         }
02657 
02658         /*
02659          * Create a dictionary whose keys all match a certain pattern.
02660          */
02661 
02662         if (Tcl_DictObjFirst(interp, objv[1], &search,
02663                 &keyObj, &valueObj, &done) != TCL_OK) {
02664             return TCL_ERROR;
02665         }
02666         pattern = TclGetString(objv[3]);
02667         resultObj = Tcl_NewDictObj();
02668         if (TclMatchIsTrivial(pattern)) {
02669             /*
02670              * Must release the search lock here to prevent a memory leak
02671              * since we are not exhausing the search. [Bug 1705778, leak K05]
02672              */
02673 
02674             Tcl_DictObjDone(&search);
02675             Tcl_DictObjGet(interp, objv[1], objv[3], &valueObj);
02676             if (valueObj != NULL) {
02677                 Tcl_DictObjPut(interp, resultObj, objv[3], valueObj);
02678             }
02679         } else {
02680             while (!done) {
02681                 if (Tcl_StringMatch(TclGetString(keyObj), pattern)) {
02682                     Tcl_DictObjPut(interp, resultObj, keyObj, valueObj);
02683                 }
02684                 Tcl_DictObjNext(&search, &keyObj, &valueObj, &done);
02685             }
02686         }
02687         Tcl_SetObjResult(interp, resultObj);
02688         return TCL_OK;
02689 
02690     case FILTER_VALUES:
02691         if (objc != 4) {
02692             Tcl_WrongNumArgs(interp, 1, objv, "dictionary value globPattern");
02693             return TCL_ERROR;
02694         }
02695 
02696         /*
02697          * Create a dictionary whose values all match a certain pattern.
02698          */
02699 
02700         if (Tcl_DictObjFirst(interp, objv[1], &search,
02701                 &keyObj, &valueObj, &done) != TCL_OK) {
02702             return TCL_ERROR;
02703         }
02704         pattern = TclGetString(objv[3]);
02705         resultObj = Tcl_NewDictObj();
02706         while (!done) {
02707             if (Tcl_StringMatch(TclGetString(valueObj), pattern)) {
02708                 Tcl_DictObjPut(interp, resultObj, keyObj, valueObj);
02709             }
02710             Tcl_DictObjNext(&search, &keyObj, &valueObj, &done);
02711         }
02712         Tcl_SetObjResult(interp, resultObj);
02713         return TCL_OK;
02714 
02715     case FILTER_SCRIPT:
02716         if (objc != 5) {
02717             Tcl_WrongNumArgs(interp, 1, objv,
02718                     "dictionary script {keyVar valueVar} filterScript");
02719             return TCL_ERROR;
02720         }
02721 
02722         /*
02723          * Create a dictionary whose key,value pairs all satisfy a script
02724          * (i.e. get a true boolean result from its evaluation). Massive
02725          * copying from the "dict for" implementation has occurred!
02726          */
02727 
02728         if (TclListObjGetElements(interp, objv[3], &varc, &varv) != TCL_OK) {
02729             return TCL_ERROR;
02730         }
02731         if (varc != 2) {
02732             Tcl_SetResult(interp, "must have exactly two variable names",
02733                     TCL_STATIC);
02734             return TCL_ERROR;
02735         }
02736         keyVarObj = varv[0];
02737         valueVarObj = varv[1];
02738         scriptObj = objv[4];
02739 
02740         /*
02741          * Make sure that these objects (which we need throughout the body of
02742          * the loop) don't vanish. Note that the dictionary internal rep is
02743          * locked internally so that updates, shimmering, etc are not a
02744          * problem.
02745          */
02746 
02747         Tcl_IncrRefCount(keyVarObj);
02748         Tcl_IncrRefCount(valueVarObj);
02749         Tcl_IncrRefCount(scriptObj);
02750 
02751         result = Tcl_DictObjFirst(interp, objv[1],
02752                 &search, &keyObj, &valueObj, &done);
02753         if (result != TCL_OK) {
02754             TclDecrRefCount(keyVarObj);
02755             TclDecrRefCount(valueVarObj);
02756             TclDecrRefCount(scriptObj);
02757             return TCL_ERROR;
02758         }
02759 
02760         resultObj = Tcl_NewDictObj();
02761 
02762         while (!done) {
02763             /*
02764              * Stop the value from getting hit in any way by any traces on the
02765              * key variable.
02766              */
02767 
02768             Tcl_IncrRefCount(keyObj);
02769             Tcl_IncrRefCount(valueObj);
02770             if (Tcl_ObjSetVar2(interp, keyVarObj, NULL, keyObj,
02771                     TCL_LEAVE_ERR_MSG) == NULL) {
02772                 Tcl_ResetResult(interp);
02773                 Tcl_AppendResult(interp, "couldn't set key variable: \"",
02774                         TclGetString(keyVarObj), "\"", NULL);
02775                 result = TCL_ERROR;
02776                 goto abnormalResult;
02777             }
02778             if (Tcl_ObjSetVar2(interp, valueVarObj, NULL, valueObj,
02779                     TCL_LEAVE_ERR_MSG) == NULL) {
02780                 Tcl_ResetResult(interp);
02781                 Tcl_AppendResult(interp, "couldn't set value variable: \"",
02782                         TclGetString(valueVarObj), "\"", NULL);
02783                 goto abnormalResult;
02784             }
02785 
02786             /*
02787              * TIP #280. Make invoking context available to loop body.
02788              */
02789 
02790             result = TclEvalObjEx(interp, scriptObj, 0, iPtr->cmdFramePtr, 4);
02791             switch (result) {
02792             case TCL_OK:
02793                 boolObj = Tcl_GetObjResult(interp);
02794                 Tcl_IncrRefCount(boolObj);
02795                 Tcl_ResetResult(interp);
02796                 if (Tcl_GetBooleanFromObj(interp, boolObj,
02797                         &satisfied) != TCL_OK) {
02798                     TclDecrRefCount(boolObj);
02799                     result = TCL_ERROR;
02800                     goto abnormalResult;
02801                 }
02802                 TclDecrRefCount(boolObj);
02803                 if (satisfied) {
02804                     Tcl_DictObjPut(interp, resultObj, keyObj, valueObj);
02805                 }
02806                 break;
02807             case TCL_BREAK:
02808                 /*
02809                  * Force loop termination by calling Tcl_DictObjDone; this
02810                  * makes the next Tcl_DictObjNext say there is nothing more to
02811                  * do.
02812                  */
02813 
02814                 Tcl_ResetResult(interp);
02815                 Tcl_DictObjDone(&search);
02816             case TCL_CONTINUE:
02817                 result = TCL_OK;
02818                 break;
02819             case TCL_ERROR:
02820                 Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
02821                         "\n    (\"dict filter\" script line %d)",
02822                         interp->errorLine));
02823             default:
02824                 goto abnormalResult;
02825             }
02826 
02827             TclDecrRefCount(keyObj);
02828             TclDecrRefCount(valueObj);
02829 
02830             Tcl_DictObjNext(&search, &keyObj, &valueObj, &done);
02831         }
02832 
02833         /*
02834          * Stop holding a reference to these objects.
02835          */
02836 
02837         TclDecrRefCount(keyVarObj);
02838         TclDecrRefCount(valueVarObj);
02839         TclDecrRefCount(scriptObj);
02840         Tcl_DictObjDone(&search);
02841 
02842         if (result == TCL_OK) {
02843             Tcl_SetObjResult(interp, resultObj);
02844         } else {
02845             TclDecrRefCount(resultObj);
02846         }
02847         return result;
02848 
02849     abnormalResult:
02850         Tcl_DictObjDone(&search);
02851         TclDecrRefCount(keyObj);
02852         TclDecrRefCount(valueObj);
02853         TclDecrRefCount(keyVarObj);
02854         TclDecrRefCount(valueVarObj);
02855         TclDecrRefCount(scriptObj);
02856         TclDecrRefCount(resultObj);
02857         return result;
02858     }
02859     Tcl_Panic("unexpected fallthrough");
02860     /* Control never reaches this point. */
02861     return TCL_ERROR;
02862 }
02863 
02864 /*
02865  *----------------------------------------------------------------------
02866  *
02867  * DictUpdateCmd --
02868  *
02869  *      This function implements the "dict update" Tcl command. See the user
02870  *      documentation for details on what it does, and TIP#212 for the formal
02871  *      specification.
02872  *
02873  * Results:
02874  *      A standard Tcl result.
02875  *
02876  * Side effects:
02877  *      See the user documentation.
02878  *
02879  *----------------------------------------------------------------------
02880  */
02881 
02882 static int
02883 DictUpdateCmd(
02884     ClientData clientData,
02885     Tcl_Interp *interp,
02886     int objc,
02887     Tcl_Obj *const *objv)
02888 {
02889     Interp *iPtr = (Interp *) interp;
02890     Tcl_Obj *dictPtr, *objPtr;
02891     int i, result, dummy;
02892     Tcl_InterpState state;
02893 
02894     if (objc < 5 || !(objc & 1)) {
02895         Tcl_WrongNumArgs(interp, 1, objv,
02896                 "varName key varName ?key varName ...? script");
02897         return TCL_ERROR;
02898     }
02899 
02900     dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG);
02901     if (dictPtr == NULL) {
02902         return TCL_ERROR;
02903     }
02904     if (Tcl_DictObjSize(interp, dictPtr, &dummy) != TCL_OK) {
02905         return TCL_ERROR;
02906     }
02907     Tcl_IncrRefCount(dictPtr);
02908     for (i=2 ; i+2<objc ; i+=2) {
02909         if (Tcl_DictObjGet(interp, dictPtr, objv[i], &objPtr) != TCL_OK) {
02910             TclDecrRefCount(dictPtr);
02911             return TCL_ERROR;
02912         }
02913         if (objPtr == NULL) {
02914             /* ??? */
02915             Tcl_UnsetVar(interp, Tcl_GetString(objv[i+1]), 0);
02916         } else if (Tcl_ObjSetVar2(interp, objv[i+1], NULL, objPtr,
02917                 TCL_LEAVE_ERR_MSG) == NULL) {
02918             TclDecrRefCount(dictPtr);
02919             return TCL_ERROR;
02920         }
02921     }
02922     TclDecrRefCount(dictPtr);
02923 
02924     /*
02925      * Execute the body.
02926      */
02927 
02928     result = TclEvalObjEx(interp, objv[objc-1], 0, iPtr->cmdFramePtr, objc-1);
02929     if (result == TCL_ERROR) {
02930         Tcl_AddErrorInfo(interp, "\n    (body of \"dict update\")");
02931     }
02932 
02933     /*
02934      * If the dictionary variable doesn't exist, drop everything silently.
02935      */
02936 
02937     dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0);
02938     if (dictPtr == NULL) {
02939         return result;
02940     }
02941 
02942     /*
02943      * Double-check that it is still a dictionary.
02944      */
02945 
02946     state = Tcl_SaveInterpState(interp, result);
02947     if (Tcl_DictObjSize(interp, dictPtr, &dummy) != TCL_OK) {
02948         Tcl_DiscardInterpState(state);
02949         return TCL_ERROR;
02950     }
02951 
02952     if (Tcl_IsShared(dictPtr)) {
02953         dictPtr = Tcl_DuplicateObj(dictPtr);
02954     }
02955 
02956     /*
02957      * Write back the values from the variables, treating failure to read as
02958      * an instruction to remove the key.
02959      */
02960 
02961     for (i=2 ; i+2<objc ; i+=2) {
02962         objPtr = Tcl_ObjGetVar2(interp, objv[i+1], NULL, 0);
02963         if (objPtr == NULL) {
02964             Tcl_DictObjRemove(interp, dictPtr, objv[i]);
02965         } else if (objPtr == dictPtr) {
02966             /*
02967              * Someone is messing us around, trying to build a recursive
02968              * structure. [Bug 1786481]
02969              */
02970 
02971             Tcl_DictObjPut(interp, dictPtr, objv[i],
02972                     Tcl_DuplicateObj(objPtr));
02973         } else {
02974             /* Shouldn't fail */
02975             Tcl_DictObjPut(interp, dictPtr, objv[i], objPtr);
02976         }
02977     }
02978 
02979     /*
02980      * Write the dictionary back to its variable.
02981      */
02982 
02983     if (Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr,
02984             TCL_LEAVE_ERR_MSG) == NULL) {
02985         Tcl_DiscardInterpState(state);
02986         return TCL_ERROR;
02987     }
02988 
02989     return Tcl_RestoreInterpState(interp, state);
02990 }
02991 
02992 /*
02993  *----------------------------------------------------------------------
02994  *
02995  * DictWithCmd --
02996  *
02997  *      This function implements the "dict with" Tcl command. See the user
02998  *      documentation for details on what it does, and TIP#212 for the formal
02999  *      specification.
03000  *
03001  * Results:
03002  *      A standard Tcl result.
03003  *
03004  * Side effects:
03005  *      See the user documentation.
03006  *
03007  *----------------------------------------------------------------------
03008  */
03009 
03010 static int
03011 DictWithCmd(
03012     ClientData dummy,
03013     Tcl_Interp *interp,
03014     int objc,
03015     Tcl_Obj *const *objv)
03016 {
03017     Interp *iPtr = (Interp *) interp;
03018     Tcl_Obj *dictPtr, *keysPtr, *keyPtr, *valPtr, **keyv, *leafPtr;
03019     Tcl_DictSearch s;
03020     Tcl_InterpState state;
03021     int done, result, keyc, i, allocdict = 0;
03022 
03023     if (objc < 3) {
03024         Tcl_WrongNumArgs(interp, 1, objv, "dictVar ?key ...? script");
03025         return TCL_ERROR;
03026     }
03027 
03028     /*
03029      * Get the dictionary to open out.
03030      */
03031 
03032     dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG);
03033     if (dictPtr == NULL) {
03034         return TCL_ERROR;
03035     }
03036     if (objc > 3) {
03037         dictPtr = TclTraceDictPath(interp, dictPtr, objc-3, objv+2,
03038                 DICT_PATH_READ);
03039         if (dictPtr == NULL) {
03040             return TCL_ERROR;
03041         }
03042     }
03043 
03044     /*
03045      * Go over the list of keys and write each corresponding value to a
03046      * variable in the current context with the same name. Also keep a copy of
03047      * the keys so we can write back properly later on even if the dictionary
03048      * has been structurally modified.
03049      */
03050 
03051     if (Tcl_DictObjFirst(interp, dictPtr, &s, &keyPtr, &valPtr,
03052             &done) != TCL_OK) {
03053         return TCL_ERROR;
03054     }
03055 
03056     TclNewObj(keysPtr);
03057     Tcl_IncrRefCount(keysPtr);
03058 
03059     for (; !done ; Tcl_DictObjNext(&s, &keyPtr, &valPtr, &done)) {
03060         Tcl_ListObjAppendElement(NULL, keysPtr, keyPtr);
03061         if (Tcl_ObjSetVar2(interp, keyPtr, NULL, valPtr,
03062                 TCL_LEAVE_ERR_MSG) == NULL) {
03063             TclDecrRefCount(keysPtr);
03064             Tcl_DictObjDone(&s);
03065             return TCL_ERROR;
03066         }
03067     }
03068 
03069     /*
03070      * Execute the body, while making the invoking context available to the
03071      * loop body (TIP#280).
03072      */
03073 
03074     result = TclEvalObjEx(interp, objv[objc-1], 0, iPtr->cmdFramePtr, objc-1);
03075     if (result == TCL_ERROR) {
03076         Tcl_AddErrorInfo(interp, "\n    (body of \"dict with\")");
03077     }
03078 
03079     /*
03080      * If the dictionary variable doesn't exist, drop everything silently.
03081      */
03082 
03083     dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0);
03084     if (dictPtr == NULL) {
03085         TclDecrRefCount(keysPtr);
03086         return result;
03087     }
03088 
03089     /*
03090      * Double-check that it is still a dictionary.
03091      */
03092 
03093     state = Tcl_SaveInterpState(interp, result);
03094     if (Tcl_DictObjSize(interp, dictPtr, &i) != TCL_OK) {
03095         TclDecrRefCount(keysPtr);
03096         Tcl_DiscardInterpState(state);
03097         return TCL_ERROR;
03098     }
03099 
03100     if (Tcl_IsShared(dictPtr)) {
03101         dictPtr = Tcl_DuplicateObj(dictPtr);
03102         allocdict = 1;
03103     }
03104 
03105     if (objc > 3) {
03106         /*
03107          * Want to get to the dictionary which we will update; need to do
03108          * prepare-for-update de-sharing along the path *but* avoid generating
03109          * an error on a non-existant path (we'll treat that the same as a
03110          * non-existant variable. Luckily, the de-sharing operation isn't
03111          * deeply damaging if we don't go on to update; it's just less than
03112          * perfectly efficient (but no memory should be leaked).
03113          */
03114 
03115         leafPtr = TclTraceDictPath(interp, dictPtr, objc-3, objv+2,
03116                 DICT_PATH_EXISTS | DICT_PATH_UPDATE);
03117         if (leafPtr == NULL) {
03118             TclDecrRefCount(keysPtr);
03119             if (allocdict) {
03120                 TclDecrRefCount(dictPtr);
03121             }
03122             Tcl_DiscardInterpState(state);
03123             return TCL_ERROR;
03124         }
03125         if (leafPtr == DICT_PATH_NON_EXISTENT) {
03126             TclDecrRefCount(keysPtr);
03127             if (allocdict) {
03128                 TclDecrRefCount(dictPtr);
03129             }
03130             return Tcl_RestoreInterpState(interp, state);
03131         }
03132     } else {
03133         leafPtr = dictPtr;
03134     }
03135 
03136     /*
03137      * Now process our updates on the leaf dictionary.
03138      */
03139 
03140     TclListObjGetElements(NULL, keysPtr, &keyc, &keyv);
03141     for (i=0 ; i<keyc ; i++) {
03142         valPtr = Tcl_ObjGetVar2(interp, keyv[i], NULL, 0);
03143         if (valPtr == NULL) {
03144             Tcl_DictObjRemove(NULL, leafPtr, keyv[i]);
03145         } else if (leafPtr == valPtr) {
03146             /*
03147              * Someone is messing us around, trying to build a recursive
03148              * structure. [Bug 1786481]
03149              */
03150 
03151             Tcl_DictObjPut(NULL, leafPtr, keyv[i], Tcl_DuplicateObj(valPtr));
03152         } else {
03153             Tcl_DictObjPut(NULL, leafPtr, keyv[i], valPtr);
03154         }
03155     }
03156     TclDecrRefCount(keysPtr);
03157 
03158     /*
03159      * Ensure that none of the dictionaries in the chain still have a string
03160      * rep.
03161      */
03162 
03163     if (objc > 3) {
03164         InvalidateDictChain(leafPtr);
03165     }
03166 
03167     /*
03168      * Write back the outermost dictionary to the variable.
03169      */
03170 
03171     if (Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr,
03172             TCL_LEAVE_ERR_MSG) == NULL) {
03173         Tcl_DiscardInterpState(state);
03174         return TCL_ERROR;
03175     }
03176     return Tcl_RestoreInterpState(interp, state);
03177 }
03178 
03179 /*
03180  *----------------------------------------------------------------------
03181  *
03182  * TclInitDictCmd --
03183  *
03184  *      This function is create the "dict" Tcl command. See the user
03185  *      documentation for details on what it does, and TIP#111 for the formal
03186  *      specification.
03187  *
03188  * Results:
03189  *      A Tcl command handle.
03190  *
03191  * Side effects:
03192  *      May advance compilation epoch.
03193  *
03194  *----------------------------------------------------------------------
03195  */
03196 
03197 Tcl_Command
03198 TclInitDictCmd(
03199     Tcl_Interp *interp)
03200 {
03201     return TclMakeEnsemble(interp, "dict", implementationMap);
03202 }
03203 
03204 /*
03205  * Local Variables:
03206  * mode: c
03207  * c-basic-offset: 4
03208  * fill-column: 78
03209  * End:
03210  */



Generated on Wed Mar 12 12:18:15 2008 by  doxygen 1.5.1