tclDictObj.cGo 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 1.5.1 |