summaryrefslogtreecommitdiff
path: root/blt/src/bltTree.c
diff options
context:
space:
mode:
Diffstat (limited to 'blt/src/bltTree.c')
-rw-r--r--blt/src/bltTree.c2511
1 files changed, 2511 insertions, 0 deletions
diff --git a/blt/src/bltTree.c b/blt/src/bltTree.c
new file mode 100644
index 00000000000..f1812038f61
--- /dev/null
+++ b/blt/src/bltTree.c
@@ -0,0 +1,2511 @@
+/*
+ * bltTree.c --
+ *
+ * Copyright 1998-1999 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies or any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "tree" data object was created by George A. Howlett.
+ */
+
+#include "bltInt.h"
+
+#ifndef NO_TREE
+
+#include "bltTree.h"
+
+static Tcl_InterpDeleteProc TreeInterpDeleteProc;
+static Blt_TreeApplyProc SizeApplyProc;
+static Tcl_IdleProc NotifyIdleProc;
+
+#define TREE_THREAD_KEY "BLT Tree Data"
+#define TREE_MAGIC ((unsigned int) 0x46170277)
+#define TREE_DESTROYED (1<<0)
+
+typedef struct Blt_TreeNodeStruct Node;
+typedef struct Blt_TreeClientStruct TreeClient;
+typedef struct Blt_TreeObjectStruct TreeObject;
+typedef struct Blt_TreeValueStruct Value;
+
+/*
+ * The hash table below is used to keep track of all the Blt_TreeKeys
+ * created so far.
+ */
+static Blt_HashTable keyTable;
+static int keyTableInitialized = 0;
+
+typedef struct {
+ Blt_HashTable treeTable; /* Table of trees. */
+ unsigned int nextId;
+ Tcl_Interp *interp;
+} TreeInterpData;
+
+typedef struct {
+ Tcl_Interp *interp;
+ ClientData clientData;
+ Blt_TreeKey key;
+ unsigned int mask;
+ Blt_TreeNotifyEventProc *proc;
+ Blt_TreeNotifyEvent event;
+ int notifyPending;
+} EventHandler;
+
+typedef struct {
+ ClientData clientData;
+ char *keyPattern;
+ Node *nodePtr;
+ unsigned int mask;
+ Blt_TreeTraceProc *proc;
+ TreeClient *clientPtr;
+ Blt_ChainLink *linkPtr;
+} TraceHandler;
+
+/*
+ * --------------------------------------------------------------
+ *
+ * GetTreeInterpData --
+ *
+ * Creates or retrieves data associated with tree data objects
+ * for a particular thread. We're using Tcl_GetAssocData rather
+ * than the Tcl thread routines so BLT can work with pre-8.0
+ * Tcl versions that don't have thread support.
+ *
+ * Results:
+ * Returns a pointer to the tree interpreter data.
+ *
+ * --------------------------------------------------------------
+ */
+static TreeInterpData *
+GetTreeInterpData(interp)
+ Tcl_Interp *interp;
+{
+ Tcl_InterpDeleteProc *proc;
+ TreeInterpData *dataPtr;
+
+ dataPtr = (TreeInterpData *)
+ Tcl_GetAssocData(interp, TREE_THREAD_KEY, &proc);
+ if (dataPtr == NULL) {
+ dataPtr = Blt_Malloc(sizeof(TreeInterpData));
+ assert(dataPtr);
+ dataPtr->interp = interp;
+ Tcl_SetAssocData(interp, TREE_THREAD_KEY, TreeInterpDeleteProc,
+ dataPtr);
+ Blt_InitHashTable(&(dataPtr->treeTable), BLT_STRING_KEYS);
+ }
+ return dataPtr;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * NewNode --
+ *
+ * Creates a new node in the tree without installing it. The
+ * number of nodes in the tree is incremented and a unique serial
+ * number is generated for the node.
+ *
+ * Also, all nodes have a label. If no label was provided (name
+ * is NULL) then automatically generate one in the form "nodeN"
+ * where N is the serial number of the node.
+ *
+ * Results:
+ * Returns a pointer to the new node.
+ *
+ * --------------------------------------------------------------
+ */
+static Node *
+NewNode(treeObjPtr, name, inode)
+ TreeObject *treeObjPtr;
+ CONST char *name;
+ int inode;
+{
+ Node *nodePtr;
+
+ /* Create the node structure */
+ nodePtr = Blt_PoolAllocItem(treeObjPtr->nodePool, sizeof(Node));
+ nodePtr->inode = inode;
+ nodePtr->treeObject = treeObjPtr;
+ nodePtr->parent = NULL;
+ nodePtr->depth = 0;
+ nodePtr->flags = 0;
+ nodePtr->next = nodePtr->prev = NULL;
+ nodePtr->first = nodePtr->last = NULL;
+ nodePtr->nChildren = 0;
+ nodePtr->values = NULL;
+ if (name == NULL) {
+#ifndef notdef /* FIXME: The automatic naming of
+ * nodes should be a "tree" command
+ * feature, not a tree object
+ * feature. Push this into "tree"
+ * command. */
+ char string[200];
+
+ sprintf(string, "node%d", inode);
+ nodePtr->label = Blt_TreeGetKey(string);
+#else
+ nodePtr->label = NULL;
+#endif
+ } else {
+ nodePtr->label = Blt_TreeGetKey(name);
+ }
+ treeObjPtr->nNodes++;
+ return nodePtr;
+}
+
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * ResetDepths --
+ *
+ * Called after moving a node, resets the depths of each node
+ * for the entire branch (node and it's decendants).
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+ResetDepths(nodePtr, depth)
+ Node *nodePtr; /* Root node. */
+ int depth; /* Depth of the node. */
+{
+ nodePtr->depth = depth;
+ /* Also reset the depth for each descendant node. */
+ for (nodePtr = nodePtr->first; nodePtr != NULL; nodePtr = nodePtr->next) {
+ ResetDepths(nodePtr, depth + 1);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * LinkBefore --
+ *
+ * Inserts a link preceding a given link.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+LinkBefore(parentPtr, nodePtr, beforePtr)
+ Node *parentPtr; /* Parent to hold the new entry. */
+ Node *nodePtr; /* New node to be inserted. */
+ Node *beforePtr; /* Node to link before. */
+{
+ int depth;
+
+ if (parentPtr->first == NULL) {
+ parentPtr->last = parentPtr->first = nodePtr;
+ } else if (beforePtr == NULL) { /* Append onto the end of the chain */
+ nodePtr->next = NULL;
+ nodePtr->prev = parentPtr->last;
+ parentPtr->last->next = nodePtr;
+ parentPtr->last = nodePtr;
+ } else {
+ nodePtr->prev = beforePtr->prev;
+ nodePtr->next = beforePtr;
+ if (beforePtr == parentPtr->first) {
+ parentPtr->first = nodePtr;
+ } else {
+ beforePtr->prev->next = nodePtr;
+ }
+ beforePtr->prev = nodePtr;
+ }
+ parentPtr->nChildren++;
+ nodePtr->parent = parentPtr;
+ depth = parentPtr->depth + 1;
+ if (nodePtr->depth != depth) {
+ /* Reset the depths of all descendant nodes. */
+ ResetDepths(nodePtr, depth);
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UnlinkNode --
+ *
+ * Unlinks a link from the chain. The link is not deallocated,
+ * but only removed from the chain.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+UnlinkNode(nodePtr)
+ Node *nodePtr;
+{
+ Node *parentPtr;
+ int unlinked; /* Indicates if the link is actually
+ * removed from the chain. */
+ parentPtr = nodePtr->parent;
+ unlinked = FALSE;
+ if (parentPtr->first == nodePtr) {
+ parentPtr->first = nodePtr->next;
+ unlinked = TRUE;
+ }
+ if (parentPtr->last == nodePtr) {
+ parentPtr->last = nodePtr->prev;
+ unlinked = TRUE;
+ }
+ if (nodePtr->next != NULL) {
+ nodePtr->next->prev = nodePtr->prev;
+ unlinked = TRUE;
+ }
+ if (nodePtr->prev != NULL) {
+ nodePtr->prev->next = nodePtr->next;
+ unlinked = TRUE;
+ }
+ if (unlinked) {
+ parentPtr->nChildren--;
+ }
+ nodePtr->prev = nodePtr->next = NULL;
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * FreeNode --
+ *
+ * Unlinks a given node from the tree, removes its data, and
+ * frees memory allocated to the node.
+ *
+ * Results:
+ * None.
+ *
+ * --------------------------------------------------------------
+ */
+static void
+FreeNode(treeObjPtr, nodePtr)
+ TreeObject *treeObjPtr;
+ Node *nodePtr;
+{
+ Blt_HashEntry *hPtr;
+ register Value *valuePtr, *nextPtr;
+
+ /*
+ * Destroy any data fields associated with this node.
+ */
+ for (valuePtr = nodePtr->values; valuePtr != NULL; valuePtr = nextPtr) {
+ nextPtr = valuePtr->next;
+ if (valuePtr->objPtr != NULL) {
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ }
+ Blt_PoolFreeItem(treeObjPtr->valuePool, (char *)valuePtr);
+ }
+ /* Unlink the node from parent's list of siblings. */
+ UnlinkNode(nodePtr);
+ treeObjPtr->nNodes--;
+#ifdef notdef
+ if (nodePtr->inode == (treeObjPtr->nextNode - 1)) {
+ treeObjPtr->nextNode--;
+ }
+#endif
+ hPtr = Blt_FindHashEntry(&(treeObjPtr->nodeTable), (char *)nodePtr->inode);
+ assert(hPtr);
+ Blt_DeleteHashEntry(&(treeObjPtr->nodeTable), hPtr);
+ Blt_PoolFreeItem(treeObjPtr->nodePool, (char *)nodePtr);
+}
+
+/*
+ * --------------------------------------------------------------
+ *
+ * NewTreeObject --
+ *
+ * Creates and initializes a new tree object. Trees always
+ * contain a root node, so one is allocated here.
+ *
+ * Results:
+ * Returns a pointer to the new tree object is successful, NULL
+ * otherwise. If a tree can't be generated, interp->result will
+ * contain an error message.
+ *
+ * -------------------------------------------------------------- */
+static TreeObject *
+NewTreeObject(dataPtr, interp, treeName)
+ TreeInterpData *dataPtr;
+ Tcl_Interp *interp;
+ CONST char *treeName;
+{
+ TreeObject *treeObjPtr;
+ int isNew;
+ Blt_HashEntry *hPtr;
+
+ treeObjPtr = Blt_Calloc(1, sizeof(TreeObject));
+ if (treeObjPtr == NULL) {
+ Tcl_AppendResult(interp, "can't allocate tree", (char *)NULL);
+ return NULL;
+ }
+ treeObjPtr->name = Blt_Strdup(treeName);
+ treeObjPtr->interp = interp;
+ treeObjPtr->valuePool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
+ treeObjPtr->nodePool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
+ treeObjPtr->clients = Blt_ChainCreate();
+ treeObjPtr->depth = 1;
+ treeObjPtr->notifyFlags = 0;
+ Blt_InitHashTableWithPool(&treeObjPtr->nodeTable, BLT_ONE_WORD_KEYS);
+
+ hPtr = Blt_CreateHashEntry(&treeObjPtr->nodeTable, (char *)0, &isNew);
+ treeObjPtr->root = NewNode(treeObjPtr, treeName, 0);
+ Blt_SetHashValue(hPtr, treeObjPtr->root);
+
+ treeObjPtr->tablePtr = &dataPtr->treeTable;
+ treeObjPtr->hashPtr = Blt_CreateHashEntry(treeObjPtr->tablePtr, treeName,
+ &isNew);
+ Blt_SetHashValue(treeObjPtr->hashPtr, treeObjPtr);
+
+ return treeObjPtr;
+}
+
+static TreeObject *
+FindTreeInNamespace(dataPtr, nsPtr, treeName)
+ TreeInterpData *dataPtr; /* Interpreter-specific data. */
+ Tcl_Namespace *nsPtr;
+ char *treeName;
+{
+ Tcl_DString dString;
+ char *name;
+ Blt_HashEntry *hPtr;
+
+ name = Blt_GetQualifiedName(nsPtr, treeName, &dString);
+ hPtr = Blt_FindHashEntry(&(dataPtr->treeTable), name);
+ Tcl_DStringFree(&dString);
+ if (hPtr != NULL) {
+ return Blt_GetHashValue(hPtr);
+ }
+ return NULL;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * GetTreeObject --
+ *
+ * Searches for the tree object associated by the name given.
+ *
+ * Results:
+ * Returns a pointer to the tree if found, otherwise NULL.
+ *
+ * ----------------------------------------------------------------------
+ */
+static TreeObject *
+GetTreeObject(interp, name, flags)
+ Tcl_Interp *interp;
+ CONST char *name;
+ int flags;
+{
+ char *treeName;
+ Tcl_Namespace *nsPtr; /* Namespace associated with the tree object.
+ * If NULL, indicates to look in first the
+ * current namespace and then the global
+ * for the tree. */
+ TreeInterpData *dataPtr; /* Interpreter-specific data. */
+ TreeObject *treeObjPtr;
+
+ treeObjPtr = NULL;
+ if (Blt_ParseQualifiedName(interp, name, &nsPtr, &treeName) != TCL_OK) {
+ Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"",
+ (char *)NULL);
+ return NULL;
+ }
+ dataPtr = GetTreeInterpData(interp);
+ if (nsPtr != NULL) {
+ treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
+ } else {
+ if (flags & NS_SEARCH_CURRENT) {
+ /* Look first in the current namespace. */
+ nsPtr = Tcl_GetCurrentNamespace(interp);
+ treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
+ }
+ if ((treeObjPtr == NULL) && (flags & NS_SEARCH_GLOBAL)) {
+ nsPtr = Tcl_GetGlobalNamespace(interp);
+ treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
+ }
+ }
+ return treeObjPtr;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TeardownTree --
+ *
+ * Destroys an entire branch. This is a special purpose routine
+ * used to speed up the final clean up of the tree.
+ *
+ * Results:
+ * None.
+ *
+ * ----------------------------------------------------------------------
+ */
+static void
+TeardownTree(treeObjPtr, nodePtr)
+ TreeObject *treeObjPtr;
+ Node *nodePtr;
+{
+ if (nodePtr->first != NULL) {
+ Node *childPtr, *nextPtr;
+
+ for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
+ nextPtr = childPtr->next;
+ TeardownTree(treeObjPtr, childPtr);
+ }
+ }
+ if (nodePtr->values != NULL) {
+ register Value *valuePtr, *nextPtr;
+ /*
+ * Run through the list of data fields, decrementing the
+ * reference counts on all the Tcl objects associated with
+ * this node.
+ */
+ for (valuePtr = nodePtr->values; valuePtr != NULL; valuePtr = nextPtr) {
+ nextPtr = valuePtr->next;
+ if (valuePtr->objPtr != NULL) {
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ }
+ Blt_PoolFreeItem(treeObjPtr->valuePool, (char *)valuePtr);
+ }
+ nodePtr->values = NULL;
+ }
+ Blt_PoolFreeItem(treeObjPtr->nodePool, (char *)nodePtr);
+}
+
+static void
+DestroyTreeObject(treeObjPtr)
+ TreeObject *treeObjPtr;
+{
+ Blt_ChainLink *linkPtr;
+ TreeClient *clientPtr;
+
+ treeObjPtr->flags |= TREE_DESTROYED;
+ treeObjPtr->nNodes = 0;
+
+ /* Remove the remaining clients. */
+ for (linkPtr = Blt_ChainFirstLink(treeObjPtr->clients); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ clientPtr = Blt_ChainGetValue(linkPtr);
+ Blt_ChainDestroy(clientPtr->events);
+ Blt_ChainDestroy(clientPtr->traces);
+ Blt_Free(clientPtr);
+ }
+ Blt_ChainDestroy(treeObjPtr->clients);
+
+ TeardownTree(treeObjPtr, treeObjPtr->root);
+ Blt_PoolDestroy(treeObjPtr->nodePool);
+ Blt_PoolDestroy(treeObjPtr->valuePool);
+ Blt_DeleteHashTable(&(treeObjPtr->nodeTable));
+
+ if (treeObjPtr->hashPtr != NULL) {
+ /* Remove the entry from the global tree table. */
+ Blt_DeleteHashEntry(treeObjPtr->tablePtr, treeObjPtr->hashPtr);
+ if ((treeObjPtr->tablePtr->numEntries == 0) && (keyTableInitialized)) {
+ keyTableInitialized = FALSE;
+ Blt_DeleteHashTable(&keyTable);
+ }
+ }
+ if (treeObjPtr->name != NULL) {
+ Blt_Free(treeObjPtr->name);
+ }
+ Blt_Free(treeObjPtr);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ *
+ * TreeInterpDeleteProc --
+ *
+ * This is called when the interpreter hosting the tree object
+ * is deleted from the interpreter.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Destroys all remaining trees and removes the hash table
+ * used to register tree names.
+ *
+ * ------------------------------------------------------------------------
+ */
+/* ARGSUSED */
+static void
+TreeInterpDeleteProc(clientData, interp)
+ ClientData clientData; /* Interpreter-specific data. */
+ Tcl_Interp *interp;
+{
+ TreeInterpData *dataPtr = clientData;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ TreeObject *treeObjPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&(dataPtr->treeTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ treeObjPtr = (TreeObject *)Blt_GetHashValue(hPtr);
+ treeObjPtr->hashPtr = NULL;
+ DestroyTreeObject(treeObjPtr);
+ }
+ if (keyTableInitialized) {
+ keyTableInitialized = FALSE;
+ Blt_DeleteHashTable(&keyTable);
+ }
+ Blt_DeleteHashTable(&(dataPtr->treeTable));
+ Tcl_DeleteAssocData(interp, TREE_THREAD_KEY);
+ Blt_Free(dataPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyIdleProc --
+ *
+ * Used to invoke event handler routines at some idle point.
+ * This routine is called from the Tcl event loop. Errors
+ * generated by the event handler routines are backgrounded.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+NotifyIdleProc(clientData)
+ ClientData clientData;
+{
+ EventHandler *handlerPtr = clientData;
+ int result;
+
+ handlerPtr->notifyPending = FALSE;
+ handlerPtr->mask |= TREE_NOTIFY_ACTIVE;
+ result = (*handlerPtr->proc)(handlerPtr->clientData, &(handlerPtr->event));
+ handlerPtr->mask &= ~TREE_NOTIFY_ACTIVE;
+ if (result != TCL_OK) {
+ Tcl_BackgroundError(handlerPtr->interp);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CheckEventHandlers --
+ *
+ * Traverses the list of client event callbacks and checks
+ * if one matches the given event. A client may trigger an
+ * action that causes the tree to notify it. The can be
+ * prevented by setting the TREE_NOTIFY_FOREIGN_ONLY bit in
+ * the event handler.
+ *
+ * If a matching handler is found, a callback may be called either
+ * immediately or at the next idle time depending upon the
+ * TREE_NOTIFY_WHENIDLE bit.
+ *
+ * Since a handler routine may trigger yet another call to
+ * itself, callbacks are ignored while the event handler is
+ * executing.
+ *
+ * Results:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+CheckEventHandlers(clientPtr, isSource, eventPtr)
+ TreeClient *clientPtr;
+ int isSource; /* Indicates if the client is the source
+ * of the event. */
+ Blt_TreeNotifyEvent *eventPtr;
+{
+ Blt_ChainLink *linkPtr, *nextPtr;
+ EventHandler *handlerPtr;
+
+ eventPtr->tree = clientPtr;
+ for (linkPtr = Blt_ChainFirstLink(clientPtr->events);
+ linkPtr != NULL; linkPtr = nextPtr) {
+ nextPtr = Blt_ChainNextLink(linkPtr);
+ handlerPtr = Blt_ChainGetValue(linkPtr);
+ if ((handlerPtr->mask & TREE_NOTIFY_ACTIVE) ||
+ (handlerPtr->mask & eventPtr->type) == 0) {
+ continue; /* Ignore callbacks that are generated
+ * inside of a notify handler routine. */
+ }
+ if ((isSource) && (handlerPtr->mask & TREE_NOTIFY_FOREIGN_ONLY)) {
+ continue; /* Don't notify yourself. */
+ }
+ if (handlerPtr->mask & TREE_NOTIFY_WHENIDLE) {
+ if (!handlerPtr->notifyPending) {
+ handlerPtr->notifyPending = TRUE;
+ handlerPtr->event = *eventPtr;
+ Tcl_DoWhenIdle(NotifyIdleProc, handlerPtr);
+ }
+ } else {
+ int result;
+
+ handlerPtr->mask |= TREE_NOTIFY_ACTIVE;
+ result = (*handlerPtr->proc) (handlerPtr->clientData, eventPtr);
+ handlerPtr->mask &= ~TREE_NOTIFY_ACTIVE;
+ if (result != TCL_OK) {
+ Tcl_BackgroundError(handlerPtr->interp);
+ }
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * NotifyClients --
+ *
+ * Traverses the list of clients for a particular tree and
+ * notifies each client that an event occurred. Clients
+ * indicate interest in a particular event through a bit
+ * flag.
+ *
+ *----------------------------------------------------------------------
+ */
+static void
+NotifyClients(sourcePtr, treeObjPtr, nodePtr, eventFlag)
+ TreeClient *sourcePtr;
+ TreeObject *treeObjPtr;
+ Node *nodePtr;
+ int eventFlag;
+{
+ Blt_ChainLink *linkPtr;
+ Blt_TreeNotifyEvent event;
+ TreeClient *clientPtr;
+ int isSource;
+
+ event.type = eventFlag;
+ event.inode = nodePtr->inode;
+
+ /*
+ * Issue callbacks to each client indicating that a new node has
+ * been created.
+ */
+ for (linkPtr = Blt_ChainFirstLink(treeObjPtr->clients);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ clientPtr = Blt_ChainGetValue(linkPtr);
+ isSource = (clientPtr == sourcePtr);
+ CheckEventHandlers(clientPtr, isSource, &event);
+ }
+}
+
+
+/* Public Routines */
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeGetKey --
+ *
+ * Given a string, returns a unique identifier for the string.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeKey
+Blt_TreeGetKey(string)
+ CONST char *string; /* String to convert. */
+{
+ Blt_HashEntry *hPtr;
+ int isNew;
+
+ if (!keyTableInitialized) {
+ Blt_InitHashTable(&keyTable, BLT_STRING_KEYS);
+ keyTableInitialized = 1;
+ }
+ hPtr = Blt_CreateHashEntry(&keyTable, string, &isNew);
+ return (Blt_TreeKey)Blt_GetHashKey(&keyTable, hPtr);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeCreateNode --
+ *
+ * Creates a new node in the given parent node. The name and
+ * position in the parent are also provided.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeNode
+Blt_TreeCreateNode(clientPtr, parentPtr, name, pos)
+ TreeClient *clientPtr; /* The tree client that is creating
+ * this node. If NULL, indicates to
+ * trigger notify events on behalf of
+ * the initiating client also. */
+ Node *parentPtr; /* Parent node where the new node will
+ * be inserted. */
+ CONST char *name; /* Name of node. */
+ int pos; /* Position in the parent's list of children
+ * where to insert the new node. */
+{
+ Blt_HashEntry *hPtr;
+ Node *beforePtr;
+ Node *nodePtr; /* Node to be inserted. */
+ TreeObject *treeObjPtr;
+ int inode;
+ int isNew;
+
+ treeObjPtr = parentPtr->treeObject;
+
+ /* Generate an unique serial number for this node. */
+ do {
+ inode = treeObjPtr->nextNode++;
+ hPtr = Blt_CreateHashEntry(&(treeObjPtr->nodeTable),(char *)inode,
+ &isNew);
+ } while (!isNew);
+ nodePtr = NewNode(treeObjPtr, name, inode);
+ Blt_SetHashValue(hPtr, nodePtr);
+
+ if ((pos == -1) || (pos >= (int)parentPtr->nChildren)) {
+ beforePtr = NULL;
+ } else {
+ beforePtr = parentPtr->first;
+ while ((pos > 0) && (beforePtr != NULL)) {
+ pos--;
+ beforePtr = beforePtr->next;
+ }
+ }
+ LinkBefore(parentPtr, nodePtr, beforePtr);
+ /*
+ * Issue callbacks to each client indicating that a new node has
+ * been created.
+ */
+ NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_CREATE);
+ return nodePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeCreateNodeWithId --
+ *
+ * Like Blt_TreeCreateNode, but provides a specific id to use
+ * for the node. If the tree already contains a node by that
+ * id, NULL is returned.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeNode
+Blt_TreeCreateNodeWithId(clientPtr, parentPtr, name, inode, position)
+ TreeClient *clientPtr;
+ Node *parentPtr; /* Parent node where the new node will
+ * be inserted. */
+ CONST char *name; /* Name of node. */
+ int inode; /* Requested id of the new node. If a
+ * node by this id already exists in the
+ * tree, no node is created. */
+ int position; /* Position in the parent's list of children
+ * where to insert the new node. */
+{
+ Blt_HashEntry *hPtr;
+ Node *beforePtr;
+ Node *nodePtr; /* Node to be inserted. */
+ TreeObject *treeObjPtr;
+ int isNew;
+
+ treeObjPtr = parentPtr->treeObject;
+ hPtr = Blt_CreateHashEntry(&treeObjPtr->nodeTable,(char *)inode, &isNew);
+ if (!isNew) {
+ return NULL;
+ }
+ nodePtr = NewNode(treeObjPtr, name, inode);
+ Blt_SetHashValue(hPtr, nodePtr);
+
+ if ((position == -1) || (position >= (int)parentPtr->nChildren)) {
+ beforePtr = NULL;
+ } else {
+ beforePtr = parentPtr->first;
+ while ((position > 0) && (beforePtr != NULL)) {
+ position--;
+ beforePtr = beforePtr->next;
+ }
+ }
+ LinkBefore(parentPtr, nodePtr, beforePtr);
+
+ /*
+ * Issue callbacks to each client indicating that a new node has
+ * been created.
+ */
+ NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_CREATE);
+ return nodePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeMoveNode --
+ *
+ * Move an entry into a new location in the hierarchy.
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+int
+Blt_TreeMoveNode(clientPtr, nodePtr, parentPtr, beforePtr)
+ TreeClient *clientPtr;
+ Node *nodePtr, *parentPtr, *beforePtr;
+{
+ TreeObject *treeObjPtr = nodePtr->treeObject;
+
+ if (nodePtr == beforePtr) {
+ return TCL_ERROR;
+ }
+ if ((beforePtr != NULL) && (beforePtr->parent != parentPtr)) {
+ return TCL_ERROR;
+ }
+ if (nodePtr->parent == NULL) {
+ return TCL_ERROR; /* Can't move root. */
+ }
+ /* Verify that the node isn't an ancestor of the new parent. */
+ if (Blt_TreeIsAncestor(nodePtr, parentPtr)) {
+ return TCL_ERROR;
+ }
+ UnlinkNode(nodePtr);
+ /*
+ * Relink the node as a child of the new parent.
+ */
+ LinkBefore(parentPtr, nodePtr, beforePtr);
+
+ /*
+ * Issue callbacks to each client indicating that a node has
+ * been moved.
+ */
+ NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_MOVE);
+ return TCL_OK;
+}
+
+int
+Blt_TreeDeleteNode(clientPtr, nodePtr)
+ TreeClient *clientPtr;
+ Node *nodePtr;
+{
+ TreeObject *treeObjPtr = nodePtr->treeObject;
+ Node *childPtr, *nextPtr;
+
+ /* In depth-first order, delete each descendant node. */
+ for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
+ nextPtr = childPtr->next;
+ Blt_TreeDeleteNode(clientPtr, childPtr);
+ }
+ /*
+ * Issue callbacks to each client indicating that the node can
+ * no longer be used.
+ */
+ NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_DELETE);
+
+ /* Now remove the actual node. */
+ FreeNode(treeObjPtr, nodePtr);
+ return TCL_OK;
+}
+
+Blt_TreeNode
+Blt_TreeGetNode(clientPtr, inode)
+ TreeClient *clientPtr;
+ unsigned int inode;
+{
+ TreeObject *treeObjPtr = clientPtr->treeObject;
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&(treeObjPtr->nodeTable), (char *)inode);
+ if (hPtr != NULL) {
+ return (Blt_TreeNode)Blt_GetHashValue(hPtr);
+ }
+ return NULL;
+}
+
+Blt_TreeTrace
+Blt_TreeCreateTrace(clientPtr, nodePtr, keyPattern, mask, proc, clientData)
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ CONST char *keyPattern;
+ unsigned int mask;
+ Blt_TreeTraceProc *proc;
+ ClientData clientData;
+{
+ TraceHandler *handlerPtr;
+
+ handlerPtr = Blt_Malloc(sizeof (TraceHandler));
+ assert(handlerPtr);
+ handlerPtr->linkPtr = Blt_ChainAppend(clientPtr->traces, handlerPtr);
+ handlerPtr->keyPattern = Blt_Strdup(keyPattern);
+ handlerPtr->clientPtr = clientPtr;
+ handlerPtr->proc = proc;
+ handlerPtr->clientData = clientData;
+ handlerPtr->mask = mask;
+ handlerPtr->nodePtr = nodePtr;
+ return (Blt_TreeTrace)handlerPtr;
+}
+
+void
+Blt_TreeDeleteTrace(trace)
+ Blt_TreeTrace trace;
+{
+ TraceHandler *handlerPtr = (TraceHandler *)trace;
+
+ Blt_ChainDeleteLink(handlerPtr->clientPtr->traces, handlerPtr->linkPtr);
+ if (handlerPtr->keyPattern != NULL) {
+ Blt_Free(handlerPtr->keyPattern);
+ }
+ Blt_Free(handlerPtr);
+}
+
+void
+Blt_TreeRelabelNode(clientPtr, nodePtr, string)
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ CONST char *string;
+{
+ nodePtr->label = Blt_TreeGetKey(string);
+ /*
+ * Issue callbacks to each client indicating that a new node has
+ * been created.
+ */
+ NotifyClients(clientPtr, clientPtr->treeObject, nodePtr,
+ TREE_NOTIFY_RELABEL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeFindChild --
+ *
+ * Searches for the named node in a parent's chain of siblings.
+ *
+ *
+ * Results:
+ * If found, the child node is returned, otherwise NULL.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeNode
+Blt_TreeFindChild(parentPtr, string)
+ Node *parentPtr;
+ CONST char *string;
+{
+ Blt_TreeKey label;
+ register Node *nodePtr;
+
+ label = Blt_TreeGetKey(string);
+ for (nodePtr = parentPtr->first; nodePtr != NULL; nodePtr = nodePtr->next) {
+ if (label == nodePtr->label) {
+ return nodePtr;
+ }
+ }
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeNodePosition --
+ *
+ * Returns the position of the node in its parent's list of
+ * children. The root's position is 0.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_TreeNodePosition(nodePtr)
+ Node *nodePtr;
+{
+ Node *parentPtr;
+ int count;
+
+ count = 0;
+ parentPtr = nodePtr->parent;
+ if (parentPtr != NULL) {
+ Node *childPtr;
+
+ for (childPtr = parentPtr->first; childPtr != NULL;
+ childPtr = childPtr->next) {
+ if (nodePtr == childPtr) {
+ break;
+ }
+ count++;
+ }
+ }
+ return count;
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreePrevNode --
+ *
+ * Returns the "previous" node in the tree. This node (in
+ * depth-first order) is its parent, if the node has no siblings
+ * that are previous to it. Otherwise it is the last descendant
+ * of the last sibling. In this case, descend the sibling's
+ * hierarchy, using the last child at any ancestor, with we
+ * we find a leaf.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeNode
+Blt_TreePrevNode(rootPtr, nodePtr)
+ Node *rootPtr, *nodePtr;
+{
+ Node *prevPtr;
+
+ if (nodePtr == rootPtr) {
+ return NULL; /* The root is the first node. */
+ }
+ prevPtr = nodePtr->prev;
+ if (prevPtr == NULL) {
+ /* There are no siblings previous to this one, so pick the parent. */
+ return nodePtr->parent;
+ }
+ /*
+ * Traverse down the right-most thread, in order to select the
+ * next entry. Stop when we reach a leaf.
+ */
+ nodePtr = prevPtr;
+ while ((prevPtr = nodePtr->last) != NULL) {
+ nodePtr = prevPtr;
+ }
+ return nodePtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeNextNode --
+ *
+ * Returns the "next" node in relation to the given node.
+ * The next node (in depth-first order) is either the first
+ * child of the given node the next sibling if the node has
+ * no children (the node is a leaf). If the given node is the
+ * last sibling, then try it's parent next sibling. Continue
+ * until we either find a next sibling for some ancestor or
+ * we reach the root node. In this case the current node is
+ * the last node in the tree.
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeNode
+Blt_TreeNextNode(rootPtr, nodePtr)
+ Node *rootPtr, *nodePtr;
+{
+ Node *nextPtr;
+
+ /* Pick the first sub-node. */
+ nextPtr = nodePtr->first;
+ if (nextPtr != NULL) {
+ return nextPtr;
+ }
+ /*
+ * Back up until we can find a level where we can pick a
+ * "next sibling". For the last entry we'll thread our
+ * way back to the root.
+ */
+ while (nodePtr != rootPtr) {
+ nextPtr = nodePtr->next;
+ if (nextPtr != NULL) {
+ return nextPtr;
+ }
+ nodePtr = nodePtr->parent;
+ }
+ return NULL; /* At root, no next node. */
+}
+
+
+int
+Blt_TreeIsBefore(n1Ptr, n2Ptr)
+ Node *n1Ptr, *n2Ptr;
+{
+ int depth;
+ register int i;
+ Node *nodePtr;
+
+ if (n1Ptr == n2Ptr) {
+ return FALSE;
+ }
+ depth = MIN(n1Ptr->depth, n2Ptr->depth);
+ if (depth == 0) { /* One of the nodes is root. */
+ return (n1Ptr->parent == NULL);
+ }
+ /*
+ * Traverse back from the deepest node, until both nodes are at
+ * the same depth. Check if this ancestor node is the same for
+ * both nodes.
+ */
+ for (i = n1Ptr->depth; i > depth; i--) {
+ n1Ptr = n1Ptr->parent;
+ }
+ if (n1Ptr == n2Ptr) {
+ return FALSE;
+ }
+ for (i = n2Ptr->depth; i > depth; i--) {
+ n2Ptr = n2Ptr->parent;
+ }
+ if (n2Ptr == n1Ptr) {
+ return TRUE;
+ }
+
+ /*
+ * First find the mutual ancestor of both nodes. Look at each
+ * preceding ancestor level-by-level for both nodes. Eventually
+ * we'll find a node that's the parent of both ancestors. Then
+ * find the first ancestor in the parent's list of subnodes.
+ */
+ for (i = depth; i > 0; i--) {
+ if (n1Ptr->parent == n2Ptr->parent) {
+ break;
+ }
+ n1Ptr = n1Ptr->parent;
+ n2Ptr = n2Ptr->parent;
+ }
+ for (nodePtr = n1Ptr->parent->first; nodePtr != NULL;
+ nodePtr = nodePtr->next) {
+ if (nodePtr == n1Ptr) {
+ return TRUE;
+ } else if (nodePtr == n2Ptr) {
+ return FALSE;
+ }
+ }
+ return FALSE;
+}
+
+static void
+CallTraces(interp, sourcePtr, treeObjPtr, nodePtr, key, flags)
+ Tcl_Interp *interp;
+ TreeClient *sourcePtr; /* Client holding a reference to the
+ * tree. If NULL, indicates to
+ * execute all handlers, including
+ * those of the caller. */
+ TreeObject *treeObjPtr; /* Tree that was changed. */
+ Node *nodePtr;
+ Blt_TreeKey key;
+ unsigned int flags;
+{
+ Blt_ChainLink *l1Ptr, *l2Ptr;
+ TreeClient *clientPtr;
+ TraceHandler *handlerPtr;
+
+ for(l1Ptr = Blt_ChainFirstLink(treeObjPtr->clients);
+ l1Ptr != NULL; l1Ptr = Blt_ChainNextLink(l1Ptr)) {
+ clientPtr = Blt_ChainGetValue(l1Ptr);
+ for(l2Ptr = Blt_ChainFirstLink(clientPtr->traces);
+ l2Ptr != NULL; l2Ptr = Blt_ChainNextLink(l2Ptr)) {
+ handlerPtr = Blt_ChainGetValue(l2Ptr);
+ if (!Tcl_StringMatch(key, handlerPtr->keyPattern)) {
+ continue;
+ }
+ if ((clientPtr == sourcePtr) &&
+ (handlerPtr->mask & TREE_TRACE_FOREIGN_ONLY)) {
+ continue;
+ }
+ if (((handlerPtr->nodePtr == NULL) ||
+ (handlerPtr->nodePtr == nodePtr)) &&
+ (handlerPtr->mask & flags)) {
+ nodePtr->flags |= TREE_TRACE_ACTIVE;
+ if ((*handlerPtr->proc) (handlerPtr->clientData,
+ treeObjPtr->interp, nodePtr, key, flags) != TCL_OK) {
+ if (interp != NULL) {
+ Tcl_BackgroundError(interp);
+ }
+ }
+ nodePtr->flags &= ~TREE_TRACE_ACTIVE;
+ }
+ }
+ }
+}
+
+static Value *
+GetTreeValue(interp, clientPtr, nodePtr, key)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ Blt_TreeKey key;
+{
+ register Value *valuePtr;
+
+ for (valuePtr = nodePtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->next) {
+ if (valuePtr->key == key) {
+ if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't access private field \"",
+ key, "\"", (char *)NULL);
+ }
+ return NULL;
+ }
+ return valuePtr;
+ }
+ }
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't find field \"", key, "\"",
+ (char *)NULL);
+ }
+ return NULL;
+}
+
+int
+Blt_TreePrivateValue(interp, clientPtr, nodePtr, key)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ Blt_TreeKey key;
+{
+ Value *valuePtr;
+
+ valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
+ if (valuePtr == NULL) {
+ return TCL_ERROR;
+ }
+ valuePtr->owner = clientPtr;
+ return TCL_OK;
+}
+
+int
+Blt_TreePublicValue(interp, clientPtr, nodePtr, key)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ Blt_TreeKey key;
+{
+ Value *valuePtr;
+
+ valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
+ if (valuePtr == NULL) {
+ return TCL_ERROR;
+ }
+ valuePtr->owner = NULL;
+ return TCL_OK;
+}
+
+int
+Blt_TreeValueExistsByKey(clientPtr, nodePtr, key)
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ Blt_TreeKey key;
+{
+ register Value *valuePtr;
+
+ valuePtr = GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key);
+ if (valuePtr == NULL) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+int
+Blt_TreeGetValueByKey(interp, clientPtr, nodePtr, key, objPtrPtr)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ Blt_TreeKey key;
+ Tcl_Obj **objPtrPtr;
+{
+ register Value *valuePtr;
+ TreeObject *treeObjPtr = nodePtr->treeObject;
+
+ valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
+ if (valuePtr == NULL) {
+ return TCL_ERROR;
+ }
+ *objPtrPtr = valuePtr->objPtr;
+ if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
+ CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key,
+ TREE_TRACE_READ);
+ }
+ return TCL_OK;
+}
+
+int
+Blt_TreeSetValueByKey(interp, clientPtr, nodePtr, key, objPtr)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr; /* Node to be updated. */
+ Blt_TreeKey key; /* Identifies the field key. */
+ Tcl_Obj *objPtr; /* New value of field. */
+{
+ TreeObject *treeObjPtr = nodePtr->treeObject;
+ Value *valuePtr;
+ unsigned int flags;
+
+ assert(objPtr != NULL);
+
+ valuePtr = NULL;
+ /* See if a data field by the given name exists. */
+ for (valuePtr = nodePtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->next) {
+ if (valuePtr->key == key) {
+ if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't set private field \"",
+ key, "\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ break;
+ }
+ }
+ flags = 0;
+ if (valuePtr == NULL) {
+ /* Create a new one and append it to the node's chain. */
+ valuePtr = Blt_PoolAllocItem(treeObjPtr->valuePool, sizeof(Value));
+ valuePtr->key = key;
+ valuePtr->owner = NULL;
+ Tcl_IncrRefCount(objPtr);
+ valuePtr->next = nodePtr->values;
+ nodePtr->values = valuePtr;
+ flags |= TREE_TRACE_CREATE;
+ } else {
+ if (objPtr != valuePtr->objPtr) {
+ Tcl_IncrRefCount(objPtr);
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ }
+ }
+ valuePtr->objPtr = objPtr;
+ flags |= TREE_TRACE_WRITE;
+
+ if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
+ CallTraces(interp, clientPtr, treeObjPtr, nodePtr, valuePtr->key,
+ flags);
+ }
+ return TCL_OK;
+}
+
+int
+Blt_TreeUnsetValueByKey(interp, clientPtr, nodePtr, key)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr; /* Node to be updated. */
+ Blt_TreeKey key; /* Name of field in node. */
+{
+ TreeObject *treeObjPtr = nodePtr->treeObject;
+ Value *valuePtr, *prevPtr;
+
+ prevPtr = valuePtr = NULL;
+ for (valuePtr = nodePtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->next) {
+ if (valuePtr->key == key) {
+ if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't unset private field \"",
+ key, "\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ break;
+ }
+ prevPtr = valuePtr;
+ }
+ if (valuePtr == NULL) {
+ return TCL_OK; /* No value was already set. */
+ }
+ if (prevPtr == NULL) {
+ nodePtr->values = valuePtr->next; /* Remove the head */
+ } else {
+ prevPtr->next = valuePtr->next;
+ }
+ CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key, TREE_TRACE_UNSET);
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ Blt_PoolFreeItem(treeObjPtr->valuePool, (char *)valuePtr);
+ return TCL_OK;
+}
+
+static int
+ParseParentheses(interp, string, leftPtr, rightPtr)
+ Tcl_Interp *interp;
+ char *string;
+ char **leftPtr, **rightPtr;
+{
+ register char *p;
+ char *left, *right;
+
+ left = right = NULL;
+ for (p = string; *p != '\0'; p++) {
+ if (*p == '(') {
+ left = p;
+ } else if (*p == ')') {
+ right = p;
+ }
+ }
+ if (left != right) {
+ if (((left != NULL) && (right == NULL)) ||
+ ((left == NULL) && (right != NULL)) ||
+ (left > right) || (right != (p - 1))) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "bad array specification \"", string,
+ "\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ }
+ *leftPtr = left;
+ *rightPtr = right;
+ return TCL_OK;
+}
+
+int
+Blt_TreeGetValue(interp, clientPtr, nodePtr, string, objPtrPtr)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ CONST char *string; /* String identifying the field in node. */
+ Tcl_Obj **objPtrPtr;
+{
+ char *copy, *left, *right;
+ int result;
+
+ copy = Blt_Strdup(string);
+ if (ParseParentheses(interp, copy, &left, &right) != TCL_OK) {
+ Blt_Free(copy);
+ return TCL_ERROR;
+ }
+ if (left != NULL) {
+ *left = *right = '\0';
+ result = Blt_TreeGetArrayValue(interp, clientPtr, nodePtr, copy,
+ left + 1, objPtrPtr);
+ } else {
+ result = Blt_TreeGetValueByKey(interp, clientPtr, nodePtr,
+ Blt_TreeGetKey(copy), objPtrPtr);
+ }
+ Blt_Free(copy);
+ return result;
+}
+
+int
+Blt_TreeSetValue(interp, clientPtr, nodePtr, string, valueObjPtr)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr; /* Node to be updated. */
+ CONST char *string; /* String identifying the field in node. */
+ Tcl_Obj *valueObjPtr; /* New value of field. If NULL, field
+ * is deleted. */
+{
+ char *copy, *left, *right;
+ int result;
+
+ copy = Blt_Strdup(string);
+ if (ParseParentheses(interp, copy, &left, &right) != TCL_OK) {
+ Blt_Free(copy);
+ return TCL_ERROR;
+ }
+ if (left != NULL) {
+ *left = *right = '\0';
+ result = Blt_TreeSetArrayValue(interp, clientPtr, nodePtr, copy,
+ left + 1, valueObjPtr);
+ } else {
+ result = Blt_TreeSetValueByKey(interp, clientPtr, nodePtr,
+ Blt_TreeGetKey(copy), valueObjPtr);
+ }
+ Blt_Free(copy);
+ return result;
+}
+
+int
+Blt_TreeUnsetValue(interp, clientPtr, nodePtr, string)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr; /* Node to be updated. */
+ CONST char *string; /* String identifying the field in node. */
+{
+ char *copy, *left, *right;
+ int result;
+
+ copy = Blt_Strdup(string);
+ if (ParseParentheses(interp, copy, &left, &right) != TCL_OK) {
+ Blt_Free(copy);
+ return TCL_ERROR;
+ }
+ if (left != NULL) {
+ *left = *right = '\0';
+ result = Blt_TreeUnsetArrayValue(interp, clientPtr, nodePtr, copy,
+ left + 1);
+ } else {
+ result = Blt_TreeUnsetValueByKey(interp, clientPtr, nodePtr,
+ Blt_TreeGetKey(copy));
+ }
+ Blt_Free(copy);
+ return result;
+}
+
+int
+Blt_TreeValueExists(clientPtr, nodePtr, string)
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ CONST char *string;
+{
+ char *copy, *left, *right;
+ int result;
+
+ copy = Blt_Strdup(string);
+ if (ParseParentheses((Tcl_Interp *)NULL, copy, &left, &right) != TCL_OK) {
+ Blt_Free(copy);
+ return FALSE;
+ }
+ if (left != NULL) {
+ *left = *right = '\0';
+ result = Blt_TreeArrayValueExists(clientPtr, nodePtr, copy, left + 1);
+ } else {
+ result = Blt_TreeValueExistsByKey(clientPtr, nodePtr,
+ Blt_TreeGetKey(string));
+ }
+ Blt_Free(copy);
+ return result;
+}
+
+Blt_TreeKey
+Blt_TreeFirstKey(clientPtr, nodePtr, iterPtr)
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ Blt_TreeKeySearch *iterPtr;
+{
+ Value *valuePtr;
+
+ valuePtr = nodePtr->values;
+ if (valuePtr == NULL) {
+ return NULL;
+ }
+ *iterPtr = (Blt_TreeKeySearch)valuePtr->next;
+ if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+ return Blt_TreeNextKey(clientPtr, iterPtr);
+ }
+ return valuePtr->key;
+}
+
+Blt_TreeKey
+Blt_TreeNextKey(clientPtr, iterPtr)
+ TreeClient *clientPtr;
+ Blt_TreeKeySearch *iterPtr;
+{
+ Value *valuePtr;
+
+ for (valuePtr = *(Value **)iterPtr; valuePtr != NULL;
+ valuePtr = valuePtr->next) {
+ if ((valuePtr->owner == NULL) || (valuePtr->owner == clientPtr)) {
+ *iterPtr = (Blt_TreeKeySearch)valuePtr->next;
+ return valuePtr->key;
+ }
+ }
+ return NULL;
+}
+
+int
+Blt_TreeIsAncestor(n1Ptr, n2Ptr)
+ Node *n1Ptr, *n2Ptr;
+{
+ if (n2Ptr != NULL) {
+ n2Ptr = n2Ptr->parent;
+ while (n2Ptr != NULL) {
+ if (n2Ptr == n1Ptr) {
+ return TRUE;
+ }
+ n2Ptr = n2Ptr->parent;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeSortNode --
+ *
+ * Sorts the subnodes at a given node.
+ *
+ * Results:
+ * Always returns TCL_OK.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+Blt_TreeSortNode(clientPtr, nodePtr, proc)
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ Blt_TreeCompareNodesProc *proc;
+{
+ Node **nodeArr;
+ Node *childPtr;
+ int nNodes;
+ register Node **p;
+
+ nNodes = nodePtr->nChildren;
+ if (nNodes < 2) {
+ return TCL_OK;
+ }
+ nodeArr = Blt_Malloc((nNodes + 1) * sizeof(Node *));
+ if (nodeArr == NULL) {
+ return TCL_ERROR; /* Out of memory. */
+ }
+ for (p = nodeArr, childPtr = nodePtr->first; childPtr != NULL;
+ childPtr = childPtr->next, p++) {
+ *p = childPtr;
+ }
+ *p = NULL;
+
+ qsort((char *)nodeArr, nNodes, sizeof(Node *), (QSortCompareProc *)proc);
+ for (p = nodeArr; *p != NULL; p++) {
+ UnlinkNode(*p);
+ LinkBefore(nodePtr, *p, (Blt_TreeNode)NULL);
+ }
+ Blt_Free(nodeArr);
+ NotifyClients(clientPtr, nodePtr->treeObject, nodePtr, TREE_NOTIFY_SORT);
+ return TCL_OK;
+}
+
+#define TEST_RESULT(result) \
+ switch (result) { \
+ case TCL_CONTINUE: \
+ return TCL_OK; \
+ case TCL_OK: \
+ break; \
+ default: \
+ return (result); \
+ }
+
+int
+Blt_TreeApply(nodePtr, proc, clientData)
+ Node *nodePtr; /* Root node of subtree. */
+ Blt_TreeApplyProc *proc; /* Procedure to call for each node. */
+ ClientData clientData; /* One-word of data passed when calling
+ * proc. */
+{
+ Node *childPtr, *nextPtr;
+ int result;
+
+ for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
+
+ /*
+ * Get the next link in the chain before calling Blt_TreeApply
+ * recursively. This is because the apply callback may delete
+ * the node and its link.
+ */
+
+ nextPtr = childPtr->next;
+ result = Blt_TreeApply(childPtr, proc, clientData);
+ TEST_RESULT(result);
+ }
+ return (*proc) (nodePtr, clientData, TREE_POSTORDER);
+}
+
+int
+Blt_TreeApplyDFS(nodePtr, proc, clientData, order)
+ Node *nodePtr; /* Root node of subtree. */
+ Blt_TreeApplyProc *proc; /* Procedure to call for each node. */
+ ClientData clientData; /* One-word of data passed when calling
+ * proc. */
+ int order; /* Order of traversal. */
+{
+ Node *childPtr, *nextPtr;
+ int result;
+
+ if (order & TREE_PREORDER) {
+ result = (*proc) (nodePtr, clientData, TREE_PREORDER);
+ TEST_RESULT(result);
+ }
+ childPtr = nodePtr->first;
+ if (order & TREE_INORDER) {
+ if (childPtr != NULL) {
+ result = Blt_TreeApplyDFS(childPtr, proc, clientData, order);
+ TEST_RESULT(result);
+ childPtr = childPtr->next;
+ }
+ result = (*proc) (nodePtr, clientData, TREE_INORDER);
+ TEST_RESULT(result);
+ }
+ for (/* empty */; childPtr != NULL; childPtr = nextPtr) {
+ /*
+ * Get the next link in the chain before calling
+ * Blt_TreeApply recursively. This is because the
+ * apply callback may delete the node and its link.
+ */
+ nextPtr = childPtr->next;
+ result = Blt_TreeApplyDFS(childPtr, proc, clientData, order);
+ TEST_RESULT(result);
+ }
+ if (order & TREE_POSTORDER) {
+ return (*proc) (nodePtr, clientData, TREE_POSTORDER);
+ }
+ return TCL_OK;
+}
+
+int
+Blt_TreeApplyBFS(nodePtr, proc, clientData)
+ Node *nodePtr; /* Root node of subtree. */
+ Blt_TreeApplyProc *proc; /* Procedure to call for each node. */
+ ClientData clientData; /* One-word of data passed when calling
+ * proc. */
+{
+ Blt_Chain *queuePtr;
+ Blt_ChainLink *linkPtr, *nextPtr;
+ Node *childPtr;
+ int result;
+
+ queuePtr = Blt_ChainCreate();
+ linkPtr = Blt_ChainAppend(queuePtr, nodePtr);
+ while (linkPtr != NULL) {
+ nodePtr = Blt_ChainGetValue(linkPtr);
+ /* Add the children to the queue. */
+ for (childPtr = nodePtr->first; childPtr != NULL;
+ childPtr = childPtr->next) {
+ Blt_ChainAppend(queuePtr, childPtr);
+ }
+ /* Process the node. */
+ result = (*proc) (nodePtr, clientData, TREE_BREADTHFIRST);
+ switch (result) {
+ case TCL_CONTINUE:
+ Blt_ChainDestroy(queuePtr);
+ return TCL_OK;
+ case TCL_OK:
+ break;
+ default:
+ Blt_ChainDestroy(queuePtr);
+ return result;
+ }
+ /* Remove the node from the queue. */
+ nextPtr = Blt_ChainNextLink(linkPtr);
+ Blt_ChainDeleteLink(queuePtr, linkPtr);
+ linkPtr = nextPtr;
+ }
+ Blt_ChainDestroy(queuePtr);
+ return TCL_OK;
+}
+
+static TreeClient *
+NewTreeClient(treeObjPtr)
+ TreeObject *treeObjPtr;
+{
+ TreeClient *clientPtr;
+
+ clientPtr = Blt_Calloc(1, sizeof(TreeClient));
+ if (clientPtr != NULL) {
+ clientPtr->magic = TREE_MAGIC;
+ clientPtr->linkPtr = Blt_ChainAppend(treeObjPtr->clients, clientPtr);
+ clientPtr->events = Blt_ChainCreate();
+ clientPtr->traces = Blt_ChainCreate();
+ clientPtr->treeObject = treeObjPtr;
+ clientPtr->root = treeObjPtr->root;
+ }
+ return clientPtr;
+}
+
+int
+Blt_TreeCreate(interp, name, clientPtrPtr)
+ Tcl_Interp *interp; /* Interpreter to report errors back to. */
+ CONST char *name; /* Name of tree in namespace. Tree
+ * must not already exist. */
+ TreeClient **clientPtrPtr; /* (out) Client token of newly created
+ * tree. Releasing the token will
+ * free the tree. If NULL, no token
+ * is generated. */
+{
+ Tcl_DString dString;
+ Tcl_Namespace *nsPtr;
+ TreeInterpData *dataPtr;
+ TreeObject *treeObjPtr;
+ char *treeName;
+ char string[200];
+
+ dataPtr = GetTreeInterpData(interp);
+ if (name != NULL) {
+ /* Check if this tree already exists the current namespace. */
+ treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_CURRENT);
+ if (treeObjPtr != NULL) {
+ Tcl_AppendResult(interp, "a tree object \"", name,
+ "\" already exists", (char *)NULL);
+ return TCL_ERROR;
+ }
+ } else {
+ /* Generate a unique tree name in the current namespace. */
+ do {
+ sprintf(string, "tree%d", dataPtr->nextId++);
+ } while (GetTreeObject(interp, name, NS_SEARCH_CURRENT) != NULL);
+ name = string;
+ }
+ /*
+ * Tear apart and put back together the namespace-qualified name
+ * of the tree. This is to ensure that naming is consistent.
+ */
+ treeName = (char *)name;
+ if (Blt_ParseQualifiedName(interp, name, &nsPtr, &treeName) != TCL_OK) {
+ Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (nsPtr == NULL) {
+ /*
+ * Note: Unlike Tcl_CreateCommand, an unqualified name
+ * doesn't imply the global namespace, but the current one.
+ */
+ nsPtr = Tcl_GetCurrentNamespace(interp);
+ }
+ name = Blt_GetQualifiedName(nsPtr, treeName, &dString);
+ treeObjPtr = NewTreeObject(dataPtr, interp, name);
+ if (treeObjPtr == NULL) {
+ Tcl_AppendResult(interp, "can't allocate tree \"", name, "\"",
+ (char *)NULL);
+ Tcl_DStringFree(&dString);
+ return TCL_ERROR;
+ }
+ Tcl_DStringFree(&dString);
+ if (clientPtrPtr != NULL) {
+ TreeClient *clientPtr;
+
+ clientPtr = NewTreeClient(treeObjPtr);
+ if (clientPtr == NULL) {
+ Tcl_AppendResult(interp, "can't allocate tree token",(char *)NULL);
+ return TCL_ERROR;
+ }
+ *clientPtrPtr = clientPtr;
+ }
+ return TCL_OK;
+}
+
+int
+Blt_TreeGetToken(interp, name, clientPtrPtr)
+ Tcl_Interp *interp; /* Interpreter to report errors back to. */
+ CONST char *name; /* Name of tree in namespace. */
+ TreeClient **clientPtrPtr;
+{
+ TreeClient *clientPtr;
+ TreeObject *treeObjPtr;
+
+ treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_BOTH);
+ if (treeObjPtr == NULL) {
+ Tcl_AppendResult(interp, "can't find a tree object \"", name, "\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ clientPtr = NewTreeClient(treeObjPtr);
+ if (clientPtr == NULL) {
+ Tcl_AppendResult(interp, "can't allocate token for tree \"",
+ name, "\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *clientPtrPtr = clientPtr;
+ return TCL_OK;
+}
+
+void
+Blt_TreeReleaseToken(clientPtr)
+ TreeClient *clientPtr;
+{
+ TreeObject *treeObjPtr;
+ Blt_ChainLink *linkPtr;
+ EventHandler *handlerPtr;
+ TraceHandler *tracePtr;
+
+ if (clientPtr->magic != TREE_MAGIC) {
+ fprintf(stderr, "invalid tree object token 0x%lx\n",
+ (unsigned long)clientPtr);
+ return;
+ }
+ /* Remove any traces that may be set. */
+ for (linkPtr = Blt_ChainFirstLink(clientPtr->traces); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ tracePtr = Blt_ChainGetValue(linkPtr);
+ if (tracePtr->keyPattern != NULL) {
+ Blt_Free(tracePtr->keyPattern);
+ }
+ Blt_Free(tracePtr);
+ }
+ Blt_ChainDestroy(clientPtr->traces);
+ /* And any event handlers. */
+ for(linkPtr = Blt_ChainFirstLink(clientPtr->events);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ handlerPtr = Blt_ChainGetValue(linkPtr);
+ if (handlerPtr->notifyPending) {
+ Tcl_CancelIdleCall(NotifyIdleProc, handlerPtr);
+ }
+ Blt_Free(handlerPtr);
+ }
+ Blt_ChainDestroy(clientPtr->events);
+ treeObjPtr = clientPtr->treeObject;
+ if (treeObjPtr != NULL) {
+ /* Remove the client from the server's list */
+ Blt_ChainDeleteLink(treeObjPtr->clients, clientPtr->linkPtr);
+ if (Blt_ChainGetLength(treeObjPtr->clients) == 0) {
+ DestroyTreeObject(treeObjPtr);
+ }
+ }
+ clientPtr->magic = 0;
+ Blt_Free(clientPtr);
+}
+
+int
+Blt_TreeExists(interp, name)
+ Tcl_Interp *interp; /* Interpreter to report errors back to. */
+ CONST char *name; /* Name of tree in designated namespace. */
+{
+ TreeObject *treeObjPtr;
+
+ treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_BOTH);
+ if (treeObjPtr == NULL) {
+ Tcl_ResetResult(interp);
+ return 0;
+ }
+ return 1;
+}
+
+/*ARGSUSED*/
+static int
+SizeApplyProc(nodePtr, clientData, order)
+ Node *nodePtr; /* Not used. */
+ ClientData clientData;
+ int order; /* Not used. */
+{
+ int *sumPtr = clientData;
+ *sumPtr = *sumPtr + 1;
+ return TCL_OK;
+}
+
+int
+Blt_TreeSize(nodePtr)
+ Node *nodePtr;
+{
+ int sum;
+
+ sum = 0;
+ Blt_TreeApply(nodePtr, SizeApplyProc, &sum);
+ return sum;
+}
+
+
+void
+Blt_TreeCreateEventHandler(clientPtr, mask, proc, clientData)
+ TreeClient *clientPtr;
+ unsigned int mask;
+ Blt_TreeNotifyEventProc *proc;
+ ClientData clientData;
+{
+ Blt_ChainLink *linkPtr;
+ EventHandler *handlerPtr;
+
+ handlerPtr = NULL; /* Suppress compiler warning. */
+
+ /* Check if the event is already handled. */
+ for(linkPtr = Blt_ChainFirstLink(clientPtr->events);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ handlerPtr = Blt_ChainGetValue(linkPtr);
+ if ((handlerPtr->proc == proc) &&
+ (handlerPtr->clientData == clientData)) {
+ break;
+ }
+ }
+ if (linkPtr == NULL) {
+ handlerPtr = Blt_Malloc(sizeof (EventHandler));
+ assert(handlerPtr);
+ linkPtr = Blt_ChainAppend(clientPtr->events, handlerPtr);
+ }
+ if (proc == NULL) {
+ Blt_ChainDeleteLink(clientPtr->events, linkPtr);
+ Blt_Free(handlerPtr);
+ } else {
+ handlerPtr->proc = proc;
+ handlerPtr->clientData = clientData;
+ handlerPtr->mask = mask;
+ handlerPtr->notifyPending = FALSE;
+ handlerPtr->interp = clientPtr->treeObject->interp;
+ }
+}
+
+void
+Blt_TreeDeleteEventHandler(clientPtr, mask, proc, clientData)
+ TreeClient *clientPtr;
+ unsigned int mask;
+ Blt_TreeNotifyEventProc *proc;
+ ClientData clientData;
+{
+ Blt_ChainLink *linkPtr;
+ EventHandler *handlerPtr;
+
+ for(linkPtr = Blt_ChainFirstLink(clientPtr->events);
+ linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
+ handlerPtr = Blt_ChainGetValue(linkPtr);
+ if ((handlerPtr->proc == proc) && (handlerPtr->mask == mask) &&
+ (handlerPtr->clientData == clientData)) {
+ if (handlerPtr->notifyPending) {
+ Tcl_CancelIdleCall(NotifyIdleProc, handlerPtr);
+ }
+ Blt_ChainDeleteLink(clientPtr->events, linkPtr);
+ Blt_Free(handlerPtr);
+ return;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeNodePath --
+ *
+ *----------------------------------------------------------------------
+ */
+char *
+Blt_TreeNodePath(nodePtr, resultPtr)
+ Node *nodePtr;
+ Tcl_DString *resultPtr;
+{
+ char **nameArr; /* Used to stack the component names. */
+ char *staticSpace[64];
+ int nLevels;
+ register int i;
+
+ nLevels = nodePtr->depth;
+ if (nLevels > 64) {
+ nameArr = Blt_Malloc(nLevels * sizeof(char *));
+ assert(nameArr);
+ } else {
+ nameArr = staticSpace;
+ }
+ for (i = nLevels - 1; i >= 0; i--) {
+ /* Save the name of each ancestor in the name array.
+ * Note that we ignore the root. */
+ nameArr[i] = nodePtr->label;
+ nodePtr = nodePtr->parent;
+ }
+ /* Append each the names in the array. */
+ Tcl_DStringInit(resultPtr);
+ for (i = 0; i < nLevels; i++) {
+ Tcl_DStringAppendElement(resultPtr, nameArr[i]);
+ }
+ if (nameArr != staticSpace) {
+ Blt_Free(nameArr);
+ }
+ return Tcl_DStringValue(resultPtr);
+}
+
+int
+Blt_TreeArrayValueExists(clientPtr, nodePtr, arrayName, elemName)
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ char *arrayName, *elemName;
+{
+ Blt_TreeKey key;
+ Blt_HashEntry *hPtr;
+ Blt_HashTable *tablePtr;
+ register Value *valuePtr;
+
+ key = Blt_TreeGetKey(arrayName);
+ valuePtr = GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key);
+ if (valuePtr == NULL) {
+ return FALSE;
+ }
+ if (Tcl_IsShared(valuePtr->objPtr)) {
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
+ Tcl_IncrRefCount(valuePtr->objPtr);
+ }
+ if (Blt_GetArrayFromObj((Tcl_Interp *)NULL, valuePtr->objPtr, &tablePtr)
+ != TCL_OK) {
+ return FALSE;
+ }
+ hPtr = Blt_FindHashEntry(tablePtr, elemName);
+ return (hPtr != NULL);
+}
+
+int
+Blt_TreeGetArrayValue(interp, clientPtr, nodePtr, arrayName, elemName,
+ valueObjPtrPtr)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ char *arrayName;
+ char *elemName;
+ Tcl_Obj **valueObjPtrPtr;
+{
+ Blt_TreeKey key;
+ Blt_HashEntry *hPtr;
+ Blt_HashTable *tablePtr;
+ register Value *valuePtr;
+
+ key = Blt_TreeGetKey(arrayName);
+ valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
+ if (valuePtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (Tcl_IsShared(valuePtr->objPtr)) {
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
+ Tcl_IncrRefCount(valuePtr->objPtr);
+ }
+ if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ hPtr = Blt_FindHashEntry(tablePtr, elemName);
+ if (hPtr == NULL) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't find \"", arrayName, "(",
+ elemName, ")\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ *valueObjPtrPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
+
+ /* Reading any element of the array can cause a trace to fire. */
+ if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
+ CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr, key,
+ TREE_TRACE_READ);
+ }
+ return TCL_OK;
+}
+
+int
+Blt_TreeSetArrayValue(interp, clientPtr, nodePtr, arrayName, elemName,
+ valueObjPtr)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr; /* Node to be updated. */
+ char *arrayName;
+ char *elemName;
+ Tcl_Obj *valueObjPtr; /* New value of element. */
+{
+ Blt_TreeKey key;
+ Blt_HashEntry *hPtr;
+ Blt_HashTable *tablePtr;
+ register Value *valuePtr;
+ unsigned int flags;
+ int isNew;
+
+ assert(valueObjPtr != NULL);
+
+ /*
+ * Search for the array in the list of data fields. If one
+ * doesn't exist, create it.
+ */
+ key = Blt_TreeGetKey(arrayName);
+
+ valuePtr = NULL;
+ /* See if a data field by the given name exists. */
+ for (valuePtr = nodePtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->next) {
+ if (valuePtr->key == key) {
+ if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't set private field \"",
+ key, "\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ break;
+ }
+ }
+ flags = 0;
+ if (valuePtr == NULL) {
+ TreeObject *treeObjPtr;
+
+ treeObjPtr = (TreeObject *)nodePtr->treeObject;
+ /* Create a new one and append it to the node's chain. */
+ valuePtr = Blt_PoolAllocItem(treeObjPtr->valuePool, sizeof(Value));
+ valuePtr->key = key;
+ valuePtr->owner = NULL;
+ valuePtr->objPtr = Blt_NewArrayObj(0, (Tcl_Obj **)NULL);
+ valuePtr->next = nodePtr->values;
+ nodePtr->values = valuePtr;
+ Tcl_IncrRefCount(valuePtr->objPtr);
+ flags |= TREE_TRACE_CREATE;
+ }
+ flags |= TREE_TRACE_WRITE;
+
+ if (Tcl_IsShared(valuePtr->objPtr)) {
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
+ Tcl_IncrRefCount(valuePtr->objPtr);
+ }
+ if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_InvalidateStringRep(valuePtr->objPtr);
+
+ hPtr = Blt_CreateHashEntry(tablePtr, elemName, &isNew);
+ assert(hPtr);
+
+ if (!isNew) {
+ Tcl_Obj *oldValueObjPtr;
+
+ /* An element by the same name already exists. Decrement the
+ * reference count of the old value. */
+
+ oldValueObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
+ if (oldValueObjPtr != NULL) {
+ Tcl_DecrRefCount(oldValueObjPtr);
+ }
+ }
+ Tcl_IncrRefCount(valueObjPtr);
+ Blt_SetHashValue(hPtr, valueObjPtr);
+
+ /*
+ * We don't handle traces on a per array element basis. Setting
+ * any element can fire traces for the value.
+ */
+ if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
+ CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr,
+ valuePtr->key, flags);
+ }
+ return TCL_OK;
+}
+
+int
+Blt_TreeUnsetArrayValue(interp, clientPtr, nodePtr, arrayName, elemName)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr; /* Node to be updated. */
+ char *arrayName;
+ char *elemName;
+{
+ Blt_TreeKey key; /* Name of field in node. */
+ Blt_HashEntry *hPtr;
+ Blt_HashTable *tablePtr;
+ Tcl_Obj *valueObjPtr;
+ Value *valuePtr;
+
+ valuePtr = NULL; /* Suppress compiler warning. */
+
+ key = Blt_TreeGetKey(arrayName);
+ for (valuePtr = nodePtr->values; valuePtr != NULL;
+ valuePtr = valuePtr->next) {
+ if (valuePtr->key == key) {
+ if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "can't unset private field \"",
+ key, "\"", (char *)NULL);
+ }
+ return TCL_ERROR;
+ }
+ break;
+ }
+ }
+ if (valuePtr == NULL) {
+ return TCL_OK; /* Array doesn't exist. */
+ }
+ if (Tcl_IsShared(valuePtr->objPtr)) {
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
+ Tcl_IncrRefCount(valuePtr->objPtr);
+ }
+ if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ hPtr = Blt_FindHashEntry(tablePtr, elemName);
+ if (hPtr == NULL) {
+ return TCL_OK; /* Element doesn't exist, Ok. */
+ }
+ valueObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
+ Tcl_DecrRefCount(valueObjPtr);
+ Blt_DeleteHashEntry(tablePtr, hPtr);
+
+ /*
+ * Un-setting any element in the array can cause the trace on the value
+ * to fire.
+ */
+ if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
+ CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr,
+ valuePtr->key, TREE_TRACE_WRITE);
+ }
+ return TCL_OK;
+}
+
+int
+Blt_TreeArrayNames(interp, clientPtr, nodePtr, arrayName, listObjPtr)
+ Tcl_Interp *interp;
+ TreeClient *clientPtr;
+ Node *nodePtr;
+ char *arrayName;
+ Tcl_Obj *listObjPtr;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Blt_HashTable *tablePtr;
+ Tcl_Obj *objPtr;
+ Value *valuePtr;
+ char *key;
+
+ key = Blt_TreeGetKey(arrayName);
+ valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
+ if (valuePtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (Tcl_IsShared(valuePtr->objPtr)) {
+ Tcl_DecrRefCount(valuePtr->objPtr);
+ valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
+ Tcl_IncrRefCount(valuePtr->objPtr);
+ }
+ if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ tablePtr = (Blt_HashTable *)valuePtr->objPtr;
+ for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ objPtr = Tcl_NewStringObj(Blt_GetHashKey(tablePtr, hPtr), -1);
+ Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeNewTagTable --
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_TreeTagTable *
+Blt_TreeNewTagTable()
+{
+ Blt_TreeTagTable *tablePtr;
+
+ tablePtr = Blt_Malloc(sizeof(Blt_TreeTagTable));
+ Blt_InitHashTable(&tablePtr->table, BLT_STRING_KEYS);
+ tablePtr->refCount = 1;
+ return tablePtr;
+}
+
+void
+Blt_TreeReleaseTagTable(tablePtr)
+ Blt_TreeTagTable *tablePtr;
+{
+ tablePtr->refCount--;
+ if (tablePtr->refCount <= 0) {
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ Blt_TreeTag *tagPtr;
+
+ for(hPtr = Blt_FirstHashEntry(&tablePtr->table, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ tagPtr = Blt_GetHashValue(hPtr);
+ Blt_DeleteHashTable(&tagPtr->nodeTable);
+ Blt_Free(tagPtr);
+ }
+ Blt_DeleteHashTable(&tablePtr->table);
+ Blt_Free(tablePtr);
+ }
+}
+
+void
+Blt_TreeClearTags(tablePtr, node)
+ Blt_TreeTagTable *tablePtr;
+ Blt_TreeNode node;
+{
+ Blt_HashEntry *hPtr, *h2Ptr;
+ Blt_HashSearch cursor;
+ Blt_TreeTag *tagPtr;
+
+ for (hPtr = Blt_FirstHashEntry(&tablePtr->table, &cursor); hPtr != NULL;
+ hPtr = Blt_NextHashEntry(&cursor)) {
+ tagPtr = Blt_GetHashValue(hPtr);
+ h2Ptr = Blt_FindHashEntry(&tagPtr->nodeTable, (char *)node);
+ if (h2Ptr != NULL) {
+ Blt_DeleteHashEntry(&tagPtr->nodeTable, h2Ptr);
+ }
+ }
+}
+
+int
+Blt_TreeHasTag(tablePtr, node, tagName)
+ Blt_TreeTagTable *tablePtr;
+ Blt_TreeNode node;
+ char *tagName;
+{
+ Blt_HashEntry *hPtr;
+ Blt_TreeTag *tagPtr;
+
+ hPtr = Blt_FindHashEntry(&tablePtr->table, tagName);
+ if (hPtr == NULL) {
+ return FALSE;
+ }
+ tagPtr = Blt_GetHashValue(hPtr);
+ hPtr = Blt_FindHashEntry(&tagPtr->nodeTable, (char *)node);
+ if (hPtr == NULL) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+int
+Blt_TreeAddTag(tablePtr, node, tagName)
+ Blt_TreeTagTable *tablePtr;
+ Blt_TreeNode node;
+ char *tagName;
+{
+ int isNew;
+ Blt_HashEntry *hPtr;
+ Blt_TreeTag *tagPtr;
+
+ hPtr = Blt_CreateHashEntry(&tablePtr->table, tagName, &isNew);
+ assert(hPtr);
+ if (isNew) {
+ tagPtr = Blt_Malloc(sizeof(Blt_TreeTag));
+ Blt_InitHashTable(&tagPtr->nodeTable, BLT_ONE_WORD_KEYS);
+ Blt_SetHashValue(hPtr, tagPtr);
+ tagPtr->hashPtr = hPtr;
+ tagPtr->tagName = Blt_GetHashKey(&tablePtr->table, hPtr);
+ } else {
+ tagPtr = Blt_GetHashValue(hPtr);
+ }
+ hPtr = Blt_CreateHashEntry(&tagPtr->nodeTable, (char *)node, &isNew);
+ assert(hPtr);
+ if (isNew) {
+ Blt_SetHashValue(hPtr, node);
+ }
+ return TCL_OK;
+}
+
+void
+Blt_TreeForgetTag(tablePtr, tagName)
+ Blt_TreeTagTable *tablePtr;
+ char *tagName;
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&tablePtr->table, tagName);
+ if (hPtr != NULL) {
+ Blt_TreeTag *tagPtr;
+
+ tagPtr = Blt_GetHashValue(hPtr);
+ Blt_DeleteHashEntry(&tablePtr->table, hPtr);
+ Blt_DeleteHashTable(&tagPtr->nodeTable);
+ Blt_Free(tagPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Blt_TreeTagHashTable --
+ *
+ *----------------------------------------------------------------------
+ */
+Blt_HashTable *
+Blt_TreeTagHashTable(tablePtr, tagName)
+ Blt_TreeTagTable *tablePtr;
+ char *tagName;
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&tablePtr->table, tagName);
+ if (hPtr != NULL) {
+ Blt_TreeTag *tagPtr;
+
+ tagPtr = Blt_GetHashValue(hPtr);
+ return &tagPtr->nodeTable;
+ }
+ return NULL;
+}
+
+#endif /* NO_TREE */