1
2 /*
3 * bltTree.c --
4 *
5 * Copyright 1998-1999 Lucent Technologies, Inc.
6 *
7 * Permission to use, copy, modify, and distribute this software and
8 * its documentation for any purpose and without fee is hereby
9 * granted, provided that the above copyright notice appear in all
10 * copies and that both that the copyright notice and warranty
11 * disclaimer appear in supporting documentation, and that the names
12 * of Lucent Technologies or any of their entities not be used in
13 * advertising or publicity pertaining to distribution of the software
14 * without specific, written prior permission.
15 *
16 * Lucent Technologies disclaims all warranties with regard to this
17 * software, including all implied warranties of merchantability and
18 * fitness. In no event shall Lucent Technologies be liable for any
19 * special, indirect or consequential damages or any damages
20 * whatsoever resulting from loss of use, data or profits, whether in
21 * an action of contract, negligence or other tortuous action, arising
22 * out of or in connection with the use or performance of this
23 * software.
24 *
25 * The "tree" data object was created by George A. Howlett.
26 * Extensive cleanups and enhancements by Peter MacDonald.
27 *
28 */
29
30 #include "bltInt.h"
31
32 /* TODO:
33 * traces and notifiers should be in one list in tree object.
34 * notifier is always fired.
35 * incorporate first/next tag routines ?
36 */
37
38
39 #ifndef NO_TREE
40
41 #include "bltTree.h"
42
43
IsTclDict(Tcl_Interp * interp,Tcl_Obj * objPtr)44 static int IsTclDict(Tcl_Interp *interp,Tcl_Obj *objPtr) {
45 static Tcl_ObjType *dictType = NULL;
46
47 if (dictType == NULL) {
48 Tcl_Obj * obj;
49 obj = Tcl_NewDictObj();
50 dictType = obj->typePtr;
51 Tcl_DecrRefCount(obj);
52 }
53 return (objPtr->typePtr == dictType);
54 }
55
56 /* 2 = per-tree key, 1 for per-interp, 0 for global (original behavior). */
57 int bltTreeUseLocalKeys = 0;
58
59 static Tcl_InterpDeleteProc TreeInterpDeleteProc;
60 static Blt_TreeApplyProc SizeApplyProc;
61 static Tcl_IdleProc NotifyIdleProc;
62
63 #define TREE_THREAD_KEY "BLT Tree Data"
64 #define TREE_MAGIC ((unsigned int) 0x46170277)
65 #define TREE_DESTROYED (1<<0)
66
67 typedef struct Blt_TreeNodeStruct Node;
68 typedef struct Blt_TreeClientStruct TreeClient;
69 typedef struct Blt_TreeObjectStruct TreeObject;
70 typedef struct Blt_TreeValueStruct Value;
71
72 /*
73 * Blt_TreeValue --
74 *
75 * Tree nodes contain heterogeneous data fields, represented as a
76 * chain of these structures. Each field contains the key of the
77 * field (Blt_TreeKey) and the value (Tcl_Obj) containing the
78 * actual data representations.
79 *
80 */
81 struct Blt_TreeValueStruct {
82 Blt_TreeKey key; /* String identifying the data field */
83 Tcl_Obj *objPtr; /* Data representation. */
84 Blt_Tree owner; /* Non-NULL if privately owned. */
85 Blt_TreeValue next; /* Next value in the chain. */
86 };
87
88 #include <stdio.h>
89 #include <string.h>
90 /* The following header is required for LP64 compilation */
91 #include <stdlib.h>
92
93 #include "bltHash.h"
94
95 static void TreeDestroyValues _ANSI_ARGS_((Blt_TreeNode node));
96
97 static Value *TreeFindValue _ANSI_ARGS_((Blt_TreeNode node,
98 Blt_TreeKey key));
99 static Value *TreeCreateValue _ANSI_ARGS_((Blt_TreeNode node,
100 Blt_TreeKey key, int *newPtr));
101
102 static int TreeDeleteValue _ANSI_ARGS_((Blt_TreeNode node,
103 Blt_TreeValue value));
104
105 static Value *TreeFirstValue _ANSI_ARGS_((Blt_TreeNode,
106 Blt_TreeKeySearch *searchPtr));
107
108 static Value *TreeNextValue _ANSI_ARGS_((Blt_TreeKeySearch *srchPtr));
109
110 /*
111 * When there are this many entries per bucket, on average, rebuild
112 * the hash table to make it larger.
113 */
114
115 #define REBUILD_MULTIPLIER 3
116
117 #define START_LOGSIZE 5 /* Initial hash table size is 32. */
118 #define MAX_LIST_VALUES 21 /* Convert to hash table when node
119 * value list gets bigger than this
120 * many values. */
121
122 #if (SIZEOF_VOID_P == 8)
123 #define RANDOM_INDEX(i) HashOneWord(mask, downshift, i)
124 #define BITSPERWORD 64
125 #else
126
127 /*
128 * The following macro takes a preliminary integer hash value and
129 * produces an index into a hash tables bucket list. The idea is
130 * to make it so that preliminary values that are arbitrarily similar
131 * will end up in different buckets. The hash function was taken
132 * from a random-number generator.
133 */
134 #define RANDOM_INDEX(i) \
135 (((((long) (i))*1103515245) >> downshift) & mask)
136 #define BITSPERWORD 32
137 #endif
138
139 #define DOWNSHIFT_START (BITSPERWORD - 2)
140
141 /*
142 * Procedure prototypes for static procedures in this file:
143 */
144
145
146 #if (SIZEOF_VOID_P == 8)
147 static Blt_Hash HashOneWord _ANSI_ARGS_((uint64_t mask, unsigned int downshift,
148 CONST void *key));
149
150 #endif /* SIZEOF_VOID_P == 8 */
151
152 /*
153 * The hash table below is used to keep track of all the Blt_TreeKeys
154 * created so far.
155 */
156 static Blt_HashTable keyTable;
157 static int keyTableInitialized = 0;
158
159 typedef struct {
160 Blt_HashTable treeTable; /* Table of trees. */
161 unsigned int nextId;
162 Tcl_Interp *interp;
163 Blt_HashTable keyTable;
164 } TreeInterpData;
165
166 typedef struct {
167 Tcl_Interp *interp;
168 ClientData clientData;
169 Blt_TreeKey key;
170 unsigned int mask;
171 Blt_TreeNotifyEventProc *proc;
172 Blt_TreeNotifyEvent event;
173 int notifyPending;
174 } EventHandler;
175
176 typedef struct {
177 ClientData clientData;
178 char *keyPattern;
179 char *withTag;
180 Node *nodePtr;
181 unsigned int mask;
182 Blt_TreeTraceProc *proc;
183 TreeClient *clientPtr;
184 Blt_ChainLink *linkPtr;
185 } TraceHandler;
186
187 /*
188 * --------------------------------------------------------------
189 *
190 * GetTreeInterpData --
191 *
192 * Creates or retrieves data associated with tree data objects
193 * for a particular thread. We're using Tcl_GetAssocData rather
194 * than the Tcl thread routines so BLT can work with pre-8.0
195 * Tcl versions that don't have thread support.
196 *
197 * Results:
198 * Returns a pointer to the tree interpreter data.
199 *
200 * --------------------------------------------------------------
201 */
202 static TreeInterpData *
GetTreeInterpData(Tcl_Interp * interp)203 GetTreeInterpData(Tcl_Interp *interp)
204 {
205 Tcl_InterpDeleteProc *proc;
206 TreeInterpData *dataPtr;
207
208 dataPtr = (TreeInterpData *)
209 Tcl_GetAssocData(interp, TREE_THREAD_KEY, &proc);
210 if (dataPtr == NULL) {
211 dataPtr = Blt_Malloc(sizeof(TreeInterpData));
212 assert(dataPtr);
213 dataPtr->interp = interp;
214 Tcl_SetAssocData(interp, TREE_THREAD_KEY, TreeInterpDeleteProc,
215 dataPtr);
216 Blt_InitHashTable(&dataPtr->treeTable, BLT_STRING_KEYS);
217 Blt_InitHashTable(&dataPtr->keyTable, BLT_STRING_KEYS);
218 }
219 return dataPtr;
220 }
221
222 /*
223 * --------------------------------------------------------------
224 *
225 * NewNode --
226 *
227 * Creates a new node in the tree without installing it. The
228 * number of nodes in the tree is incremented and a unique serial
229 * number is generated for the node.
230 *
231 * Also, all nodes have a label. If no label was provided (name
232 * is NULL) then automatically generate a serial number for the node.
233 *
234 * Results:
235 * Returns a pointer to the new node.
236 *
237 * --------------------------------------------------------------
238 */
239 static Node *
NewNode(TreeObject * treeObjPtr,CONST char * name,int inode)240 NewNode(TreeObject *treeObjPtr, CONST char *name, int inode)
241 {
242 Node *nodePtr;
243
244 /* Create the node structure */
245 nodePtr = Blt_PoolAllocItem(treeObjPtr->nodePool, sizeof(Node));
246 nodePtr->inode = inode;
247 nodePtr->treeObject = treeObjPtr;
248 nodePtr->parent = NULL;
249 nodePtr->depth = 0;
250 nodePtr->flags = 0;
251 nodePtr->next = nodePtr->prev = NULL;
252 nodePtr->first = nodePtr->last = NULL;
253 nodePtr->nChildren = 0;
254 nodePtr->values = NULL;
255 nodePtr->logSize = 0;
256 nodePtr->nValues = 0;
257 nodePtr->label = NULL;
258 if (name != NULL) {
259 nodePtr->label = Blt_TreeKeyGet(NULL, treeObjPtr, name);
260 }
261 treeObjPtr->nNodes++;
262 return nodePtr;
263 }
264
265 /*
266 *----------------------------------------------------------------------
267 *
268 * ReleaseTagTable --
269 *
270 *----------------------------------------------------------------------
271 */
272 static void
ReleaseTagTable(Blt_TreeTagTable * tablePtr)273 ReleaseTagTable(Blt_TreeTagTable *tablePtr)
274 {
275 tablePtr->refCount--;
276 if (tablePtr->refCount <= 0) {
277 Blt_HashEntry *hPtr;
278 Blt_HashSearch cursor;
279
280 for(hPtr = Blt_FirstHashEntry(&tablePtr->tagTable, &cursor);
281 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
282 Blt_TreeTagEntry *tPtr;
283
284 tPtr = Blt_GetHashValue(hPtr);
285 Blt_DeleteHashTable(&tPtr->nodeTable);
286 Blt_TreeTagRefDecr(tPtr);
287 }
288 Blt_DeleteHashTable(&tablePtr->tagTable);
289 Blt_Free(tablePtr);
290 }
291 }
292
293 /*
294 * ----------------------------------------------------------------------
295 *
296 * ResetDepths --
297 *
298 * Called after moving a node, resets the depths of each node
299 * for the entire branch (node and it's decendants).
300 *
301 * Results:
302 * None.
303 *
304 * ----------------------------------------------------------------------
305 */
306 static void
ResetDepths(Node * nodePtr,int depth)307 ResetDepths(
308 Node *nodePtr, /* Root node. */
309 int depth) /* Depth of the node. */
310 {
311 nodePtr->depth = depth;
312 /* Also reset the depth for each descendant node. */
313 for (nodePtr = nodePtr->first; nodePtr != NULL; nodePtr = nodePtr->next) {
314 ResetDepths(nodePtr, depth + 1);
315 }
316 }
317
318 /*
319 *----------------------------------------------------------------------
320 *
321 * LinkBefore --
322 *
323 * Inserts a link preceding a given link.
324 *
325 * Results:
326 * None.
327 *
328 *----------------------------------------------------------------------
329 */
330 static void
LinkBefore(Node * parentPtr,Node * nodePtr,Node * beforePtr)331 LinkBefore(
332 Node *parentPtr, /* Parent to hold the new entry. */
333 Node *nodePtr, /* New node to be inserted. */
334 Node *beforePtr) /* Node to link before. */
335 {
336 if (parentPtr->first == NULL) {
337 parentPtr->last = parentPtr->first = nodePtr;
338 } else if (beforePtr == NULL) { /* Append onto the end of the chain */
339 nodePtr->next = NULL;
340 nodePtr->prev = parentPtr->last;
341 parentPtr->last->next = nodePtr;
342 parentPtr->last = nodePtr;
343 } else {
344 nodePtr->prev = beforePtr->prev;
345 nodePtr->next = beforePtr;
346 if (beforePtr == parentPtr->first) {
347 parentPtr->first = nodePtr;
348 } else {
349 beforePtr->prev->next = nodePtr;
350 }
351 beforePtr->prev = nodePtr;
352 }
353 parentPtr->nChildren++;
354 nodePtr->parent = parentPtr;
355 }
356
357
358 /*
359 *----------------------------------------------------------------------
360 *
361 * UnlinkNode --
362 *
363 * Unlinks a link from the chain. The link is not deallocated,
364 * but only removed from the chain.
365 *
366 * Results:
367 * None.
368 *
369 *----------------------------------------------------------------------
370 */
371 static void
UnlinkNode(Node * nodePtr)372 UnlinkNode(Node *nodePtr)
373 {
374 Node *parentPtr;
375 int unlinked; /* Indicates if the link is actually
376 * removed from the chain. */
377 parentPtr = nodePtr->parent;
378 unlinked = FALSE;
379 if (parentPtr->first == nodePtr) {
380 parentPtr->first = nodePtr->next;
381 unlinked = TRUE;
382 }
383 if (parentPtr->last == nodePtr) {
384 parentPtr->last = nodePtr->prev;
385 unlinked = TRUE;
386 }
387 if (nodePtr->next != NULL) {
388 nodePtr->next->prev = nodePtr->prev;
389 unlinked = TRUE;
390 }
391 if (nodePtr->prev != NULL) {
392 nodePtr->prev->next = nodePtr->next;
393 unlinked = TRUE;
394 }
395 if (unlinked) {
396 parentPtr->nChildren--;
397 }
398 nodePtr->prev = nodePtr->next = nodePtr->parent = NULL;
399 }
400
401 /*
402 * --------------------------------------------------------------
403 *
404 * FreeNode --
405 *
406 * Unlinks a given node from the tree, removes its data, and
407 * frees memory allocated to the node.
408 *
409 * Results:
410 * None.
411 *
412 * --------------------------------------------------------------
413 */
414 static void
FreeNode(TreeObject * treeObjPtr,Node * nodePtr)415 FreeNode(TreeObject *treeObjPtr, Node *nodePtr)
416 {
417 Blt_HashEntry *hPtr;
418
419 /*
420 * Destroy any data fields associated with this node.
421 */
422 TreeDestroyValues(nodePtr);
423 UnlinkNode(nodePtr);
424 treeObjPtr->nNodes--;
425 hPtr = Blt_FindHashEntry(&treeObjPtr->nodeTable, (char *)nodePtr->inode);
426 assert(hPtr);
427 Blt_DeleteHashEntry(&treeObjPtr->nodeTable, hPtr);
428 nodePtr->inode = -1;
429 nodePtr->flags = 0;
430 Blt_PoolFreeItem(treeObjPtr->nodePool, (char *)nodePtr);
431 }
432
433 /*
434 * --------------------------------------------------------------
435 *
436 * NewTreeObject --
437 *
438 * Creates and initializes a new tree object. Trees always
439 * contain a root node, so one is allocated here.
440 *
441 * Results:
442 * Returns a pointer to the new tree object is successful, NULL
443 * otherwise. If a tree can't be generated, interp->result will
444 * contain an error message.
445 *
446 * -------------------------------------------------------------- */
447 static TreeObject *
NewTreeObject(TreeInterpData * dataPtr,Tcl_Interp * interp,CONST char * treeName)448 NewTreeObject(TreeInterpData *dataPtr, Tcl_Interp *interp, CONST char *treeName)
449 {
450 TreeObject *treeObjPtr;
451 int isNew;
452 Blt_HashEntry *hPtr;
453
454 treeObjPtr = Blt_Calloc(1, sizeof(TreeObject));
455 if (treeObjPtr == NULL) {
456 if (interp != NULL)
457 Tcl_AppendResult(interp, "can't allocate tree", (char *)NULL);
458 return NULL;
459 }
460 treeObjPtr->name = Blt_Strdup(treeName);
461 treeObjPtr->interp = interp;
462 treeObjPtr->valuePool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
463 treeObjPtr->nodePool = Blt_PoolCreate(BLT_FIXED_SIZE_ITEMS);
464 treeObjPtr->clients = Blt_ChainCreate();
465 treeObjPtr->depth = 1;
466 treeObjPtr->flags = 0;
467 treeObjPtr->maxKeyList = 0;
468 if (bltTreeUseLocalKeys) {
469 if (bltTreeUseLocalKeys>1) {
470 treeObjPtr->interpKeyPtr = &treeObjPtr->keyTable;
471 } else {
472 treeObjPtr->interpKeyPtr = &dataPtr->keyTable;
473 }
474 }
475 treeObjPtr->notifyFlags = 0;
476 Blt_InitHashTable(&treeObjPtr->keyTable, BLT_STRING_KEYS);
477 Blt_InitHashTableWithPool(&treeObjPtr->nodeTable, BLT_ONE_WORD_KEYS);
478
479 hPtr = Blt_CreateHashEntry(&treeObjPtr->nodeTable, (char *)0, &isNew);
480 treeObjPtr->root = NewNode(treeObjPtr, treeName, 0);
481 Blt_SetHashValue(hPtr, treeObjPtr->root);
482
483 treeObjPtr->tablePtr = &dataPtr->treeTable;
484 treeObjPtr->hashPtr = Blt_CreateHashEntry(treeObjPtr->tablePtr, treeName,
485 &isNew);
486 Blt_SetHashValue(treeObjPtr->hashPtr, treeObjPtr);
487
488 return treeObjPtr;
489 }
490
491 static TreeObject *
FindTreeInNamespace(TreeInterpData * dataPtr,Tcl_Namespace * nsPtr,CONST char * treeName)492 FindTreeInNamespace(
493 TreeInterpData *dataPtr, /* Interpreter-specific data. */
494 Tcl_Namespace *nsPtr,
495 CONST char *treeName)
496 {
497 Tcl_DString dString;
498 char *name;
499 Blt_HashEntry *hPtr;
500
501 name = Blt_GetQualifiedName(nsPtr, treeName, &dString);
502 hPtr = Blt_FindHashEntry(&dataPtr->treeTable, name);
503 Tcl_DStringFree(&dString);
504 if (hPtr != NULL) {
505 return Blt_GetHashValue(hPtr);
506 }
507 return NULL;
508 }
509
510 /*
511 * ----------------------------------------------------------------------
512 *
513 * GetTreeObject --
514 *
515 * Searches for the tree object associated by the name given.
516 *
517 * Results:
518 * Returns a pointer to the tree if found, otherwise NULL.
519 *
520 * ----------------------------------------------------------------------
521 */
522 static TreeObject *
GetTreeObject(Tcl_Interp * interp,CONST char * name,int flags)523 GetTreeObject(Tcl_Interp *interp, CONST char *name, int flags)
524 {
525 CONST char *treeName;
526 Tcl_Namespace *nsPtr; /* Namespace associated with the tree object.
527 * If NULL, indicates to look in first the
528 * current namespace and then the global
529 * for the tree. */
530 TreeInterpData *dataPtr; /* Interpreter-specific data. */
531 TreeObject *treeObjPtr;
532
533 treeObjPtr = NULL;
534 if (Blt_ParseQualifiedName(interp, name, &nsPtr, &treeName) != TCL_OK) {
535 if (interp != NULL)
536 Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"",
537 (char *)NULL);
538 return NULL;
539 }
540 dataPtr = GetTreeInterpData(interp);
541 if (nsPtr != NULL) {
542 treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
543 } else {
544 if (flags & NS_SEARCH_CURRENT) {
545 /* Look first in the current namespace. */
546 nsPtr = Tcl_GetCurrentNamespace(interp);
547 treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
548 }
549 if ((treeObjPtr == NULL) && (flags & NS_SEARCH_GLOBAL)) {
550 nsPtr = Tcl_GetGlobalNamespace(interp);
551 treeObjPtr = FindTreeInNamespace(dataPtr, nsPtr, treeName);
552 }
553 }
554 return treeObjPtr;
555 }
556
557 /*
558 * ----------------------------------------------------------------------
559 *
560 * TeardownTree --
561 *
562 * Destroys an entire branch. This is a special purpose routine
563 * used to speed up the final clean up of the tree.
564 *
565 * Results:
566 * None.
567 *
568 * ----------------------------------------------------------------------
569 */
570 static void
TeardownTree(TreeObject * treeObjPtr,Node * nodePtr)571 TeardownTree(TreeObject *treeObjPtr, Node *nodePtr)
572 {
573 if (nodePtr->first != NULL) {
574 Node *childPtr, *nextPtr;
575
576 for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
577 nextPtr = childPtr->next;
578 TeardownTree(treeObjPtr, childPtr);
579 }
580 }
581 if (nodePtr->values != NULL) {
582 TreeDestroyValues(nodePtr);
583 }
584 Blt_PoolFreeItem(treeObjPtr->nodePool, (char *)nodePtr);
585 }
586
587 static void
DestroyTreeObject(char * treeObj)588 DestroyTreeObject(char *treeObj)
589 {
590 Blt_ChainLink *linkPtr;
591 TreeClient *clientPtr;
592 TreeObject *treeObjPtr = (TreeObject*)treeObj;
593
594 if (treeObjPtr->flags & TREE_DESTROYED) return;
595 treeObjPtr->flags |= TREE_DESTROYED;
596 treeObjPtr->nNodes = 0;
597
598 /* Remove the remaining clients. */
599 for (linkPtr = Blt_ChainFirstLink(treeObjPtr->clients); linkPtr != NULL;
600 linkPtr = Blt_ChainNextLink(linkPtr)) {
601 clientPtr = Blt_ChainGetValue(linkPtr);
602 Blt_ChainDestroy(clientPtr->events);
603 Blt_ChainDestroy(clientPtr->traces);
604 Blt_Free(clientPtr);
605 }
606 Blt_ChainDestroy(treeObjPtr->clients);
607
608 TeardownTree(treeObjPtr, treeObjPtr->root);
609 Blt_PoolDestroy(treeObjPtr->nodePool);
610 Blt_PoolDestroy(treeObjPtr->valuePool);
611 Blt_DeleteHashTable(&treeObjPtr->nodeTable);
612 Blt_DeleteHashTable(&treeObjPtr->keyTable);
613
614 if (treeObjPtr->hashPtr != NULL) {
615 /* Remove the entry from the global tree table. */
616 Blt_DeleteHashEntry(treeObjPtr->tablePtr, treeObjPtr->hashPtr);
617 if ((treeObjPtr->tablePtr->numEntries == 0) && (keyTableInitialized)) {
618 keyTableInitialized = FALSE;
619 Blt_DeleteHashTable(&keyTable);
620 }
621 }
622 if (treeObjPtr->name != NULL) {
623 Blt_Free(treeObjPtr->name);
624 }
625 Blt_Free(treeObjPtr);
626 }
627
628 /*
629 * -----------------------------------------------------------------------
630 *
631 * TreeInterpDeleteProc --
632 *
633 * This is called when the interpreter hosting the tree object
634 * is deleted from the interpreter.
635 *
636 * Results:
637 * None.
638 *
639 * Side effects:
640 * Destroys all remaining trees and removes the hash table
641 * used to register tree names.
642 *
643 * ------------------------------------------------------------------------
644 */
645 /* ARGSUSED */
646 static void
TreeInterpDeleteProc(ClientData clientData,Tcl_Interp * interp)647 TreeInterpDeleteProc(
648 ClientData clientData, /* Interpreter-specific data. */
649 Tcl_Interp *interp)
650 {
651 TreeInterpData *dataPtr = clientData;
652 Blt_HashEntry *hPtr;
653 Blt_HashSearch cursor;
654 TreeObject *treeObjPtr;
655
656 for (hPtr = Blt_FirstHashEntry(&dataPtr->treeTable, &cursor);
657 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
658 treeObjPtr = (TreeObject *)Blt_GetHashValue(hPtr);
659 treeObjPtr->hashPtr = NULL;
660 treeObjPtr->delete = 1;
661 Tcl_EventuallyFree(treeObjPtr, DestroyTreeObject);
662 /*DestroyTreeObject(treeObjPtr); */
663 }
664 if (keyTableInitialized) {
665 keyTableInitialized = FALSE;
666 Blt_DeleteHashTable(&keyTable);
667 }
668 Blt_DeleteHashTable(&dataPtr->treeTable);
669 Blt_DeleteHashTable(&dataPtr->keyTable);
670 Tcl_DeleteAssocData(interp, TREE_THREAD_KEY);
671 Blt_Free(dataPtr);
672 }
673
674 /*
675 *----------------------------------------------------------------------
676 *
677 * NotifyIdleProc --
678 *
679 * Used to invoke event handler routines at some idle point.
680 * This routine is called from the Tcl event loop. Errors
681 * generated by the event handler routines are backgrounded.
682 *
683 * Results:
684 * None.
685 *
686 *----------------------------------------------------------------------
687 */
688 static void
NotifyIdleProc(ClientData clientData)689 NotifyIdleProc(ClientData clientData)
690 {
691 EventHandler *notifyPtr = clientData;
692 int result;
693
694 notifyPtr->notifyPending = FALSE;
695 notifyPtr->mask |= TREE_NOTIFY_ACTIVE;
696 result = (*notifyPtr->proc)(notifyPtr->clientData, ¬ifyPtr->event);
697 notifyPtr->mask &= ~TREE_NOTIFY_ACTIVE;
698 if (result != TCL_OK) {
699 Tcl_BackgroundError(notifyPtr->interp);
700 }
701 }
702
703 /*
704 *----------------------------------------------------------------------
705 *
706 * CheckEventHandlers --
707 *
708 * Traverses the list of client event callbacks and checks
709 * if one matches the given event. A client may trigger an
710 * action that causes the tree to notify it. The can be
711 * prevented by setting the TREE_NOTIFY_FOREIGN_ONLY bit in
712 * the event handler.
713 *
714 * If a matching handler is found, a callback may be called either
715 * immediately or at the next idle time depending upon the
716 * TREE_NOTIFY_WHENIDLE bit.
717 *
718 * Since a handler routine may trigger yet another call to
719 * itself, callbacks are ignored while the event handler is
720 * executing.
721 *
722 * Results:
723 * None.
724 *
725 *----------------------------------------------------------------------
726 */
727 static int
CheckEventHandlers(TreeClient * clientPtr,int isSource,Blt_TreeNotifyEvent * eventPtr)728 CheckEventHandlers(
729 TreeClient *clientPtr,
730 int isSource, /* Indicates if the client is the source
731 * of the event. */
732 Blt_TreeNotifyEvent *eventPtr)
733 {
734 Blt_ChainLink *linkPtr, *nextPtr;
735 EventHandler *notifyPtr;
736
737 eventPtr->tree = clientPtr;
738 for (linkPtr = Blt_ChainFirstLink(clientPtr->events);
739 linkPtr != NULL; linkPtr = nextPtr) {
740 nextPtr = Blt_ChainNextLink(linkPtr);
741 notifyPtr = Blt_ChainGetValue(linkPtr);
742 if ((notifyPtr->mask & TREE_NOTIFY_ACTIVE) ||
743 (notifyPtr->mask & eventPtr->type) == 0) {
744 continue; /* Ignore callbacks that are generated
745 * inside of a notify handler routine. */
746 }
747 if ((isSource) && (notifyPtr->mask & TREE_NOTIFY_FOREIGN_ONLY)) {
748 continue; /* Don't notify yourself. */
749 }
750 if (notifyPtr->mask & TREE_NOTIFY_WHENIDLE) {
751 if (!notifyPtr->notifyPending) {
752 notifyPtr->notifyPending = TRUE;
753 notifyPtr->event = *eventPtr;
754 Tcl_DoWhenIdle(NotifyIdleProc, notifyPtr);
755 }
756 } else {
757 int result;
758
759 notifyPtr->mask |= TREE_NOTIFY_ACTIVE;
760 result = (*notifyPtr->proc) (notifyPtr->clientData, eventPtr);
761 notifyPtr->mask &= ~TREE_NOTIFY_ACTIVE;
762 if (result != TCL_OK) {
763 if (notifyPtr->mask & TREE_NOTIFY_BGERROR) {
764 Tcl_BackgroundError(notifyPtr->interp);
765 }
766 return result;
767 }
768 }
769 }
770 return TCL_OK;
771 }
772
773 /*
774 *----------------------------------------------------------------------
775 *
776 * NotifyClients --
777 *
778 * Traverses the list of clients for a particular tree and
779 * notifies each client that an event occurred. Clients
780 * indicate interest in a particular event through a bit
781 * flag.
782 *
783 *----------------------------------------------------------------------
784 */
785 static int
NotifyClients(TreeClient * sourcePtr,TreeObject * treeObjPtr,Node * nodePtr,int eventFlag)786 NotifyClients(
787 TreeClient *sourcePtr,
788 TreeObject *treeObjPtr,
789 Node *nodePtr,
790 int eventFlag)
791 {
792 Blt_ChainLink *linkPtr;
793 Blt_TreeNotifyEvent event;
794 TreeClient *clientPtr;
795 int isSource, result;
796
797 if (Tcl_InterpDeleted(treeObjPtr->interp) ||
798 Tcl_InterpDeleted(sourcePtr->root->treeObject->interp)) {
799 return TCL_OK;
800 }
801 event.type = eventFlag;
802 event.inode = nodePtr->inode;
803
804 /*
805 * Issue callbacks to each client indicating that a new node has
806 * been created.
807 */
808 for (linkPtr = Blt_ChainFirstLink(treeObjPtr->clients);
809 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
810 clientPtr = Blt_ChainGetValue(linkPtr);
811 isSource = (clientPtr == sourcePtr);
812 result = CheckEventHandlers(clientPtr, isSource, &event);
813 if (result != TCL_OK) {
814 return TCL_ERROR;
815 }
816 if (Blt_TreeNodeDeleted(nodePtr) || nodePtr->inode != event.inode) {
817 return TCL_BREAK;
818 }
819 }
820 return TCL_OK;
821 }
822
823 static void
FreeValue(Node * nodePtr,Value * valuePtr)824 FreeValue(Node *nodePtr, Value *valuePtr)
825 {
826 if (valuePtr->objPtr != NULL) {
827 Tcl_DecrRefCount(valuePtr->objPtr);
828 }
829 Blt_PoolFreeItem(nodePtr->treeObject->valuePool, valuePtr);
830 }
831
832
833 /* Public Routines */
834 /*
835 *----------------------------------------------------------------------
836 *
837 * Blt_TreeGetKey --
838 *
839 * Given a string, returns a unique identifier for the string.
840 *
841 *----------------------------------------------------------------------
842 */
843 Blt_TreeKey
Blt_TreeGetKey(string)844 Blt_TreeGetKey(string)
845 CONST char *string; /* String to convert. */
846 {
847 Blt_HashEntry *hPtr;
848 int isNew;
849
850 if (!keyTableInitialized) {
851 Blt_InitHashTable(&keyTable, BLT_STRING_KEYS);
852 keyTableInitialized = 1;
853 }
854 hPtr = Blt_CreateHashEntry(&keyTable, string, &isNew);
855 return (Blt_TreeKey)Blt_GetHashKey(&keyTable, hPtr);
856 }
857
858 /*
859 *----------------------------------------------------------------------
860 *
861 * Blt_TreeKeyGet --
862 *
863 * tree-unique keys.
864 * TODO: use per-interp keyTable rather than global one.
865 *
866 *----------------------------------------------------------------------
867 */
868 Blt_TreeKey
Blt_TreeKeyGet(interp,treeObjPtr,string)869 Blt_TreeKeyGet(interp, treeObjPtr, string)
870 Tcl_Interp *interp;
871 TreeObject *treeObjPtr;
872 CONST char *string; /* String to convert. */
873 {
874 Blt_HashTable *tablePtr;
875 Blt_HashEntry *hPtr;
876 int isNew;
877
878 tablePtr = NULL;
879 if (treeObjPtr != NULL && treeObjPtr->interpKeyPtr != NULL) {
880 tablePtr = treeObjPtr->interpKeyPtr;
881 }
882 if (tablePtr == NULL && interp != NULL && bltTreeUseLocalKeys != 0) {
883 TreeInterpData *dataPtr;
884 dataPtr = GetTreeInterpData(interp);
885 tablePtr = &dataPtr->keyTable;
886 }
887 if (tablePtr == NULL) {
888 return Blt_TreeGetKey(string);
889 }
890 hPtr = Blt_CreateHashEntry(tablePtr, string, &isNew);
891 return (Blt_TreeKey)Blt_GetHashKey(tablePtr, hPtr);
892 }
893
894 /* Clear the unmodified flag for node. */
SetModified(Node * nodePtr)895 static void SetModified(Node *nodePtr)
896 {
897 nodePtr->flags &= ~TREE_NODE_UNMODIFIED;
898 nodePtr->treeObject->flags &= ~TREE_UNMODIFIED;
899 }
900
901 /*
902 *----------------------------------------------------------------------
903 *
904 * Blt_TreeCreateNode --
905 *
906 * Creates a new node in the given parent node. The name and
907 * position in the parent are also provided.
908 *
909 *----------------------------------------------------------------------
910 */
911 Blt_TreeNode
Blt_TreeCreateNode(TreeClient * clientPtr,Node * parentPtr,CONST char * name,int pos)912 Blt_TreeCreateNode(
913 TreeClient *clientPtr, /* The tree client that is creating
914 * this node. If NULL, indicates to
915 * trigger notify events on behalf of
916 * the initiating client also. */
917 Node *parentPtr, /* Parent node where the new node will
918 * be inserted. */
919 CONST char *name, /* Name of node. */
920 int pos) /* Position in the parent's list of children
921 * where to insert the new node. */
922 {
923 Blt_HashEntry *hPtr;
924 Node *beforePtr;
925 Node *nodePtr; /* Node to be inserted. */
926 TreeObject *treeObjPtr;
927 int inode;
928 int isNew;
929
930 treeObjPtr = parentPtr->treeObject;
931
932 /* Generate an unique serial number for this node. */
933 do {
934 inode = treeObjPtr->nextInode++;
935 hPtr = Blt_CreateHashEntry(&treeObjPtr->nodeTable,(char *)inode,
936 &isNew);
937 } while (!isNew);
938 nodePtr = NewNode(treeObjPtr, name, inode);
939 Blt_SetHashValue(hPtr, nodePtr);
940
941 if ((pos == -1) || (pos >= (int)parentPtr->nChildren)) {
942 beforePtr = NULL;
943 } else {
944 beforePtr = parentPtr->first;
945 while ((pos > 0) && (beforePtr != NULL)) {
946 pos--;
947 beforePtr = beforePtr->next;
948 }
949 }
950 LinkBefore(parentPtr, nodePtr, beforePtr);
951 nodePtr->depth = parentPtr->depth + 1;
952 /*
953 * Issue callbacks to each client indicating that a new node has
954 * been created.
955 */
956 if (NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_CREATE) != TCL_OK) {
957 nodePtr->flags |= TREE_NODE_INSERT_FAIL;
958 Blt_TreeDeleteNode(clientPtr, nodePtr);
959 return NULL;
960 }
961 treeObjPtr->flags &= ~TREE_UNMODIFIED;
962 return nodePtr;
963 }
964 /*
965 *----------------------------------------------------------------------
966 *
967 * Blt_TreeInsertPost --
968 *
969 * Trigger notify for -insert
970 *
971 *----------------------------------------------------------------------
972 */
973 Blt_TreeNode
Blt_TreeInsertPost(TreeClient * clientPtr,Node * nodePtr)974 Blt_TreeInsertPost(
975 TreeClient *clientPtr, /* The tree client that is creating
976 * this node. If NULL, indicates to
977 * trigger notify events on behalf of
978 * the initiating client also. */
979 Node *nodePtr) /* node */
980 {
981 if (NotifyClients(clientPtr, nodePtr->treeObject, nodePtr, TREE_NOTIFY_INSERT) != TCL_OK) {
982 nodePtr->flags |= TREE_NODE_INSERT_FAIL;
983 Blt_TreeDeleteNode(clientPtr, nodePtr);
984 return NULL;
985 }
986 return nodePtr;
987 }
988
989 int
Blt_TreeNotifyGet(TreeClient * clientPtr,Node * nodePtr)990 Blt_TreeNotifyGet(
991 TreeClient *clientPtr, /* The tree client that is creating
992 * this node. If NULL, indicates to
993 * trigger notify events on behalf of
994 * the initiating client also. */
995 Node *nodePtr) /* node */
996 {
997 if (nodePtr->nValues != 0) return TCL_OK;
998 return NotifyClients(clientPtr, nodePtr->treeObject, nodePtr, TREE_NOTIFY_GET);
999 }
1000
1001 /*
1002 *----------------------------------------------------------------------
1003 *
1004 * Blt_TreeCreateNodeWithId --
1005 *
1006 * Like Blt_TreeCreateNode, but provides a specific id to use
1007 * for the node. If the tree already contains a node by that
1008 * id, NULL is returned.
1009 *
1010 *----------------------------------------------------------------------
1011 */
1012 Blt_TreeNode
Blt_TreeCreateNodeWithId(TreeClient * clientPtr,Node * parentPtr,CONST char * name,unsigned int inode,int position)1013 Blt_TreeCreateNodeWithId(
1014 TreeClient *clientPtr,
1015 Node *parentPtr, /* Parent node where the new node will
1016 * be inserted. */
1017 CONST char *name, /* Name of node. */
1018 unsigned int inode,/* Requested id of the new node. If a
1019 * node by this id already exists in the
1020 * tree, no node is created. */
1021 int position) /* Position in the parent's list of children
1022 * where to insert the new node. */
1023 {
1024 Blt_HashEntry *hPtr;
1025 Node *beforePtr;
1026 Node *nodePtr; /* Node to be inserted. */
1027 TreeObject *treeObjPtr;
1028 int isNew, result;
1029
1030 treeObjPtr = parentPtr->treeObject;
1031 hPtr = Blt_CreateHashEntry(&treeObjPtr->nodeTable,(char *)inode, &isNew);
1032 if (!isNew) {
1033 return NULL;
1034 }
1035 nodePtr = NewNode(treeObjPtr, name, inode);
1036 Blt_SetHashValue(hPtr, nodePtr);
1037
1038 if ((position == -1) || (position >= (int)parentPtr->nChildren)) {
1039 beforePtr = NULL;
1040 } else {
1041 beforePtr = parentPtr->first;
1042 while ((position > 0) && (beforePtr != NULL)) {
1043 position--;
1044 beforePtr = beforePtr->next;
1045 }
1046 }
1047 LinkBefore(parentPtr, nodePtr, beforePtr);
1048 nodePtr->depth = parentPtr->depth + 1;
1049 /*
1050 * Issue callbacks to each client indicating that a new node has
1051 * been created.
1052 */
1053 result=NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_CREATE);
1054 if (result != TCL_OK) {
1055 if (result != TCL_BREAK) {
1056 nodePtr->flags |= TREE_NODE_INSERT_FAIL;
1057 Blt_TreeDeleteNode(clientPtr, nodePtr);
1058 }
1059 return NULL;
1060 }
1061 treeObjPtr->flags &= ~TREE_UNMODIFIED;
1062 return nodePtr;
1063 }
1064
1065 /*
1066 *----------------------------------------------------------------------
1067 *
1068 * Blt_TreeMoveNode --
1069 *
1070 * Move an entry into a new location in the hierarchy.
1071 *
1072 *----------------------------------------------------------------------
1073 */
1074 /*ARGSUSED*/
1075 int
Blt_TreeMoveNode(TreeClient * clientPtr,Node * nodePtr,Node * parentPtr,Node * beforePtr)1076 Blt_TreeMoveNode(
1077 TreeClient *clientPtr,
1078 Node *nodePtr,
1079 Node *parentPtr,
1080 Node *beforePtr)
1081 {
1082 TreeObject *treeObjPtr = nodePtr->treeObject;
1083 int newDepth, result;
1084
1085 if (nodePtr == beforePtr) {
1086 return TCL_ERROR;
1087 }
1088 if ((beforePtr != NULL) && (beforePtr->parent != parentPtr)) {
1089 return TCL_ERROR;
1090 }
1091 if (nodePtr->parent == NULL) {
1092 return TCL_ERROR; /* Can't move root. */
1093 }
1094 /* Verify that the node isn't an ancestor of the new parent. */
1095 if (Blt_TreeIsAncestor(nodePtr, parentPtr)) {
1096 return TCL_ERROR;
1097 }
1098 /*
1099 * Issue callbacks to each client indicating that a node has
1100 * been moved.
1101 */
1102 if (NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_MOVE) != TCL_OK) {
1103 return TCL_ERROR;
1104 }
1105
1106 UnlinkNode(nodePtr);
1107 /*
1108 * Relink the node as a child of the new parent.
1109 */
1110 LinkBefore(parentPtr, nodePtr, beforePtr);
1111 newDepth = parentPtr->depth + 1;
1112 if (nodePtr->depth != newDepth) {
1113 /* Reset the depths of all descendant nodes. */
1114 ResetDepths(nodePtr, newDepth);
1115 }
1116 /*
1117 * Issue callbacks to each client indicating that a node has
1118 * been moved.
1119 */
1120 if ((result=NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_MOVEPOST)) != TCL_OK) {
1121 return result;
1122 }
1123
1124 return TCL_OK;
1125 }
1126
1127 int
Blt_TreeDeleteNode(TreeClient * clientPtr,Node * nodePtr)1128 Blt_TreeDeleteNode(TreeClient *clientPtr, Node *nodePtr)
1129 {
1130 TreeObject *treeObjPtr = nodePtr->treeObject;
1131 Node *childPtr, *nextPtr;
1132 int result;
1133
1134 /*
1135 * Issue callbacks to each client indicating that the node can
1136 * no longer be used.
1137 */
1138 if (Blt_TreeNodeDeleted(nodePtr)) {
1139 return TCL_OK;
1140 }
1141 if ((nodePtr->flags & TREE_NODE_INSERT_FAIL) == 0 &&
1142 (result=NotifyClients(clientPtr, treeObjPtr, nodePtr, TREE_NOTIFY_DELETE)) != TCL_OK) {
1143 return result;
1144 }
1145 nodePtr->flags &= ~TREE_NODE_FIXED_FIELDS;
1146
1147 /* In depth-first order, delete each descendant node. */
1148 for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
1149 nextPtr = childPtr->next;
1150 Blt_TreeDeleteNode(clientPtr, childPtr);
1151 }
1152
1153 /* Now remove the actual node. */
1154 FreeNode(treeObjPtr, nodePtr);
1155 if (treeObjPtr->nodeTable.numEntries <= 1) {
1156 treeObjPtr->nextInode = 1;
1157 }
1158 return TCL_OK;
1159 }
1160
1161 Blt_TreeNode
Blt_TreeGetNode(TreeClient * clientPtr,unsigned int inode)1162 Blt_TreeGetNode(TreeClient *clientPtr, unsigned int inode)
1163 {
1164 TreeObject *treeObjPtr = clientPtr->treeObject;
1165 Blt_HashEntry *hPtr;
1166
1167 hPtr = Blt_FindHashEntry(&treeObjPtr->nodeTable, (char *)inode);
1168 if (hPtr != NULL) {
1169 return (Blt_TreeNode)Blt_GetHashValue(hPtr);
1170 }
1171 return NULL;
1172 }
1173
1174 /*
1175 static Node*
1176 GetNode(TreeObject *treeObjPtr, unsigned int inode)
1177 {
1178 Blt_HashEntry *hPtr;
1179
1180 hPtr = Blt_FindHashEntry(&treeObjPtr->nodeTable, (char *)inode);
1181 if (hPtr != NULL) {
1182 return (Node*)Blt_GetHashValue(hPtr);
1183 }
1184 return NULL;
1185 }
1186 */
1187
1188 Blt_TreeTrace
Blt_TreeCreateTrace(TreeClient * clientPtr,Node * nodePtr,CONST char * keyPattern,CONST char * tagName,unsigned int mask,Blt_TreeTraceProc * proc,ClientData clientData)1189 Blt_TreeCreateTrace(
1190 TreeClient *clientPtr,
1191 Node *nodePtr,
1192 CONST char *keyPattern,
1193 CONST char *tagName,
1194 unsigned int mask,
1195 Blt_TreeTraceProc *proc,
1196 ClientData clientData)
1197 {
1198 TraceHandler *tracePtr;
1199
1200 tracePtr = Blt_Calloc(1, sizeof (TraceHandler));
1201 assert(tracePtr);
1202 tracePtr->linkPtr = Blt_ChainAppend(clientPtr->traces, tracePtr);
1203 if (keyPattern != NULL) {
1204 tracePtr->keyPattern = Blt_Strdup(keyPattern);
1205 }
1206 if (tagName != NULL) {
1207 tracePtr->withTag = Blt_Strdup(tagName);
1208 }
1209 tracePtr->clientPtr = clientPtr;
1210 tracePtr->proc = proc;
1211 tracePtr->clientData = clientData;
1212 tracePtr->mask = mask;
1213 tracePtr->nodePtr = nodePtr;
1214 return (Blt_TreeTrace)tracePtr;
1215 }
1216
1217 void
Blt_TreeDeleteTrace(Blt_TreeTrace trace)1218 Blt_TreeDeleteTrace(Blt_TreeTrace trace)
1219 {
1220 TraceHandler *tracePtr = (TraceHandler *)trace;
1221
1222 Blt_ChainDeleteLink(tracePtr->clientPtr->traces, tracePtr->linkPtr);
1223 if (tracePtr->keyPattern != NULL) {
1224 Blt_Free(tracePtr->keyPattern);
1225 }
1226 if (tracePtr->withTag != NULL) {
1227 Blt_Free(tracePtr->withTag);
1228 }
1229 Blt_Free(tracePtr);
1230 }
1231
1232 int
Blt_TreeRelabelNode(TreeClient * clientPtr,Node * nodePtr,CONST char * string)1233 Blt_TreeRelabelNode(TreeClient *clientPtr, Node *nodePtr, CONST char *string)
1234 {
1235 int result, inode;
1236 if ((result=NotifyClients(clientPtr, clientPtr->treeObject, nodePtr,
1237 TREE_NOTIFY_RELABEL)) != TCL_OK) {
1238 return result;
1239 }
1240 nodePtr->label = Blt_TreeKeyGet(NULL, clientPtr->treeObject,string);
1241 /*
1242 * Issue callbacks to each client indicating that a new node has
1243 * been created.
1244 */
1245 SetModified(nodePtr);
1246 inode = nodePtr->inode;
1247 result = NotifyClients(clientPtr, clientPtr->treeObject, nodePtr,
1248 TREE_NOTIFY_RELABELPOST);
1249 return result;
1250 }
1251
1252 int
Blt_TreeNotifyAttach(TreeClient * clientPtr)1253 Blt_TreeNotifyAttach(TreeClient *clientPtr)
1254 {
1255 /* return NotifyClients(clientPtr, clientPtr->treeObject, clientPtr->root,
1256 TREE_NOTIFY_ATTACH); */
1257 return TCL_OK;
1258 }
1259
1260
1261 int
Blt_TreeRelabelNode2(Node * nodePtr,CONST char * string)1262 Blt_TreeRelabelNode2(Node *nodePtr, CONST char *string)
1263 {
1264 nodePtr->label = Blt_TreeKeyGet(NULL, nodePtr->treeObject,string);
1265 SetModified(nodePtr);
1266 return TCL_OK;
1267 }
1268
1269 /*
1270 *----------------------------------------------------------------------
1271 *
1272 * Blt_TreeFindChild --
1273 *
1274 * Searches for the named node in a parent's chain of siblings.
1275 *
1276 *
1277 * Results:
1278 * If found, the child node is returned, otherwise NULL.
1279 *
1280 *----------------------------------------------------------------------
1281 */
1282 Blt_TreeNode
Blt_TreeFindChild(Node * parentPtr,CONST char * string)1283 Blt_TreeFindChild(Node *parentPtr, CONST char *string)
1284 {
1285 Blt_TreeKey label;
1286 register Node *nodePtr;
1287
1288 label = Blt_TreeKeyGet(NULL, parentPtr->treeObject,string);
1289 for (nodePtr = parentPtr->first; nodePtr != NULL; nodePtr = nodePtr->next) {
1290 if (label == nodePtr->label) {
1291 return nodePtr;
1292 }
1293 }
1294 return NULL;
1295 }
1296 /*
1297 * Like Blt_TreeFindChild above, but looks forward firstN elements
1298 * from front, then falls back to reverse search starting from the end.
1299 * This speeds up insertion at the end of really long trees in treeview.
1300 * If firstN is < 0, just calls Blt_TreeFindChild.
1301 */
1302 Blt_TreeNode
Blt_TreeFindChildRev(Node * parentPtr,CONST char * string,int firstN)1303 Blt_TreeFindChildRev(Node *parentPtr, CONST char *string, int firstN)
1304 {
1305 Blt_TreeKey label;
1306 register Node *nodePtr, *endNode;
1307 int n;
1308
1309 if (firstN<0) {
1310 return Blt_TreeFindChild(parentPtr, string);
1311 }
1312 label = Blt_TreeKeyGet(NULL, parentPtr->treeObject,string);
1313 for (nodePtr = parentPtr->first, n = 0;
1314 nodePtr != NULL && n < firstN;
1315 nodePtr = nodePtr->next, n++) {
1316 if (label == nodePtr->label) {
1317 return nodePtr;
1318 }
1319 }
1320 if (nodePtr == NULL) {
1321 return NULL;
1322 }
1323 endNode = nodePtr;
1324 for (nodePtr = parentPtr->last; nodePtr != NULL; nodePtr = nodePtr->prev) {
1325 if (label == nodePtr->label) {
1326 return nodePtr;
1327 }
1328 if (nodePtr == endNode) break;
1329 }
1330 return NULL;
1331 }
1332
1333 /*
1334 *----------------------------------------------------------------------
1335 *
1336 * Blt_TreeNodePosition --
1337 *
1338 * Returns the position of the node in its parent's list of
1339 * children. The root's position is 0.
1340 *
1341 *----------------------------------------------------------------------
1342 */
1343 int
Blt_TreeNodePosition(Node * nodePtr)1344 Blt_TreeNodePosition(Node *nodePtr)
1345 {
1346 Node *parentPtr;
1347 int count;
1348
1349 count = 0;
1350 parentPtr = nodePtr->parent;
1351 if (parentPtr != NULL) {
1352 Node *childPtr;
1353
1354 for (childPtr = parentPtr->first; childPtr != NULL;
1355 childPtr = childPtr->next) {
1356 if (nodePtr == childPtr) {
1357 break;
1358 }
1359 count++;
1360 }
1361 }
1362 return count;
1363 }
1364
1365
1366 /*
1367 *----------------------------------------------------------------------
1368 *
1369 * Blt_TreePrevNode --
1370 *
1371 * Returns the "previous" node in the tree. This node (in
1372 * depth-first order) is its parent, if the node has no siblings
1373 * that are previous to it. Otherwise it is the last descendant
1374 * of the last sibling. In this case, descend the sibling's
1375 * hierarchy, using the last child at any ancestor, with we
1376 * we find a leaf.
1377 *
1378 *----------------------------------------------------------------------
1379 */
1380 Blt_TreeNode
Blt_TreePrevNode(Node * rootPtr,Node * nodePtr)1381 Blt_TreePrevNode(Node *rootPtr, Node *nodePtr)
1382 {
1383 Node *prevPtr;
1384
1385 if (nodePtr == rootPtr) {
1386 return NULL; /* The root is the first node. */
1387 }
1388 prevPtr = nodePtr->prev;
1389 if (prevPtr == NULL) {
1390 /* There are no siblings previous to this one, so pick the parent. */
1391 return nodePtr->parent;
1392 }
1393 /*
1394 * Traverse down the right-most thread, in order to select the
1395 * next entry. Stop when we reach a leaf.
1396 */
1397 nodePtr = prevPtr;
1398 while ((prevPtr = nodePtr->last) != NULL) {
1399 nodePtr = prevPtr;
1400 }
1401 return nodePtr;
1402 }
1403
1404 /*
1405 *----------------------------------------------------------------------
1406 *
1407 * Blt_TreeNextNode --
1408 *
1409 * Returns the "next" node in relation to the given node.
1410 * The next node (in depth-first order) is either the first
1411 * child of the given node the next sibling if the node has
1412 * no children (the node is a leaf). If the given node is the
1413 * last sibling, then try it's parent next sibling. Continue
1414 * until we either find a next sibling for some ancestor or
1415 * we reach the root node. In this case the current node is
1416 * the last node in the tree.
1417 *
1418 *----------------------------------------------------------------------
1419 */
1420 Blt_TreeNode
Blt_TreeNextNode(Node * rootPtr,Node * nodePtr)1421 Blt_TreeNextNode(Node *rootPtr, Node *nodePtr)
1422 {
1423 Node *nextPtr;
1424
1425 /* Pick the first sub-node. */
1426 nextPtr = nodePtr->first;
1427 if (nextPtr != NULL) {
1428 return nextPtr;
1429 }
1430 /*
1431 * Back up until we can find a level where we can pick a
1432 * "next sibling". For the last entry we'll thread our
1433 * way back to the root.
1434 */
1435 while (nodePtr != rootPtr) {
1436 nextPtr = nodePtr->next;
1437 if (nextPtr != NULL) {
1438 return nextPtr;
1439 }
1440 nodePtr = nodePtr->parent;
1441 }
1442 return NULL; /* At root, no next node. */
1443 }
1444
1445
1446 int
Blt_TreeIsBefore(Node * n1Ptr,Node * n2Ptr)1447 Blt_TreeIsBefore(Node *n1Ptr, Node *n2Ptr)
1448 {
1449 int depth;
1450 register int i;
1451 Node *nodePtr;
1452
1453 if (n1Ptr == n2Ptr) {
1454 return FALSE;
1455 }
1456 depth = MIN(n1Ptr->depth, n2Ptr->depth);
1457 if (depth == 0) { /* One of the nodes is root. */
1458 return (n1Ptr->parent == NULL);
1459 }
1460 /*
1461 * Traverse back from the deepest node, until both nodes are at
1462 * the same depth. Check if this ancestor node is the same for
1463 * both nodes.
1464 */
1465 for (i = n1Ptr->depth; i > depth; i--) {
1466 n1Ptr = n1Ptr->parent;
1467 }
1468 if (n1Ptr == n2Ptr) {
1469 return FALSE;
1470 }
1471 for (i = n2Ptr->depth; i > depth; i--) {
1472 n2Ptr = n2Ptr->parent;
1473 }
1474 if (n2Ptr == n1Ptr) {
1475 return TRUE;
1476 }
1477
1478 /*
1479 * First find the mutual ancestor of both nodes. Look at each
1480 * preceding ancestor level-by-level for both nodes. Eventually
1481 * we'll find a node that's the parent of both ancestors. Then
1482 * find the first ancestor in the parent's list of subnodes.
1483 */
1484 for (i = depth; i > 0; i--) {
1485 if (n1Ptr->parent == n2Ptr->parent) {
1486 break;
1487 }
1488 n1Ptr = n1Ptr->parent;
1489 n2Ptr = n2Ptr->parent;
1490 }
1491 for (nodePtr = n1Ptr->parent->first; nodePtr != NULL;
1492 nodePtr = nodePtr->next) {
1493 if (nodePtr == n1Ptr) {
1494 return TRUE;
1495 } else if (nodePtr == n2Ptr) {
1496 return FALSE;
1497 }
1498 }
1499 return FALSE;
1500 }
1501
1502 static int
CallTraces(Tcl_Interp * interp,TreeClient * sourcePtr,TreeObject * treeObjPtr,Node * nodePtr,Blt_TreeKey key,unsigned int flags,int * cnt)1503 CallTraces(
1504 Tcl_Interp *interp,
1505 TreeClient *sourcePtr, /* Client holding a reference to the
1506 * tree. If NULL, indicates to
1507 * execute all handlers, including
1508 * those of the caller. */
1509 TreeObject *treeObjPtr, /* Tree that was changed. */
1510 Node *nodePtr, /* Node that received the event. */
1511 Blt_TreeKey key,
1512 unsigned int flags,
1513 int *cnt)
1514 {
1515 Blt_ChainLink *l1Ptr, *l2Ptr;
1516 TreeClient *clientPtr;
1517 TraceHandler *tracePtr;
1518 unsigned int inode = nodePtr->inode;
1519 int result;
1520
1521 for(l1Ptr = Blt_ChainFirstLink(treeObjPtr->clients);
1522 l1Ptr != NULL; l1Ptr = Blt_ChainNextLink(l1Ptr)) {
1523 clientPtr = Blt_ChainGetValue(l1Ptr);
1524 for(l2Ptr = Blt_ChainFirstLink(clientPtr->traces);
1525 l2Ptr != NULL; l2Ptr = Blt_ChainNextLink(l2Ptr)) {
1526 tracePtr = Blt_ChainGetValue(l2Ptr);
1527 if ((tracePtr->mask & flags) == 0) {
1528 continue; /* Flags don't match. */
1529 }
1530 if ((clientPtr == sourcePtr) &&
1531 (tracePtr->mask & TREE_TRACE_FOREIGN_ONLY)) {
1532 continue; /* This client initiated the trace. */
1533 }
1534 if ((tracePtr->nodePtr != NULL) && (tracePtr->nodePtr != nodePtr)) {
1535 continue; /* Nodes don't match. */
1536 }
1537 if ((tracePtr->keyPattern != NULL) &&
1538 (!Tcl_StringMatch(key, tracePtr->keyPattern))) {
1539 continue; /* Key pattern doesn't match. */
1540 }
1541 if ((tracePtr->withTag != NULL) &&
1542 (!Blt_TreeHasTag(clientPtr, nodePtr, tracePtr->withTag))) {
1543 continue; /* Doesn't have the tag. */
1544 }
1545 nodePtr->flags |= TREE_TRACE_ACTIVE;
1546 *cnt += 1;
1547
1548 Tcl_Preserve(treeObjPtr);
1549 result = (*tracePtr->proc) (tracePtr->clientData, treeObjPtr->interp,
1550 nodePtr, key, flags);
1551 if (result != TCL_OK) {
1552 Tcl_Release(treeObjPtr);
1553 if ((tracePtr->mask & TREE_TRACE_BGERROR) && interp != NULL) {
1554 Tcl_BackgroundError(interp);
1555 } else {
1556 nodePtr->flags &= ~TREE_TRACE_ACTIVE;
1557 return TCL_ERROR;
1558 }
1559 }
1560 nodePtr->flags &= ~TREE_TRACE_ACTIVE;
1561 if (Blt_TreeNodeDeleted(nodePtr) || nodePtr->inode != inode) {
1562 Tcl_Release(treeObjPtr);
1563 return TCL_ERROR;
1564 }
1565 if (treeObjPtr->delete) {
1566 Tcl_Release(treeObjPtr);
1567 if (interp != NULL) {
1568 Tcl_AppendResult(interp, "tree deleted", 0);
1569 }
1570 return TCL_ERROR;
1571 }
1572 Tcl_Release(treeObjPtr);
1573 /* nodePtr = GetNode(treeObjPtr, inode);
1574 if (nodePtr == NULL) {
1575 if (interp != NULL) {
1576 Tcl_AppendResult(interp, "node deleted", 0);
1577 }
1578 return TCL_ERROR;
1579 }*/
1580 }
1581 }
1582 return TCL_OK;
1583 }
1584
1585 static Value *
GetTreeValue(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,Blt_TreeKey key)1586 GetTreeValue(
1587 Tcl_Interp *interp,
1588 TreeClient *clientPtr,
1589 Node *nodePtr,
1590 Blt_TreeKey key)
1591 {
1592 register Value *valuePtr;
1593
1594 valuePtr = TreeFindValue(nodePtr, key);
1595 if (valuePtr == NULL) {
1596 if (interp != NULL) {
1597 Tcl_AppendResult(interp, "can't find field \"", key, "\"",
1598 (char *)NULL);
1599 }
1600 return NULL;
1601 }
1602 if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
1603 if (interp != NULL) {
1604 Tcl_AppendResult(interp, "can't access private field \"",
1605 key, "\"", (char *)NULL);
1606 }
1607 return NULL;
1608 }
1609 return valuePtr;
1610 }
1611
1612 int
Blt_TreePrivateValue(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,Blt_TreeKey key)1613 Blt_TreePrivateValue(
1614 Tcl_Interp *interp,
1615 TreeClient *clientPtr,
1616 Node *nodePtr,
1617 Blt_TreeKey key)
1618 {
1619 Value *valuePtr;
1620
1621 valuePtr = TreeFindValue(nodePtr, key);
1622 if (valuePtr == NULL) {
1623 if (interp != NULL) {
1624 Tcl_AppendResult(interp, "can't find field \"", key, "\"",
1625 (char *)NULL);
1626 }
1627 return TCL_ERROR;
1628 }
1629 valuePtr->owner = clientPtr;
1630 return TCL_OK;
1631 }
1632
1633 int
Blt_TreePublicValue(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,Blt_TreeKey key)1634 Blt_TreePublicValue(
1635 Tcl_Interp *interp,
1636 TreeClient *clientPtr,
1637 Node *nodePtr,
1638 Blt_TreeKey key)
1639 {
1640 Value *valuePtr;
1641
1642 valuePtr = TreeFindValue(nodePtr, key);
1643 if (valuePtr == NULL) {
1644 if (interp != NULL) {
1645 Tcl_AppendResult(interp, "can't find field \"", key, "\"",
1646 (char *)NULL);
1647 }
1648 return TCL_ERROR;
1649 }
1650 if (valuePtr->owner != clientPtr) {
1651 if (interp != NULL) {
1652 Tcl_AppendResult(interp, "not the owner of \"", key, "\"",
1653 (char *)NULL);
1654 }
1655 return TCL_ERROR;
1656 }
1657 valuePtr->owner = NULL;
1658 return TCL_OK;
1659 }
1660
1661 int
Blt_TreeValueExistsByKey(clientPtr,nodePtr,key)1662 Blt_TreeValueExistsByKey(clientPtr, nodePtr, key)
1663 TreeClient *clientPtr;
1664 Node *nodePtr;
1665 Blt_TreeKey key;
1666 {
1667 register Value *valuePtr;
1668 int cnt;
1669 TreeObject *treeObjPtr = nodePtr->treeObject;
1670 Tcl_Interp *interp = treeObjPtr->interp;
1671
1672 valuePtr = GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key);
1673 if (valuePtr == NULL && (!(nodePtr->flags & TREE_TRACE_ACTIVE))) {
1674 if (CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key,
1675 TREE_TRACE_EXISTS, &cnt) != TCL_OK) {
1676 Tcl_ResetResult(interp);
1677 } else {
1678 valuePtr = GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key);
1679 }
1680 }
1681 if (valuePtr == NULL) {
1682 return FALSE;
1683 }
1684 return TRUE;
1685 }
1686
1687 int
Blt_TreeGetValueByKey(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,Blt_TreeKey key,Tcl_Obj ** objPtrPtr)1688 Blt_TreeGetValueByKey(
1689 Tcl_Interp *interp,
1690 TreeClient *clientPtr,
1691 Node *nodePtr,
1692 Blt_TreeKey key,
1693 Tcl_Obj **objPtrPtr)
1694 {
1695 register Value *valuePtr;
1696 TreeObject *treeObjPtr = nodePtr->treeObject;
1697 int cnt = 0;
1698
1699 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
1700 if (CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key,
1701 TREE_TRACE_READ, &cnt) != TCL_OK) {
1702 return TCL_ERROR;
1703 }
1704 }
1705 valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
1706 if (valuePtr == NULL) {
1707 return TCL_ERROR;
1708 }
1709 *objPtrPtr = valuePtr->objPtr;
1710 return TCL_OK;
1711 }
1712
1713 int
bltTreeGetValueByKey(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,Blt_TreeKey key,Value ** valuePtrPtr)1714 bltTreeGetValueByKey(
1715 Tcl_Interp *interp,
1716 TreeClient *clientPtr,
1717 Node *nodePtr,
1718 Blt_TreeKey key,
1719 Value** valuePtrPtr)
1720 {
1721 register Value *valuePtr;
1722 TreeObject *treeObjPtr = nodePtr->treeObject;
1723 int cnt = 0;
1724
1725 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
1726 if (CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key,
1727 TREE_TRACE_READ, &cnt) != TCL_OK) {
1728 return TCL_ERROR;
1729 }
1730 }
1731 valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
1732 if (valuePtr == NULL) {
1733 return TCL_ERROR;
1734 }
1735 *valuePtrPtr = valuePtr;
1736 return TCL_OK;
1737 }
1738
1739 static void
UpdateOldValue(TreeClient * clientPtr,Tcl_Obj * objPtr)1740 UpdateOldValue(
1741 TreeClient *clientPtr,
1742 Tcl_Obj *objPtr)
1743 {
1744 if (clientPtr->oldValue != NULL) {
1745 Tcl_DecrRefCount(clientPtr->oldValue);
1746 }
1747 clientPtr->oldValue = objPtr;
1748 }
1749
1750 void
Blt_TreeOldValue(Tcl_Interp * interp,TreeClient * clientPtr,Tcl_Obj ** oldPtr,Tcl_Obj * newPtr)1751 Blt_TreeOldValue(
1752 Tcl_Interp *interp,
1753 TreeClient *clientPtr,
1754 Tcl_Obj **oldPtr,
1755 Tcl_Obj *newPtr)
1756 {
1757 if (newPtr) {
1758 if (clientPtr->oldValue != NULL) {
1759 Tcl_DecrRefCount(clientPtr->oldValue);
1760 }
1761 clientPtr->oldValue = newPtr;
1762 Tcl_IncrRefCount(clientPtr->oldValue);
1763 } else if (oldPtr != NULL) {
1764 *oldPtr = clientPtr->oldValue;
1765 }
1766 }
1767
1768 int
Blt_TreeSetValueByKey(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,Blt_TreeKey key,Tcl_Obj * objPtr)1769 Blt_TreeSetValueByKey(
1770 Tcl_Interp *interp,
1771 TreeClient *clientPtr,
1772 Node *nodePtr, /* Node to be updated. */
1773 Blt_TreeKey key, /* Identifies the field key. */
1774 Tcl_Obj *objPtr) /* New value of field. */
1775 {
1776 TreeObject *treeObjPtr;
1777 Value *valuePtr;
1778 unsigned int flags;
1779 int isNew = 0, cnt = 0;
1780
1781 if (nodePtr == NULL) {
1782 return TCL_ERROR;
1783 }
1784 treeObjPtr = nodePtr->treeObject;
1785 assert(objPtr != NULL);
1786 if (nodePtr->flags & TREE_NODE_FIXED_FIELDS) {
1787 valuePtr = TreeFindValue(nodePtr, key);
1788 if (valuePtr == NULL) {
1789 if (interp != NULL) {
1790 Tcl_AppendResult(interp, "fixed field \"", key, "\"",
1791 (char *)NULL);
1792 }
1793 return TCL_ERROR;
1794 }
1795 } else {
1796 valuePtr = TreeCreateValue(nodePtr, key, &isNew);
1797 }
1798 if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
1799 if (interp != NULL) {
1800 Tcl_AppendResult(interp, "can't set private field \"",
1801 key, "\"", (char *)NULL);
1802 }
1803 return TCL_ERROR;
1804 }
1805 SetModified(nodePtr);
1806 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
1807 UpdateOldValue(clientPtr, valuePtr->objPtr);
1808 valuePtr->objPtr = NULL;
1809 }
1810 if (objPtr != valuePtr->objPtr) {
1811 Tcl_IncrRefCount(objPtr);
1812 if (valuePtr->objPtr != NULL) {
1813 Tcl_DecrRefCount(valuePtr->objPtr);
1814 }
1815 valuePtr->objPtr = objPtr;
1816 }
1817 flags = TREE_TRACE_WRITE;
1818 if (isNew) {
1819 flags |= TREE_TRACE_CREATE;
1820 }
1821 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
1822 return CallTraces(interp, clientPtr, treeObjPtr, nodePtr, valuePtr->key,
1823 flags, &cnt);
1824 }
1825 return TCL_OK;
1826 }
1827
1828 int
Blt_TreeUnsetValueByKey(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,Blt_TreeKey key)1829 Blt_TreeUnsetValueByKey(
1830 Tcl_Interp *interp,
1831 TreeClient *clientPtr,
1832 Node *nodePtr, /* Node to be updated. */
1833 Blt_TreeKey key) /* Name of field in node. */
1834 {
1835 TreeObject *treeObjPtr = nodePtr->treeObject;
1836 Value *valuePtr;
1837 int cnt = 0;
1838
1839 if (nodePtr->flags & TREE_NODE_FIXED_FIELDS) {
1840 if (interp != NULL) {
1841 Tcl_AppendResult(interp, "fixed field", 0);
1842 }
1843 return TCL_ERROR;
1844 }
1845 valuePtr = TreeFindValue(nodePtr, key);
1846 if (valuePtr == NULL) {
1847 return TCL_OK; /* It's okay to unset values that don't
1848 * exist in the node. */
1849 }
1850 if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
1851 if (interp != NULL) {
1852 Tcl_AppendResult(interp, "can't unset private field \"",
1853 key, "\"", (char *)NULL);
1854 }
1855 return TCL_ERROR;
1856 }
1857 SetModified(nodePtr);
1858 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
1859 UpdateOldValue(clientPtr, valuePtr->objPtr);
1860 valuePtr->objPtr = NULL;
1861 }
1862 TreeDeleteValue(nodePtr, valuePtr);
1863 return CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key, TREE_TRACE_UNSET, &cnt);
1864 }
1865
1866 static int
ParseParentheses(Tcl_Interp * interp,CONST char * string,char ** leftPtr,char ** rightPtr)1867 ParseParentheses(
1868 Tcl_Interp *interp,
1869 CONST char *string,
1870 char **leftPtr,
1871 char **rightPtr)
1872 {
1873 register char *p;
1874 char *left, *right;
1875
1876 left = right = NULL;
1877 for (p = (char *)string; *p != '\0'; p++) {
1878 if (*p == '(') {
1879 left = p;
1880 } else if (*p == ')') {
1881 right = p;
1882 }
1883 }
1884 if (left != right) {
1885 if (((left != NULL) && (right == NULL)) ||
1886 ((left == NULL) && (right != NULL)) ||
1887 (left > right) || (right != (p - 1))) {
1888 if (interp != NULL) {
1889 Tcl_AppendResult(interp, "bad array specification \"", string,
1890 "\"", (char *)NULL);
1891 }
1892 return TCL_ERROR;
1893 }
1894 }
1895 *leftPtr = left;
1896 *rightPtr = right;
1897 return TCL_OK;
1898 }
1899
1900 int
Blt_TreeGetValue(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,CONST char * string,Tcl_Obj ** objPtrPtr)1901 Blt_TreeGetValue(
1902 Tcl_Interp *interp,
1903 TreeClient *clientPtr,
1904 Node *nodePtr,
1905 CONST char *string, /* String identifying the field in node. */
1906 Tcl_Obj **objPtrPtr)
1907 {
1908 char *left, *right;
1909 int result;
1910
1911 if (ParseParentheses(interp, string, &left, &right) != TCL_OK) {
1912 return TCL_ERROR;
1913 }
1914 if (left != NULL) {
1915 Tcl_DString kStr, iStr;
1916 Tcl_DStringInit(&kStr);
1917 Tcl_DStringInit(&iStr);
1918 Tcl_DStringAppend(&kStr, left+1, right-left-1);
1919 Tcl_DStringAppend(&iStr, string, left-string);
1920 result = Blt_TreeGetArrayValue(interp, clientPtr, nodePtr,
1921 Tcl_DStringValue(&iStr), Tcl_DStringValue(&kStr), objPtrPtr);
1922 Tcl_DStringFree(&kStr);
1923 Tcl_DStringFree(&iStr);
1924 } else {
1925 result = Blt_TreeGetValueByKey(interp, clientPtr, nodePtr,
1926 Blt_TreeKeyGet(NULL, clientPtr->treeObject,string), objPtrPtr);
1927 }
1928 return result;
1929 }
1930
1931 int
Blt_TreeSetValue(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,CONST char * string,Tcl_Obj * valueObjPtr)1932 Blt_TreeSetValue(
1933 Tcl_Interp *interp,
1934 TreeClient *clientPtr,
1935 Node *nodePtr, /* Node to be updated. */
1936 CONST char *string, /* String identifying the field in node. */
1937 Tcl_Obj *valueObjPtr) /* New value of field. If NULL, field
1938 * is deleted. */
1939 {
1940 char *left, *right;
1941 int result;
1942
1943 if (nodePtr->flags & TREE_NODE_FIXED_FIELDS) {
1944 return Blt_TreeUpdateValue( interp, clientPtr, nodePtr, string, valueObjPtr);
1945 }
1946 if (ParseParentheses(interp, string, &left, &right) != TCL_OK) {
1947 return TCL_ERROR;
1948 }
1949 if (left != NULL) {
1950 Tcl_DString kStr, iStr;
1951 Tcl_DStringInit(&kStr);
1952 Tcl_DStringInit(&iStr);
1953 Tcl_DStringAppend(&kStr, left+1, right-left-1);
1954 Tcl_DStringAppend(&iStr, string, left-string);
1955 result = Blt_TreeSetArrayValue(interp, clientPtr, nodePtr,
1956 Tcl_DStringValue(&iStr), Tcl_DStringValue(&kStr), valueObjPtr);
1957 Tcl_DStringFree(&kStr);
1958 Tcl_DStringFree(&iStr);
1959 } else {
1960 result = Blt_TreeSetValueByKey(interp, clientPtr, nodePtr,
1961 Blt_TreeKeyGet(NULL, clientPtr->treeObject,string), valueObjPtr);
1962 }
1963 return result;
1964 }
1965
1966 int
Blt_TreeUpdateValue(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,CONST char * string,Tcl_Obj * valueObjPtr)1967 Blt_TreeUpdateValue(
1968 Tcl_Interp *interp,
1969 TreeClient *clientPtr,
1970 Node *nodePtr, /* Node to be updated. */
1971 CONST char *string, /* String identifying the field in node. */
1972 Tcl_Obj *valueObjPtr) /* New value of field. If NULL, field
1973 * is deleted. */
1974 {
1975 char *left, *right;
1976 int result;
1977 Blt_TreeKey key;
1978
1979 if (ParseParentheses(interp, string, &left, &right) != TCL_OK) {
1980 return TCL_ERROR;
1981 }
1982 if (left != NULL) {
1983 Tcl_DString kStr, iStr;
1984 Tcl_DStringInit(&kStr);
1985 Tcl_DStringInit(&iStr);
1986 Tcl_DStringAppend(&kStr, left+1, right-left-1);
1987 Tcl_DStringAppend(&iStr, string, left-string);
1988 result = Blt_TreeUpdateArrayValue(interp, clientPtr, nodePtr,
1989 Tcl_DStringValue(&iStr), Tcl_DStringValue(&kStr), valueObjPtr);
1990 Tcl_DStringFree(&kStr);
1991 Tcl_DStringFree(&iStr);
1992 } else {
1993 key = Blt_TreeKeyGet(NULL, clientPtr->treeObject,string);
1994 if (GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key) == NULL) {
1995 if (interp != NULL) {
1996 Tcl_AppendResult(interp, "unknown key: ", string, 0);
1997 }
1998 return TCL_ERROR;
1999 }
2000 result = Blt_TreeSetValueByKey(interp, clientPtr, nodePtr,
2001 key, valueObjPtr);
2002 }
2003 return result;
2004 }
2005
2006 int
Blt_TreeUnsetValue(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,CONST char * string)2007 Blt_TreeUnsetValue(
2008 Tcl_Interp *interp,
2009 TreeClient *clientPtr,
2010 Node *nodePtr, /* Node to be updated. */
2011 CONST char *string) /* String identifying the field in node. */
2012 {
2013 char *left, *right;
2014 int result;
2015
2016 if (nodePtr->flags & TREE_NODE_FIXED_FIELDS) {
2017 if (interp != NULL) {
2018 Tcl_AppendResult(interp, "fixed field", 0);
2019 }
2020 return TCL_ERROR;
2021 }
2022 if (ParseParentheses(interp, string, &left, &right) != TCL_OK) {
2023 return TCL_ERROR;
2024 }
2025 if (left != NULL) {
2026 Tcl_DString kStr, iStr;
2027 Tcl_DStringInit(&kStr);
2028 Tcl_DStringInit(&iStr);
2029 Tcl_DStringAppend(&kStr, left+1, right-left-1);
2030 Tcl_DStringAppend(&iStr, string, left-string);
2031 result = Blt_TreeUnsetArrayValue(interp, clientPtr, nodePtr,
2032 Tcl_DStringValue(&iStr), Tcl_DStringValue(&kStr));
2033 Tcl_DStringFree(&kStr);
2034 Tcl_DStringFree(&iStr);
2035 } else {
2036 result = Blt_TreeUnsetValueByKey(interp, clientPtr, nodePtr,
2037 Blt_TreeKeyGet(NULL, clientPtr->treeObject,string));
2038 }
2039 return result;
2040 }
2041
2042 int
Blt_TreeValueExists(TreeClient * clientPtr,Node * nodePtr,CONST char * string)2043 Blt_TreeValueExists(TreeClient *clientPtr, Node *nodePtr, CONST char *string)
2044 {
2045 char *left, *right;
2046 int result;
2047
2048 if (ParseParentheses((Tcl_Interp *)NULL, string, &left, &right) != TCL_OK) {
2049 return FALSE;
2050 }
2051 if (left != NULL) {
2052 Tcl_DString kStr, iStr;
2053 Tcl_DStringInit(&kStr);
2054 Tcl_DStringInit(&iStr);
2055 Tcl_DStringAppend(&kStr, left+1, right-left-1);
2056 Tcl_DStringAppend(&iStr, string, left-string);
2057 result = Blt_TreeArrayValueExists(clientPtr, nodePtr,
2058 Tcl_DStringValue(&iStr), Tcl_DStringValue(&kStr));
2059 Tcl_DStringFree(&kStr);
2060 Tcl_DStringFree(&iStr);
2061 } else {
2062 result = Blt_TreeValueExistsByKey(clientPtr, nodePtr,
2063 Blt_TreeKeyGet(NULL, clientPtr->treeObject,string));
2064 }
2065 return result;
2066 }
2067
2068 Blt_TreeKey
Blt_TreeFirstKey(TreeClient * clientPtr,Node * nodePtr,Blt_TreeKeySearch * iterPtr)2069 Blt_TreeFirstKey(
2070 TreeClient *clientPtr,
2071 Node *nodePtr,
2072 Blt_TreeKeySearch *iterPtr)
2073 {
2074 Value *valuePtr;
2075
2076 valuePtr = TreeFirstValue(nodePtr, iterPtr);
2077 if (valuePtr == NULL) {
2078 return NULL;
2079 }
2080 while ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
2081 valuePtr = TreeNextValue(iterPtr);
2082 if (valuePtr == NULL) {
2083 return NULL;
2084 }
2085 }
2086 return valuePtr->key;
2087 }
2088
2089 Blt_TreeKey
Blt_TreeNextKey(TreeClient * clientPtr,Blt_TreeKeySearch * iterPtr)2090 Blt_TreeNextKey(TreeClient *clientPtr, Blt_TreeKeySearch *iterPtr)
2091 {
2092 Value *valuePtr;
2093
2094 valuePtr = TreeNextValue(iterPtr);
2095 if (valuePtr == NULL) {
2096 return NULL;
2097 }
2098 while ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
2099 valuePtr = TreeNextValue(iterPtr);
2100 if (valuePtr == NULL) {
2101 return NULL;
2102 }
2103 }
2104 return valuePtr->key;
2105 }
2106
Blt_TreeCountKeys(TreeClient * clientPtr,Node * nodePtr)2107 int Blt_TreeCountKeys(TreeClient *clientPtr, Node *nodePtr) {
2108 int cnt = 0;
2109 Blt_TreeKey key;
2110 Blt_TreeKeySearch cursor;
2111
2112
2113 for (key = Blt_TreeFirstKey(clientPtr, nodePtr, &cursor); key != NULL;
2114 key = Blt_TreeNextKey(clientPtr, &cursor)) {
2115 cnt++;
2116 }
2117 return cnt;
2118 }
2119
2120
2121 int
Blt_TreeIsAncestor(Node * n1Ptr,Node * n2Ptr)2122 Blt_TreeIsAncestor(Node *n1Ptr, Node *n2Ptr)
2123 {
2124 if (n2Ptr != NULL) {
2125 n2Ptr = n2Ptr->parent;
2126 while (n2Ptr != NULL) {
2127 if (n2Ptr == n1Ptr) {
2128 return TRUE;
2129 }
2130 n2Ptr = n2Ptr->parent;
2131 }
2132 }
2133 return FALSE;
2134 }
2135
2136 /*
2137 *----------------------------------------------------------------------
2138 *
2139 * Blt_TreeSortNode --
2140 *
2141 * Sorts the subnodes at a given node.
2142 *
2143 * Results:
2144 * Always returns TCL_OK.
2145 *
2146 *----------------------------------------------------------------------
2147 */
2148 int
Blt_TreeSortNode(TreeClient * clientPtr,Node * nodePtr,Blt_TreeCompareNodesProc * proc)2149 Blt_TreeSortNode(
2150 TreeClient *clientPtr,
2151 Node *nodePtr,
2152 Blt_TreeCompareNodesProc *proc)
2153 {
2154 Node **nodeArr;
2155 Node *childPtr;
2156 int nNodes;
2157 register Node **p;
2158
2159 nNodes = nodePtr->nChildren;
2160 if (nNodes < 2) {
2161 return TCL_OK;
2162 }
2163 nodeArr = Blt_Malloc((nNodes + 1) * sizeof(Node *));
2164 if (nodeArr == NULL) {
2165 return TCL_ERROR; /* Out of memory. */
2166 }
2167 for (p = nodeArr, childPtr = nodePtr->first; childPtr != NULL;
2168 childPtr = childPtr->next, p++) {
2169 *p = childPtr;
2170 }
2171 *p = NULL;
2172
2173 qsort((char *)nodeArr, nNodes, sizeof(Node *), (QSortCompareProc *)proc);
2174 for (p = nodeArr; *p != NULL; p++) {
2175 UnlinkNode(*p);
2176 LinkBefore(nodePtr, *p, (Blt_TreeNode)NULL);
2177 }
2178 Blt_Free(nodeArr);
2179 return NotifyClients(clientPtr, nodePtr->treeObject, nodePtr, TREE_NOTIFY_SORT);
2180 }
2181
2182 #define TEST_RESULT(result) \
2183 switch (result) { \
2184 case TCL_CONTINUE: \
2185 return TCL_OK; \
2186 case TCL_OK: \
2187 break; \
2188 default: \
2189 return (result); \
2190 }
2191
2192 int
Blt_TreeApply(Node * nodePtr,Blt_TreeApplyProc * proc,ClientData clientData)2193 Blt_TreeApply(
2194 Node *nodePtr, /* Root node of subtree. */
2195 Blt_TreeApplyProc *proc, /* Procedure to call for each node. */
2196 ClientData clientData) /* One-word of data passed when calling
2197 * proc. */
2198 {
2199 Node *childPtr, *nextPtr;
2200 int result;
2201
2202 for (childPtr = nodePtr->first; childPtr != NULL; childPtr = nextPtr) {
2203
2204 /*
2205 * Get the next link in the chain before calling Blt_TreeApply
2206 * recursively. This is because the apply callback may delete
2207 * the node and its link.
2208 */
2209
2210 nextPtr = childPtr->next;
2211 if (Blt_TreeNodeDeleted(childPtr)) return TCL_OK;
2212 result = Blt_TreeApply(childPtr, proc, clientData);
2213 TEST_RESULT(result);
2214 }
2215 if (Blt_TreeNodeDeleted(nodePtr)) return TCL_OK;
2216 return (*proc) (nodePtr, clientData, TREE_POSTORDER);
2217 }
2218
2219 int
Blt_TreeApplyDFS(Node * nodePtr,Blt_TreeApplyProc * proc,ClientData clientData,int order)2220 Blt_TreeApplyDFS(
2221 Node *nodePtr, /* Root node of subtree. */
2222 Blt_TreeApplyProc *proc, /* Procedure to call for each node. */
2223 ClientData clientData, /* One-word of data passed when calling
2224 * proc. */
2225 int order) /* Order of traversal. */
2226 {
2227 Node *childPtr, *nextPtr;
2228 int result;
2229
2230 if (Blt_TreeNodeDeleted(nodePtr)) return TCL_OK;
2231 if (order & TREE_PREORDER) {
2232 result = (*proc) (nodePtr, clientData, TREE_PREORDER);
2233 TEST_RESULT(result);
2234 }
2235 childPtr = nodePtr->first;
2236 if (order & TREE_INORDER) {
2237 if (childPtr != NULL) {
2238 result = Blt_TreeApplyDFS(childPtr, proc, clientData, order);
2239 TEST_RESULT(result);
2240 childPtr = childPtr->next;
2241 }
2242 result = (*proc) (nodePtr, clientData, TREE_INORDER);
2243 TEST_RESULT(result);
2244 }
2245 for (/* empty */; childPtr != NULL; childPtr = nextPtr) {
2246 /*
2247 * Get the next link in the chain before calling
2248 * Blt_TreeApply recursively. This is because the
2249 * apply callback may delete the node and its link.
2250 */
2251 nextPtr = childPtr->next;
2252 if (Blt_TreeNodeDeleted(childPtr)) break;
2253 result = Blt_TreeApplyDFS(childPtr, proc, clientData, order);
2254 TEST_RESULT(result);
2255 }
2256 if (Blt_TreeNodeDeleted(nodePtr)) return TCL_OK;
2257 if (order & TREE_POSTORDER) {
2258 return (*proc) (nodePtr, clientData, TREE_POSTORDER);
2259 }
2260 return TCL_OK;
2261 }
2262
2263 int
Blt_TreeApplyBFS(nodePtr,proc,clientData)2264 Blt_TreeApplyBFS(nodePtr, proc, clientData)
2265 Node *nodePtr; /* Root node of subtree. */
2266 Blt_TreeApplyProc *proc; /* Procedure to call for each node. */
2267 ClientData clientData; /* One-word of data passed when calling
2268 * proc. */
2269 {
2270 Blt_Chain *queuePtr;
2271 Blt_ChainLink *linkPtr, *nextPtr;
2272 Node *childPtr;
2273 int result;
2274
2275 queuePtr = Blt_ChainCreate();
2276 linkPtr = Blt_ChainAppend(queuePtr, nodePtr);
2277 while (linkPtr != NULL) {
2278 nodePtr = Blt_ChainGetValue(linkPtr);
2279 /* Add the children to the queue. */
2280 for (childPtr = nodePtr->first; childPtr != NULL;
2281 childPtr = childPtr->next) {
2282 Blt_ChainAppend(queuePtr, childPtr);
2283 }
2284 if (Blt_TreeNodeDeleted(nodePtr)) break;
2285 /* Process the node. */
2286 result = (*proc) (nodePtr, clientData, TREE_BREADTHFIRST);
2287 switch (result) {
2288 case TCL_CONTINUE:
2289 Blt_ChainDestroy(queuePtr);
2290 return TCL_OK;
2291 case TCL_OK:
2292 break;
2293 default:
2294 Blt_ChainDestroy(queuePtr);
2295 return result;
2296 }
2297 /* Remove the node from the queue. */
2298 nextPtr = Blt_ChainNextLink(linkPtr);
2299 Blt_ChainDeleteLink(queuePtr, linkPtr);
2300 linkPtr = nextPtr;
2301 }
2302 Blt_ChainDestroy(queuePtr);
2303 return TCL_OK;
2304 }
2305
2306 static TreeClient *
NewTreeClient(TreeObject * treeObjPtr,int sharetags)2307 NewTreeClient(TreeObject *treeObjPtr, int sharetags)
2308 {
2309 TreeClient *clientPtr;
2310
2311 clientPtr = Blt_Calloc(1, sizeof(TreeClient));
2312 if (clientPtr != NULL) {
2313 Blt_TreeTagTable *tablePtr;
2314 int hascl = (treeObjPtr->clients->headPtr != NULL);
2315
2316 clientPtr->magic = TREE_MAGIC;
2317 clientPtr->linkPtr = Blt_ChainAppend(treeObjPtr->clients, clientPtr);
2318 clientPtr->events = Blt_ChainCreate();
2319 clientPtr->traces = Blt_ChainCreate();
2320 clientPtr->treeObject = treeObjPtr;
2321 clientPtr->root = treeObjPtr->root;
2322 if (sharetags && hascl) {
2323 TreeClient *ctPtr;
2324 ctPtr = Blt_ChainGetValue(treeObjPtr->clients->headPtr);
2325 if (ctPtr && ctPtr->tagTablePtr) {
2326 clientPtr->tagTablePtr = ctPtr->tagTablePtr;
2327 clientPtr->tagTablePtr->refCount++;
2328 }
2329 }
2330 if (clientPtr->tagTablePtr == NULL) {
2331 tablePtr = Blt_Malloc(sizeof(Blt_TreeTagTable));
2332 Blt_InitHashTable(&tablePtr->tagTable, BLT_STRING_KEYS);
2333 tablePtr->refCount = 1;
2334 clientPtr->tagTablePtr = tablePtr;
2335 }
2336 }
2337 return clientPtr;
2338 }
2339
2340 int
Blt_TreeCreate(Tcl_Interp * interp,CONST char * name,TreeClient ** clientPtrPtr)2341 Blt_TreeCreate(
2342 Tcl_Interp *interp, /* Interpreter to report errors back to. */
2343 CONST char *name, /* Name of tree in namespace. Tree
2344 * must not already exist. */
2345 TreeClient **clientPtrPtr) /* (out) Client token of newly created
2346 * tree. Releasing the token will
2347 * free the tree. If NULL, no token
2348 * is generated. */
2349
2350 {
2351 Tcl_DString dString;
2352 Tcl_Namespace *nsPtr;
2353 TreeInterpData *dataPtr;
2354 TreeObject *treeObjPtr;
2355 CONST char *treeName;
2356 char string[200];
2357
2358 dataPtr = GetTreeInterpData(interp);
2359 if (name != NULL) {
2360 /* Check if this tree already exists the current namespace. */
2361 treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_CURRENT);
2362 if (treeObjPtr != NULL) {
2363 if (interp != NULL)
2364 Tcl_AppendResult(interp, "a tree object \"", name,
2365 "\" already exists", (char *)NULL);
2366 return TCL_ERROR;
2367 }
2368 } else {
2369 /* Generate a unique tree name in the current namespace. */
2370 do {
2371 sprintf(string, "tree%d", dataPtr->nextId++);
2372 } while (GetTreeObject(interp, string, NS_SEARCH_CURRENT) != NULL);
2373 name = string;
2374 }
2375 /*
2376 * Tear apart and put back together the namespace-qualified name
2377 * of the tree. This is to ensure that naming is consistent.
2378 */
2379 treeName = name;
2380 if (Blt_ParseQualifiedName(interp, name, &nsPtr, &treeName) != TCL_OK) {
2381 if (interp != NULL)
2382 Tcl_AppendResult(interp, "can't find namespace in \"", name, "\"",
2383 (char *)NULL);
2384 return TCL_ERROR;
2385 }
2386 if (nsPtr == NULL) {
2387 /*
2388 * Note: Unlike Tcl_CreateCommand, an unqualified name
2389 * doesn't imply the global namespace, but the current one.
2390 */
2391 nsPtr = Tcl_GetCurrentNamespace(interp);
2392 }
2393 name = Blt_GetQualifiedName(nsPtr, treeName, &dString);
2394 treeObjPtr = NewTreeObject(dataPtr, interp, name);
2395 if (treeObjPtr == NULL) {
2396 if (interp != NULL)
2397 Tcl_AppendResult(interp, "can't allocate tree \"", name, "\"",
2398 (char *)NULL);
2399 Tcl_DStringFree(&dString);
2400 return TCL_ERROR;
2401 }
2402 Tcl_DStringFree(&dString);
2403 if (clientPtrPtr != NULL) {
2404 TreeClient *clientPtr;
2405
2406 clientPtr = NewTreeClient(treeObjPtr, 0);
2407 if (clientPtr == NULL) {
2408 if (interp != NULL)
2409 Tcl_AppendResult(interp, "can't allocate tree token",(char *)NULL);
2410 return TCL_ERROR;
2411 }
2412 *clientPtrPtr = clientPtr;
2413 }
2414 return TCL_OK;
2415 }
2416
2417 int
Blt_TreeGetToken(Tcl_Interp * interp,CONST char * name,TreeClient ** clientPtrPtr)2418 Blt_TreeGetToken(
2419 Tcl_Interp *interp, /* Interpreter to report errors back to. */
2420 CONST char *name, /* Name of tree in namespace. */
2421 TreeClient **clientPtrPtr)
2422 {
2423 TreeClient *clientPtr;
2424 TreeObject *treeObjPtr;
2425
2426 treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_BOTH);
2427 if (treeObjPtr == NULL) {
2428 if (interp != NULL)
2429 Tcl_AppendResult(interp, "can't find a tree object \"", name, "\"",
2430 (char *)NULL);
2431 return TCL_ERROR;
2432 }
2433 clientPtr = NewTreeClient(treeObjPtr, 0);
2434 if (clientPtr == NULL) {
2435 if (interp != NULL)
2436 Tcl_AppendResult(interp, "can't allocate token for tree \"",
2437 name, "\"", (char *)NULL);
2438 return TCL_ERROR;
2439 }
2440 *clientPtrPtr = clientPtr;
2441 return TCL_OK;
2442 }
2443
2444 int
Blt_TreeGetTokenTag(Tcl_Interp * interp,CONST char * name,TreeClient ** clientPtrPtr)2445 Blt_TreeGetTokenTag(
2446 Tcl_Interp *interp, /* Interpreter to report errors back to. */
2447 CONST char *name, /* Name of tree in namespace. */
2448 TreeClient **clientPtrPtr)
2449 {
2450 TreeClient *clientPtr;
2451 TreeObject *treeObjPtr;
2452
2453 treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_BOTH);
2454 if (treeObjPtr == NULL) {
2455 if (interp != NULL)
2456 Tcl_AppendResult(interp, "can't find a tree object \"", name, "\"",
2457 (char *)NULL);
2458 return TCL_ERROR;
2459 }
2460 clientPtr = NewTreeClient(treeObjPtr, 1);
2461 if (clientPtr == NULL) {
2462 if (interp != NULL)
2463 Tcl_AppendResult(interp, "can't allocate token for tree \"",
2464 name, "\"", (char *)NULL);
2465 return TCL_ERROR;
2466 }
2467 *clientPtrPtr = clientPtr;
2468 return TCL_OK;
2469 }
2470
2471 void
Blt_TreeReleaseToken(TreeClient * clientPtr)2472 Blt_TreeReleaseToken(TreeClient *clientPtr)
2473 {
2474 TreeObject *treeObjPtr;
2475 Blt_ChainLink *linkPtr;
2476 EventHandler *notifyPtr;
2477 TraceHandler *tracePtr;
2478
2479 if (clientPtr->magic != TREE_MAGIC) {
2480 fprintf(stderr, "invalid tree object token 0x%lx\n",
2481 (unsigned long)clientPtr);
2482 return;
2483 }
2484 /* Remove any traces that may be set. */
2485 for (linkPtr = Blt_ChainFirstLink(clientPtr->traces); linkPtr != NULL;
2486 linkPtr = Blt_ChainNextLink(linkPtr)) {
2487 tracePtr = Blt_ChainGetValue(linkPtr);
2488 if (tracePtr->keyPattern != NULL) {
2489 Blt_Free(tracePtr->keyPattern);
2490 }
2491 Blt_Free(tracePtr);
2492 }
2493 Blt_ChainDestroy(clientPtr->traces);
2494 /* And any event handlers. */
2495 for(linkPtr = Blt_ChainFirstLink(clientPtr->events);
2496 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
2497 notifyPtr = Blt_ChainGetValue(linkPtr);
2498 if (notifyPtr->notifyPending) {
2499 Tcl_CancelIdleCall(NotifyIdleProc, notifyPtr);
2500 }
2501 Blt_Free(notifyPtr);
2502 }
2503 if (clientPtr->tagTablePtr != NULL) {
2504 ReleaseTagTable(clientPtr->tagTablePtr);
2505 }
2506 Blt_ChainDestroy(clientPtr->events);
2507 treeObjPtr = clientPtr->treeObject;
2508 if (treeObjPtr != NULL) {
2509 /* Remove the client from the server's list */
2510 Blt_ChainDeleteLink(treeObjPtr->clients, clientPtr->linkPtr);
2511 if (Blt_ChainGetLength(treeObjPtr->clients) == 0) {
2512 treeObjPtr->delete = 1;
2513 Tcl_EventuallyFree(treeObjPtr, DestroyTreeObject);
2514 /* DestroyTreeObject(treeObjPtr); */
2515 }
2516 }
2517 clientPtr->magic = 0;
2518 Blt_Free(clientPtr);
2519 }
2520
2521 int
Blt_TreeExists(interp,name)2522 Blt_TreeExists(interp, name)
2523 Tcl_Interp *interp; /* Interpreter to report errors back to. */
2524 CONST char *name; /* Name of tree in designated namespace. */
2525 {
2526 TreeObject *treeObjPtr;
2527
2528 treeObjPtr = GetTreeObject(interp, name, NS_SEARCH_BOTH);
2529 if (treeObjPtr == NULL) {
2530 Tcl_ResetResult(interp);
2531 return 0;
2532 }
2533 return 1;
2534 }
2535
2536 /*ARGSUSED*/
2537 static int
SizeApplyProc(Node * nodePtr,ClientData clientData,int order)2538 SizeApplyProc(
2539 Node *nodePtr, /* Not used. */
2540 ClientData clientData,
2541 int order) /* Not used. */
2542 {
2543 int *sumPtr = clientData;
2544 *sumPtr = *sumPtr + 1;
2545 return TCL_OK;
2546 }
2547
2548 int
Blt_TreeSize(Node * nodePtr)2549 Blt_TreeSize(Node *nodePtr)
2550 {
2551 int sum;
2552
2553 sum = 0;
2554 Blt_TreeApply(nodePtr, SizeApplyProc, &sum);
2555 return sum;
2556 }
2557
2558
2559 void
Blt_TreeCreateEventHandler(TreeClient * clientPtr,unsigned int mask,Blt_TreeNotifyEventProc * proc,ClientData clientData)2560 Blt_TreeCreateEventHandler(
2561 TreeClient *clientPtr,
2562 unsigned int mask,
2563 Blt_TreeNotifyEventProc *proc,
2564 ClientData clientData)
2565 {
2566 Blt_ChainLink *linkPtr;
2567 EventHandler *notifyPtr;
2568
2569 notifyPtr = NULL; /* Suppress compiler warning. */
2570
2571 /* Check if the event is already handled. */
2572 for(linkPtr = Blt_ChainFirstLink(clientPtr->events);
2573 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
2574 notifyPtr = Blt_ChainGetValue(linkPtr);
2575 if ((notifyPtr->proc == proc) &&
2576 (notifyPtr->mask == mask) &&
2577 (notifyPtr->clientData == clientData)) {
2578 break;
2579 }
2580 }
2581 if (linkPtr == NULL) {
2582 notifyPtr = Blt_Malloc(sizeof (EventHandler));
2583 assert(notifyPtr);
2584 linkPtr = Blt_ChainAppend(clientPtr->events, notifyPtr);
2585 }
2586 if (proc == NULL) {
2587 Blt_ChainDeleteLink(clientPtr->events, linkPtr);
2588 Blt_Free(notifyPtr);
2589 } else {
2590 notifyPtr->proc = proc;
2591 notifyPtr->clientData = clientData;
2592 notifyPtr->mask = mask;
2593 notifyPtr->notifyPending = FALSE;
2594 notifyPtr->interp = clientPtr->treeObject->interp;
2595 }
2596 }
2597
2598 void
Blt_TreeDeleteEventHandler(TreeClient * clientPtr,unsigned int mask,Blt_TreeNotifyEventProc * proc,ClientData clientData)2599 Blt_TreeDeleteEventHandler(
2600 TreeClient *clientPtr,
2601 unsigned int mask,
2602 Blt_TreeNotifyEventProc *proc,
2603 ClientData clientData)
2604 {
2605 Blt_ChainLink *linkPtr;
2606 EventHandler *notifyPtr;
2607
2608 if (!clientPtr) return;
2609 for(linkPtr = Blt_ChainFirstLink(clientPtr->events);
2610 linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) {
2611 notifyPtr = Blt_ChainGetValue(linkPtr);
2612 if ((notifyPtr->proc == proc) && (notifyPtr->mask == mask) &&
2613 (notifyPtr->clientData == clientData)) {
2614 if (notifyPtr->notifyPending) {
2615 Tcl_CancelIdleCall(NotifyIdleProc, notifyPtr);
2616 }
2617 Blt_ChainDeleteLink(clientPtr->events, linkPtr);
2618 Blt_Free(notifyPtr);
2619 return;
2620 }
2621 }
2622 }
2623
2624 /*
2625 *----------------------------------------------------------------------
2626 *
2627 * Blt_TreeNodePath --
2628 *
2629 *----------------------------------------------------------------------
2630 */
2631 char *
Blt_TreeNodePath(Node * nodePtr,Tcl_DString * resultPtr)2632 Blt_TreeNodePath(Node *nodePtr, Tcl_DString *resultPtr)
2633 {
2634 CONST char **nameArr; /* Used to stack the component names. */
2635 CONST char *staticSpace[64];
2636 int nLevels;
2637 register int i;
2638
2639 nLevels = nodePtr->depth;
2640 if (nLevels > 64) {
2641 nameArr = Blt_Malloc(nLevels * sizeof(char *));
2642 assert(nameArr);
2643 } else {
2644 nameArr = staticSpace;
2645 }
2646 for (i = nLevels - 1; i >= 0; i--) {
2647 /* Save the name of each ancestor in the name array.
2648 * Note that we ignore the root. */
2649 nameArr[i] = nodePtr->label;
2650 nodePtr = nodePtr->parent;
2651 }
2652 /* Append each the names in the array. */
2653 Tcl_DStringInit(resultPtr);
2654 for (i = 0; i < nLevels; i++) {
2655 Tcl_DStringAppendElement(resultPtr, nameArr[i]);
2656 }
2657 if (nameArr != staticSpace) {
2658 Blt_Free(nameArr);
2659 }
2660 return Tcl_DStringValue(resultPtr);
2661 }
2662
2663 char *
Blt_TreeNodePathStr(Node * nodePtr,Tcl_DString * resultPtr,char * prefix,char * delim)2664 Blt_TreeNodePathStr(Node *nodePtr, Tcl_DString *resultPtr, char *prefix, char *delim)
2665 {
2666 CONST char **nameArr; /* Used to stack the component names. */
2667 CONST char *staticSpace[64];
2668 int nLevels;
2669 register int i;
2670
2671 nLevels = nodePtr->depth;
2672 if (nLevels > 64) {
2673 nameArr = Blt_Malloc(nLevels * sizeof(char *));
2674 assert(nameArr);
2675 } else {
2676 nameArr = staticSpace;
2677 }
2678 for (i = nLevels - 1; i >= 0; i--) {
2679 /* Save the name of each ancestor in the name array.
2680 * Note that we ignore the root. */
2681 nameArr[i] = nodePtr->label;
2682 nodePtr = nodePtr->parent;
2683 }
2684 /* Append each of the names in the array. */
2685 Tcl_DStringInit(resultPtr);
2686 if (prefix != NULL) {
2687 Tcl_DStringAppend(resultPtr, prefix, -1);
2688 }
2689 for (i = 0; i < nLevels; i++) {
2690 if (i > 0 && delim != NULL) {
2691 Tcl_DStringAppend(resultPtr, delim, -1);
2692 }
2693 Tcl_DStringAppend(resultPtr, nameArr[i], -1);
2694 }
2695 if (nameArr != staticSpace) {
2696 Blt_Free(nameArr);
2697 }
2698 return Tcl_DStringValue(resultPtr);
2699 }
2700
2701 int
Blt_TreeArrayValueExists(TreeClient * clientPtr,Node * nodePtr,CONST char * arrayName,CONST char * elemName)2702 Blt_TreeArrayValueExists(
2703 TreeClient *clientPtr,
2704 Node *nodePtr,
2705 CONST char *arrayName,
2706 CONST char *elemName)
2707 {
2708 Blt_TreeKey key;
2709 Blt_HashEntry *hPtr;
2710 Blt_HashTable *tablePtr;
2711 register Value *valuePtr;
2712 int cnt;
2713 TreeObject *treeObjPtr = nodePtr->treeObject;
2714 Tcl_Interp *interp = treeObjPtr->interp;
2715
2716 key = Blt_TreeKeyGet(NULL, clientPtr->treeObject,arrayName);
2717
2718 valuePtr = GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key);
2719 if (valuePtr == NULL && (!(nodePtr->flags & TREE_TRACE_ACTIVE))) {
2720 if (CallTraces(interp, clientPtr, treeObjPtr, nodePtr, key,
2721 TREE_TRACE_EXISTS, &cnt) != TCL_OK) {
2722 Tcl_ResetResult(interp);
2723 } else {
2724 valuePtr = GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key);
2725 }
2726 }
2727 if (valuePtr == NULL) {
2728 return FALSE;
2729 }
2730 if (IsTclDict(interp, valuePtr->objPtr)) {
2731 /* Preserve type if this was a dict */
2732 int result;
2733 Tcl_Obj *keyPtr, *valueObjPtr = NULL;
2734
2735 keyPtr = Tcl_NewStringObj(elemName, -1);
2736 Tcl_IncrRefCount(keyPtr);
2737 result = Tcl_DictObjGet(interp, valuePtr->objPtr, keyPtr, &valueObjPtr);
2738 Tcl_DecrRefCount(keyPtr);
2739 if (result != TCL_OK) {
2740 return FALSE;
2741 }
2742 if (valueObjPtr == NULL) {
2743 return FALSE;
2744 }
2745 return TRUE;
2746 }
2747
2748 if (Blt_IsArrayObj(valuePtr->objPtr) == 0 && Tcl_IsShared(valuePtr->objPtr)) {
2749 Tcl_DecrRefCount(valuePtr->objPtr);
2750 valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
2751 Tcl_IncrRefCount(valuePtr->objPtr);
2752 }
2753 if (Blt_GetArrayFromObj((Tcl_Interp *)NULL, valuePtr->objPtr, &tablePtr)
2754 != TCL_OK) {
2755 return FALSE;
2756 }
2757 hPtr = Blt_FindHashEntry(tablePtr, elemName);
2758 return (hPtr != NULL);
2759 }
2760
2761 int
Blt_TreeGetArrayValue(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,CONST char * arrayName,CONST char * elemName,Tcl_Obj ** valueObjPtrPtr)2762 Blt_TreeGetArrayValue(
2763 Tcl_Interp *interp,
2764 TreeClient *clientPtr,
2765 Node *nodePtr,
2766 CONST char *arrayName,
2767 CONST char *elemName,
2768 Tcl_Obj **valueObjPtrPtr)
2769 {
2770 Blt_TreeKey key;
2771 Blt_HashEntry *hPtr;
2772 Blt_HashTable *tablePtr;
2773 register Value *valuePtr;
2774 int cnt = 0;
2775
2776 key = Blt_TreeKeyGet(interp, clientPtr->treeObject,arrayName);
2777
2778 /* Reading any element of the array can cause a trace to fire. */
2779 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
2780 if (CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr, key,
2781 TREE_TRACE_READ, &cnt) != TCL_OK) {
2782 return TCL_ERROR;
2783 }
2784 }
2785 valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
2786 if (valuePtr == NULL) {
2787 return TCL_ERROR;
2788 }
2789 if (IsTclDict(interp, valuePtr->objPtr)) {
2790 /* Preserve type if this was a dict */
2791 int result;
2792 Tcl_Obj *keyPtr;
2793 keyPtr = Tcl_NewStringObj(elemName, -1);
2794 Tcl_IncrRefCount(keyPtr);
2795 result = Tcl_DictObjGet(interp, valuePtr->objPtr, keyPtr, valueObjPtrPtr);
2796 Tcl_DecrRefCount(keyPtr);
2797 if (result != TCL_OK) {
2798 return result;
2799 }
2800 if (*valueObjPtrPtr == NULL) {
2801 if (interp != NULL) {
2802 Tcl_AppendResult(interp, "can't find \"", arrayName, "(",
2803 elemName, ")\"", (char *)NULL);
2804 }
2805 return TCL_ERROR;
2806 }
2807 return TCL_OK;
2808 }
2809 if (Blt_IsArrayObj(valuePtr->objPtr) == 0 && Tcl_IsShared(valuePtr->objPtr)) {
2810 Tcl_DecrRefCount(valuePtr->objPtr);
2811 valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
2812 Tcl_IncrRefCount(valuePtr->objPtr);
2813 }
2814 if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
2815 return TCL_ERROR;
2816 }
2817 hPtr = Blt_FindHashEntry(tablePtr, elemName);
2818 if (hPtr == NULL) {
2819 if (interp != NULL) {
2820 Tcl_AppendResult(interp, "can't find \"", arrayName, "(",
2821 elemName, ")\"", (char *)NULL);
2822 }
2823 return TCL_ERROR;
2824 }
2825
2826 *valueObjPtrPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
2827 return TCL_OK;
2828 }
2829
2830 static int
TreeSetArrayValue(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,CONST char * arrayName,CONST char * elemName,Tcl_Obj * valueObjPtr,int create)2831 TreeSetArrayValue(
2832 Tcl_Interp *interp,
2833 TreeClient *clientPtr,
2834 Node *nodePtr, /* Node to be updated. */
2835 CONST char *arrayName,
2836 CONST char *elemName,
2837 Tcl_Obj *valueObjPtr, /* New value of element. */
2838 int create) /* Create the node key if required. */
2839 {
2840 Blt_TreeKey key;
2841 Blt_HashEntry *hPtr;
2842 Blt_HashTable *tablePtr;
2843 register Value *valuePtr;
2844 unsigned int flags;
2845 int isNew, cnt = 0;
2846
2847 assert(valueObjPtr != NULL);
2848
2849 /*
2850 * Search for the array in the list of data fields. If one
2851 * doesn't exist, create it.
2852 */
2853 key = Blt_TreeKeyGet(interp, clientPtr->treeObject,arrayName);
2854 valuePtr = GetTreeValue((Tcl_Interp *)NULL, clientPtr, nodePtr, key);
2855 if (valuePtr == NULL && create == 0) {
2856 return TCL_ERROR;
2857 }
2858 if (valuePtr == NULL) {
2859 if ((nodePtr->flags & TREE_NODE_FIXED_FIELDS)) {
2860 return TCL_ERROR;
2861 }
2862 valuePtr = TreeCreateValue(nodePtr, key, &isNew);
2863 isNew = 1;
2864 } else {
2865 isNew = 0;
2866 }
2867 if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
2868 if (interp != NULL) {
2869 Tcl_AppendResult(interp, "can't set private field \"",
2870 key, "\"", (char *)NULL);
2871 }
2872 return TCL_ERROR;
2873 }
2874 flags = TREE_TRACE_WRITE;
2875 if (isNew) {
2876 valuePtr->objPtr = Blt_NewArrayObj(0, (Tcl_Obj **)NULL);
2877 Tcl_IncrRefCount(valuePtr->objPtr);
2878 flags |= TREE_TRACE_CREATE;
2879
2880 } else if (Tcl_IsShared(valuePtr->objPtr)) {
2881 Tcl_DecrRefCount(valuePtr->objPtr);
2882 valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
2883 Tcl_IncrRefCount(valuePtr->objPtr);
2884 }
2885
2886 if ((clientPtr->treeObject->flags & TREE_DICT_KEYS) &&
2887 IsTclDict(interp, valuePtr->objPtr)) {
2888 int dSiz;
2889
2890 if (Tcl_DictObjSize(interp, valuePtr->objPtr, &dSiz) != TCL_OK) {
2891 return TCL_ERROR;
2892 }
2893 }
2894 if (IsTclDict(interp, valuePtr->objPtr)) {
2895 /* Preserve type if this was a dict */
2896 int result;
2897 Tcl_Obj *keyPtr, *valObjPtr;
2898
2899 keyPtr = Tcl_NewStringObj(elemName, -1);
2900 Tcl_IncrRefCount(keyPtr);
2901 if (!create) {
2902 result = Tcl_DictObjGet(interp, valuePtr->objPtr, keyPtr, &valObjPtr);
2903 if (result != TCL_OK || valObjPtr == NULL) {
2904 Tcl_AppendResult(interp, "can't find field: ", elemName, 0);
2905 Tcl_DecrRefCount(keyPtr);
2906 return TCL_ERROR;
2907 }
2908 }
2909 result = Tcl_DictObjPut(interp, valuePtr->objPtr, keyPtr, valueObjPtr);
2910 Tcl_DecrRefCount(keyPtr);
2911 if (result != TCL_OK) {
2912 return result;
2913 }
2914 goto finishset;
2915 }
2916
2917
2918 if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
2919 return TCL_ERROR;
2920 }
2921 Tcl_InvalidateStringRep(valuePtr->objPtr);
2922 if (create) {
2923 hPtr = Blt_CreateHashEntry(tablePtr, elemName, &isNew);
2924 assert(hPtr);
2925 } else {
2926 hPtr = Blt_FindHashEntry(tablePtr, elemName);
2927 if (hPtr == NULL) {
2928 if (interp != NULL) {
2929 Tcl_AppendResult(interp, "can't find array field: ", elemName, 0);
2930 }
2931 return TCL_ERROR;
2932 }
2933 isNew = 0;
2934 }
2935
2936 SetModified(nodePtr);
2937 Tcl_IncrRefCount(valueObjPtr);
2938 if (!isNew) {
2939 Tcl_Obj *oldValueObjPtr;
2940
2941 /* An element by the same name already exists. Decrement the
2942 * reference count of the old value. */
2943
2944 oldValueObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
2945 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
2946 UpdateOldValue(clientPtr, oldValueObjPtr);
2947 oldValueObjPtr = NULL;
2948 }
2949 if (oldValueObjPtr != NULL) {
2950 Tcl_DecrRefCount(oldValueObjPtr);
2951 }
2952 } else {
2953 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
2954 UpdateOldValue(clientPtr, NULL);
2955 }
2956 }
2957 Blt_SetHashValue(hPtr, valueObjPtr);
2958
2959 finishset:
2960 /*
2961 * We don't handle traces on a per array element basis. Setting
2962 * any element can fire traces for the value.
2963 */
2964 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
2965 return CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr,
2966 valuePtr->key, flags, &cnt);
2967 }
2968 return TCL_OK;
2969 }
2970
2971 int
Blt_TreeSetArrayValue(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,CONST char * arrayName,CONST char * elemName,Tcl_Obj * valueObjPtr)2972 Blt_TreeSetArrayValue(
2973 Tcl_Interp *interp,
2974 TreeClient *clientPtr,
2975 Node *nodePtr, /* Node to be updated. */
2976 CONST char *arrayName,
2977 CONST char *elemName,
2978 Tcl_Obj *valueObjPtr) /* New value of element. */
2979 {
2980 return TreeSetArrayValue(interp, clientPtr, nodePtr, arrayName, elemName, valueObjPtr, 1);
2981 }
2982
2983 int
Blt_TreeUpdateArrayValue(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,CONST char * arrayName,CONST char * elemName,Tcl_Obj * valueObjPtr)2984 Blt_TreeUpdateArrayValue(
2985 Tcl_Interp *interp,
2986 TreeClient *clientPtr,
2987 Node *nodePtr, /* Node to be updated. */
2988 CONST char *arrayName,
2989 CONST char *elemName,
2990 Tcl_Obj *valueObjPtr) /* New value of element. */
2991 {
2992 return TreeSetArrayValue(interp, clientPtr, nodePtr, arrayName, elemName, valueObjPtr, 0);
2993 }
2994
2995 int
Blt_TreeUnsetArrayValue(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,CONST char * arrayName,CONST char * elemName)2996 Blt_TreeUnsetArrayValue(
2997 Tcl_Interp *interp,
2998 TreeClient *clientPtr,
2999 Node *nodePtr, /* Node to be updated. */
3000 CONST char *arrayName,
3001 CONST char *elemName)
3002 {
3003 Blt_TreeKey key; /* Name of field in node. */
3004 Blt_HashEntry *hPtr;
3005 Blt_HashTable *tablePtr;
3006 Tcl_Obj *valueObjPtr;
3007 Value *valuePtr;
3008 int cnt = 0;
3009
3010 key = Blt_TreeKeyGet(interp, clientPtr->treeObject,arrayName);
3011 valuePtr = TreeFindValue(nodePtr, key);
3012 if (valuePtr == NULL) {
3013 return TCL_OK;
3014 }
3015 if ((valuePtr->owner != NULL) && (valuePtr->owner != clientPtr)) {
3016 if (interp != NULL) {
3017 Tcl_AppendResult(interp, "can't unset private field \"",
3018 key, "\"", (char *)NULL);
3019 }
3020 return TCL_ERROR;
3021 }
3022
3023 if (Tcl_IsShared(valuePtr->objPtr)) {
3024 Tcl_DecrRefCount(valuePtr->objPtr);
3025 valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
3026 Tcl_IncrRefCount(valuePtr->objPtr);
3027 }
3028
3029 if (IsTclDict(interp, valuePtr->objPtr)) {
3030 /* Preserve type if this was a dict */
3031 int result;
3032 Tcl_Obj *keyPtr;
3033 keyPtr = Tcl_NewStringObj(elemName, -1);
3034 Tcl_IncrRefCount(keyPtr);
3035 result = Tcl_DictObjRemove(interp, valuePtr->objPtr, keyPtr);
3036 Tcl_DecrRefCount(keyPtr);
3037 if (result != TCL_OK) {
3038 return result;
3039 }
3040 goto finishrm;
3041 }
3042
3043 if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
3044 return TCL_ERROR;
3045 }
3046 hPtr = Blt_FindHashEntry(tablePtr, elemName);
3047 if (hPtr == NULL) {
3048 goto finishrm; /* Element doesn't exist, Ok. */
3049 }
3050 SetModified(nodePtr);
3051 valueObjPtr = (Tcl_Obj *)Blt_GetHashValue(hPtr);
3052 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
3053 UpdateOldValue(clientPtr, valueObjPtr);
3054 } else {
3055 Tcl_DecrRefCount(valueObjPtr);
3056 }
3057 Blt_DeleteHashEntry(tablePtr, hPtr);
3058 Tcl_InvalidateStringRep(valuePtr->objPtr);
3059
3060 finishrm:
3061 /*
3062 * Un-setting any element in the array can cause the trace on the value
3063 * to fire.
3064 */
3065 if (!(nodePtr->flags & TREE_TRACE_ACTIVE)) {
3066 return CallTraces(interp, clientPtr, nodePtr->treeObject, nodePtr,
3067 valuePtr->key, TREE_TRACE_WRITE, &cnt);
3068 }
3069 return TCL_OK;
3070 }
3071
3072 int
Blt_TreeArrayNames(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,CONST char * arrayName,Tcl_Obj * listObjPtr,CONST char * pattern)3073 Blt_TreeArrayNames(
3074 Tcl_Interp *interp,
3075 TreeClient *clientPtr,
3076 Node *nodePtr,
3077 CONST char *arrayName,
3078 Tcl_Obj *listObjPtr,
3079 CONST char *pattern)
3080 {
3081 Blt_HashEntry *hPtr;
3082 Blt_HashSearch cursor;
3083 Blt_HashTable *tablePtr;
3084 Tcl_Obj *objPtr;
3085 Value *valuePtr;
3086 CONST char *key;
3087
3088 key = Blt_TreeKeyGet(interp, clientPtr->treeObject,arrayName);
3089 valuePtr = GetTreeValue(interp, clientPtr, nodePtr, key);
3090 if (valuePtr == NULL) {
3091 return TCL_ERROR;
3092 }
3093 if (IsTclDict(interp, valuePtr->objPtr)) {
3094 /* Preserve type if this was a dict */
3095
3096 Tcl_DictSearch search;
3097 Tcl_Obj *keyPtr;
3098 int done;
3099
3100 Tcl_DictObjFirst(NULL, valuePtr->objPtr, &search, &keyPtr, NULL, &done);
3101 for (; !done ; Tcl_DictObjNext(&search, &keyPtr, NULL, &done)) {
3102 if (!pattern || Tcl_StringMatch(Tcl_GetString(keyPtr), pattern)) {
3103 Tcl_ListObjAppendElement(NULL, listObjPtr, keyPtr);
3104 }
3105 }
3106 Tcl_DictObjDone(&search);
3107 return TCL_OK;
3108 }
3109
3110 if (Blt_IsArrayObj(valuePtr->objPtr) == 0 && Tcl_IsShared(valuePtr->objPtr)) {
3111 Tcl_DecrRefCount(valuePtr->objPtr);
3112 valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
3113 Tcl_IncrRefCount(valuePtr->objPtr);
3114 }
3115 if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
3116 return TCL_ERROR;
3117 }
3118 /*tablePtr = (Blt_HashTable *)valuePtr->objPtr; */
3119 for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); hPtr != NULL;
3120 hPtr = Blt_NextHashEntry(&cursor)) {
3121 char *str;
3122
3123 str = Blt_GetHashKey(tablePtr, hPtr);
3124 if (pattern == NULL || Tcl_StringMatch(str, pattern)) {
3125 objPtr = Tcl_NewStringObj(str, -1);
3126 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
3127 }
3128 }
3129 return TCL_OK;
3130 }
3131
3132 int
Blt_TreeArrayValues(Tcl_Interp * interp,TreeClient * clientPtr,Node * nodePtr,CONST char * arrayName,Tcl_Obj * listObjPtr,int names)3133 Blt_TreeArrayValues(
3134 Tcl_Interp *interp,
3135 TreeClient *clientPtr,
3136 Node *nodePtr,
3137 CONST char *arrayName,
3138 Tcl_Obj *listObjPtr,
3139 int names)
3140 {
3141 Blt_HashEntry *hPtr;
3142 Blt_HashSearch cursor;
3143 Blt_HashTable *tablePtr;
3144 Tcl_Obj *objPtr;
3145 Value *valuePtr;
3146 CONST char *key;
3147
3148 key = Blt_TreeKeyGet(interp, clientPtr->treeObject,arrayName);
3149 if ( bltTreeGetValueByKey(interp, clientPtr, nodePtr, key, &valuePtr) != TCL_OK) {
3150 return TCL_ERROR;
3151 }
3152 if (IsTclDict(interp, valuePtr->objPtr)) {
3153 /* Preserve type if this was a dict */
3154
3155 Tcl_DictSearch search;
3156 Tcl_Obj *keyPtr;
3157 int done;
3158 int result;
3159
3160 Tcl_DictObjFirst(NULL, valuePtr->objPtr, &search, &keyPtr, NULL, &done);
3161 for (; !done ; Tcl_DictObjNext(&search, &keyPtr, NULL, &done)) {
3162 Tcl_Obj *valueObjPtr;
3163 if (names) {
3164 Tcl_ListObjAppendElement(NULL, listObjPtr, keyPtr);
3165 }
3166
3167 valueObjPtr = NULL;
3168 result = Tcl_DictObjGet(interp, valuePtr->objPtr, keyPtr, &valueObjPtr);
3169 if (result != TCL_OK) {
3170 continue;
3171 }
3172 if (valueObjPtr == NULL) {
3173 valueObjPtr = Tcl_NewStringObj("",-1);
3174 }
3175 Tcl_ListObjAppendElement(NULL, listObjPtr, valueObjPtr);
3176 }
3177 Tcl_DictObjDone(&search);
3178 return TCL_OK;
3179 }
3180 if (Blt_IsArrayObj(valuePtr->objPtr) == 0 && Tcl_IsShared(valuePtr->objPtr)) {
3181 Tcl_DecrRefCount(valuePtr->objPtr);
3182 valuePtr->objPtr = Tcl_DuplicateObj(valuePtr->objPtr);
3183 Tcl_IncrRefCount(valuePtr->objPtr);
3184 }
3185 if (Blt_GetArrayFromObj(interp, valuePtr->objPtr, &tablePtr) != TCL_OK) {
3186 return TCL_ERROR;
3187 }
3188 /*tablePtr = (Blt_HashTable *)valuePtr->objPtr; */
3189 for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); hPtr != NULL;
3190 hPtr = Blt_NextHashEntry(&cursor)) {
3191 if (names) {
3192 Tcl_ListObjAppendElement(interp, listObjPtr,
3193 Tcl_NewStringObj( Blt_GetHashKey(tablePtr, hPtr), -1));
3194 }
3195 objPtr = Blt_GetHashValue(hPtr);
3196 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr?objPtr:Tcl_NewStringObj("",-1));
3197 }
3198 return TCL_OK;
3199 }
3200
3201
3202 /*
3203 *----------------------------------------------------------------------
3204 *
3205 * Blt_TreeShareTagTable --
3206 *
3207 *----------------------------------------------------------------------
3208 */
3209 int
Blt_TreeShareTagTable(TreeClient * sourcePtr,TreeClient * targetPtr)3210 Blt_TreeShareTagTable(
3211 TreeClient *sourcePtr,
3212 TreeClient *targetPtr)
3213 {
3214 sourcePtr->tagTablePtr->refCount++;
3215 if (targetPtr->tagTablePtr != NULL) {
3216 ReleaseTagTable(targetPtr->tagTablePtr);
3217 }
3218 targetPtr->tagTablePtr = sourcePtr->tagTablePtr;
3219 return TCL_OK;
3220 }
3221
3222 int
Blt_TreeTagTableIsShared(TreeClient * clientPtr)3223 Blt_TreeTagTableIsShared(TreeClient *clientPtr)
3224 {
3225 return (clientPtr->tagTablePtr->refCount > 1);
3226 }
3227
3228 void
Blt_TreeClearTags(TreeClient * clientPtr,Blt_TreeNode node)3229 Blt_TreeClearTags(TreeClient *clientPtr, Blt_TreeNode node)
3230 {
3231 Blt_HashEntry *hPtr, *h2Ptr;
3232 Blt_HashSearch cursor;
3233
3234 for (hPtr = Blt_FirstHashEntry(&clientPtr->tagTablePtr->tagTable, &cursor);
3235 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
3236 Blt_TreeTagEntry *tPtr;
3237
3238 tPtr = Blt_GetHashValue(hPtr);
3239 h2Ptr = Blt_FindHashEntry(&tPtr->nodeTable, (char *)node);
3240 if (h2Ptr != NULL) {
3241 SetModified(node);
3242 Blt_DeleteHashEntry(&tPtr->nodeTable, h2Ptr);
3243 }
3244 }
3245 }
3246
3247 int
Blt_TreeHasTag(TreeClient * clientPtr,Blt_TreeNode node,CONST char * tagName)3248 Blt_TreeHasTag(
3249 TreeClient *clientPtr,
3250 Blt_TreeNode node,
3251 CONST char *tagName)
3252 {
3253 Blt_HashEntry *hPtr;
3254 Blt_TreeTagEntry *tPtr;
3255
3256 if (strcmp(tagName, "all") == 0) {
3257 return TRUE;
3258 }
3259 if (strcmp(tagName, "nonroot") == 0) {
3260 return TRUE;
3261 }
3262 if (strcmp(tagName, "rootchildren") == 0) {
3263 return TRUE;
3264 }
3265 if ((strcmp(tagName, "root") == 0) &&
3266 (node == Blt_TreeRootNode(clientPtr))) {
3267 return TRUE;
3268 }
3269 hPtr = Blt_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName);
3270 if (hPtr == NULL) {
3271 return FALSE;
3272 }
3273 tPtr = Blt_GetHashValue(hPtr);
3274 hPtr = Blt_FindHashEntry(&tPtr->nodeTable, (char *)node);
3275 if (hPtr == NULL) {
3276 return FALSE;
3277 }
3278 return TRUE;
3279 }
3280
3281 int
Blt_TreeAddTag(TreeClient * clientPtr,Blt_TreeNode node,CONST char * tagName)3282 Blt_TreeAddTag(
3283 TreeClient *clientPtr,
3284 Blt_TreeNode node,
3285 CONST char *tagName)
3286 {
3287 int isNew, isNewN, cnt = 0, flags;
3288 Blt_HashEntry *hPtr;
3289 Blt_HashTable *tablePtr;
3290 Blt_TreeTagEntry *tPtr;
3291 Tcl_Interp *interp = clientPtr->treeObject->interp;
3292
3293 if ((strcmp(tagName, "all") == 0) || (strcmp(tagName, "root") == 0)
3294 || (strcmp(tagName, "nonroot") == 0) || (strcmp(tagName, "rootchildren") == 0)) {
3295 Tcl_AppendResult(interp, "reserved tag", 0);
3296 return TCL_ERROR;
3297 }
3298 tablePtr = &clientPtr->tagTablePtr->tagTable;
3299 hPtr = Blt_CreateHashEntry(tablePtr, tagName, &isNewN);
3300 assert(hPtr);
3301 if (isNewN) {
3302
3303 tPtr = Blt_Calloc(sizeof(Blt_TreeTagEntry), 1);
3304 Blt_InitHashTable(&tPtr->nodeTable, BLT_ONE_WORD_KEYS);
3305 Blt_SetHashValue(hPtr, tPtr);
3306 tPtr->hashPtr = hPtr;
3307 tPtr->tagName = Blt_GetHashKey(tablePtr, hPtr);
3308 Blt_TreeTagRefIncr(tPtr);
3309 } else {
3310 tPtr = Blt_GetHashValue(hPtr);
3311 }
3312 if (node == NULL) {
3313 return TCL_OK;
3314 }
3315 if (!(node->flags & TREE_TRACE_ACTIVE)) {
3316 int result;
3317 flags = TREE_TRACE_TAGADD;
3318 if (tPtr->nodeTable.numEntries > 0) {
3319 flags |= TREE_TRACE_TAGMULTIPLE;
3320 }
3321 result = CallTraces(interp, clientPtr, node->treeObject, node, tagName,
3322 flags, &cnt);
3323 if (result == TCL_BREAK) {
3324 return TCL_OK;
3325 }
3326 if (result != TCL_OK) {
3327 return result;
3328 }
3329 }
3330 hPtr = Blt_CreateHashEntry(&tPtr->nodeTable, (char *)node, &isNew);
3331 assert(hPtr);
3332 if (isNew) {
3333 SetModified(node);
3334 Blt_SetHashValue(hPtr, node);
3335 }
3336 return TCL_OK;
3337 }
3338
3339 /* Trigger tag delete traces. */
3340 int
Blt_TreeTagDelTrace(TreeClient * clientPtr,Blt_TreeNode node,CONST char * tagName)3341 Blt_TreeTagDelTrace(
3342 TreeClient *clientPtr,
3343 Blt_TreeNode node,
3344 CONST char *tagName)
3345 {
3346 Tcl_Interp *interp = clientPtr->treeObject->interp;
3347 int cnt;
3348
3349 if (!(node->flags & TREE_TRACE_ACTIVE)) {
3350 return CallTraces(interp, clientPtr, node->treeObject, node, tagName,
3351 (TREE_TRACE_TAGDELETE), &cnt);
3352 }
3353 return TCL_OK;
3354 }
3355
3356 int
Blt_TreeForgetTag(TreeClient * clientPtr,CONST char * tagName)3357 Blt_TreeForgetTag(TreeClient *clientPtr, CONST char *tagName)
3358 {
3359 Blt_HashEntry *hPtr;
3360 if ((strcmp(tagName, "all") == 0) || (strcmp(tagName, "root") == 0)
3361 || (strcmp(tagName, "nonroot") == 0)
3362 || (strcmp(tagName, "rootchildren") == 0)) {
3363 return TCL_OK;
3364 }
3365
3366 hPtr = Blt_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName);
3367 if (hPtr != NULL) {
3368 Blt_TreeTagEntry *tPtr;
3369 Blt_TreeNode node;
3370 Blt_HashSearch cursor;
3371
3372 Blt_DeleteHashEntry(&clientPtr->tagTablePtr->tagTable, hPtr);
3373 tPtr = Blt_GetHashValue(hPtr);
3374 for (hPtr = Blt_FirstHashEntry(&tPtr->nodeTable, &cursor);
3375 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
3376
3377 node = Blt_GetHashKey(&tPtr->nodeTable, hPtr);
3378 if (Blt_TreeTagDelTrace(clientPtr, node, tagName) != TCL_OK) {
3379 return TCL_ERROR;
3380 }
3381 SetModified(node);
3382 }
3383 Blt_DeleteHashTable(&tPtr->nodeTable);
3384 Blt_TreeTagRefDecr(tPtr);
3385 }
3386 return TCL_OK;
3387 }
3388
3389 /*
3390 *----------------------------------------------------------------------
3391 *
3392 * Blt_TreeTagHashTable --
3393 *
3394 *----------------------------------------------------------------------
3395 */
3396 Blt_HashTable *
Blt_TreeTagHashTable(TreeClient * clientPtr,CONST char * tagName)3397 Blt_TreeTagHashTable(TreeClient *clientPtr, CONST char *tagName)
3398 {
3399 Blt_HashEntry *hPtr;
3400
3401 hPtr = Blt_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName);
3402 if (hPtr != NULL) {
3403 Blt_TreeTagEntry *tPtr;
3404
3405 tPtr = Blt_GetHashValue(hPtr);
3406 return &tPtr->nodeTable;
3407 }
3408 return NULL;
3409 }
3410
3411 Blt_TreeTagEntry *
Blt_TreeTagHashEntry(TreeClient * clientPtr,CONST char * tagName)3412 Blt_TreeTagHashEntry(TreeClient *clientPtr, CONST char *tagName)
3413 {
3414 Blt_HashEntry *hPtr;
3415
3416 hPtr = Blt_FindHashEntry(&clientPtr->tagTablePtr->tagTable, tagName);
3417 if (hPtr != NULL) {
3418 Blt_TreeTagEntry *tPtr;
3419
3420 tPtr = Blt_GetHashValue(hPtr);
3421 return tPtr;
3422 }
3423 return NULL;
3424 }
3425
3426 Blt_HashEntry *
Blt_TreeFirstTag(TreeClient * clientPtr,Blt_HashSearch * cursorPtr)3427 Blt_TreeFirstTag(TreeClient *clientPtr, Blt_HashSearch *cursorPtr)
3428 {
3429 return Blt_FirstHashEntry(&clientPtr->tagTablePtr->tagTable, cursorPtr);
3430 }
3431
3432 #if (SIZEOF_VOID_P == 8)
3433 /*
3434 *----------------------------------------------------------------------
3435 *
3436 * HashOneWord --
3437 *
3438 * Compute a one-word hash value of a 64-bit word, which then can
3439 * be used to generate a hash index.
3440 *
3441 * From Knuth, it's a multiplicative hash. Multiplies an unsigned
3442 * 64-bit value with the golden ratio (sqrt(5) - 1) / 2. The
3443 * downshift value is 64 - n, when n is the log2 of the size of
3444 * the hash table.
3445 *
3446 * Results:
3447 * The return value is a one-word summary of the information in
3448 * 64 bit word.
3449 *
3450 * Side effects:
3451 * None.
3452 *
3453 *----------------------------------------------------------------------
3454 */
3455 static Blt_Hash
HashOneWord(uint64_t mask,unsigned int downshift,CONST void * key)3456 HashOneWord(
3457 uint64_t mask,
3458 unsigned int downshift,
3459 CONST void *key)
3460 {
3461 uint64_t a0, a1;
3462 uint64_t y0, y1;
3463 uint64_t y2, y3;
3464 uint64_t p1, p2;
3465 uint64_t result;
3466
3467 /* Compute key * GOLDEN_RATIO in 128-bit arithmetic */
3468 a0 = (uint64_t)key & 0x00000000FFFFFFFF;
3469 a1 = (uint64_t)key >> 32;
3470
3471 y0 = a0 * 0x000000007f4a7c13;
3472 y1 = a0 * 0x000000009e3779b9;
3473 y2 = a1 * 0x000000007f4a7c13;
3474 y3 = a1 * 0x000000009e3779b9;
3475 y1 += y0 >> 32; /* Can't carry */
3476 y1 += y2; /* Might carry */
3477 if (y1 < y2) {
3478 y3 += (1LL << 32); /* Propagate */
3479 }
3480
3481 /* 128-bit product: p1 = loword, p2 = hiword */
3482 p1 = ((y1 & 0x00000000FFFFFFFF) << 32) + (y0 & 0x00000000FFFFFFFF);
3483 p2 = y3 + (y1 >> 32);
3484
3485 /* Left shift the value downward by the size of the table */
3486 if (downshift > 0) {
3487 if (downshift < 64) {
3488 result = ((p2 << (64 - downshift)) | (p1 >> (downshift & 63)));
3489 } else {
3490 result = p2 >> (downshift & 63);
3491 }
3492 } else {
3493 result = p1;
3494 }
3495 /* Finally mask off the high bits */
3496 return (Blt_Hash)(result & mask);
3497 }
3498
3499 #endif /* SIZEOF_VOID_P == 8 */
3500
3501 /*
3502 *----------------------------------------------------------------------
3503 *
3504 * RebuildTable --
3505 *
3506 * This procedure is invoked when the ratio of entries to hash
3507 * buckets becomes too large. It creates a new table with a
3508 * larger bucket array and moves all of the entries into the
3509 * new table.
3510 *
3511 * Results:
3512 * None.
3513 *
3514 * Side effects:
3515 * Memory gets reallocated and entries get re-hashed to new
3516 * buckets.
3517 *
3518 *----------------------------------------------------------------------
3519 */
3520 static void
RebuildTable(Node * nodePtr)3521 RebuildTable(Node *nodePtr) /* Table to enlarge. */
3522 {
3523 Value **newBucketPtr, **oldBuckets;
3524 register Value **bucketPtr, **endPtr;
3525 register Value *valuePtr, *nextPtr;
3526 unsigned int downshift;
3527 unsigned long mask;
3528 Value **buckets;
3529 size_t nBuckets;
3530
3531 oldBuckets = (Value **)nodePtr->values;
3532 nBuckets = (1 << nodePtr->logSize);
3533 endPtr = oldBuckets + nBuckets;
3534
3535 /*
3536 * Allocate and initialize the new bucket array, and set up
3537 * hashing constants for new array size.
3538 */
3539 nodePtr->logSize += 2;
3540 nBuckets = (1 << nodePtr->logSize);
3541 buckets = Blt_Calloc(nBuckets, sizeof(Value *));
3542
3543 /*
3544 * Move all of the existing entries into the new bucket array,
3545 * based on their hash values.
3546 */
3547 mask = nBuckets - 1;
3548 downshift = DOWNSHIFT_START - nodePtr->logSize;
3549 for (bucketPtr = oldBuckets; bucketPtr < endPtr; bucketPtr++) {
3550 for (valuePtr = *bucketPtr; valuePtr != NULL; valuePtr = nextPtr) {
3551 nextPtr = valuePtr->next;
3552 newBucketPtr = buckets + RANDOM_INDEX(valuePtr->key);
3553 valuePtr->next = *newBucketPtr;
3554 *newBucketPtr = valuePtr;
3555 }
3556 }
3557 nodePtr->values = (Value *)buckets;
3558 Blt_Free(oldBuckets);
3559 }
3560
3561 static void
ConvertValues(Node * nodePtr)3562 ConvertValues(Node *nodePtr)
3563 {
3564 unsigned int nBuckets;
3565 Value **buckets;
3566 unsigned int mask;
3567 int downshift;
3568 Value *valuePtr, *nextPtr, **bucketPtr;
3569
3570 /*
3571 * Convert list of values into a hash table.
3572 */
3573 nodePtr->logSize = START_LOGSIZE;
3574 nBuckets = 1 << nodePtr->logSize;
3575 buckets = Blt_Calloc(nBuckets, sizeof(Value *));
3576 mask = nBuckets - 1;
3577 downshift = DOWNSHIFT_START - nodePtr->logSize;
3578 for (valuePtr = nodePtr->values; valuePtr != NULL; valuePtr = nextPtr) {
3579 nextPtr = valuePtr->next;
3580 bucketPtr = buckets + RANDOM_INDEX(valuePtr->key);
3581 valuePtr->next = *bucketPtr;
3582 *bucketPtr = valuePtr;
3583 }
3584 nodePtr->values = (Value *)buckets;
3585 }
3586
3587 /*
3588 *----------------------------------------------------------------------
3589 *
3590 * TreeDeleteValue --
3591 *
3592 * Remove a single entry from a hash table.
3593 *
3594 * Results:
3595 * None.
3596 *
3597 * Side effects:
3598 * The entry given by entryPtr is deleted from its table and
3599 * should never again be used by the caller. It is up to the
3600 * caller to free the clientData field of the entry, if that
3601 * is relevant.
3602 *
3603 *----------------------------------------------------------------------
3604 */
3605 static int
TreeDeleteValue(Node * nodePtr,Blt_TreeValue value)3606 TreeDeleteValue(Node *nodePtr, Blt_TreeValue value)
3607 {
3608 Value *valuePtr = value;
3609 register Value *prevPtr;
3610
3611 if (nodePtr->logSize > 0) {
3612 Value **bucketPtr;
3613 unsigned int downshift;
3614 unsigned long mask;
3615
3616 mask = (1 << nodePtr->logSize) - 1;
3617 downshift = DOWNSHIFT_START - nodePtr->logSize;
3618
3619 bucketPtr = (Value **)nodePtr->values + RANDOM_INDEX(valuePtr->key);
3620 if (*bucketPtr == valuePtr) {
3621 *bucketPtr = valuePtr->next;
3622 } else {
3623 for (prevPtr = *bucketPtr; /*empty*/; prevPtr = prevPtr->next) {
3624 if (prevPtr == NULL) {
3625 return TCL_ERROR; /* Can't find value in hash bucket. */
3626 }
3627 if (prevPtr->next == valuePtr) {
3628 prevPtr->next = valuePtr->next;
3629 break;
3630 }
3631 }
3632 }
3633 } else {
3634 prevPtr = NULL;
3635 for (valuePtr = nodePtr->values; valuePtr != NULL;
3636 valuePtr = valuePtr->next) {
3637 if (valuePtr == value) {
3638 break;
3639 }
3640 prevPtr = valuePtr;
3641 }
3642 if (valuePtr == NULL) {
3643 return TCL_ERROR; /* Can't find value in list. */
3644 }
3645 if (prevPtr == NULL) {
3646 nodePtr->values = valuePtr->next;
3647 } else {
3648 prevPtr->next = valuePtr->next;
3649 }
3650 }
3651 nodePtr->nValues--;
3652 FreeValue(nodePtr, valuePtr);
3653 return TCL_OK;
3654 }
3655
3656 /*
3657 *----------------------------------------------------------------------
3658 *
3659 * TreeDestroyValues --
3660 *
3661 * Free up everything associated with a hash table except for
3662 * the record for the table itself.
3663 *
3664 * Results:
3665 * None.
3666 *
3667 * Side effects:
3668 * The hash table is no longer useable.
3669 *
3670 *----------------------------------------------------------------------
3671 */
3672 static void
TreeDestroyValues(Node * nodePtr)3673 TreeDestroyValues(Node *nodePtr)
3674 {
3675 register Value *valuePtr;
3676 Value *nextPtr;
3677
3678 /*
3679 * Free up all the entries in the table.
3680 */
3681 if (nodePtr->values == NULL) {
3682 return;
3683 }
3684 if (nodePtr->logSize > 0) {
3685 Value **buckets;
3686 int nBuckets;
3687 int i;
3688
3689 buckets = (Value **)nodePtr->values;
3690 nBuckets = (1 << nodePtr->logSize);
3691 for (i = 0; i < nBuckets; i++) {
3692 for (valuePtr = buckets[i]; valuePtr != NULL; valuePtr = nextPtr) {
3693 nextPtr = valuePtr->next;
3694 FreeValue(nodePtr, valuePtr);
3695 }
3696 }
3697 Blt_Free(buckets);
3698 } else {
3699 for (valuePtr = nodePtr->values; valuePtr != NULL; valuePtr = nextPtr) {
3700 nextPtr = valuePtr->next;
3701 FreeValue(nodePtr, valuePtr);
3702 }
3703 }
3704 nodePtr->values = NULL;
3705 nodePtr->nValues = 0;
3706 nodePtr->logSize = 0;
3707 }
3708
3709 /*
3710 *----------------------------------------------------------------------
3711 *
3712 * TreeFirstValue --
3713 *
3714 * Locate the first entry in a hash table and set up a record
3715 * that can be used to step through all the remaining entries
3716 * of the table.
3717 *
3718 * Results:
3719 * The return value is a pointer to the first value in tablePtr,
3720 * or NULL if tablePtr has no entries in it. The memory at
3721 * *searchPtr is initialized so that subsequent calls to
3722 * Blt_TreeNextValue will return all of the values in the table,
3723 * one at a time.
3724 *
3725 * Side effects:
3726 * None.
3727 *
3728 *----------------------------------------------------------------------
3729 */
3730 static Value *
TreeFirstValue(Node * nodePtr,Blt_TreeKeySearch * searchPtr)3731 TreeFirstValue(
3732 Node *nodePtr,
3733 Blt_TreeKeySearch *searchPtr) /* Place to store information about
3734 * progress through the table. */
3735 {
3736 searchPtr->node = nodePtr;
3737 searchPtr->nextIndex = 0;
3738 searchPtr->cnt = 1;
3739 if (nodePtr->logSize > 0) {
3740 searchPtr->nextValue = NULL;
3741 } else {
3742 searchPtr->nextValue = nodePtr->values;
3743 }
3744 return TreeNextValue(searchPtr);
3745 }
3746
3747 /*
3748 *----------------------------------------------------------------------
3749 *
3750 * TreeNextValue --
3751 *
3752 * Once a hash table enumeration has been initiated by calling
3753 * Blt_TreeFirstValue, this procedure may be called to return
3754 * successive elements of the table.
3755 *
3756 * Results:
3757 * The return value is the next entry in the hash table being
3758 * enumerated, or NULL if the end of the table is reached.
3759 *
3760 * Side effects:
3761 * None.
3762 *
3763 *----------------------------------------------------------------------
3764 */
3765 static Value *
TreeNextValue(Blt_TreeKeySearch * searchPtr)3766 TreeNextValue(
3767 Blt_TreeKeySearch *searchPtr) /* Place to store information about
3768 * progress through the table. Must
3769 * have been initialized by calling
3770 * Blt_TreeFirstValue. */
3771 {
3772 Value *valuePtr;
3773
3774 if (searchPtr->node->logSize > 0) {
3775 size_t nBuckets;
3776 Value **buckets;
3777
3778 nBuckets = (1 << searchPtr->node->logSize);
3779 buckets = (Value **)searchPtr->node->values;
3780 while (searchPtr->nextValue == NULL) {
3781 if (searchPtr->nextIndex >= nBuckets) {
3782 return NULL;
3783 }
3784 searchPtr->nextValue = buckets[searchPtr->nextIndex];
3785 searchPtr->nextIndex++;
3786 }
3787 }
3788 searchPtr->cnt++;
3789 /* Sanity check. */
3790 if (searchPtr->cnt>100000000) return NULL;
3791 valuePtr = searchPtr->nextValue;
3792 if (valuePtr != NULL) {
3793 searchPtr->nextValue = valuePtr->next;
3794 }
3795 return valuePtr;
3796 }
3797
3798 /*
3799 *----------------------------------------------------------------------
3800 *
3801 * TreeFindValue --
3802 *
3803 * Given a hash table with one-word keys, and a one-word key, find
3804 * the entry with a matching key.
3805 *
3806 * Results:
3807 * The return value is a token for the matching entry in the
3808 * hash table, or NULL if there was no matching entry.
3809 *
3810 * Side effects:
3811 * None.
3812 *
3813 *----------------------------------------------------------------------
3814 */
3815 static Value *
TreeFindValue(Node * nodePtr,Blt_TreeKey key)3816 TreeFindValue(
3817 Node *nodePtr,
3818 Blt_TreeKey key) /* Key to use to find matching entry. */
3819 {
3820 register Value *valuePtr;
3821 Value *bucket;
3822
3823 if (nodePtr->logSize > 0) {
3824 unsigned int downshift;
3825 unsigned long mask;
3826
3827 mask = (1 << nodePtr->logSize) - 1;
3828 downshift = DOWNSHIFT_START - nodePtr->logSize;
3829 bucket = ((Value **)(nodePtr->values))[RANDOM_INDEX((void *)key)];
3830 } else {
3831 bucket = nodePtr->values; /* Single chain list. */
3832 }
3833 /*
3834 * Search all of the entries in the appropriate bucket.
3835 */
3836 for (valuePtr = bucket; valuePtr != NULL; valuePtr = valuePtr->next) {
3837 if (valuePtr->key == key) {
3838 return valuePtr;
3839 }
3840 }
3841 return NULL;
3842 }
3843
3844 /*
3845 *----------------------------------------------------------------------
3846 *
3847 * Blt_TreeCreateValue --
3848 *
3849 * Find the value with a matching key. If there is no matching
3850 * value, then create a new one.
3851 *
3852 * Results:
3853 * The return value is a pointer to the matching value. If this
3854 * is a newly-created value, then *newPtr will be set to a non-zero
3855 * value; otherwise *newPtr will be set to 0.
3856 *
3857 * Side effects:
3858 * A new value may be added to the hash table.
3859 *
3860 *----------------------------------------------------------------------
3861 */
3862 static Value *
TreeCreateValue(Node * nodePtr,Blt_TreeKey key,int * newPtr)3863 TreeCreateValue(
3864 Node *nodePtr,
3865 Blt_TreeKey key, /* Key to use to find or create matching
3866 * entry. */
3867 int *newPtr) /* Store info here telling whether a new
3868 * entry was created. */
3869 {
3870 register Value *valuePtr;
3871 TreeObject *treeObjPtr = nodePtr->treeObject;
3872 int maxVals;
3873
3874 if (treeObjPtr->maxKeyList>0) {
3875 maxVals = treeObjPtr->maxKeyList;
3876 } else {
3877 maxVals = MAX_LIST_VALUES;
3878 }
3879 /*
3880 * Check if there as so many values that storage should be
3881 * converted from a hash table instead of a list.
3882 */
3883 if ((nodePtr->logSize == 0) && (nodePtr->nValues >= maxVals)) {
3884 ConvertValues(nodePtr);
3885 }
3886 if (nodePtr->logSize > 0) {
3887 Value **bucketPtr;
3888 size_t nBuckets;
3889 unsigned int downshift;
3890 unsigned long mask;
3891
3892 nBuckets = (1 << nodePtr->logSize);
3893 mask = nBuckets - 1;
3894 downshift = DOWNSHIFT_START - nodePtr->logSize;
3895 bucketPtr = (Value **)nodePtr->values + RANDOM_INDEX((void *)key);
3896
3897 *newPtr = FALSE;
3898 for (valuePtr = *bucketPtr; valuePtr != NULL;
3899 valuePtr = valuePtr->next) {
3900 if (valuePtr->key == key) {
3901 return valuePtr;
3902 }
3903 }
3904
3905 /* Value not found. Add a new value to the bucket. */
3906 *newPtr = TRUE;
3907 valuePtr = Blt_PoolAllocItem(nodePtr->treeObject->valuePool,
3908 sizeof(Value));
3909 valuePtr->key = key;
3910 valuePtr->owner = NULL;
3911 valuePtr->next = *bucketPtr;
3912 valuePtr->objPtr = NULL;
3913 *bucketPtr = valuePtr;
3914 nodePtr->nValues++;
3915
3916 /*
3917 * If the table has exceeded a decent size, rebuild it with many
3918 * more buckets.
3919 */
3920 if ((unsigned int)nodePtr->nValues >= (nBuckets * 3)) {
3921 RebuildTable(nodePtr);
3922 }
3923 } else {
3924 Value *prevPtr;
3925
3926 prevPtr = NULL;
3927 *newPtr = FALSE;
3928 for (valuePtr = nodePtr->values; valuePtr != NULL;
3929 valuePtr = valuePtr->next) {
3930 if (valuePtr->key == key) {
3931 return valuePtr;
3932 }
3933 prevPtr = valuePtr;
3934 }
3935 /* Value not found. Add a new value to the list. */
3936 *newPtr = TRUE;
3937 valuePtr = Blt_PoolAllocItem(nodePtr->treeObject->valuePool,
3938 sizeof(Value));
3939 valuePtr->key = key;
3940 valuePtr->owner = NULL;
3941 valuePtr->next = NULL;
3942 valuePtr->objPtr = NULL;
3943 if (prevPtr == NULL) {
3944 nodePtr->values = valuePtr;
3945 } else {
3946 prevPtr->next = valuePtr;
3947 }
3948 nodePtr->nValues++;
3949 }
3950 return valuePtr;
3951 }
3952
3953
Blt_TreeEndNode(Blt_TreeNode node,unsigned int nodeFlags)3954 Blt_TreeNode Blt_TreeEndNode (Blt_TreeNode node,
3955 unsigned int nodeFlags) {
3956 /* TODO: finish. */
3957 return NULL;
3958 }
3959
3960 #undef Blt_TreeFirstChild
3961 #undef Blt_TreeLastChild
3962 #undef Blt_TreeNextSibling
3963 #undef Blt_TreePrevSibling
3964 #undef Blt_TreeChangeRoot
3965
Blt_TreeFirstChild(Blt_TreeNode parent)3966 Blt_TreeNode Blt_TreeFirstChild (Blt_TreeNode parent) {
3967 return parent->first;
3968 }
3969
Blt_TreeNextSibling(Blt_TreeNode node)3970 Blt_TreeNode Blt_TreeNextSibling (Blt_TreeNode node) {
3971 return (node == NULL ? NULL : node->next);
3972 }
3973
Blt_TreeLastChild(Blt_TreeNode parent)3974 Blt_TreeNode Blt_TreeLastChild (Blt_TreeNode parent) {
3975 return parent->last;
3976 }
3977
Blt_TreePrevSibling(Blt_TreeNode node)3978 Blt_TreeNode Blt_TreePrevSibling (Blt_TreeNode node) {
3979 return (node == NULL ? NULL : node->prev);
3980 }
3981
Blt_TreeChangeRoot(Blt_Tree tree,Blt_TreeNode node)3982 Blt_TreeNode Blt_TreeChangeRoot (Blt_Tree tree, Blt_TreeNode node) {
3983 tree->root = node;
3984 return node;
3985 }
3986
3987 #endif /* NO_TREE */
3988