summaryrefslogtreecommitdiffstats
path: root/tcl8.6/generic/tclDictObj.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2016-12-21 22:47:21 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2016-12-21 22:47:21 (GMT)
commit5514e37335c012cc70f5b9aee3cedfe3d57f583f (patch)
tree4ba7d8aad13735e52f59bdce7ca5ba3151ebd7e3 /tcl8.6/generic/tclDictObj.c
parent768f87f613cc9789fcf8073018fa02178c8c91df (diff)
downloadblt-5514e37335c012cc70f5b9aee3cedfe3d57f583f.zip
blt-5514e37335c012cc70f5b9aee3cedfe3d57f583f.tar.gz
blt-5514e37335c012cc70f5b9aee3cedfe3d57f583f.tar.bz2
undo subtree
Diffstat (limited to 'tcl8.6/generic/tclDictObj.c')
-rw-r--r--tcl8.6/generic/tclDictObj.c3664
1 files changed, 0 insertions, 3664 deletions
diff --git a/tcl8.6/generic/tclDictObj.c b/tcl8.6/generic/tclDictObj.c
deleted file mode 100644
index 428173d..0000000
--- a/tcl8.6/generic/tclDictObj.c
+++ /dev/null
@@ -1,3664 +0,0 @@
-/*
- * tclDictObj.c --
- *
- * This file contains functions that implement the Tcl dict object type
- * and its accessor command.
- *
- * Copyright (c) 2002-2010 by Donal K. Fellows.
- *
- * See the file "license.terms" for information on usage and redistribution of
- * this file, and for a DISCLAIMER OF ALL WARRANTIES.
- */
-
-#include "tclInt.h"
-#include "tommath.h"
-
-/*
- * Forward declaration.
- */
-struct Dict;
-
-/*
- * Prototypes for functions defined later in this file:
- */
-
-static void DeleteDict(struct Dict *dict);
-static int DictAppendCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictCreateCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictExistsCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictFilterCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictGetCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictIncrCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictInfoCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictKeysCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictLappendCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictMergeCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictRemoveCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictReplaceCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictSetCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictSizeCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictUnsetCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictUpdateCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictValuesCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static int DictWithCmd(ClientData dummy, Tcl_Interp *interp,
- int objc, Tcl_Obj *const *objv);
-static void DupDictInternalRep(Tcl_Obj *srcPtr, Tcl_Obj *copyPtr);
-static void FreeDictInternalRep(Tcl_Obj *dictPtr);
-static void InvalidateDictChain(Tcl_Obj *dictObj);
-static int SetDictFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
-static void UpdateStringOfDict(Tcl_Obj *dictPtr);
-static Tcl_HashEntry * AllocChainEntry(Tcl_HashTable *tablePtr,void *keyPtr);
-static inline void InitChainTable(struct Dict *dict);
-static inline void DeleteChainTable(struct Dict *dict);
-static inline Tcl_HashEntry *CreateChainEntry(struct Dict *dict,
- Tcl_Obj *keyPtr, int *newPtr);
-static inline int DeleteChainEntry(struct Dict *dict, Tcl_Obj *keyPtr);
-static Tcl_NRPostProc FinalizeDictUpdate;
-static Tcl_NRPostProc FinalizeDictWith;
-static Tcl_ObjCmdProc DictForNRCmd;
-static Tcl_ObjCmdProc DictMapNRCmd;
-static Tcl_NRPostProc DictForLoopCallback;
-static Tcl_NRPostProc DictMapLoopCallback;
-
-/*
- * Table of dict subcommand names and implementations.
- */
-
-static const EnsembleImplMap implementationMap[] = {
- {"append", DictAppendCmd, TclCompileDictAppendCmd, NULL, NULL, 0 },
- {"create", DictCreateCmd, TclCompileDictCreateCmd, NULL, NULL, 0 },
- {"exists", DictExistsCmd, TclCompileDictExistsCmd, NULL, NULL, 0 },
- {"filter", DictFilterCmd, NULL, NULL, NULL, 0 },
- {"for", NULL, TclCompileDictForCmd, DictForNRCmd, NULL, 0 },
- {"get", DictGetCmd, TclCompileDictGetCmd, NULL, NULL, 0 },
- {"incr", DictIncrCmd, TclCompileDictIncrCmd, NULL, NULL, 0 },
- {"info", DictInfoCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0 },
- {"keys", DictKeysCmd, TclCompileBasic1Or2ArgCmd, NULL, NULL, 0 },
- {"lappend", DictLappendCmd, TclCompileDictLappendCmd, NULL, NULL, 0 },
- {"map", NULL, TclCompileDictMapCmd, DictMapNRCmd, NULL, 0 },
- {"merge", DictMergeCmd, TclCompileDictMergeCmd, NULL, NULL, 0 },
- {"remove", DictRemoveCmd, TclCompileBasicMin1ArgCmd, NULL, NULL, 0 },
- {"replace", DictReplaceCmd, NULL, NULL, NULL, 0 },
- {"set", DictSetCmd, TclCompileDictSetCmd, NULL, NULL, 0 },
- {"size", DictSizeCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0 },
- {"unset", DictUnsetCmd, TclCompileDictUnsetCmd, NULL, NULL, 0 },
- {"update", DictUpdateCmd, TclCompileDictUpdateCmd, NULL, NULL, 0 },
- {"values", DictValuesCmd, TclCompileBasic1Or2ArgCmd, NULL, NULL, 0 },
- {"with", DictWithCmd, TclCompileDictWithCmd, NULL, NULL, 0 },
- {NULL, NULL, NULL, NULL, NULL, 0}
-};
-
-/*
- * Internal representation of the entries in the hash table that backs a
- * dictionary.
- */
-
-typedef struct ChainEntry {
- Tcl_HashEntry entry;
- struct ChainEntry *prevPtr;
- struct ChainEntry *nextPtr;
-} ChainEntry;
-
-/*
- * Internal representation of a dictionary.
- *
- * The internal representation of a dictionary object is a hash table (with
- * Tcl_Objs for both keys and values), a reference count and epoch number for
- * detecting concurrent modifications of the dictionary, and a pointer to the
- * parent object (used when invalidating string reps of pathed dictionary
- * trees) which is NULL in normal use. The fact that hash tables know (with
- * appropriate initialisation) already about objects makes key management /so/
- * much easier!
- *
- * Reference counts are used to enable safe iteration across hashes while
- * allowing the type of the containing object to be modified.
- */
-
-typedef struct Dict {
- Tcl_HashTable table; /* Object hash table to store mapping in. */
- ChainEntry *entryChainHead; /* Linked list of all entries in the
- * dictionary. Used for doing traversal of the
- * entries in the order that they are
- * created. */
- ChainEntry *entryChainTail; /* Other end of linked list of all entries in
- * the dictionary. Used for doing traversal of
- * the entries in the order that they are
- * created. */
- int epoch; /* Epoch counter */
- int refcount; /* Reference counter (see above) */
- Tcl_Obj *chain; /* Linked list used for invalidating the
- * string representations of updated nested
- * dictionaries. */
-} Dict;
-
-/*
- * Accessor macro for converting between a Tcl_Obj* and a Dict. Note that this
- * must be assignable as well as readable.
- */
-
-#define DICT(dictObj) (*((Dict **)&(dictObj)->internalRep.twoPtrValue.ptr1))
-
-/*
- * The structure below defines the dictionary object type by means of
- * functions that can be invoked by generic object code.
- */
-
-const Tcl_ObjType tclDictType = {
- "dict",
- FreeDictInternalRep, /* freeIntRepProc */
- DupDictInternalRep, /* dupIntRepProc */
- UpdateStringOfDict, /* updateStringProc */
- SetDictFromAny /* setFromAnyProc */
-};
-
-/*
- * The type of the specially adapted version of the Tcl_Obj*-containing hash
- * table defined in the tclObj.c code. This version differs in that it
- * allocates a bit more space in each hash entry in order to hold the pointers
- * used to keep the hash entries in a linked list.
- *
- * Note that this type of hash table is *only* suitable for direct use in
- * *this* file. Everything else should use the dict iterator API.
- */
-
-static const Tcl_HashKeyType chainHashType = {
- TCL_HASH_KEY_TYPE_VERSION,
- 0,
- TclHashObjKey,
- TclCompareObjKeys,
- AllocChainEntry,
- TclFreeObjEntry
-};
-
-/*
- * Structure used in implementation of 'dict map' to hold the state that gets
- * passed between parts of the implementation.
- */
-
-typedef struct {
- Tcl_Obj *keyVarObj; /* The name of the variable that will have
- * keys assigned to it. */
- Tcl_Obj *valueVarObj; /* The name of the variable that will have
- * values assigned to it. */
- Tcl_DictSearch search; /* The dictionary search structure. */
- Tcl_Obj *scriptObj; /* The script to evaluate each time through
- * the loop. */
- Tcl_Obj *accumulatorObj; /* The dictionary used to accumulate the
- * results. */
-} DictMapStorage;
-
-/***** START OF FUNCTIONS IMPLEMENTING DICT CORE API *****/
-
-/*
- *----------------------------------------------------------------------
- *
- * AllocChainEntry --
- *
- * Allocate space for a Tcl_HashEntry containing the Tcl_Obj * key, and
- * which has a bit of extra space afterwards for storing pointers to the
- * rest of the chain of entries (the extra pointers are left NULL).
- *
- * Results:
- * The return value is a pointer to the created entry.
- *
- * Side effects:
- * Increments the reference count on the object.
- *
- *----------------------------------------------------------------------
- */
-
-static Tcl_HashEntry *
-AllocChainEntry(
- Tcl_HashTable *tablePtr,
- void *keyPtr)
-{
- Tcl_Obj *objPtr = keyPtr;
- ChainEntry *cPtr;
-
- cPtr = ckalloc(sizeof(ChainEntry));
- cPtr->entry.key.objPtr = objPtr;
- Tcl_IncrRefCount(objPtr);
- cPtr->entry.clientData = NULL;
- cPtr->prevPtr = cPtr->nextPtr = NULL;
-
- return &cPtr->entry;
-}
-
-/*
- * Helper functions that disguise most of the details relating to how the
- * linked list of hash entries is managed. In particular, these manage the
- * creation of the table and initializing of the chain, the deletion of the
- * table and chain, the adding of an entry to the chain, and the removal of an
- * entry from the chain.
- */
-
-static inline void
-InitChainTable(
- Dict *dict)
-{
- Tcl_InitCustomHashTable(&dict->table, TCL_CUSTOM_PTR_KEYS,
- &chainHashType);
- dict->entryChainHead = dict->entryChainTail = NULL;
-}
-
-static inline void
-DeleteChainTable(
- Dict *dict)
-{
- ChainEntry *cPtr;
-
- for (cPtr=dict->entryChainHead ; cPtr!=NULL ; cPtr=cPtr->nextPtr) {
- Tcl_Obj *valuePtr = Tcl_GetHashValue(&cPtr->entry);
-
- TclDecrRefCount(valuePtr);
- }
- Tcl_DeleteHashTable(&dict->table);
-}
-
-static inline Tcl_HashEntry *
-CreateChainEntry(
- Dict *dict,
- Tcl_Obj *keyPtr,
- int *newPtr)
-{
- ChainEntry *cPtr = (ChainEntry *)
- Tcl_CreateHashEntry(&dict->table, keyPtr, newPtr);
-
- /*
- * If this is a new entry in the hash table, stitch it into the chain.
- */
-
- if (*newPtr) {
- cPtr->nextPtr = NULL;
- if (dict->entryChainHead == NULL) {
- cPtr->prevPtr = NULL;
- dict->entryChainHead = cPtr;
- dict->entryChainTail = cPtr;
- } else {
- cPtr->prevPtr = dict->entryChainTail;
- dict->entryChainTail->nextPtr = cPtr;
- dict->entryChainTail = cPtr;
- }
- }
-
- return &cPtr->entry;
-}
-
-static inline int
-DeleteChainEntry(
- Dict *dict,
- Tcl_Obj *keyPtr)
-{
- ChainEntry *cPtr = (ChainEntry *)
- Tcl_FindHashEntry(&dict->table, keyPtr);
-
- if (cPtr == NULL) {
- return 0;
- } else {
- Tcl_Obj *valuePtr = Tcl_GetHashValue(&cPtr->entry);
-
- TclDecrRefCount(valuePtr);
- }
-
- /*
- * Unstitch from the chain.
- */
-
- if (cPtr->nextPtr) {
- cPtr->nextPtr->prevPtr = cPtr->prevPtr;
- } else {
- dict->entryChainTail = cPtr->prevPtr;
- }
- if (cPtr->prevPtr) {
- cPtr->prevPtr->nextPtr = cPtr->nextPtr;
- } else {
- dict->entryChainHead = cPtr->nextPtr;
- }
-
- Tcl_DeleteHashEntry(&cPtr->entry);
- return 1;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DupDictInternalRep --
- *
- * Initialize the internal representation of a dictionary Tcl_Obj to a
- * copy of the internal representation of an existing dictionary object.
- *
- * Results:
- * None.
- *
- * Side effects:
- * "srcPtr"s dictionary internal rep pointer should not be NULL and we
- * assume it is not NULL. We set "copyPtr"s internal rep to a pointer to
- * a newly allocated dictionary rep that, in turn, points to "srcPtr"s
- * key and value objects. Those objects are not actually copied but are
- * shared between "srcPtr" and "copyPtr". The ref count of each key and
- * value object is incremented.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-DupDictInternalRep(
- Tcl_Obj *srcPtr,
- Tcl_Obj *copyPtr)
-{
- Dict *oldDict = DICT(srcPtr);
- Dict *newDict = ckalloc(sizeof(Dict));
- ChainEntry *cPtr;
-
- /*
- * Copy values across from the old hash table.
- */
-
- InitChainTable(newDict);
- for (cPtr=oldDict->entryChainHead ; cPtr!=NULL ; cPtr=cPtr->nextPtr) {
- Tcl_Obj *key = Tcl_GetHashKey(&oldDict->table, &cPtr->entry);
- Tcl_Obj *valuePtr = Tcl_GetHashValue(&cPtr->entry);
- int n;
- Tcl_HashEntry *hPtr = CreateChainEntry(newDict, key, &n);
-
- /*
- * Fill in the contents.
- */
-
- Tcl_SetHashValue(hPtr, valuePtr);
- Tcl_IncrRefCount(valuePtr);
- }
-
- /*
- * Initialise other fields.
- */
-
- newDict->epoch = 0;
- newDict->chain = NULL;
- newDict->refcount = 1;
-
- /*
- * Store in the object.
- */
-
- DICT(copyPtr) = newDict;
- copyPtr->internalRep.twoPtrValue.ptr2 = NULL;
- copyPtr->typePtr = &tclDictType;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * FreeDictInternalRep --
- *
- * Deallocate the storage associated with a dictionary object's internal
- * representation.
- *
- * Results:
- * None
- *
- * Side effects:
- * Frees the memory holding the dictionary's internal hash table unless
- * it is locked by an iteration going over it.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-FreeDictInternalRep(
- Tcl_Obj *dictPtr)
-{
- Dict *dict = DICT(dictPtr);
-
- dict->refcount--;
- if (dict->refcount <= 0) {
- DeleteDict(dict);
- }
- dictPtr->typePtr = NULL;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DeleteDict --
- *
- * Delete the structure that is used to implement a dictionary's internal
- * representation. Called when either the dictionary object loses its
- * internal representation or when the last iteration over the dictionary
- * completes.
- *
- * Results:
- * None
- *
- * Side effects:
- * Decrements the reference count of all key and value objects in the
- * dictionary, which may free them.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-DeleteDict(
- Dict *dict)
-{
- DeleteChainTable(dict);
- ckfree(dict);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * UpdateStringOfDict --
- *
- * Update the string representation for a dictionary object. Note: This
- * function does not invalidate an existing old string rep so storage
- * will be lost if this has not already been done. This code is based on
- * UpdateStringOfList in tclListObj.c
- *
- * Results:
- * None.
- *
- * Side effects:
- * The object's string is set to a valid string that results from the
- * dict-to-string conversion. This string will be empty if the dictionary
- * has no key/value pairs. The dictionary internal representation should
- * not be NULL and we assume it is not NULL.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-UpdateStringOfDict(
- Tcl_Obj *dictPtr)
-{
-#define LOCAL_SIZE 20
- int localFlags[LOCAL_SIZE], *flagPtr = NULL;
- Dict *dict = DICT(dictPtr);
- ChainEntry *cPtr;
- Tcl_Obj *keyPtr, *valuePtr;
- int i, length, bytesNeeded = 0;
- const char *elem;
- char *dst;
- const int maxFlags = UINT_MAX / sizeof(int);
-
- /*
- * This field is the most useful one in the whole hash structure, and it
- * is not exposed by any API function...
- */
-
- int numElems = dict->table.numEntries * 2;
-
- /* Handle empty list case first, simplifies what follows */
- if (numElems == 0) {
- dictPtr->bytes = tclEmptyStringRep;
- dictPtr->length = 0;
- return;
- }
-
- /*
- * Pass 1: estimate space, gather flags.
- */
-
- if (numElems <= LOCAL_SIZE) {
- flagPtr = localFlags;
- } else if (numElems > maxFlags) {
- Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX);
- } else {
- flagPtr = ckalloc(numElems * sizeof(int));
- }
- for (i=0,cPtr=dict->entryChainHead; i<numElems; i+=2,cPtr=cPtr->nextPtr) {
- /*
- * Assume that cPtr is never NULL since we know the number of array
- * elements already.
- */
-
- flagPtr[i] = ( i ? TCL_DONT_QUOTE_HASH : 0 );
- keyPtr = Tcl_GetHashKey(&dict->table, &cPtr->entry);
- elem = TclGetStringFromObj(keyPtr, &length);
- bytesNeeded += TclScanElement(elem, length, flagPtr+i);
- if (bytesNeeded < 0) {
- Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX);
- }
-
- flagPtr[i+1] = TCL_DONT_QUOTE_HASH;
- valuePtr = Tcl_GetHashValue(&cPtr->entry);
- elem = TclGetStringFromObj(valuePtr, &length);
- bytesNeeded += TclScanElement(elem, length, flagPtr+i+1);
- if (bytesNeeded < 0) {
- Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX);
- }
- }
- if (bytesNeeded > INT_MAX - numElems + 1) {
- Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX);
- }
- bytesNeeded += numElems;
-
- /*
- * Pass 2: copy into string rep buffer.
- */
-
- dictPtr->length = bytesNeeded - 1;
- dictPtr->bytes = ckalloc(bytesNeeded);
- dst = dictPtr->bytes;
- for (i=0,cPtr=dict->entryChainHead; i<numElems; i+=2,cPtr=cPtr->nextPtr) {
- flagPtr[i] |= ( i ? TCL_DONT_QUOTE_HASH : 0 );
- keyPtr = Tcl_GetHashKey(&dict->table, &cPtr->entry);
- elem = TclGetStringFromObj(keyPtr, &length);
- dst += TclConvertElement(elem, length, dst, flagPtr[i]);
- *dst++ = ' ';
-
- flagPtr[i+1] |= TCL_DONT_QUOTE_HASH;
- valuePtr = Tcl_GetHashValue(&cPtr->entry);
- elem = TclGetStringFromObj(valuePtr, &length);
- dst += TclConvertElement(elem, length, dst, flagPtr[i+1]);
- *dst++ = ' ';
- }
- dictPtr->bytes[dictPtr->length] = '\0';
-
- if (flagPtr != localFlags) {
- ckfree(flagPtr);
- }
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * SetDictFromAny --
- *
- * Convert a non-dictionary object into a dictionary object. This code is
- * very closely related to SetListFromAny in tclListObj.c but does not
- * actually guarantee that a dictionary object will have a string rep (as
- * conversions from lists are handled with a special case.)
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * If the string can be converted, it loses any old internal
- * representation that it had and gains a dictionary's internalRep.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-SetDictFromAny(
- Tcl_Interp *interp,
- Tcl_Obj *objPtr)
-{
- Tcl_HashEntry *hPtr;
- int isNew;
- Dict *dict = ckalloc(sizeof(Dict));
-
- InitChainTable(dict);
-
- /*
- * Since lists and dictionaries have very closely-related string
- * representations (i.e. the same parsing code) we can safely special-case
- * the conversion from lists to dictionaries.
- */
-
- if (objPtr->typePtr == &tclListType) {
- int objc, i;
- Tcl_Obj **objv;
-
- /* Cannot fail, we already know the Tcl_ObjType is "list". */
- TclListObjGetElements(NULL, objPtr, &objc, &objv);
- if (objc & 1) {
- goto missingValue;
- }
-
- for (i=0 ; i<objc ; i+=2) {
-
- /* Store key and value in the hash table we're building. */
- hPtr = CreateChainEntry(dict, objv[i], &isNew);
- if (!isNew) {
- Tcl_Obj *discardedValue = Tcl_GetHashValue(hPtr);
-
- /*
- * Not really a well-formed dictionary as there are duplicate
- * keys, so better get the string rep here so that we can
- * convert back.
- */
-
- (void) Tcl_GetString(objPtr);
-
- TclDecrRefCount(discardedValue);
- }
- Tcl_SetHashValue(hPtr, objv[i+1]);
- Tcl_IncrRefCount(objv[i+1]); /* Since hash now holds ref to it */
- }
- } else {
- int length;
- const char *nextElem = TclGetStringFromObj(objPtr, &length);
- const char *limit = (nextElem + length);
-
- while (nextElem < limit) {
- Tcl_Obj *keyPtr, *valuePtr;
- const char *elemStart;
- int elemSize, literal;
-
- if (TclFindDictElement(interp, nextElem, (limit - nextElem),
- &elemStart, &nextElem, &elemSize, &literal) != TCL_OK) {
- goto errorInFindDictElement;
- }
- if (elemStart == limit) {
- break;
- }
- if (nextElem == limit) {
- goto missingValue;
- }
-
- if (literal) {
- TclNewStringObj(keyPtr, elemStart, elemSize);
- } else {
- /* Avoid double copy */
- TclNewObj(keyPtr);
- keyPtr->bytes = ckalloc((unsigned) elemSize + 1);
- keyPtr->length = TclCopyAndCollapse(elemSize, elemStart,
- keyPtr->bytes);
- }
-
- if (TclFindDictElement(interp, nextElem, (limit - nextElem),
- &elemStart, &nextElem, &elemSize, &literal) != TCL_OK) {
- TclDecrRefCount(keyPtr);
- goto errorInFindDictElement;
- }
-
- if (literal) {
- TclNewStringObj(valuePtr, elemStart, elemSize);
- } else {
- /* Avoid double copy */
- TclNewObj(valuePtr);
- valuePtr->bytes = ckalloc((unsigned) elemSize + 1);
- valuePtr->length = TclCopyAndCollapse(elemSize, elemStart,
- valuePtr->bytes);
- }
-
- /* Store key and value in the hash table we're building. */
- hPtr = CreateChainEntry(dict, keyPtr, &isNew);
- if (!isNew) {
- Tcl_Obj *discardedValue = Tcl_GetHashValue(hPtr);
-
- TclDecrRefCount(keyPtr);
- TclDecrRefCount(discardedValue);
- }
- Tcl_SetHashValue(hPtr, valuePtr);
- Tcl_IncrRefCount(valuePtr); /* since hash now holds ref to it */
- }
- }
-
- /*
- * Free the old internalRep before setting the new one. We do this as late
- * as possible to allow the conversion code, in particular
- * Tcl_GetStringFromObj, to use that old internalRep.
- */
-
- TclFreeIntRep(objPtr);
- dict->epoch = 0;
- dict->chain = NULL;
- dict->refcount = 1;
- DICT(objPtr) = dict;
- objPtr->internalRep.twoPtrValue.ptr2 = NULL;
- objPtr->typePtr = &tclDictType;
- return TCL_OK;
-
- missingValue:
- if (interp != NULL) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "missing value to go with key", -1));
- Tcl_SetErrorCode(interp, "TCL", "VALUE", "DICTIONARY", NULL);
- }
- errorInFindDictElement:
- DeleteChainTable(dict);
- ckfree(dict);
- return TCL_ERROR;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclTraceDictPath --
- *
- * Trace through a tree of dictionaries using the array of keys given. If
- * the flags argument has the DICT_PATH_UPDATE flag is set, a
- * backward-pointing chain of dictionaries is also built (in the Dict's
- * chain field) and the chained dictionaries are made into unshared
- * dictionaries (if they aren't already.)
- *
- * Results:
- * The object at the end of the path, or NULL if there was an error. Note
- * that this it is an error for an intermediate dictionary on the path to
- * not exist. If the flags argument has the DICT_PATH_EXISTS set, a
- * non-existent path gives a DICT_PATH_NON_EXISTENT result.
- *
- * Side effects:
- * If the flags argument is zero or DICT_PATH_EXISTS, there are no side
- * effects (other than potential conversion of objects to dictionaries.)
- * If the flags argument is DICT_PATH_UPDATE, the following additional
- * side effects occur. Shared dictionaries along the path are converted
- * into unshared objects, and a backward-pointing chain is built using
- * the chain fields of the dictionaries (for easy invalidation of string
- * representations using InvalidateDictChain). If the flags argument has
- * the DICT_PATH_CREATE bits set (and not the DICT_PATH_EXISTS bit),
- * non-existant keys will be inserted with a value of an empty
- * dictionary, resulting in the path being built.
- *
- *----------------------------------------------------------------------
- */
-
-Tcl_Obj *
-TclTraceDictPath(
- Tcl_Interp *interp,
- Tcl_Obj *dictPtr,
- int keyc,
- Tcl_Obj *const keyv[],
- int flags)
-{
- Dict *dict, *newDict;
- int i;
-
- if (dictPtr->typePtr != &tclDictType
- && SetDictFromAny(interp, dictPtr) != TCL_OK) {
- return NULL;
- }
- dict = DICT(dictPtr);
- if (flags & DICT_PATH_UPDATE) {
- dict->chain = NULL;
- }
-
- for (i=0 ; i<keyc ; i++) {
- Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&dict->table, keyv[i]);
- Tcl_Obj *tmpObj;
-
- if (hPtr == NULL) {
- int isNew; /* Dummy */
-
- if (flags & DICT_PATH_EXISTS) {
- return DICT_PATH_NON_EXISTENT;
- }
- if ((flags & DICT_PATH_CREATE) != DICT_PATH_CREATE) {
- if (interp != NULL) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "key \"%s\" not known in dictionary",
- TclGetString(keyv[i])));
- Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "DICT",
- TclGetString(keyv[i]), NULL);
- }
- return NULL;
- }
-
- /*
- * The next line should always set isNew to 1.
- */
-
- hPtr = CreateChainEntry(dict, keyv[i], &isNew);
- tmpObj = Tcl_NewDictObj();
- Tcl_IncrRefCount(tmpObj);
- Tcl_SetHashValue(hPtr, tmpObj);
- } else {
- tmpObj = Tcl_GetHashValue(hPtr);
- if (tmpObj->typePtr != &tclDictType
- && SetDictFromAny(interp, tmpObj) != TCL_OK) {
- return NULL;
- }
- }
-
- newDict = DICT(tmpObj);
- if (flags & DICT_PATH_UPDATE) {
- if (Tcl_IsShared(tmpObj)) {
- TclDecrRefCount(tmpObj);
- tmpObj = Tcl_DuplicateObj(tmpObj);
- Tcl_IncrRefCount(tmpObj);
- Tcl_SetHashValue(hPtr, tmpObj);
- dict->epoch++;
- newDict = DICT(tmpObj);
- }
-
- newDict->chain = dictPtr;
- }
- dict = newDict;
- dictPtr = tmpObj;
- }
- return dictPtr;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * InvalidateDictChain --
- *
- * Go through a dictionary chain (built by an updating invokation of
- * TclTraceDictPath) and invalidate the string representations of all the
- * dictionaries on the chain.
- *
- * Results:
- * None
- *
- * Side effects:
- * String reps are invalidated and epoch counters (for detecting illegal
- * concurrent modifications) are updated through the chain of updated
- * dictionaries.
- *
- *----------------------------------------------------------------------
- */
-
-static void
-InvalidateDictChain(
- Tcl_Obj *dictObj)
-{
- Dict *dict = DICT(dictObj);
-
- do {
- TclInvalidateStringRep(dictObj);
- dict->epoch++;
- dictObj = dict->chain;
- if (dictObj == NULL) {
- break;
- }
- dict->chain = NULL;
- dict = DICT(dictObj);
- } while (dict != NULL);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_DictObjPut --
- *
- * Add a key,value pair to a dictionary, or update the value for a key if
- * that key already has a mapping in the dictionary.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * The object pointed to by dictPtr is converted to a dictionary if it is
- * not already one, and any string representation that it has is
- * invalidated.
- *
- *----------------------------------------------------------------------
- */
-
-int
-Tcl_DictObjPut(
- Tcl_Interp *interp,
- Tcl_Obj *dictPtr,
- Tcl_Obj *keyPtr,
- Tcl_Obj *valuePtr)
-{
- Dict *dict;
- Tcl_HashEntry *hPtr;
- int isNew;
-
- if (Tcl_IsShared(dictPtr)) {
- Tcl_Panic("%s called with shared object", "Tcl_DictObjPut");
- }
-
- if (dictPtr->typePtr != &tclDictType
- && SetDictFromAny(interp, dictPtr) != TCL_OK) {
- return TCL_ERROR;
- }
-
- if (dictPtr->bytes != NULL) {
- TclInvalidateStringRep(dictPtr);
- }
- dict = DICT(dictPtr);
- hPtr = CreateChainEntry(dict, keyPtr, &isNew);
- Tcl_IncrRefCount(valuePtr);
- if (!isNew) {
- Tcl_Obj *oldValuePtr = Tcl_GetHashValue(hPtr);
-
- TclDecrRefCount(oldValuePtr);
- }
- Tcl_SetHashValue(hPtr, valuePtr);
- dict->epoch++;
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_DictObjGet --
- *
- * Given a key, get its value from the dictionary (or NULL if key is not
- * found in dictionary.)
- *
- * Results:
- * A standard Tcl result. The variable pointed to by valuePtrPtr is
- * updated with the value for the key. Note that it is not an error for
- * the key to have no mapping in the dictionary.
- *
- * Side effects:
- * The object pointed to by dictPtr is converted to a dictionary if it is
- * not already one.
- *
- *----------------------------------------------------------------------
- */
-
-int
-Tcl_DictObjGet(
- Tcl_Interp *interp,
- Tcl_Obj *dictPtr,
- Tcl_Obj *keyPtr,
- Tcl_Obj **valuePtrPtr)
-{
- Dict *dict;
- Tcl_HashEntry *hPtr;
-
- if (dictPtr->typePtr != &tclDictType
- && SetDictFromAny(interp, dictPtr) != TCL_OK) {
- *valuePtrPtr = NULL;
- return TCL_ERROR;
- }
-
- dict = DICT(dictPtr);
- hPtr = Tcl_FindHashEntry(&dict->table, keyPtr);
- if (hPtr == NULL) {
- *valuePtrPtr = NULL;
- } else {
- *valuePtrPtr = Tcl_GetHashValue(hPtr);
- }
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_DictObjRemove --
- *
- * Remove the key,value pair with the given key from the dictionary; the
- * key does not need to be present in the dictionary.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * The object pointed to by dictPtr is converted to a dictionary if it is
- * not already one, and any string representation that it has is
- * invalidated.
- *
- *----------------------------------------------------------------------
- */
-
-int
-Tcl_DictObjRemove(
- Tcl_Interp *interp,
- Tcl_Obj *dictPtr,
- Tcl_Obj *keyPtr)
-{
- Dict *dict;
-
- if (Tcl_IsShared(dictPtr)) {
- Tcl_Panic("%s called with shared object", "Tcl_DictObjRemove");
- }
-
- if (dictPtr->typePtr != &tclDictType
- && SetDictFromAny(interp, dictPtr) != TCL_OK) {
- return TCL_ERROR;
- }
-
- dict = DICT(dictPtr);
- if (DeleteChainEntry(dict, keyPtr)) {
- if (dictPtr->bytes != NULL) {
- TclInvalidateStringRep(dictPtr);
- }
- dict->epoch++;
- }
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_DictObjSize --
- *
- * How many key,value pairs are there in the dictionary?
- *
- * Results:
- * A standard Tcl result. Updates the variable pointed to by sizePtr with
- * the number of key,value pairs in the dictionary.
- *
- * Side effects:
- * The dictPtr object is converted to a dictionary type if it is not a
- * dictionary already.
- *
- *----------------------------------------------------------------------
- */
-
-int
-Tcl_DictObjSize(
- Tcl_Interp *interp,
- Tcl_Obj *dictPtr,
- int *sizePtr)
-{
- Dict *dict;
-
- if (dictPtr->typePtr != &tclDictType
- && SetDictFromAny(interp, dictPtr) != TCL_OK) {
- return TCL_ERROR;
- }
-
- dict = DICT(dictPtr);
- *sizePtr = dict->table.numEntries;
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_DictObjFirst --
- *
- * Start a traversal of the dictionary. Caller must supply the search
- * context, pointers for returning key and value, and a pointer to allow
- * indication of whether the dictionary has been traversed (i.e. the
- * dictionary is empty). The order of traversal is undefined.
- *
- * Results:
- * A standard Tcl result. Updates the variables pointed to by keyPtrPtr,
- * valuePtrPtr and donePtr. Either of keyPtrPtr and valuePtrPtr may be
- * NULL, in which case the key/value is not made available to the caller.
- *
- * Side effects:
- * The dictPtr object is converted to a dictionary type if it is not a
- * dictionary already. The search context is initialised if the search
- * has not finished. The dictionary's internal rep is Tcl_Preserve()d if
- * the dictionary has at least one element.
- *
- *----------------------------------------------------------------------
- */
-
-int
-Tcl_DictObjFirst(
- Tcl_Interp *interp, /* For error messages, or NULL if no error
- * messages desired. */
- Tcl_Obj *dictPtr, /* Dictionary to traverse. */
- Tcl_DictSearch *searchPtr, /* Pointer to a dict search context. */
- Tcl_Obj **keyPtrPtr, /* Pointer to a variable to have the first key
- * written into, or NULL. */
- Tcl_Obj **valuePtrPtr, /* Pointer to a variable to have the first
- * value written into, or NULL.*/
- int *donePtr) /* Pointer to a variable which will have a 1
- * written into when there are no further
- * values in the dictionary, or a 0
- * otherwise. */
-{
- Dict *dict;
- ChainEntry *cPtr;
-
- if (dictPtr->typePtr != &tclDictType
- && SetDictFromAny(interp, dictPtr) != TCL_OK) {
- return TCL_ERROR;
- }
-
- dict = DICT(dictPtr);
- cPtr = dict->entryChainHead;
- if (cPtr == NULL) {
- searchPtr->epoch = -1;
- *donePtr = 1;
- } else {
- *donePtr = 0;
- searchPtr->dictionaryPtr = (Tcl_Dict) dict;
- searchPtr->epoch = dict->epoch;
- searchPtr->next = cPtr->nextPtr;
- dict->refcount++;
- if (keyPtrPtr != NULL) {
- *keyPtrPtr = Tcl_GetHashKey(&dict->table, &cPtr->entry);
- }
- if (valuePtrPtr != NULL) {
- *valuePtrPtr = Tcl_GetHashValue(&cPtr->entry);
- }
- }
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_DictObjNext --
- *
- * Continue a traversal of a dictionary previously started with
- * Tcl_DictObjFirst. This function is safe against concurrent
- * modification of the underlying object (including type shimmering),
- * treating such situations as if the search has terminated, though it is
- * up to the caller to ensure that the object itself is not disposed
- * until the search has finished. It is _not_ safe against modifications
- * from other threads.
- *
- * Results:
- * Updates the variables pointed to by keyPtrPtr, valuePtrPtr and
- * donePtr. Either of keyPtrPtr and valuePtrPtr may be NULL, in which
- * case the key/value is not made available to the caller.
- *
- * Side effects:
- * Removes a reference to the dictionary's internal rep if the search
- * terminates.
- *
- *----------------------------------------------------------------------
- */
-
-void
-Tcl_DictObjNext(
- Tcl_DictSearch *searchPtr, /* Pointer to a hash search context. */
- Tcl_Obj **keyPtrPtr, /* Pointer to a variable to have the first key
- * written into, or NULL. */
- Tcl_Obj **valuePtrPtr, /* Pointer to a variable to have the first
- * value written into, or NULL.*/
- int *donePtr) /* Pointer to a variable which will have a 1
- * written into when there are no further
- * values in the dictionary, or a 0
- * otherwise. */
-{
- ChainEntry *cPtr;
-
- /*
- * If the searh is done; we do no work.
- */
-
- if (searchPtr->epoch == -1) {
- *donePtr = 1;
- return;
- }
-
- /*
- * Bail out if the dictionary has had any elements added, modified or
- * removed. This *shouldn't* happen, but...
- */
-
- if (((Dict *)searchPtr->dictionaryPtr)->epoch != searchPtr->epoch) {
- Tcl_Panic("concurrent dictionary modification and search");
- }
-
- cPtr = searchPtr->next;
- if (cPtr == NULL) {
- Tcl_DictObjDone(searchPtr);
- *donePtr = 1;
- return;
- }
-
- searchPtr->next = cPtr->nextPtr;
- *donePtr = 0;
- if (keyPtrPtr != NULL) {
- *keyPtrPtr = Tcl_GetHashKey(
- &((Dict *)searchPtr->dictionaryPtr)->table, &cPtr->entry);
- }
- if (valuePtrPtr != NULL) {
- *valuePtrPtr = Tcl_GetHashValue(&cPtr->entry);
- }
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_DictObjDone --
- *
- * Call this if you want to stop a search before you reach the end of the
- * dictionary (e.g. because of abnormal termination of the search). It
- * need not be used if the search reaches its natural end (i.e. if either
- * Tcl_DictObjFirst or Tcl_DictObjNext sets its donePtr variable to 1).
- *
- * Results:
- * None.
- *
- * Side effects:
- * Removes a reference to the dictionary's internal rep.
- *
- *----------------------------------------------------------------------
- */
-
-void
-Tcl_DictObjDone(
- Tcl_DictSearch *searchPtr) /* Pointer to a hash search context. */
-{
- Dict *dict;
-
- if (searchPtr->epoch != -1) {
- searchPtr->epoch = -1;
- dict = (Dict *) searchPtr->dictionaryPtr;
- dict->refcount--;
- if (dict->refcount <= 0) {
- DeleteDict(dict);
- }
- }
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_DictObjPutKeyList --
- *
- * Add a key...key,value pair to a dictionary tree. The main dictionary
- * value must not be shared, though sub-dictionaries may be. All
- * intermediate dictionaries on the path must exist.
- *
- * Results:
- * A standard Tcl result. Note that in the error case, a message is left
- * in interp unless that is NULL.
- *
- * Side effects:
- * If the dictionary and any of its sub-dictionaries on the path have
- * string representations, these are invalidated.
- *
- *----------------------------------------------------------------------
- */
-
-int
-Tcl_DictObjPutKeyList(
- Tcl_Interp *interp,
- Tcl_Obj *dictPtr,
- int keyc,
- Tcl_Obj *const keyv[],
- Tcl_Obj *valuePtr)
-{
- Dict *dict;
- Tcl_HashEntry *hPtr;
- int isNew;
-
- if (Tcl_IsShared(dictPtr)) {
- Tcl_Panic("%s called with shared object", "Tcl_DictObjPutKeyList");
- }
- if (keyc < 1) {
- Tcl_Panic("%s called with empty key list", "Tcl_DictObjPutKeyList");
- }
-
- dictPtr = TclTraceDictPath(interp, dictPtr, keyc-1,keyv, DICT_PATH_CREATE);
- if (dictPtr == NULL) {
- return TCL_ERROR;
- }
-
- dict = DICT(dictPtr);
- hPtr = CreateChainEntry(dict, keyv[keyc-1], &isNew);
- Tcl_IncrRefCount(valuePtr);
- if (!isNew) {
- Tcl_Obj *oldValuePtr = Tcl_GetHashValue(hPtr);
-
- TclDecrRefCount(oldValuePtr);
- }
- Tcl_SetHashValue(hPtr, valuePtr);
- InvalidateDictChain(dictPtr);
-
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_DictObjRemoveKeyList --
- *
- * Remove a key...key,value pair from a dictionary tree (the value
- * removed is implicit in the key path). The main dictionary value must
- * not be shared, though sub-dictionaries may be. It is not an error if
- * there is no value associated with the given key list, but all
- * intermediate dictionaries on the key path must exist.
- *
- * Results:
- * A standard Tcl result. Note that in the error case, a message is left
- * in interp unless that is NULL.
- *
- * Side effects:
- * If the dictionary and any of its sub-dictionaries on the key path have
- * string representations, these are invalidated.
- *
- *----------------------------------------------------------------------
- */
-
-int
-Tcl_DictObjRemoveKeyList(
- Tcl_Interp *interp,
- Tcl_Obj *dictPtr,
- int keyc,
- Tcl_Obj *const keyv[])
-{
- Dict *dict;
-
- if (Tcl_IsShared(dictPtr)) {
- Tcl_Panic("%s called with shared object", "Tcl_DictObjRemoveKeyList");
- }
- if (keyc < 1) {
- Tcl_Panic("%s called with empty key list", "Tcl_DictObjRemoveKeyList");
- }
-
- dictPtr = TclTraceDictPath(interp, dictPtr, keyc-1,keyv, DICT_PATH_UPDATE);
- if (dictPtr == NULL) {
- return TCL_ERROR;
- }
-
- dict = DICT(dictPtr);
- DeleteChainEntry(dict, keyv[keyc-1]);
- InvalidateDictChain(dictPtr);
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_NewDictObj --
- *
- * This function is normally called when not debugging: i.e., when
- * TCL_MEM_DEBUG is not defined. It creates a new dict object without any
- * content.
- *
- * When TCL_MEM_DEBUG is defined, this function just returns the result
- * of calling the debugging version Tcl_DbNewDictObj.
- *
- * Results:
- * A new dict object is returned; it has no keys defined in it. The new
- * object's string representation is left NULL, and the ref count of the
- * object is 0.
- *
- * Side Effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-Tcl_Obj *
-Tcl_NewDictObj(void)
-{
-#ifdef TCL_MEM_DEBUG
- return Tcl_DbNewDictObj("unknown", 0);
-#else /* !TCL_MEM_DEBUG */
-
- Tcl_Obj *dictPtr;
- Dict *dict;
-
- TclNewObj(dictPtr);
- TclInvalidateStringRep(dictPtr);
- dict = ckalloc(sizeof(Dict));
- InitChainTable(dict);
- dict->epoch = 0;
- dict->chain = NULL;
- dict->refcount = 1;
- DICT(dictPtr) = dict;
- dictPtr->internalRep.twoPtrValue.ptr2 = NULL;
- dictPtr->typePtr = &tclDictType;
- return dictPtr;
-#endif
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_DbNewDictObj --
- *
- * This function is normally called when debugging: i.e., when
- * TCL_MEM_DEBUG is defined. It creates new dict objects. It is the same
- * as the Tcl_NewDictObj function above except that it calls
- * Tcl_DbCkalloc directly with the file name and line number from its
- * caller. This simplifies debugging since then the [memory active]
- * command will report the correct file name and line number when
- * reporting objects that haven't been freed.
- *
- * When TCL_MEM_DEBUG is not defined, this function just returns the
- * result of calling Tcl_NewDictObj.
- *
- * Results:
- * A new dict object is returned; it has no keys defined in it. The new
- * object's string representation is left NULL, and the ref count of the
- * object is 0.
- *
- * Side Effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
-Tcl_Obj *
-Tcl_DbNewDictObj(
- const char *file,
- int line)
-{
-#ifdef TCL_MEM_DEBUG
- Tcl_Obj *dictPtr;
- Dict *dict;
-
- TclDbNewObj(dictPtr, file, line);
- TclInvalidateStringRep(dictPtr);
- dict = ckalloc(sizeof(Dict));
- InitChainTable(dict);
- dict->epoch = 0;
- dict->chain = NULL;
- dict->refcount = 1;
- DICT(dictPtr) = dict;
- dictPtr->internalRep.twoPtrValue.ptr2 = NULL;
- dictPtr->typePtr = &tclDictType;
- return dictPtr;
-#else /* !TCL_MEM_DEBUG */
- return Tcl_NewDictObj();
-#endif
-}
-
-/***** START OF FUNCTIONS IMPLEMENTING TCL COMMANDS *****/
-
-/*
- *----------------------------------------------------------------------
- *
- * DictCreateCmd --
- *
- * This function implements the "dict create" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictCreateCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Tcl_Obj *dictObj;
- int i;
-
- /*
- * Must have an even number of arguments; note that number of preceding
- * arguments (i.e. "dict create" is also even, which makes this much
- * easier.)
- */
-
- if ((objc & 1) == 0) {
- Tcl_WrongNumArgs(interp, 1, objv, "?key value ...?");
- return TCL_ERROR;
- }
-
- dictObj = Tcl_NewDictObj();
- for (i=1 ; i<objc ; i+=2) {
- /*
- * The next command is assumed to never fail...
- */
- Tcl_DictObjPut(NULL, dictObj, objv[i], objv[i+1]);
- }
- Tcl_SetObjResult(interp, dictObj);
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictGetCmd --
- *
- * This function implements the "dict get" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictGetCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Tcl_Obj *dictPtr, *valuePtr = NULL;
- int result;
-
- if (objc < 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "dictionary ?key ...?");
- return TCL_ERROR;
- }
-
- /*
- * Test for the special case of no keys, which returns a *list* of all
- * key,value pairs. We produce a copy here because that makes subsequent
- * list handling more efficient.
- */
-
- if (objc == 2) {
- Tcl_Obj *keyPtr = NULL, *listPtr;
- Tcl_DictSearch search;
- int done;
-
- result = Tcl_DictObjFirst(interp, objv[1], &search,
- &keyPtr, &valuePtr, &done);
- if (result != TCL_OK) {
- return result;
- }
- listPtr = Tcl_NewListObj(0, NULL);
- while (!done) {
- /*
- * Assume these won't fail as we have complete control over the
- * types of things here.
- */
-
- Tcl_ListObjAppendElement(interp, listPtr, keyPtr);
- Tcl_ListObjAppendElement(interp, listPtr, valuePtr);
-
- Tcl_DictObjNext(&search, &keyPtr, &valuePtr, &done);
- }
- Tcl_SetObjResult(interp, listPtr);
- return TCL_OK;
- }
-
- /*
- * Loop through the list of keys, looking up the key at the current index
- * in the current dictionary each time. Once we've done the lookup, we set
- * the current dictionary to be the value we looked up (in case the value
- * was not the last one and we are going through a chain of searches.)
- * Note that this loop always executes at least once.
- */
-
- dictPtr = TclTraceDictPath(interp, objv[1], objc-3,objv+2, DICT_PATH_READ);
- if (dictPtr == NULL) {
- return TCL_ERROR;
- }
- result = Tcl_DictObjGet(interp, dictPtr, objv[objc-1], &valuePtr);
- if (result != TCL_OK) {
- return result;
- }
- if (valuePtr == NULL) {
- Tcl_SetObjResult(interp, Tcl_ObjPrintf(
- "key \"%s\" not known in dictionary",
- TclGetString(objv[objc-1])));
- Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "DICT",
- TclGetString(objv[objc-1]), NULL);
- return TCL_ERROR;
- }
- Tcl_SetObjResult(interp, valuePtr);
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictReplaceCmd --
- *
- * This function implements the "dict replace" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictReplaceCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Tcl_Obj *dictPtr;
- int i;
-
- if ((objc < 2) || (objc & 1)) {
- Tcl_WrongNumArgs(interp, 1, objv, "dictionary ?key value ...?");
- return TCL_ERROR;
- }
-
- dictPtr = objv[1];
- if (dictPtr->typePtr != &tclDictType
- && SetDictFromAny(interp, dictPtr) != TCL_OK) {
- return TCL_ERROR;
- }
- if (Tcl_IsShared(dictPtr)) {
- dictPtr = Tcl_DuplicateObj(dictPtr);
- }
- if (dictPtr->bytes != NULL) {
- TclInvalidateStringRep(dictPtr);
- }
- for (i=2 ; i<objc ; i+=2) {
- Tcl_DictObjPut(NULL, dictPtr, objv[i], objv[i+1]);
- }
- Tcl_SetObjResult(interp, dictPtr);
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictRemoveCmd --
- *
- * This function implements the "dict remove" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictRemoveCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Tcl_Obj *dictPtr;
- int i;
-
- if (objc < 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "dictionary ?key ...?");
- return TCL_ERROR;
- }
-
- dictPtr = objv[1];
- if (dictPtr->typePtr != &tclDictType
- && SetDictFromAny(interp, dictPtr) != TCL_OK) {
- return TCL_ERROR;
- }
- if (Tcl_IsShared(dictPtr)) {
- dictPtr = Tcl_DuplicateObj(dictPtr);
- }
- if (dictPtr->bytes != NULL) {
- TclInvalidateStringRep(dictPtr);
- }
- for (i=2 ; i<objc ; i++) {
- Tcl_DictObjRemove(NULL, dictPtr, objv[i]);
- }
- Tcl_SetObjResult(interp, dictPtr);
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictMergeCmd --
- *
- * This function implements the "dict merge" Tcl command. See the user
- * documentation for details on what it does, and TIP#163 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictMergeCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Tcl_Obj *targetObj, *keyObj = NULL, *valueObj = NULL;
- int allocatedDict = 0;
- int i, done;
- Tcl_DictSearch search;
-
- if (objc == 1) {
- /*
- * No dictionary arguments; return default (empty value).
- */
-
- return TCL_OK;
- }
-
- /*
- * Make sure first argument is a dictionary.
- */
-
- targetObj = objv[1];
- if (targetObj->typePtr != &tclDictType
- && SetDictFromAny(interp, targetObj) != TCL_OK) {
- return TCL_ERROR;
- }
-
- if (objc == 2) {
- /*
- * Single argument, return it.
- */
-
- Tcl_SetObjResult(interp, objv[1]);
- return TCL_OK;
- }
-
- /*
- * Normal behaviour: combining two (or more) dictionaries.
- */
-
- if (Tcl_IsShared(targetObj)) {
- targetObj = Tcl_DuplicateObj(targetObj);
- allocatedDict = 1;
- }
- for (i=2 ; i<objc ; i++) {
- if (Tcl_DictObjFirst(interp, objv[i], &search, &keyObj, &valueObj,
- &done) != TCL_OK) {
- if (allocatedDict) {
- TclDecrRefCount(targetObj);
- }
- return TCL_ERROR;
- }
- while (!done) {
- /*
- * Next line can't fail; already know we have a dictionary in
- * targetObj.
- */
-
- Tcl_DictObjPut(NULL, targetObj, keyObj, valueObj);
- Tcl_DictObjNext(&search, &keyObj, &valueObj, &done);
- }
- Tcl_DictObjDone(&search);
- }
- Tcl_SetObjResult(interp, targetObj);
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictKeysCmd --
- *
- * This function implements the "dict keys" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictKeysCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Tcl_Obj *listPtr;
- const char *pattern = NULL;
-
- if (objc!=2 && objc!=3) {
- Tcl_WrongNumArgs(interp, 1, objv, "dictionary ?pattern?");
- return TCL_ERROR;
- }
-
- /*
- * A direct check that we have a dictionary. We don't start the iteration
- * yet because that might allocate memory or set locks that we do not
- * need. [Bug 1705778, leak K04]
- */
-
- if (objv[1]->typePtr != &tclDictType
- && SetDictFromAny(interp, objv[1]) != TCL_OK) {
- return TCL_ERROR;
- }
-
- if (objc == 3) {
- pattern = TclGetString(objv[2]);
- }
- listPtr = Tcl_NewListObj(0, NULL);
- if ((pattern != NULL) && TclMatchIsTrivial(pattern)) {
- Tcl_Obj *valuePtr = NULL;
-
- Tcl_DictObjGet(interp, objv[1], objv[2], &valuePtr);
- if (valuePtr != NULL) {
- Tcl_ListObjAppendElement(NULL, listPtr, objv[2]);
- }
- } else {
- Tcl_DictSearch search;
- Tcl_Obj *keyPtr = NULL;
- int done = 0;
-
- /*
- * At this point, we know we have a dictionary (or at least something
- * that can be represented; it could theoretically have shimmered away
- * when the pattern was fetched, but that shouldn't be damaging) so we
- * can start the iteration process without checking for failures.
- */
-
- Tcl_DictObjFirst(NULL, objv[1], &search, &keyPtr, NULL, &done);
- for (; !done ; Tcl_DictObjNext(&search, &keyPtr, NULL, &done)) {
- if (!pattern || Tcl_StringMatch(TclGetString(keyPtr), pattern)) {
- Tcl_ListObjAppendElement(NULL, listPtr, keyPtr);
- }
- }
- Tcl_DictObjDone(&search);
- }
-
- Tcl_SetObjResult(interp, listPtr);
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictValuesCmd --
- *
- * This function implements the "dict values" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictValuesCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Tcl_Obj *valuePtr = NULL, *listPtr;
- Tcl_DictSearch search;
- int done;
- const char *pattern;
-
- if (objc!=2 && objc!=3) {
- Tcl_WrongNumArgs(interp, 1, objv, "dictionary ?pattern?");
- return TCL_ERROR;
- }
-
- if (Tcl_DictObjFirst(interp, objv[1], &search, NULL, &valuePtr,
- &done) != TCL_OK) {
- return TCL_ERROR;
- }
- if (objc == 3) {
- pattern = TclGetString(objv[2]);
- } else {
- pattern = NULL;
- }
- listPtr = Tcl_NewListObj(0, NULL);
- for (; !done ; Tcl_DictObjNext(&search, NULL, &valuePtr, &done)) {
- if (pattern==NULL || Tcl_StringMatch(TclGetString(valuePtr),pattern)) {
- /*
- * Assume this operation always succeeds.
- */
-
- Tcl_ListObjAppendElement(interp, listPtr, valuePtr);
- }
- }
- Tcl_DictObjDone(&search);
-
- Tcl_SetObjResult(interp, listPtr);
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictSizeCmd --
- *
- * This function implements the "dict size" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictSizeCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- int result, size;
-
- if (objc != 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "dictionary");
- return TCL_ERROR;
- }
- result = Tcl_DictObjSize(interp, objv[1], &size);
- if (result == TCL_OK) {
- Tcl_SetObjResult(interp, Tcl_NewIntObj(size));
- }
- return result;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictExistsCmd --
- *
- * This function implements the "dict exists" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictExistsCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Tcl_Obj *dictPtr, *valuePtr;
-
- if (objc < 3) {
- Tcl_WrongNumArgs(interp, 1, objv, "dictionary key ?key ...?");
- return TCL_ERROR;
- }
-
- dictPtr = TclTraceDictPath(interp, objv[1], objc-3, objv+2,
- DICT_PATH_EXISTS);
- if (dictPtr == NULL || dictPtr == DICT_PATH_NON_EXISTENT
- || Tcl_DictObjGet(interp, dictPtr, objv[objc-1],
- &valuePtr) != TCL_OK) {
- Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0));
- } else {
- Tcl_SetObjResult(interp, Tcl_NewBooleanObj(valuePtr != NULL));
- }
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictInfoCmd --
- *
- * This function implements the "dict info" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictInfoCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Tcl_Obj *dictPtr;
- Dict *dict;
- char *statsStr;
-
- if (objc != 2) {
- Tcl_WrongNumArgs(interp, 1, objv, "dictionary");
- return TCL_ERROR;
- }
-
- dictPtr = objv[1];
- if (dictPtr->typePtr != &tclDictType
- && SetDictFromAny(interp, dictPtr) != TCL_OK) {
- return TCL_ERROR;
- }
- dict = DICT(dictPtr);
-
- statsStr = Tcl_HashStats(&dict->table);
- Tcl_SetObjResult(interp, Tcl_NewStringObj(statsStr, -1));
- ckfree(statsStr);
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictIncrCmd --
- *
- * This function implements the "dict incr" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictIncrCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- int code = TCL_OK;
- Tcl_Obj *dictPtr, *valuePtr = NULL;
-
- if (objc < 3 || objc > 4) {
- Tcl_WrongNumArgs(interp, 1, objv, "dictVarName key ?increment?");
- return TCL_ERROR;
- }
-
- dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0);
- if (dictPtr == NULL) {
- /*
- * Variable didn't yet exist. Create new dictionary value.
- */
-
- dictPtr = Tcl_NewDictObj();
- } else if (Tcl_DictObjGet(interp, dictPtr, objv[2], &valuePtr) != TCL_OK) {
- /*
- * Variable contents are not a dict, report error.
- */
-
- return TCL_ERROR;
- }
- if (Tcl_IsShared(dictPtr)) {
- /*
- * A little internals surgery to avoid copying a string rep that will
- * soon be no good.
- */
-
- char *saved = dictPtr->bytes;
- Tcl_Obj *oldPtr = dictPtr;
-
- dictPtr->bytes = NULL;
- dictPtr = Tcl_DuplicateObj(dictPtr);
- oldPtr->bytes = saved;
- }
- if (valuePtr == NULL) {
- /*
- * Key not in dictionary. Create new key with increment as value.
- */
-
- if (objc == 4) {
- /*
- * Verify increment is an integer.
- */
-
- mp_int increment;
-
- code = Tcl_GetBignumFromObj(interp, objv[3], &increment);
- if (code != TCL_OK) {
- Tcl_AddErrorInfo(interp, "\n (reading increment)");
- } else {
- /*
- * Remember to dispose with the bignum as we're not actually
- * using it directly. [Bug 2874678]
- */
-
- mp_clear(&increment);
- Tcl_DictObjPut(NULL, dictPtr, objv[2], objv[3]);
- }
- } else {
- Tcl_DictObjPut(NULL, dictPtr, objv[2], Tcl_NewIntObj(1));
- }
- } else {
- /*
- * Key in dictionary. Increment its value with minimum dup.
- */
-
- if (Tcl_IsShared(valuePtr)) {
- valuePtr = Tcl_DuplicateObj(valuePtr);
- Tcl_DictObjPut(NULL, dictPtr, objv[2], valuePtr);
- }
- if (objc == 4) {
- code = TclIncrObj(interp, valuePtr, objv[3]);
- } else {
- Tcl_Obj *incrPtr = Tcl_NewIntObj(1);
-
- Tcl_IncrRefCount(incrPtr);
- code = TclIncrObj(interp, valuePtr, incrPtr);
- TclDecrRefCount(incrPtr);
- }
- }
- if (code == TCL_OK) {
- TclInvalidateStringRep(dictPtr);
- valuePtr = Tcl_ObjSetVar2(interp, objv[1], NULL,
- dictPtr, TCL_LEAVE_ERR_MSG);
- if (valuePtr == NULL) {
- code = TCL_ERROR;
- } else {
- Tcl_SetObjResult(interp, valuePtr);
- }
- } else if (dictPtr->refCount == 0) {
- TclDecrRefCount(dictPtr);
- }
- return code;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictLappendCmd --
- *
- * This function implements the "dict lappend" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictLappendCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Tcl_Obj *dictPtr, *valuePtr, *resultPtr;
- int i, allocatedDict = 0, allocatedValue = 0;
-
- if (objc < 3) {
- Tcl_WrongNumArgs(interp, 1, objv, "dictVarName key ?value ...?");
- return TCL_ERROR;
- }
-
- dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0);
- if (dictPtr == NULL) {
- allocatedDict = 1;
- dictPtr = Tcl_NewDictObj();
- } else if (Tcl_IsShared(dictPtr)) {
- allocatedDict = 1;
- dictPtr = Tcl_DuplicateObj(dictPtr);
- }
-
- if (Tcl_DictObjGet(interp, dictPtr, objv[2], &valuePtr) != TCL_OK) {
- if (allocatedDict) {
- TclDecrRefCount(dictPtr);
- }
- return TCL_ERROR;
- }
-
- if (valuePtr == NULL) {
- valuePtr = Tcl_NewListObj(objc-3, objv+3);
- allocatedValue = 1;
- } else {
- if (Tcl_IsShared(valuePtr)) {
- allocatedValue = 1;
- valuePtr = Tcl_DuplicateObj(valuePtr);
- }
-
- for (i=3 ; i<objc ; i++) {
- if (Tcl_ListObjAppendElement(interp, valuePtr,
- objv[i]) != TCL_OK) {
- if (allocatedValue) {
- TclDecrRefCount(valuePtr);
- }
- if (allocatedDict) {
- TclDecrRefCount(dictPtr);
- }
- return TCL_ERROR;
- }
- }
- }
-
- if (allocatedValue) {
- Tcl_DictObjPut(NULL, dictPtr, objv[2], valuePtr);
- } else if (dictPtr->bytes != NULL) {
- TclInvalidateStringRep(dictPtr);
- }
-
- resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr,
- TCL_LEAVE_ERR_MSG);
- if (resultPtr == NULL) {
- return TCL_ERROR;
- }
- Tcl_SetObjResult(interp, resultPtr);
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictAppendCmd --
- *
- * This function implements the "dict append" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictAppendCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Tcl_Obj *dictPtr, *valuePtr, *resultPtr;
- int i, allocatedDict = 0;
-
- if (objc < 3) {
- Tcl_WrongNumArgs(interp, 1, objv, "dictVarName key ?value ...?");
- return TCL_ERROR;
- }
-
- dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0);
- if (dictPtr == NULL) {
- allocatedDict = 1;
- dictPtr = Tcl_NewDictObj();
- } else if (Tcl_IsShared(dictPtr)) {
- allocatedDict = 1;
- dictPtr = Tcl_DuplicateObj(dictPtr);
- }
-
- if (Tcl_DictObjGet(interp, dictPtr, objv[2], &valuePtr) != TCL_OK) {
- if (allocatedDict) {
- TclDecrRefCount(dictPtr);
- }
- return TCL_ERROR;
- }
-
- if (valuePtr == NULL) {
- TclNewObj(valuePtr);
- } else if (Tcl_IsShared(valuePtr)) {
- valuePtr = Tcl_DuplicateObj(valuePtr);
- }
-
- for (i=3 ; i<objc ; i++) {
- Tcl_AppendObjToObj(valuePtr, objv[i]);
- }
-
- Tcl_DictObjPut(NULL, dictPtr, objv[2], valuePtr);
-
- resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr,
- TCL_LEAVE_ERR_MSG);
- if (resultPtr == NULL) {
- return TCL_ERROR;
- }
- Tcl_SetObjResult(interp, resultPtr);
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictForNRCmd --
- *
- * These functions implement the "dict for" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictForNRCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Interp *iPtr = (Interp *) interp;
- Tcl_Obj *scriptObj, *keyVarObj, *valueVarObj;
- Tcl_Obj **varv, *keyObj, *valueObj;
- Tcl_DictSearch *searchPtr;
- int varc, done;
-
- if (objc != 4) {
- Tcl_WrongNumArgs(interp, 1, objv,
- "{keyVarName valueVarName} dictionary script");
- return TCL_ERROR;
- }
-
- /*
- * Parse arguments.
- */
-
- if (TclListObjGetElements(interp, objv[1], &varc, &varv) != TCL_OK) {
- return TCL_ERROR;
- }
- if (varc != 2) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "must have exactly two variable names", -1));
- Tcl_SetErrorCode(interp, "TCL", "SYNTAX", "dict", "for", NULL);
- return TCL_ERROR;
- }
- searchPtr = TclStackAlloc(interp, sizeof(Tcl_DictSearch));
- if (Tcl_DictObjFirst(interp, objv[2], searchPtr, &keyObj, &valueObj,
- &done) != TCL_OK) {
- TclStackFree(interp, searchPtr);
- return TCL_ERROR;
- }
- if (done) {
- TclStackFree(interp, searchPtr);
- return TCL_OK;
- }
- TclListObjGetElements(NULL, objv[1], &varc, &varv);
- keyVarObj = varv[0];
- valueVarObj = varv[1];
- scriptObj = objv[3];
-
- /*
- * Make sure that these objects (which we need throughout the body of the
- * loop) don't vanish. Note that the dictionary internal rep is locked
- * internally so that updates, shimmering, etc are not a problem.
- */
-
- Tcl_IncrRefCount(keyVarObj);
- Tcl_IncrRefCount(valueVarObj);
- Tcl_IncrRefCount(scriptObj);
-
- /*
- * Stop the value from getting hit in any way by any traces on the key
- * variable.
- */
-
- Tcl_IncrRefCount(valueObj);
- if (Tcl_ObjSetVar2(interp, keyVarObj, NULL, keyObj,
- TCL_LEAVE_ERR_MSG) == NULL) {
- TclDecrRefCount(valueObj);
- goto error;
- }
- TclDecrRefCount(valueObj);
- if (Tcl_ObjSetVar2(interp, valueVarObj, NULL, valueObj,
- TCL_LEAVE_ERR_MSG) == NULL) {
- goto error;
- }
-
- /*
- * Run the script.
- */
-
- TclNRAddCallback(interp, DictForLoopCallback, searchPtr, keyVarObj,
- valueVarObj, scriptObj);
- return TclNREvalObjEx(interp, scriptObj, 0, iPtr->cmdFramePtr, 3);
-
- /*
- * For unwinding everything on error.
- */
-
- error:
- TclDecrRefCount(keyVarObj);
- TclDecrRefCount(valueVarObj);
- TclDecrRefCount(scriptObj);
- Tcl_DictObjDone(searchPtr);
- TclStackFree(interp, searchPtr);
- return TCL_ERROR;
-}
-
-static int
-DictForLoopCallback(
- ClientData data[],
- Tcl_Interp *interp,
- int result)
-{
- Interp *iPtr = (Interp *) interp;
- Tcl_DictSearch *searchPtr = data[0];
- Tcl_Obj *keyVarObj = data[1];
- Tcl_Obj *valueVarObj = data[2];
- Tcl_Obj *scriptObj = data[3];
- Tcl_Obj *keyObj, *valueObj;
- int done;
-
- /*
- * Process the result from the previous execution of the script body.
- */
-
- if (result == TCL_CONTINUE) {
- result = TCL_OK;
- } else if (result != TCL_OK) {
- if (result == TCL_BREAK) {
- Tcl_ResetResult(interp);
- result = TCL_OK;
- } else if (result == TCL_ERROR) {
- Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
- "\n (\"dict for\" body line %d)",
- Tcl_GetErrorLine(interp)));
- }
- goto done;
- }
-
- /*
- * Get the next mapping from the dictionary.
- */
-
- Tcl_DictObjNext(searchPtr, &keyObj, &valueObj, &done);
- if (done) {
- Tcl_ResetResult(interp);
- goto done;
- }
-
- /*
- * Stop the value from getting hit in any way by any traces on the key
- * variable.
- */
-
- Tcl_IncrRefCount(valueObj);
- if (Tcl_ObjSetVar2(interp, keyVarObj, NULL, keyObj,
- TCL_LEAVE_ERR_MSG) == NULL) {
- TclDecrRefCount(valueObj);
- result = TCL_ERROR;
- goto done;
- }
- TclDecrRefCount(valueObj);
- if (Tcl_ObjSetVar2(interp, valueVarObj, NULL, valueObj,
- TCL_LEAVE_ERR_MSG) == NULL) {
- result = TCL_ERROR;
- goto done;
- }
-
- /*
- * Run the script.
- */
-
- TclNRAddCallback(interp, DictForLoopCallback, searchPtr, keyVarObj,
- valueVarObj, scriptObj);
- return TclNREvalObjEx(interp, scriptObj, 0, iPtr->cmdFramePtr, 3);
-
- /*
- * For unwinding everything once the iterating is done.
- */
-
- done:
- TclDecrRefCount(keyVarObj);
- TclDecrRefCount(valueVarObj);
- TclDecrRefCount(scriptObj);
- Tcl_DictObjDone(searchPtr);
- TclStackFree(interp, searchPtr);
- return result;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictMapNRCmd --
- *
- * These functions implement the "dict map" Tcl command. See the user
- * documentation for details on what it does, and TIP#405 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictMapNRCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Interp *iPtr = (Interp *) interp;
- Tcl_Obj **varv, *keyObj, *valueObj;
- DictMapStorage *storagePtr;
- int varc, done;
-
- if (objc != 4) {
- Tcl_WrongNumArgs(interp, 1, objv,
- "{keyVarName valueVarName} dictionary script");
- return TCL_ERROR;
- }
-
- /*
- * Parse arguments.
- */
-
- if (TclListObjGetElements(interp, objv[1], &varc, &varv) != TCL_OK) {
- return TCL_ERROR;
- }
- if (varc != 2) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "must have exactly two variable names", -1));
- Tcl_SetErrorCode(interp, "TCL", "SYNTAX", "dict", "map", NULL);
- return TCL_ERROR;
- }
- storagePtr = TclStackAlloc(interp, sizeof(DictMapStorage));
- if (Tcl_DictObjFirst(interp, objv[2], &storagePtr->search, &keyObj,
- &valueObj, &done) != TCL_OK) {
- TclStackFree(interp, storagePtr);
- return TCL_ERROR;
- }
- if (done) {
- /*
- * Note that this exit leaves an empty value in the result (due to
- * command calling conventions) but that is OK since an empty value is
- * an empty dictionary.
- */
-
- TclStackFree(interp, storagePtr);
- return TCL_OK;
- }
- TclNewObj(storagePtr->accumulatorObj);
- TclListObjGetElements(NULL, objv[1], &varc, &varv);
- storagePtr->keyVarObj = varv[0];
- storagePtr->valueVarObj = varv[1];
- storagePtr->scriptObj = objv[3];
-
- /*
- * Make sure that these objects (which we need throughout the body of the
- * loop) don't vanish. Note that the dictionary internal rep is locked
- * internally so that updates, shimmering, etc are not a problem.
- */
-
- Tcl_IncrRefCount(storagePtr->accumulatorObj);
- Tcl_IncrRefCount(storagePtr->keyVarObj);
- Tcl_IncrRefCount(storagePtr->valueVarObj);
- Tcl_IncrRefCount(storagePtr->scriptObj);
-
- /*
- * Stop the value from getting hit in any way by any traces on the key
- * variable.
- */
-
- Tcl_IncrRefCount(valueObj);
- if (Tcl_ObjSetVar2(interp, storagePtr->keyVarObj, NULL, keyObj,
- TCL_LEAVE_ERR_MSG) == NULL) {
- TclDecrRefCount(valueObj);
- goto error;
- }
- if (Tcl_ObjSetVar2(interp, storagePtr->valueVarObj, NULL, valueObj,
- TCL_LEAVE_ERR_MSG) == NULL) {
- TclDecrRefCount(valueObj);
- goto error;
- }
- TclDecrRefCount(valueObj);
-
- /*
- * Run the script.
- */
-
- TclNRAddCallback(interp, DictMapLoopCallback, storagePtr, NULL,NULL,NULL);
- return TclNREvalObjEx(interp, storagePtr->scriptObj, 0,
- iPtr->cmdFramePtr, 3);
-
- /*
- * For unwinding everything on error.
- */
-
- error:
- TclDecrRefCount(storagePtr->keyVarObj);
- TclDecrRefCount(storagePtr->valueVarObj);
- TclDecrRefCount(storagePtr->scriptObj);
- TclDecrRefCount(storagePtr->accumulatorObj);
- Tcl_DictObjDone(&storagePtr->search);
- TclStackFree(interp, storagePtr);
- return TCL_ERROR;
-}
-
-static int
-DictMapLoopCallback(
- ClientData data[],
- Tcl_Interp *interp,
- int result)
-{
- Interp *iPtr = (Interp *) interp;
- DictMapStorage *storagePtr = data[0];
- Tcl_Obj *keyObj, *valueObj;
- int done;
-
- /*
- * Process the result from the previous execution of the script body.
- */
-
- if (result == TCL_CONTINUE) {
- result = TCL_OK;
- } else if (result != TCL_OK) {
- if (result == TCL_BREAK) {
- Tcl_ResetResult(interp);
- result = TCL_OK;
- } else if (result == TCL_ERROR) {
- Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
- "\n (\"dict map\" body line %d)",
- Tcl_GetErrorLine(interp)));
- }
- goto done;
- } else {
- keyObj = Tcl_ObjGetVar2(interp, storagePtr->keyVarObj, NULL,
- TCL_LEAVE_ERR_MSG);
- if (keyObj == NULL) {
- result = TCL_ERROR;
- goto done;
- }
- Tcl_DictObjPut(NULL, storagePtr->accumulatorObj, keyObj,
- Tcl_GetObjResult(interp));
- }
-
- /*
- * Get the next mapping from the dictionary.
- */
-
- Tcl_DictObjNext(&storagePtr->search, &keyObj, &valueObj, &done);
- if (done) {
- Tcl_SetObjResult(interp, storagePtr->accumulatorObj);
- goto done;
- }
-
- /*
- * Stop the value from getting hit in any way by any traces on the key
- * variable.
- */
-
- Tcl_IncrRefCount(valueObj);
- if (Tcl_ObjSetVar2(interp, storagePtr->keyVarObj, NULL, keyObj,
- TCL_LEAVE_ERR_MSG) == NULL) {
- TclDecrRefCount(valueObj);
- result = TCL_ERROR;
- goto done;
- }
- if (Tcl_ObjSetVar2(interp, storagePtr->valueVarObj, NULL, valueObj,
- TCL_LEAVE_ERR_MSG) == NULL) {
- TclDecrRefCount(valueObj);
- result = TCL_ERROR;
- goto done;
- }
- TclDecrRefCount(valueObj);
-
- /*
- * Run the script.
- */
-
- TclNRAddCallback(interp, DictMapLoopCallback, storagePtr, NULL,NULL,NULL);
- return TclNREvalObjEx(interp, storagePtr->scriptObj, 0,
- iPtr->cmdFramePtr, 3);
-
- /*
- * For unwinding everything once the iterating is done.
- */
-
- done:
- TclDecrRefCount(storagePtr->keyVarObj);
- TclDecrRefCount(storagePtr->valueVarObj);
- TclDecrRefCount(storagePtr->scriptObj);
- TclDecrRefCount(storagePtr->accumulatorObj);
- Tcl_DictObjDone(&storagePtr->search);
- TclStackFree(interp, storagePtr);
- return result;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictSetCmd --
- *
- * This function implements the "dict set" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictSetCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Tcl_Obj *dictPtr, *resultPtr;
- int result, allocatedDict = 0;
-
- if (objc < 4) {
- Tcl_WrongNumArgs(interp, 1, objv, "dictVarName key ?key ...? value");
- return TCL_ERROR;
- }
-
- dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0);
- if (dictPtr == NULL) {
- allocatedDict = 1;
- dictPtr = Tcl_NewDictObj();
- } else if (Tcl_IsShared(dictPtr)) {
- allocatedDict = 1;
- dictPtr = Tcl_DuplicateObj(dictPtr);
- }
-
- result = Tcl_DictObjPutKeyList(interp, dictPtr, objc-3, objv+2,
- objv[objc-1]);
- if (result != TCL_OK) {
- if (allocatedDict) {
- TclDecrRefCount(dictPtr);
- }
- return TCL_ERROR;
- }
-
- resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr,
- TCL_LEAVE_ERR_MSG);
- if (resultPtr == NULL) {
- return TCL_ERROR;
- }
- Tcl_SetObjResult(interp, resultPtr);
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictUnsetCmd --
- *
- * This function implements the "dict unset" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictUnsetCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Tcl_Obj *dictPtr, *resultPtr;
- int result, allocatedDict = 0;
-
- if (objc < 3) {
- Tcl_WrongNumArgs(interp, 1, objv, "dictVarName key ?key ...?");
- return TCL_ERROR;
- }
-
- dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, 0);
- if (dictPtr == NULL) {
- allocatedDict = 1;
- dictPtr = Tcl_NewDictObj();
- } else if (Tcl_IsShared(dictPtr)) {
- allocatedDict = 1;
- dictPtr = Tcl_DuplicateObj(dictPtr);
- }
-
- result = Tcl_DictObjRemoveKeyList(interp, dictPtr, objc-2, objv+2);
- if (result != TCL_OK) {
- if (allocatedDict) {
- TclDecrRefCount(dictPtr);
- }
- return TCL_ERROR;
- }
-
- resultPtr = Tcl_ObjSetVar2(interp, objv[1], NULL, dictPtr,
- TCL_LEAVE_ERR_MSG);
- if (resultPtr == NULL) {
- return TCL_ERROR;
- }
- Tcl_SetObjResult(interp, resultPtr);
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictFilterCmd --
- *
- * This function implements the "dict filter" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictFilterCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Interp *iPtr = (Interp *) interp;
- static const char *const filters[] = {
- "key", "script", "value", NULL
- };
- enum FilterTypes {
- FILTER_KEYS, FILTER_SCRIPT, FILTER_VALUES
- };
- Tcl_Obj *scriptObj, *keyVarObj, *valueVarObj;
- Tcl_Obj **varv, *keyObj = NULL, *valueObj = NULL, *resultObj, *boolObj;
- Tcl_DictSearch search;
- int index, varc, done, result, satisfied;
- const char *pattern;
-
- if (objc < 3) {
- Tcl_WrongNumArgs(interp, 1, objv, "dictionary filterType ?arg ...?");
- return TCL_ERROR;
- }
- if (Tcl_GetIndexFromObj(interp, objv[2], filters, "filterType",
- 0, &index) != TCL_OK) {
- return TCL_ERROR;
- }
-
- switch ((enum FilterTypes) index) {
- case FILTER_KEYS:
- /*
- * Create a dictionary whose keys all match a certain pattern.
- */
-
- if (Tcl_DictObjFirst(interp, objv[1], &search,
- &keyObj, &valueObj, &done) != TCL_OK) {
- return TCL_ERROR;
- }
- if (objc == 3) {
- /*
- * Nothing to match, so return nothing (== empty dictionary).
- */
-
- Tcl_DictObjDone(&search);
- return TCL_OK;
- } else if (objc == 4) {
- pattern = TclGetString(objv[3]);
- resultObj = Tcl_NewDictObj();
- if (TclMatchIsTrivial(pattern)) {
- /*
- * Must release the search lock here to prevent a memory leak
- * since we are not exhausing the search. [Bug 1705778, leak
- * K05]
- */
-
- Tcl_DictObjDone(&search);
- Tcl_DictObjGet(interp, objv[1], objv[3], &valueObj);
- if (valueObj != NULL) {
- Tcl_DictObjPut(NULL, resultObj, objv[3], valueObj);
- }
- } else {
- while (!done) {
- if (Tcl_StringMatch(TclGetString(keyObj), pattern)) {
- Tcl_DictObjPut(NULL, resultObj, keyObj, valueObj);
- }
- Tcl_DictObjNext(&search, &keyObj, &valueObj, &done);
- }
- }
- } else {
- /*
- * Can't optimize this match for trivial globbing: would disturb
- * order.
- */
-
- resultObj = Tcl_NewDictObj();
- while (!done) {
- int i;
-
- for (i=3 ; i<objc ; i++) {
- pattern = TclGetString(objv[i]);
- if (Tcl_StringMatch(TclGetString(keyObj), pattern)) {
- Tcl_DictObjPut(NULL, resultObj, keyObj, valueObj);
- break; /* stop inner loop */
- }
- }
- Tcl_DictObjNext(&search, &keyObj, &valueObj, &done);
- }
- }
- Tcl_SetObjResult(interp, resultObj);
- return TCL_OK;
-
- case FILTER_VALUES:
- /*
- * Create a dictionary whose values all match a certain pattern.
- */
-
- if (Tcl_DictObjFirst(interp, objv[1], &search,
- &keyObj, &valueObj, &done) != TCL_OK) {
- return TCL_ERROR;
- }
- resultObj = Tcl_NewDictObj();
- while (!done) {
- int i;
-
- for (i=3 ; i<objc ; i++) {
- pattern = TclGetString(objv[i]);
- if (Tcl_StringMatch(TclGetString(valueObj), pattern)) {
- Tcl_DictObjPut(NULL, resultObj, keyObj, valueObj);
- break; /* stop inner loop */
- }
- }
- Tcl_DictObjNext(&search, &keyObj, &valueObj, &done);
- }
- Tcl_SetObjResult(interp, resultObj);
- return TCL_OK;
-
- case FILTER_SCRIPT:
- if (objc != 5) {
- Tcl_WrongNumArgs(interp, 1, objv,
- "dictionary script {keyVarName valueVarName} filterScript");
- return TCL_ERROR;
- }
-
- /*
- * Create a dictionary whose key,value pairs all satisfy a script
- * (i.e. get a true boolean result from its evaluation). Massive
- * copying from the "dict for" implementation has occurred!
- */
-
- if (TclListObjGetElements(interp, objv[3], &varc, &varv) != TCL_OK) {
- return TCL_ERROR;
- }
- if (varc != 2) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "must have exactly two variable names", -1));
- Tcl_SetErrorCode(interp, "TCL", "SYNTAX", "dict", "filter", NULL);
- return TCL_ERROR;
- }
- keyVarObj = varv[0];
- valueVarObj = varv[1];
- scriptObj = objv[4];
-
- /*
- * Make sure that these objects (which we need throughout the body of
- * the loop) don't vanish. Note that the dictionary internal rep is
- * locked internally so that updates, shimmering, etc are not a
- * problem.
- */
-
- Tcl_IncrRefCount(keyVarObj);
- Tcl_IncrRefCount(valueVarObj);
- Tcl_IncrRefCount(scriptObj);
-
- result = Tcl_DictObjFirst(interp, objv[1],
- &search, &keyObj, &valueObj, &done);
- if (result != TCL_OK) {
- TclDecrRefCount(keyVarObj);
- TclDecrRefCount(valueVarObj);
- TclDecrRefCount(scriptObj);
- return TCL_ERROR;
- }
-
- resultObj = Tcl_NewDictObj();
-
- while (!done) {
- /*
- * Stop the value from getting hit in any way by any traces on the
- * key variable.
- */
-
- Tcl_IncrRefCount(keyObj);
- Tcl_IncrRefCount(valueObj);
- if (Tcl_ObjSetVar2(interp, keyVarObj, NULL, keyObj,
- TCL_LEAVE_ERR_MSG) == NULL) {
- Tcl_AddErrorInfo(interp,
- "\n (\"dict filter\" filter script key variable)");
- result = TCL_ERROR;
- goto abnormalResult;
- }
- if (Tcl_ObjSetVar2(interp, valueVarObj, NULL, valueObj,
- TCL_LEAVE_ERR_MSG) == NULL) {
- Tcl_AddErrorInfo(interp,
- "\n (\"dict filter\" filter script value variable)");
- result = TCL_ERROR;
- goto abnormalResult;
- }
-
- /*
- * TIP #280. Make invoking context available to loop body.
- */
-
- result = TclEvalObjEx(interp, scriptObj, 0, iPtr->cmdFramePtr, 4);
- switch (result) {
- case TCL_OK:
- boolObj = Tcl_GetObjResult(interp);
- Tcl_IncrRefCount(boolObj);
- Tcl_ResetResult(interp);
- if (Tcl_GetBooleanFromObj(interp, boolObj,
- &satisfied) != TCL_OK) {
- TclDecrRefCount(boolObj);
- result = TCL_ERROR;
- goto abnormalResult;
- }
- TclDecrRefCount(boolObj);
- if (satisfied) {
- Tcl_DictObjPut(NULL, resultObj, keyObj, valueObj);
- }
- break;
- case TCL_BREAK:
- /*
- * Force loop termination by calling Tcl_DictObjDone; this
- * makes the next Tcl_DictObjNext say there is nothing more to
- * do.
- */
-
- Tcl_ResetResult(interp);
- Tcl_DictObjDone(&search);
- case TCL_CONTINUE:
- result = TCL_OK;
- break;
- case TCL_ERROR:
- Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
- "\n (\"dict filter\" script line %d)",
- Tcl_GetErrorLine(interp)));
- default:
- goto abnormalResult;
- }
-
- TclDecrRefCount(keyObj);
- TclDecrRefCount(valueObj);
-
- Tcl_DictObjNext(&search, &keyObj, &valueObj, &done);
- }
-
- /*
- * Stop holding a reference to these objects.
- */
-
- TclDecrRefCount(keyVarObj);
- TclDecrRefCount(valueVarObj);
- TclDecrRefCount(scriptObj);
- Tcl_DictObjDone(&search);
-
- if (result == TCL_OK) {
- Tcl_SetObjResult(interp, resultObj);
- } else {
- TclDecrRefCount(resultObj);
- }
- return result;
-
- abnormalResult:
- Tcl_DictObjDone(&search);
- TclDecrRefCount(keyObj);
- TclDecrRefCount(valueObj);
- TclDecrRefCount(keyVarObj);
- TclDecrRefCount(valueVarObj);
- TclDecrRefCount(scriptObj);
- TclDecrRefCount(resultObj);
- return result;
- }
- Tcl_Panic("unexpected fallthrough");
- /* Control never reaches this point. */
- return TCL_ERROR;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictUpdateCmd --
- *
- * This function implements the "dict update" Tcl command. See the user
- * documentation for details on what it does, and TIP#212 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictUpdateCmd(
- ClientData clientData,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Interp *iPtr = (Interp *) interp;
- Tcl_Obj *dictPtr, *objPtr;
- int i, dummy;
-
- if (objc < 5 || !(objc & 1)) {
- Tcl_WrongNumArgs(interp, 1, objv,
- "dictVarName key varName ?key varName ...? script");
- return TCL_ERROR;
- }
-
- dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG);
- if (dictPtr == NULL) {
- return TCL_ERROR;
- }
- if (Tcl_DictObjSize(interp, dictPtr, &dummy) != TCL_OK) {
- return TCL_ERROR;
- }
- Tcl_IncrRefCount(dictPtr);
- for (i=2 ; i+2<objc ; i+=2) {
- if (Tcl_DictObjGet(interp, dictPtr, objv[i], &objPtr) != TCL_OK) {
- TclDecrRefCount(dictPtr);
- return TCL_ERROR;
- }
- if (objPtr == NULL) {
- /* ??? */
- Tcl_UnsetVar(interp, Tcl_GetString(objv[i+1]), 0);
- } else if (Tcl_ObjSetVar2(interp, objv[i+1], NULL, objPtr,
- TCL_LEAVE_ERR_MSG) == NULL) {
- TclDecrRefCount(dictPtr);
- return TCL_ERROR;
- }
- }
- TclDecrRefCount(dictPtr);
-
- /*
- * Execute the body after setting up the NRE handler to process the
- * results.
- */
-
- objPtr = Tcl_NewListObj(objc-3, objv+2);
- Tcl_IncrRefCount(objPtr);
- Tcl_IncrRefCount(objv[1]);
- TclNRAddCallback(interp, FinalizeDictUpdate, objv[1], objPtr, NULL,NULL);
-
- return TclNREvalObjEx(interp, objv[objc-1], 0, iPtr->cmdFramePtr, objc-1);
-}
-
-static int
-FinalizeDictUpdate(
- ClientData data[],
- Tcl_Interp *interp,
- int result)
-{
- Tcl_Obj *dictPtr, *objPtr, **objv;
- Tcl_InterpState state;
- int i, objc;
- Tcl_Obj *varName = data[0];
- Tcl_Obj *argsObj = data[1];
-
- /*
- * ErrorInfo handling.
- */
-
- if (result == TCL_ERROR) {
- Tcl_AddErrorInfo(interp, "\n (body of \"dict update\")");
- }
-
- /*
- * If the dictionary variable doesn't exist, drop everything silently.
- */
-
- dictPtr = Tcl_ObjGetVar2(interp, varName, NULL, 0);
- if (dictPtr == NULL) {
- TclDecrRefCount(varName);
- TclDecrRefCount(argsObj);
- return result;
- }
-
- /*
- * Double-check that it is still a dictionary.
- */
-
- state = Tcl_SaveInterpState(interp, result);
- if (Tcl_DictObjSize(interp, dictPtr, &objc) != TCL_OK) {
- Tcl_DiscardInterpState(state);
- TclDecrRefCount(varName);
- TclDecrRefCount(argsObj);
- return TCL_ERROR;
- }
-
- if (Tcl_IsShared(dictPtr)) {
- dictPtr = Tcl_DuplicateObj(dictPtr);
- }
-
- /*
- * Write back the values from the variables, treating failure to read as
- * an instruction to remove the key.
- */
-
- Tcl_ListObjGetElements(NULL, argsObj, &objc, &objv);
- for (i=0 ; i<objc ; i+=2) {
- objPtr = Tcl_ObjGetVar2(interp, objv[i+1], NULL, 0);
- if (objPtr == NULL) {
- Tcl_DictObjRemove(NULL, dictPtr, objv[i]);
- } else if (objPtr == dictPtr) {
- /*
- * Someone is messing us around, trying to build a recursive
- * structure. [Bug 1786481]
- */
-
- Tcl_DictObjPut(NULL, dictPtr, objv[i], Tcl_DuplicateObj(objPtr));
- } else {
- /* Shouldn't fail */
- Tcl_DictObjPut(NULL, dictPtr, objv[i], objPtr);
- }
- }
- TclDecrRefCount(argsObj);
-
- /*
- * Write the dictionary back to its variable.
- */
-
- if (Tcl_ObjSetVar2(interp, varName, NULL, dictPtr,
- TCL_LEAVE_ERR_MSG) == NULL) {
- Tcl_DiscardInterpState(state);
- TclDecrRefCount(varName);
- return TCL_ERROR;
- }
-
- TclDecrRefCount(varName);
- return Tcl_RestoreInterpState(interp, state);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * DictWithCmd --
- *
- * This function implements the "dict with" Tcl command. See the user
- * documentation for details on what it does, and TIP#212 for the formal
- * specification.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-DictWithCmd(
- ClientData dummy,
- Tcl_Interp *interp,
- int objc,
- Tcl_Obj *const *objv)
-{
- Interp *iPtr = (Interp *) interp;
- Tcl_Obj *dictPtr, *keysPtr, *pathPtr;
-
- if (objc < 3) {
- Tcl_WrongNumArgs(interp, 1, objv, "dictVarName ?key ...? script");
- return TCL_ERROR;
- }
-
- /*
- * Get the dictionary to open out.
- */
-
- dictPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG);
- if (dictPtr == NULL) {
- return TCL_ERROR;
- }
-
- keysPtr = TclDictWithInit(interp, dictPtr, objc-3, objv+2);
- if (keysPtr == NULL) {
- return TCL_ERROR;
- }
- Tcl_IncrRefCount(keysPtr);
-
- /*
- * Execute the body, while making the invoking context available to the
- * loop body (TIP#280) and postponing the cleanup until later (NRE).
- */
-
- pathPtr = NULL;
- if (objc > 3) {
- pathPtr = Tcl_NewListObj(objc-3, objv+2);
- Tcl_IncrRefCount(pathPtr);
- }
- Tcl_IncrRefCount(objv[1]);
- TclNRAddCallback(interp, FinalizeDictWith, objv[1], keysPtr, pathPtr,
- NULL);
-
- return TclNREvalObjEx(interp, objv[objc-1], 0, iPtr->cmdFramePtr, objc-1);
-}
-
-static int
-FinalizeDictWith(
- ClientData data[],
- Tcl_Interp *interp,
- int result)
-{
- Tcl_Obj **pathv;
- int pathc;
- Tcl_InterpState state;
- Tcl_Obj *varName = data[0];
- Tcl_Obj *keysPtr = data[1];
- Tcl_Obj *pathPtr = data[2];
- Var *varPtr, *arrayPtr;
-
- if (result == TCL_ERROR) {
- Tcl_AddErrorInfo(interp, "\n (body of \"dict with\")");
- }
-
- /*
- * Save the result state; TDWF doesn't guarantee to not modify that on
- * TCL_OK result.
- */
-
- state = Tcl_SaveInterpState(interp, result);
- if (pathPtr != NULL) {
- Tcl_ListObjGetElements(NULL, pathPtr, &pathc, &pathv);
- } else {
- pathc = 0;
- pathv = NULL;
- }
-
- /*
- * Pack from local variables back into the dictionary.
- */
-
- varPtr = TclObjLookupVarEx(interp, varName, NULL, TCL_LEAVE_ERR_MSG, "set",
- /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
- if (varPtr == NULL) {
- result = TCL_ERROR;
- } else {
- result = TclDictWithFinish(interp, varPtr, arrayPtr, varName, NULL, -1,
- pathc, pathv, keysPtr);
- }
-
- /*
- * Tidy up and return the real result (unless we had an error).
- */
-
- TclDecrRefCount(varName);
- TclDecrRefCount(keysPtr);
- if (pathPtr != NULL) {
- TclDecrRefCount(pathPtr);
- }
- if (result != TCL_OK) {
- Tcl_DiscardInterpState(state);
- return TCL_ERROR;
- }
- return Tcl_RestoreInterpState(interp, state);
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclDictWithInit --
- *
- * Part of the core of [dict with]. Pokes into a dictionary and converts
- * the mappings there into assignments to (presumably) local variables.
- * Returns a list of all the names that were mapped so that removal of
- * either the variable or the dictionary entry won't surprise us when we
- * come to stuffing everything back.
- *
- * Result:
- * List of mapped names, or NULL if there was an error.
- *
- * Side effects:
- * Assigns to variables, so potentially legion due to traces.
- *
- *----------------------------------------------------------------------
- */
-
-Tcl_Obj *
-TclDictWithInit(
- Tcl_Interp *interp,
- Tcl_Obj *dictPtr,
- int pathc,
- Tcl_Obj *const pathv[])
-{
- Tcl_DictSearch s;
- Tcl_Obj *keyPtr, *valPtr, *keysPtr;
- int done;
-
- if (pathc > 0) {
- dictPtr = TclTraceDictPath(interp, dictPtr, pathc, pathv,
- DICT_PATH_READ);
- if (dictPtr == NULL) {
- return NULL;
- }
- }
-
- /*
- * Go over the list of keys and write each corresponding value to a
- * variable in the current context with the same name. Also keep a copy of
- * the keys so we can write back properly later on even if the dictionary
- * has been structurally modified.
- */
-
- if (Tcl_DictObjFirst(interp, dictPtr, &s, &keyPtr, &valPtr,
- &done) != TCL_OK) {
- return NULL;
- }
-
- TclNewObj(keysPtr);
-
- for (; !done ; Tcl_DictObjNext(&s, &keyPtr, &valPtr, &done)) {
- Tcl_ListObjAppendElement(NULL, keysPtr, keyPtr);
- if (Tcl_ObjSetVar2(interp, keyPtr, NULL, valPtr,
- TCL_LEAVE_ERR_MSG) == NULL) {
- TclDecrRefCount(keysPtr);
- Tcl_DictObjDone(&s);
- return NULL;
- }
- }
-
- return keysPtr;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclDictWithFinish --
- *
- * Part of the core of [dict with]. Reassembles the piece of the dict (in
- * varName, location given by pathc/pathv) from the variables named in
- * the keysPtr argument. NB, does not try to preserve errors or manage
- * argument lifetimes.
- *
- * Result:
- * TCL_OK if we succeeded, or TCL_ERROR if we failed.
- *
- * Side effects:
- * Assigns to a variable, so potentially legion due to traces. Updates
- * the dictionary in the named variable.
- *
- *----------------------------------------------------------------------
- */
-
-int
-TclDictWithFinish(
- Tcl_Interp *interp, /* Command interpreter in which variable
- * exists. Used for state management, traces
- * and error reporting. */
- Var *varPtr, /* Reference to the variable holding the
- * dictionary. */
- Var *arrayPtr, /* Reference to the array containing the
- * variable, or NULL if the variable is a
- * scalar. */
- Tcl_Obj *part1Ptr, /* Name of an array (if part2 is non-NULL) or
- * the name of a variable. NULL if the 'index'
- * parameter is >= 0 */
- Tcl_Obj *part2Ptr, /* If non-NULL, gives the name of an element
- * in the array part1. */
- int index, /* Index into the local variable table of the
- * variable, or -1. Only used when part1Ptr is
- * NULL. */
- int pathc, /* The number of elements in the path into the
- * dictionary. */
- Tcl_Obj *const pathv[], /* The elements of the path to the subdict. */
- Tcl_Obj *keysPtr) /* List of keys to be synchronized. This is
- * the result value from TclDictWithInit. */
-{
- Tcl_Obj *dictPtr, *leafPtr, *valPtr;
- int i, allocdict, keyc;
- Tcl_Obj **keyv;
-
- /*
- * If the dictionary variable doesn't exist, drop everything silently.
- */
-
- dictPtr = TclPtrGetVar(interp, varPtr, arrayPtr, part1Ptr, part2Ptr,
- TCL_LEAVE_ERR_MSG, index);
- if (dictPtr == NULL) {
- return TCL_OK;
- }
-
- /*
- * Double-check that it is still a dictionary.
- */
-
- if (Tcl_DictObjSize(interp, dictPtr, &i) != TCL_OK) {
- return TCL_ERROR;
- }
-
- if (Tcl_IsShared(dictPtr)) {
- dictPtr = Tcl_DuplicateObj(dictPtr);
- allocdict = 1;
- } else {
- allocdict = 0;
- }
-
- if (pathc > 0) {
- /*
- * Want to get to the dictionary which we will update; need to do
- * prepare-for-update de-sharing along the path *but* avoid generating
- * an error on a non-existant path (we'll treat that the same as a
- * non-existant variable. Luckily, the de-sharing operation isn't
- * deeply damaging if we don't go on to update; it's just less than
- * perfectly efficient (but no memory should be leaked).
- */
-
- leafPtr = TclTraceDictPath(interp, dictPtr, pathc, pathv,
- DICT_PATH_EXISTS | DICT_PATH_UPDATE);
- if (leafPtr == NULL) {
- if (allocdict) {
- TclDecrRefCount(dictPtr);
- }
- return TCL_ERROR;
- }
- if (leafPtr == DICT_PATH_NON_EXISTENT) {
- if (allocdict) {
- TclDecrRefCount(dictPtr);
- }
- return TCL_OK;
- }
- } else {
- leafPtr = dictPtr;
- }
-
- /*
- * Now process our updates on the leaf dictionary.
- */
-
- TclListObjGetElements(NULL, keysPtr, &keyc, &keyv);
- for (i=0 ; i<keyc ; i++) {
- valPtr = Tcl_ObjGetVar2(interp, keyv[i], NULL, 0);
- if (valPtr == NULL) {
- Tcl_DictObjRemove(NULL, leafPtr, keyv[i]);
- } else if (leafPtr == valPtr) {
- /*
- * Someone is messing us around, trying to build a recursive
- * structure. [Bug 1786481]
- */
-
- Tcl_DictObjPut(NULL, leafPtr, keyv[i], Tcl_DuplicateObj(valPtr));
- } else {
- Tcl_DictObjPut(NULL, leafPtr, keyv[i], valPtr);
- }
- }
-
- /*
- * Ensure that none of the dictionaries in the chain still have a string
- * rep.
- */
-
- if (pathc > 0) {
- InvalidateDictChain(leafPtr);
- }
-
- /*
- * Write back the outermost dictionary to the variable.
- */
-
- if (TclPtrSetVar(interp, varPtr, arrayPtr, part1Ptr, part2Ptr, dictPtr,
- TCL_LEAVE_ERR_MSG, index) == NULL) {
- if (allocdict) {
- TclDecrRefCount(dictPtr);
- }
- return TCL_ERROR;
- }
- return TCL_OK;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * TclInitDictCmd --
- *
- * This function is create the "dict" Tcl command. See the user
- * documentation for details on what it does, and TIP#111 for the formal
- * specification.
- *
- * Results:
- * A Tcl command handle.
- *
- * Side effects:
- * May advance compilation epoch.
- *
- *----------------------------------------------------------------------
- */
-
-Tcl_Command
-TclInitDictCmd(
- Tcl_Interp *interp)
-{
- return TclMakeEnsemble(interp, "dict", implementationMap);
-}
-
-/*
- * Local Variables:
- * mode: c
- * c-basic-offset: 4
- * fill-column: 78
- * End:
- */