source: trunk/kitgen/8.x/blt/generic/bltTreeCmd.c@ 201

Last change on this file since 201 was 175, checked in by demin, 12 years ago

initial commit

File size: 146.1 KB
RevLine 
[175]1
2/*
3 *
4 * bltTreeCmd.c --
5 *
6 * Copyright 1998-1999 Lucent Technologies, Inc.
7 *
8 * Permission to use, copy, modify, and distribute this software and
9 * its documentation for any purpose and without fee is hereby
10 * granted, provided that the above copyright notice appear in all
11 * copies and that both that the copyright notice and warranty
12 * disclaimer appear in supporting documentation, and that the names
13 * of Lucent Technologies or any of their entities not be used in
14 * advertising or publicity pertaining to distribution of the software
15 * without specific, written prior permission.
16 *
17 * Lucent Technologies disclaims all warranties with regard to this
18 * software, including all implied warranties of merchantability and
19 * fitness. In no event shall Lucent Technologies be liable for any
20 * special, indirect or consequential damages or any damages
21 * whatsoever resulting from loss of use, data or profits, whether in
22 * an action of contract, negligence or other tortuous action, arising
23 * out of or in connection with the use or performance of this
24 * software.
25 *
26 * The "tree" data object was created by George A. Howlett.
27 */
28
29/*
30 tree create t0 t1 t2
31 tree names
32 t0 destroy
33 -or-
34 tree destroy t0
35 tree copy tree@node tree@node -recurse -tags
36
37 tree move node after|before|into t2@node
38
39 $t apply -recurse $root command arg arg
40
41 $t attach treename
42
43 $t children $n
44 t0 copy node1 node2 node3 node4 node5 destName
45 $t delete $n...
46 $t depth $n
47 $t dump $root
48 $t dumpfile $root fileName
49 $t dup $t2
50 $t find $root -name pat -name pattern
51 $t firstchild $n
52 $t get $n $key
53 $t get $n $key(abc)
54 $t index $n
55 $t insert $parent $switches?
56 $t isancestor $n1 $n2
57 $t isbefore $n1 $n2
58 $t isleaf $n
59 $t lastchild $n
60 $t move $n1 after|before|into $n2
61 $t next $n
62 $t nextsibling $n
63 $t path $n1 $n2 $n3...
64 $t parent $n
65 $t previous $n
66 $t prevsibling $n
67 $t restore $root data -overwrite
68 $t root ?$n?
69
70 $t set $n $key $value ?$key $value?
71 $t size $n
72 $t slink $n $t2@$node ???
73 $t sort -recurse $root
74
75 $t tag delete tag1 tag2 tag3...
76 $t tag names
77 $t tag nodes $tag
78 $t tag set $n tag1 tag2 tag3...
79 $t tag unset $n tag1 tag2 tag3...
80
81 $t trace create $n $key how command
82 $t trace delete id1 id2 id3...
83 $t trace names
84 $t trace info $id
85
86 $t unset $n key1 key2 key3...
87
88 $t notify create -oncreate -ondelete -onmove command
89 $t notify create -oncreate -ondelete -onmove -onsort command arg arg arg
90 $t notify delete id1 id2 id3
91 $t notify names
92 $t notify info id
93
94 for { set n [$t firstchild $node] } { $n >= 0 } {
95 set n [$t nextsibling $n] } {
96 }
97 foreach n [$t children $node] {
98
99 }
100 set n [$t next $node]
101 set n [$t previous $node]
102
103*/
104
105#include <bltInt.h>
106
107#ifndef NO_TREE
108
109#include <bltTree.h>
110#include <bltHash.h>
111#include <bltList.h>
112#include "bltSwitch.h"
113#include <ctype.h>
114
115#define TREE_THREAD_KEY "BLT Tree Command Data"
116#define TREE_MAGIC ((unsigned int) 0x46170277)
117
118enum TagTypes { TAG_TYPE_NONE, TAG_TYPE_ALL, TAG_TYPE_TAG };
119
120typedef struct {
121 Blt_HashTable treeTable; /* Hash table of trees keyed by address. */
122 Tcl_Interp *interp;
123} TreeCmdInterpData;
124
125typedef struct {
126 Tcl_Interp *interp;
127 Tcl_Command cmdToken; /* Token for tree's Tcl command. */
128 Blt_Tree tree; /* Token holding internal tree. */
129
130 Blt_HashEntry *hashPtr;
131 Blt_HashTable *tablePtr;
132
133 TreeCmdInterpData *dataPtr; /* */
134
135 int traceCounter; /* Used to generate trace id strings. */
136 Blt_HashTable traceTable; /* Table of active traces. Maps trace ids
137 * back to their TraceInfo records. */
138
139 int notifyCounter; /* Used to generate notify id strings. */
140 Blt_HashTable notifyTable; /* Table of event handlers. Maps notify ids
141 * back to their NotifyInfo records. */
142} TreeCmd;
143
144typedef struct {
145 TreeCmd *cmdPtr;
146 Blt_TreeNode node;
147
148 Blt_TreeTrace traceToken;
149
150 char *withTag; /* If non-NULL, the event or trace was
151 * specified with this tag. This
152 * value is saved for informational
153 * purposes. The tree's trace
154 * matching routines do the real
155 * checking, not the client's
156 * callback. */
157
158 char command[1]; /* Command prefix for the trace or notify
159 * Tcl callback. Extra arguments will be
160 * appended to the end. Extra space will
161 * be allocated for the length of the string
162 */
163} TraceInfo;
164
165typedef struct {
166 TreeCmd *cmdPtr;
167 int mask;
168 Tcl_Obj **objv;
169 int objc;
170 Blt_TreeNode node; /* Node affected by event. */
171 Blt_TreeTrace notifyToken;
172} NotifyInfo;
173
174
175typedef struct {
176 int mask;
177} NotifyData;
178
179static Blt_SwitchSpec notifySwitches[] =
180{
181 {BLT_SWITCH_FLAG, "-create", Blt_Offset(NotifyData, mask), 0, 0,
182 TREE_NOTIFY_CREATE},
183 {BLT_SWITCH_FLAG, "-delete", Blt_Offset(NotifyData, mask), 0, 0,
184 TREE_NOTIFY_DELETE},
185 {BLT_SWITCH_FLAG, "-move", Blt_Offset(NotifyData, mask), 0, 0,
186 TREE_NOTIFY_MOVE},
187 {BLT_SWITCH_FLAG, "-sort", Blt_Offset(NotifyData, mask), 0, 0,
188 TREE_NOTIFY_SORT},
189 {BLT_SWITCH_FLAG, "-relabel", Blt_Offset(NotifyData, mask), 0, 0,
190 TREE_NOTIFY_RELABEL},
191 {BLT_SWITCH_FLAG, "-allevents", Blt_Offset(NotifyData, mask), 0, 0,
192 TREE_NOTIFY_ALL},
193 {BLT_SWITCH_FLAG, "-whenidle", Blt_Offset(NotifyData, mask), 0, 0,
194 TREE_NOTIFY_WHENIDLE},
195 {BLT_SWITCH_END, NULL, 0, 0}
196};
197
198static Blt_SwitchParseProc StringToChild;
199#define INSERT_BEFORE (ClientData)0
200#define INSERT_AFTER (ClientData)1
201static Blt_SwitchCustom beforeSwitch =
202{
203 StringToChild, (Blt_SwitchFreeProc *)NULL, INSERT_BEFORE,
204};
205static Blt_SwitchCustom afterSwitch =
206{
207 StringToChild, (Blt_SwitchFreeProc *)NULL, INSERT_AFTER,
208};
209
210typedef struct {
211 char *label;
212 int insertPos;
213 int inode;
214 char **tags;
215 char **dataPairs;
216 Blt_TreeNode parent;
217} InsertData;
218
219static Blt_SwitchSpec insertSwitches[] =
220{
221 {BLT_SWITCH_CUSTOM, "-after", Blt_Offset(InsertData, insertPos), 0,
222 &afterSwitch},
223 {BLT_SWITCH_INT_NONNEGATIVE, "-at", Blt_Offset(InsertData, insertPos), 0},
224 {BLT_SWITCH_CUSTOM, "-before", Blt_Offset(InsertData, insertPos), 0,
225 &beforeSwitch},
226 {BLT_SWITCH_LIST, "-data", Blt_Offset(InsertData, dataPairs), 0},
227 {BLT_SWITCH_STRING, "-label", Blt_Offset(InsertData, label), 0},
228 {BLT_SWITCH_INT_NONNEGATIVE, "-node", Blt_Offset(InsertData, inode), 0},
229 {BLT_SWITCH_LIST, "-tags", Blt_Offset(InsertData, tags), 0},
230 {BLT_SWITCH_END, NULL, 0, 0}
231};
232
233#define PATTERN_EXACT (1)
234#define PATTERN_GLOB (2)
235#define PATTERN_MASK (0x3)
236#define PATTERN_NONE (0)
237#define PATTERN_REGEXP (3)
238#define MATCH_INVERT (1<<8)
239#define MATCH_LEAFONLY (1<<4)
240#define MATCH_NOCASE (1<<5)
241#define MATCH_PATHNAME (1<<6)
242
243typedef struct {
244 TreeCmd *cmdPtr; /* Tree to examine. */
245 Tcl_Obj *listObjPtr; /* List to accumulate the indices of
246 * matching nodes. */
247 Tcl_Obj **objv; /* Command converted into an array of
248 * Tcl_Obj's. */
249 int objc; /* Number of Tcl_Objs in above array. */
250
251 int nMatches; /* Current number of matches. */
252
253 unsigned int flags; /* See flags definitions above. */
254
255 /* Integer options. */
256 int maxMatches; /* If > 0, stop after this many matches. */
257 int maxDepth; /* If > 0, don't descend more than this
258 * many levels. */
259 int order; /* Order of search: Can be either
260 * TREE_PREORDER, TREE_POSTORDER,
261 * TREE_INORDER, TREE_BREADTHFIRST. */
262 /* String options. */
263 Blt_List patternList; /* List of patterns to compare with labels
264 * or values. */
265 char *addTag; /* If non-NULL, tag added to selected nodes. */
266
267 char **command; /* Command split into a Tcl list. */
268
269 Blt_List keyList; /* List of key name patterns. */
270 char *withTag;
271
272} FindData;
273
274static Blt_SwitchParseProc StringToOrder;
275static Blt_SwitchCustom orderSwitch =
276{
277 StringToOrder, (Blt_SwitchFreeProc *)NULL, (ClientData)0,
278};
279
280static Blt_SwitchParseProc StringToPattern;
281static Blt_SwitchFreeProc FreePatterns;
282
283static Blt_SwitchCustom regexpSwitch =
284{
285 StringToPattern, FreePatterns, (ClientData)PATTERN_REGEXP,
286};
287static Blt_SwitchCustom globSwitch =
288{
289 StringToPattern, FreePatterns, (ClientData)PATTERN_GLOB,
290};
291static Blt_SwitchCustom exactSwitch =
292{
293 StringToPattern, FreePatterns, (ClientData)PATTERN_EXACT,
294};
295
296
297static Blt_SwitchSpec findSwitches[] =
298{
299 {BLT_SWITCH_STRING, "-addtag", Blt_Offset(FindData, addTag), 0},
300 {BLT_SWITCH_INT_NONNEGATIVE, "-count", Blt_Offset(FindData, maxMatches), 0},
301 {BLT_SWITCH_INT_NONNEGATIVE, "-depth", Blt_Offset(FindData, maxDepth), 0},
302 {BLT_SWITCH_CUSTOM, "-exact", Blt_Offset(FindData, patternList), 0,
303 &exactSwitch},
304 {BLT_SWITCH_LIST, "-exec", Blt_Offset(FindData, command), 0},
305 {BLT_SWITCH_CUSTOM, "-glob", Blt_Offset(FindData, patternList), 0,
306 &globSwitch},
307 {BLT_SWITCH_FLAG, "-invert", Blt_Offset(FindData, flags), 0, 0,
308 MATCH_INVERT},
309 {BLT_SWITCH_CUSTOM, "-key", Blt_Offset(FindData, keyList), 0, &exactSwitch},
310 {BLT_SWITCH_CUSTOM, "-keyexact", Blt_Offset(FindData, keyList), 0,
311 &exactSwitch},
312 {BLT_SWITCH_CUSTOM, "-keyglob", Blt_Offset(FindData, keyList), 0,
313 &globSwitch},
314 {BLT_SWITCH_CUSTOM, "-keyregexp", Blt_Offset(FindData, keyList), 0,
315 &regexpSwitch},
316 {BLT_SWITCH_FLAG, "-leafonly", Blt_Offset(FindData, flags), 0, 0,
317 MATCH_LEAFONLY},
318 {BLT_SWITCH_FLAG, "-nocase", Blt_Offset(FindData, flags), 0, 0,
319 MATCH_NOCASE},
320 {BLT_SWITCH_CUSTOM, "-order", Blt_Offset(FindData, order), 0, &orderSwitch},
321 {BLT_SWITCH_FLAG, "-path", Blt_Offset(FindData, flags), 0, 0,
322 MATCH_PATHNAME},
323 {BLT_SWITCH_CUSTOM, "-regexp", Blt_Offset(FindData, patternList), 0,
324 &regexpSwitch},
325 {BLT_SWITCH_STRING, "-tag", Blt_Offset(FindData, withTag), 0},
326 {BLT_SWITCH_END, NULL, 0, 0}
327};
328
329static Blt_SwitchParseProc StringToNode;
330static Blt_SwitchCustom nodeSwitch =
331{
332 StringToNode, (Blt_SwitchFreeProc *)NULL, (ClientData)0,
333};
334
335typedef struct {
336 TreeCmd *cmdPtr; /* Tree to move nodes. */
337 Blt_TreeNode node;
338 int movePos;
339} MoveData;
340
341static Blt_SwitchSpec moveSwitches[] =
342{
343 {BLT_SWITCH_CUSTOM, "-after", Blt_Offset(MoveData, node), 0, &nodeSwitch},
344 {BLT_SWITCH_INT_NONNEGATIVE, "-at", Blt_Offset(MoveData, movePos), 0},
345 {BLT_SWITCH_CUSTOM, "-before", Blt_Offset(MoveData, node), 0, &nodeSwitch},
346 {BLT_SWITCH_END, NULL, 0, 0}
347};
348
349typedef struct {
350 Blt_TreeNode srcNode, destNode;
351 Blt_Tree srcTree, destTree;
352 TreeCmd *srcPtr, *destPtr;
353 unsigned int flags;
354 char *label;
355} CopyData;
356
357#define COPY_RECURSE (1<<0)
358#define COPY_TAGS (1<<1)
359#define COPY_OVERWRITE (1<<2)
360
361static Blt_SwitchSpec copySwitches[] =
362{
363 {BLT_SWITCH_STRING, "-label", Blt_Offset(CopyData, label), 0},
364 {BLT_SWITCH_FLAG, "-recurse", Blt_Offset(CopyData, flags), 0, 0,
365 COPY_RECURSE},
366 {BLT_SWITCH_FLAG, "-tags", Blt_Offset(CopyData, flags), 0, 0,
367 COPY_TAGS},
368 {BLT_SWITCH_FLAG, "-overwrite", Blt_Offset(CopyData, flags), 0, 0,
369 COPY_OVERWRITE},
370 {BLT_SWITCH_END, NULL, 0, 0}
371};
372
373typedef struct {
374 TreeCmd *cmdPtr; /* Tree to examine. */
375 Tcl_Obj **preObjv; /* Command converted into an array of
376 * Tcl_Obj's. */
377 int preObjc; /* Number of Tcl_Objs in above array. */
378
379 Tcl_Obj **postObjv; /* Command converted into an array of
380 * Tcl_Obj's. */
381 int postObjc; /* Number of Tcl_Objs in above array. */
382
383 unsigned int flags; /* See flags definitions above. */
384
385 int maxDepth; /* If > 0, don't descend more than this
386 * many levels. */
387 /* String options. */
388 Blt_List patternList; /* List of label or value patterns. */
389 char **preCmd; /* Pre-command split into a Tcl list. */
390 char **postCmd; /* Post-command split into a Tcl list. */
391
392 Blt_List keyList; /* List of key-name patterns. */
393 char *withTag;
394} ApplyData;
395
396static Blt_SwitchSpec applySwitches[] =
397{
398 {BLT_SWITCH_LIST, "-precommand", Blt_Offset(ApplyData, preCmd), 0},
399 {BLT_SWITCH_LIST, "-postcommand", Blt_Offset(ApplyData, postCmd), 0},
400 {BLT_SWITCH_INT_NONNEGATIVE, "-depth", Blt_Offset(ApplyData, maxDepth), 0},
401 {BLT_SWITCH_CUSTOM, "-exact", Blt_Offset(ApplyData, patternList), 0,
402 &exactSwitch},
403 {BLT_SWITCH_CUSTOM, "-glob", Blt_Offset(ApplyData, patternList), 0,
404 &globSwitch},
405 {BLT_SWITCH_FLAG, "-invert", Blt_Offset(ApplyData, flags), 0, 0,
406 MATCH_INVERT},
407 {BLT_SWITCH_CUSTOM, "-key", Blt_Offset(ApplyData, keyList), 0,
408 &exactSwitch},
409 {BLT_SWITCH_CUSTOM, "-keyexact", Blt_Offset(ApplyData, keyList), 0,
410 &exactSwitch},
411 {BLT_SWITCH_CUSTOM, "-keyglob", Blt_Offset(ApplyData, keyList), 0,
412 &globSwitch},
413 {BLT_SWITCH_CUSTOM, "-keyregexp", Blt_Offset(ApplyData, keyList), 0,
414 &regexpSwitch},
415 {BLT_SWITCH_FLAG, "-leafonly", Blt_Offset(ApplyData, flags), 0, 0,
416 MATCH_LEAFONLY},
417 {BLT_SWITCH_FLAG, "-nocase", Blt_Offset(ApplyData, flags), 0, 0,
418 MATCH_NOCASE},
419 {BLT_SWITCH_FLAG, "-path", Blt_Offset(ApplyData, flags), 0, 0,
420 MATCH_PATHNAME},
421 {BLT_SWITCH_CUSTOM, "-regexp", Blt_Offset(ApplyData, patternList), 0,
422 &regexpSwitch},
423 {BLT_SWITCH_STRING, "-tag", Blt_Offset(ApplyData, withTag), 0},
424 {BLT_SWITCH_END, NULL, 0, 0}
425};
426
427typedef struct {
428 unsigned int flags;
429 Blt_HashTable idTable;
430 Blt_TreeNode root;
431} RestoreData;
432
433#define RESTORE_NO_TAGS (1<<0)
434#define RESTORE_OVERWRITE (1<<1)
435
436static Blt_SwitchSpec restoreSwitches[] =
437{
438 {BLT_SWITCH_FLAG, "-notags", Blt_Offset(RestoreData, flags), 0, 0,
439 RESTORE_NO_TAGS},
440 {BLT_SWITCH_FLAG, "-overwrite", Blt_Offset(RestoreData, flags), 0, 0,
441 RESTORE_OVERWRITE},
442 {BLT_SWITCH_END, NULL, 0, 0}
443};
444
445static Blt_SwitchParseProc StringToFormat;
446static Blt_SwitchCustom formatSwitch =
447{
448 StringToFormat, (Blt_SwitchFreeProc *)NULL, (ClientData)0,
449};
450
451typedef struct {
452 int sort; /* If non-zero, sort the nodes. */
453 int withParent; /* If non-zero, add the parent node id
454 * to the output of the command.*/
455 int withId; /* If non-zero, echo the node id in the
456 * output of the command. */
457} PositionData;
458
459#define POSITION_SORTED (1<<0)
460
461static Blt_SwitchSpec positionSwitches[] =
462{
463 {BLT_SWITCH_FLAG, "-sort", Blt_Offset(PositionData, sort), 0, 0,
464 POSITION_SORTED},
465 {BLT_SWITCH_CUSTOM, "-format", 0, 0, &formatSwitch},
466 {BLT_SWITCH_END, NULL, 0, 0}
467};
468
469
470static Tcl_InterpDeleteProc TreeInterpDeleteProc;
471static Blt_TreeApplyProc MatchNodeProc, SortApplyProc;
472static Blt_TreeApplyProc ApplyNodeProc;
473static Blt_TreeTraceProc TreeTraceProc;
474static Tcl_CmdDeleteProc TreeInstDeleteProc;
475static Blt_TreeCompareNodesProc CompareNodes;
476
477static Tcl_ObjCmdProc TreeObjCmd;
478static Tcl_ObjCmdProc CompareDictionaryCmd;
479static Tcl_ObjCmdProc ExitCmd;
480static Blt_TreeNotifyEventProc TreeEventProc;
481
482static int GetNode _ANSI_ARGS_((TreeCmd *cmdPtr, Tcl_Obj *objPtr,
483 Blt_TreeNode *nodePtr));
484
485static int nLines;
486
487
488
489
490/*
491 *----------------------------------------------------------------------
492 *
493 * StringToChild --
494 *
495 * Convert a string represent a node number into its integer
496 * value.
497 *
498 * Results:
499 * The return value is a standard Tcl result.
500 *
501 *----------------------------------------------------------------------
502 */
503/*ARGSUSED*/
504static int
505StringToChild(
506 ClientData clientData, /* Flag indicating if the node is
507 * considered before or after the
508 * insertion position. */
509 Tcl_Interp *interp, /* Interpreter to send results back to */
510 char *switchName, /* Not used. */
511 char *string, /* String representation */
512 char *record, /* Structure record */
513 int offset) /* Offset to field in structure */
514{
515 InsertData *dataPtr = (InsertData *)record;
516 Blt_TreeNode node;
517
518 node = Blt_TreeFindChild(dataPtr->parent, string);
519 if (node == NULL) {
520 Tcl_AppendResult(interp, "can't find a child named \"", string,
521 "\" in \"", Blt_TreeNodeLabel(dataPtr->parent), "\"",
522 (char *)NULL);
523 return TCL_ERROR;
524 }
525 dataPtr->insertPos = Blt_TreeNodeDegree(node);
526 if (clientData == INSERT_AFTER) {
527 dataPtr->insertPos++;
528 }
529 return TCL_OK;
530}
531
532/*
533 *----------------------------------------------------------------------
534 *
535 * StringToNode --
536 *
537 * Convert a string represent a node number into its integer
538 * value.
539 *
540 * Results:
541 * The return value is a standard Tcl result.
542 *
543 *----------------------------------------------------------------------
544 */
545/*ARGSUSED*/
546static int
547StringToNode(
548 ClientData clientData, /* Not used. */
549 Tcl_Interp *interp, /* Interpreter to send results back to */
550 char *switchName, /* Not used. */
551 char *string, /* String representation */
552 char *record, /* Structure record */
553 int offset) /* Offset to field in structure */
554{
555 MoveData *dataPtr = (MoveData *)record;
556 Blt_TreeNode node;
557 Tcl_Obj *objPtr;
558 TreeCmd *cmdPtr = dataPtr->cmdPtr;
559 int result;
560
561 objPtr = Tcl_NewStringObj(string, -1);
562 result = GetNode(cmdPtr, objPtr, &node);
563 Tcl_DecrRefCount(objPtr);
564 if (result != TCL_OK) {
565 return TCL_ERROR;
566 }
567 dataPtr->node = node;
568 return TCL_OK;
569}
570
571
572
573/*
574 *----------------------------------------------------------------------
575 *
576 * StringToOrder --
577 *
578 * Convert a string represent a node number into its integer
579 * value.
580 *
581 * Results:
582 * The return value is a standard Tcl result.
583 *
584 *----------------------------------------------------------------------
585 */
586/*ARGSUSED*/
587static int
588StringToOrder(
589 ClientData clientData, /* Not used. */
590 Tcl_Interp *interp, /* Interpreter to send results back to */
591 char *switchName, /* Not used. */
592 char *string, /* String representation */
593 char *record, /* Structure record */
594 int offset) /* Offset to field in structure */
595{
596 int *orderPtr = (int *)(record + offset);
597 char c;
598
599 c = string[0];
600 if ((c == 'b') && (strcmp(string, "breadthfirst") == 0)) {
601 *orderPtr = TREE_BREADTHFIRST;
602 } else if ((c == 'i') && (strcmp(string, "inorder") == 0)) {
603 *orderPtr = TREE_INORDER;
604 } else if ((c == 'p') && (strcmp(string, "preorder") == 0)) {
605 *orderPtr = TREE_PREORDER;
606 } else if ((c == 'p') && (strcmp(string, "postorder") == 0)) {
607 *orderPtr = TREE_POSTORDER;
608 } else {
609 Tcl_AppendResult(interp, "bad order \"", string,
610 "\": should be breadthfirst, inorder, preorder, or postorder",
611 (char *)NULL);
612 return TCL_ERROR;
613 }
614 return TCL_OK;
615}
616
617/*
618 *----------------------------------------------------------------------
619 *
620 * StringToPattern --
621 *
622 * Convert a string represent a node number into its integer
623 * value.
624 *
625 * Results:
626 * The return value is a standard Tcl result.
627 *
628 *----------------------------------------------------------------------
629 */
630/*ARGSUSED*/
631static int
632StringToPattern(
633 ClientData clientData, /* Flag indicating type of pattern. */
634 Tcl_Interp *interp, /* Interpreter to send results back to */
635 char *switchName, /* Not used. */
636 char *string, /* String representation */
637 char *record, /* Structure record */
638 int offset) /* Offset to field in structure */
639{
640 Blt_List *listPtr = (Blt_List *)(record + offset);
641
642 if (*listPtr == NULL) {
643 *listPtr = Blt_ListCreate(BLT_STRING_KEYS);
644 }
645 Blt_ListAppend(*listPtr, string, clientData);
646 return TCL_OK;
647}
648
649/*
650 *----------------------------------------------------------------------
651 *
652 * FreePatterns --
653 *
654 * Convert a string represent a node number into its integer
655 * value.
656 *
657 * Results:
658 * The return value is a standard Tcl result.
659 *
660 *----------------------------------------------------------------------
661 */
662/*ARGSUSED*/
663static void
664FreePatterns(char *object)
665{
666 Blt_List list = (Blt_List)object;
667
668 if (list != NULL) {
669 Blt_ListDestroy(list);
670 }
671}
672
673
674/*
675 *----------------------------------------------------------------------
676 *
677 * StringToFormat --
678 *
679 * Convert a string represent a node number into its integer
680 * value.
681 *
682 * Results:
683 * The return value is a standard Tcl result.
684 *
685 *----------------------------------------------------------------------
686 */
687/*ARGSUSED*/
688static int
689StringToFormat(
690 ClientData clientData, /* Not used. */
691 Tcl_Interp *interp, /* Interpreter to send results back to */
692 char *switchName, /* Not used. */
693 char *string, /* String representation */
694 char *record, /* Structure record */
695 int offset) /* Offset to field in structure */
696{
697 PositionData *dataPtr = (PositionData *)record;
698
699 if (strcmp(string, "position") == 0) {
700 dataPtr->withParent = FALSE;
701 dataPtr->withId = FALSE;
702 } else if (strcmp(string, "id+position") == 0) {
703 dataPtr->withParent = FALSE;
704 dataPtr->withId = TRUE;
705 } else if (strcmp(string, "parent-at-position") == 0) {
706 dataPtr->withParent = TRUE;
707 dataPtr->withId = FALSE;
708 } else if (strcmp(string, "id+parent-at-position") == 0) {
709 dataPtr->withParent = TRUE;
710 dataPtr->withId = TRUE;
711 } else {
712 Tcl_AppendResult(interp, "bad format \"", string,
713 "\": should be position, parent-at-position, id+position, or id+parent-at-position",
714 (char *)NULL);
715 return TCL_ERROR;
716 }
717 return TCL_OK;
718}
719
720/*
721 *----------------------------------------------------------------------
722 *
723 * GetTreeCmdInterpData --
724 *
725 *----------------------------------------------------------------------
726 */
727static TreeCmdInterpData *
728GetTreeCmdInterpData(Tcl_Interp *interp)
729{
730 TreeCmdInterpData *dataPtr;
731 Tcl_InterpDeleteProc *proc;
732
733 dataPtr = (TreeCmdInterpData *)
734 Tcl_GetAssocData(interp, TREE_THREAD_KEY, &proc);
735 if (dataPtr == NULL) {
736 dataPtr = Blt_Malloc(sizeof(TreeCmdInterpData));
737 assert(dataPtr);
738 dataPtr->interp = interp;
739 Tcl_SetAssocData(interp, TREE_THREAD_KEY, TreeInterpDeleteProc,
740 dataPtr);
741 Blt_InitHashTable(&dataPtr->treeTable, BLT_ONE_WORD_KEYS);
742 }
743 return dataPtr;
744}
745
746/*
747 *----------------------------------------------------------------------
748 *
749 * GetTreeCmd --
750 *
751 * Find the tree command associated with the Tcl command "string".
752 *
753 * We have to do multiple lookups to get this right.
754 *
755 * The first step is to generate a canonical command name. If an
756 * unqualified command name (i.e. no namespace qualifier) is
757 * given, we should search first the current namespace and then
758 * the global one. Most Tcl commands (like Tcl_GetCmdInfo) look
759 * only at the global namespace.
760 *
761 * Next check if the string is
762 * a) a Tcl command and
763 * b) really is a command for a tree object.
764 * Tcl_GetCommandInfo will get us the objClientData field that
765 * should be a cmdPtr. We can verify that by searching our hashtable
766 * of cmdPtr addresses.
767 *
768 * Results:
769 * A pointer to the tree command. If no associated tree command
770 * can be found, NULL is returned. It's up to the calling routines
771 * to generate an error message.
772 *
773 *----------------------------------------------------------------------
774 */
775static TreeCmd *
776GetTreeCmd(
777 TreeCmdInterpData *dataPtr,
778 Tcl_Interp *interp,
779 CONST char *string)
780{
781 CONST char *name;
782 Tcl_Namespace *nsPtr;
783 Tcl_CmdInfo cmdInfo;
784 Blt_HashEntry *hPtr;
785 Tcl_DString dString;
786 char *treeName;
787 int result;
788
789 /* Put apart the tree name and put is back together in a standard
790 * format. */
791 if (Blt_ParseQualifiedName(interp, string, &nsPtr, &name) != TCL_OK) {
792 return NULL; /* No such parent namespace. */
793 }
794 if (nsPtr == NULL) {
795 nsPtr = Tcl_GetCurrentNamespace(interp);
796 }
797 /* Rebuild the fully qualified name. */
798 treeName = Blt_GetQualifiedName(nsPtr, name, &dString);
799 result = Tcl_GetCommandInfo(interp, treeName, &cmdInfo);
800 Tcl_DStringFree(&dString);
801
802 if (!result) {
803 return NULL;
804 }
805 hPtr = Blt_FindHashEntry(&dataPtr->treeTable,
806 (char *)(cmdInfo.objClientData));
807 if (hPtr == NULL) {
808 return NULL;
809 }
810 return Blt_GetHashValue(hPtr);
811}
812
813static Blt_TreeNode
814ParseModifiers(
815 Tcl_Interp *interp,
816 Blt_Tree tree,
817 Blt_TreeNode node,
818 char *modifiers)
819{
820 char *p, *np;
821
822 p = modifiers;
823 do {
824 p += 2; /* Skip the initial "->" */
825 np = strstr(p, "->");
826 if (np != NULL) {
827 *np = '\0';
828 }
829 if ((*p == 'p') && (strcmp(p, "parent") == 0)) {
830 node = Blt_TreeNodeParent(node);
831 } else if ((*p == 'f') && (strcmp(p, "firstchild") == 0)) {
832 node = Blt_TreeFirstChild(node);
833 } else if ((*p == 'l') && (strcmp(p, "lastchild") == 0)) {
834 node = Blt_TreeLastChild(node);
835 } else if ((*p == 'n') && (strcmp(p, "next") == 0)) {
836 node = Blt_TreeNextNode(Blt_TreeRootNode(tree), node);
837 } else if ((*p == 'n') && (strcmp(p, "nextsibling") == 0)) {
838 node = Blt_TreeNextSibling(node);
839 } else if ((*p == 'p') && (strcmp(p, "previous") == 0)) {
840 node = Blt_TreePrevNode(Blt_TreeRootNode(tree), node);
841 } else if ((*p == 'p') && (strcmp(p, "prevsibling") == 0)) {
842 node = Blt_TreePrevSibling(node);
843 } else if (isdigit(UCHAR(*p))) {
844 int inode;
845
846 if (Tcl_GetInt(interp, p, &inode) != TCL_OK) {
847 node = NULL;
848 } else {
849 node = Blt_TreeGetNode(tree, inode);
850 }
851 } else {
852 char *endp;
853
854 if (np != NULL) {
855 endp = np - 1;
856 } else {
857 endp = p + strlen(p) - 1;
858 }
859 if ((*p == '"') && (*endp == '"')) {
860 *endp = '\0';
861 node = Blt_TreeFindChild(node, p + 1);
862 *endp = '"';
863 } else {
864 node = Blt_TreeFindChild(node, p);
865 }
866 }
867 if (node == NULL) {
868 goto error;
869 }
870 if (np != NULL) {
871 *np = '-'; /* Repair the string */
872 }
873 p = np;
874 } while (np != NULL);
875 return node;
876 error:
877 if (np != NULL) {
878 *np = '-'; /* Repair the string */
879 }
880 return NULL;
881}
882
883/*
884 *----------------------------------------------------------------------
885 *
886 * GetForeignNode --
887 *
888 *----------------------------------------------------------------------
889 */
890static int
891GetForeignNode(
892 Tcl_Interp *interp,
893 Blt_Tree tree,
894 Tcl_Obj *objPtr,
895 Blt_TreeNode *nodePtr)
896{
897 char c;
898 Blt_TreeNode node;
899 char *string;
900 char *p;
901
902 string = Tcl_GetString(objPtr);
903 c = string[0];
904
905 /*
906 * Check if modifiers are present.
907 */
908 p = strstr(string, "->");
909 if (isdigit(UCHAR(c))) {
910 int inode;
911
912 if (p != NULL) {
913 char save;
914 int result;
915
916 save = *p;
917 *p = '\0';
918 result = Tcl_GetInt(interp, string, &inode);
919 *p = save;
920 if (result != TCL_OK) {
921 return TCL_ERROR;
922 }
923 } else {
924 if (Tcl_GetIntFromObj(interp, objPtr, &inode) != TCL_OK) {
925 return TCL_ERROR;
926 }
927 }
928 node = Blt_TreeGetNode(tree, inode);
929 if (p != NULL) {
930 node = ParseModifiers(interp, tree, node, p);
931 }
932 if (node != NULL) {
933 *nodePtr = node;
934 return TCL_OK;
935 }
936 }
937 Tcl_AppendResult(interp, "can't find tag or id \"", string, "\" in ",
938 Blt_TreeName(tree), (char *)NULL);
939 return TCL_ERROR;
940}
941
942/*
943 *----------------------------------------------------------------------
944 *
945 * GetNode --
946 *
947 *----------------------------------------------------------------------
948 */
949static int
950GetNode(TreeCmd *cmdPtr, Tcl_Obj *objPtr, Blt_TreeNode *nodePtr)
951{
952 Tcl_Interp *interp = cmdPtr->interp;
953 Blt_Tree tree = cmdPtr->tree;
954 char c;
955 Blt_TreeNode node;
956 char *string;
957 char *p;
958
959 string = Tcl_GetString(objPtr);
960 c = string[0];
961
962 /*
963 * Check if modifiers are present.
964 */
965 p = strstr(string, "->");
966 if (isdigit(UCHAR(c))) {
967 int inode;
968
969 if (p != NULL) {
970 char save;
971 int result;
972
973 save = *p;
974 *p = '\0';
975 result = Tcl_GetInt(interp, string, &inode);
976 *p = save;
977 if (result != TCL_OK) {
978 return TCL_ERROR;
979 }
980 } else {
981 if (Tcl_GetIntFromObj(interp, objPtr, &inode) != TCL_OK) {
982 return TCL_ERROR;
983 }
984 }
985 node = Blt_TreeGetNode(tree, inode);
986 } else if (cmdPtr != NULL) {
987 char save;
988
989 save = '\0'; /* Suppress compiler warning. */
990 if (p != NULL) {
991 save = *p;
992 *p = '\0';
993 }
994 if (strcmp(string, "all") == 0) {
995 if (Blt_TreeSize(Blt_TreeRootNode(tree)) > 1) {
996 Tcl_AppendResult(interp, "more than one node tagged as \"",
997 string, "\"", (char *)NULL);
998 if (p != NULL) {
999 *p = save;
1000 }
1001 return TCL_ERROR;
1002 }
1003 node = Blt_TreeRootNode(tree);
1004 } else if (strcmp(string, "root") == 0) {
1005 node = Blt_TreeRootNode(tree);
1006 } else {
1007 Blt_HashTable *tablePtr;
1008 Blt_HashSearch cursor;
1009 Blt_HashEntry *hPtr;
1010 int result;
1011
1012 node = NULL;
1013 result = TCL_ERROR;
1014 tablePtr = Blt_TreeTagHashTable(cmdPtr->tree, string);
1015 if (tablePtr == NULL) {
1016 Tcl_AppendResult(interp, "can't find tag or id \"", string,
1017 "\" in ", Blt_TreeName(cmdPtr->tree), (char *)NULL);
1018 } else if (tablePtr->numEntries > 1) {
1019 Tcl_AppendResult(interp, "more than one node tagged as \"",
1020 string, "\"", (char *)NULL);
1021 } else if (tablePtr->numEntries > 0) {
1022 hPtr = Blt_FirstHashEntry(tablePtr, &cursor);
1023 node = Blt_GetHashValue(hPtr);
1024 result = TCL_OK;
1025 }
1026 if (result == TCL_ERROR) {
1027 if (p != NULL) {
1028 *p = save;
1029 }
1030 return TCL_ERROR;
1031 }
1032 }
1033 if (p != NULL) {
1034 *p = save;
1035 }
1036 }
1037 if (node != NULL) {
1038 if (p != NULL) {
1039 node = ParseModifiers(interp, tree, node, p);
1040 if (node == NULL) {
1041 goto error;
1042 }
1043 }
1044 *nodePtr = node;
1045 return TCL_OK;
1046 }
1047 error:
1048 Tcl_AppendResult(interp, "can't find tag or id \"", string, "\" in ",
1049 Blt_TreeName(tree), (char *)NULL);
1050 return TCL_ERROR;
1051}
1052
1053typedef struct {
1054 int tagType;
1055 Blt_TreeNode root;
1056 Blt_HashSearch cursor;
1057} TagSearch;
1058
1059/*
1060 *----------------------------------------------------------------------
1061 *
1062 * FirstTaggedNode --
1063 *
1064 * Returns the id of the first node tagged by the given tag in
1065 * objPtr. It basically hides much of the cumbersome special
1066 * case details. For example, the special tags "root" and "all"
1067 * always exist, so they don't have entries in the tag hashtable.
1068 * If it's a hashed tag (not "root" or "all"), we have to save
1069 * the place of where we are in the table for the next call to
1070 * NextTaggedNode.
1071 *
1072 *----------------------------------------------------------------------
1073 */
1074static Blt_TreeNode
1075FirstTaggedNode(
1076 Tcl_Interp *interp,
1077 TreeCmd *cmdPtr,
1078 Tcl_Obj *objPtr,
1079 TagSearch *cursorPtr)
1080{
1081 Blt_TreeNode node, root;
1082 char *string;
1083
1084 node = NULL;
1085
1086 root = Blt_TreeRootNode(cmdPtr->tree);
1087 string = Tcl_GetString(objPtr);
1088 cursorPtr->tagType = TAG_TYPE_NONE;
1089 cursorPtr->root = root;
1090
1091 /* Process strings with modifiers or digits as simple ids, not
1092 * tags. */
1093 if ((strstr(string, "->") != NULL) || (isdigit(UCHAR(*string)))) {
1094 if (GetNode(cmdPtr, objPtr, &node) != TCL_OK) {
1095 return NULL;
1096 }
1097 return node;
1098 }
1099 if (strcmp(string, "all") == 0) {
1100 cursorPtr->tagType = TAG_TYPE_ALL;
1101 return root;
1102 } else if (strcmp(string, "root") == 0) {
1103 return root;
1104 } else {
1105 Blt_HashTable *tablePtr;
1106
1107 tablePtr = Blt_TreeTagHashTable(cmdPtr->tree, string);
1108 if (tablePtr != NULL) {
1109 Blt_HashEntry *hPtr;
1110
1111 cursorPtr->tagType = TAG_TYPE_TAG;
1112 hPtr = Blt_FirstHashEntry(tablePtr, &(cursorPtr->cursor));
1113 if (hPtr == NULL) {
1114 return NULL;
1115 }
1116 node = Blt_GetHashValue(hPtr);
1117 return node;
1118 }
1119 }
1120 Tcl_AppendResult(interp, "can't find tag or id \"", string, "\" in ",
1121 Blt_TreeName(cmdPtr->tree), (char *)NULL);
1122 return NULL;
1123}
1124
1125/*
1126 *----------------------------------------------------------------------
1127 *
1128 * NextTaggedNode --
1129 *
1130 *----------------------------------------------------------------------
1131 */
1132static Blt_TreeNode
1133NextTaggedNode(Blt_TreeNode node, TagSearch *cursorPtr)
1134{
1135 if (cursorPtr->tagType == TAG_TYPE_ALL) {
1136 return Blt_TreeNextNode(cursorPtr->root, node);
1137 }
1138 if (cursorPtr->tagType == TAG_TYPE_TAG) {
1139 Blt_HashEntry *hPtr;
1140
1141 hPtr = Blt_NextHashEntry(&(cursorPtr->cursor));
1142 if (hPtr == NULL) {
1143 return NULL;
1144 }
1145 return Blt_GetHashValue(hPtr);
1146 }
1147 return NULL;
1148}
1149
1150static int
1151AddTag(TreeCmd *cmdPtr, Blt_TreeNode node, CONST char *tagName)
1152{
1153 if (strcmp(tagName, "root") == 0) {
1154 Tcl_AppendResult(cmdPtr->interp, "can't add reserved tag \"",
1155 tagName, "\"", (char *)NULL);
1156 return TCL_ERROR;
1157 }
1158 Blt_TreeAddTag(cmdPtr->tree, node, tagName);
1159 return TCL_OK;
1160}
1161
1162
1163/*
1164 *----------------------------------------------------------------------
1165 *
1166 * DeleteNode --
1167 *
1168 *----------------------------------------------------------------------
1169 */
1170static void
1171DeleteNode(TreeCmd *cmdPtr, Blt_TreeNode node)
1172{
1173 Blt_TreeNode root;
1174
1175 if (!Blt_TreeTagTableIsShared(cmdPtr->tree)) {
1176 Blt_TreeClearTags(cmdPtr->tree, node);
1177 }
1178 root = Blt_TreeRootNode(cmdPtr->tree);
1179 if (node == root) {
1180 Blt_TreeNode next;
1181 /* Don't delete the root node. Simply clean out the tree. */
1182 for (node = Blt_TreeFirstChild(node); node != NULL; node = next) {
1183 next = Blt_TreeNextSibling(node);
1184 Blt_TreeDeleteNode(cmdPtr->tree, node);
1185 }
1186 } else if (Blt_TreeIsAncestor(root, node)) {
1187 Blt_TreeDeleteNode(cmdPtr->tree, node);
1188 }
1189}
1190
1191/*
1192 *----------------------------------------------------------------------
1193 *
1194 * GetNodePath --
1195 *
1196 *----------------------------------------------------------------------
1197 */
1198static char *
1199GetNodePath(
1200 TreeCmd *cmdPtr,
1201 Blt_TreeNode root,
1202 Blt_TreeNode node,
1203 int rootFlag, /* If non-zero, indicates to include
1204 * the root name in the path */
1205 Tcl_DString *resultPtr)
1206{
1207 char **nameArr; /* Used to stack the component names. */
1208 char *staticSpace[64];
1209 register int i;
1210 int nLevels;
1211
1212 nLevels = Blt_TreeNodeDepth(cmdPtr->tree, node) -
1213 Blt_TreeNodeDepth(cmdPtr->tree, root);
1214 if (rootFlag) {
1215 nLevels++;
1216 }
1217 if (nLevels > 64) {
1218 nameArr = Blt_Malloc(nLevels * sizeof(char *));
1219 assert(nameArr);
1220 } else {
1221 nameArr = staticSpace;
1222 }
1223 for (i = nLevels; i > 0; i--) {
1224 /* Save the name of each ancestor in the name array.
1225 * Note that we ignore the root. */
1226 nameArr[i - 1] = Blt_TreeNodeLabel(node);
1227 node = Blt_TreeNodeParent(node);
1228 }
1229 /* Append each the names in the array. */
1230 Tcl_DStringInit(resultPtr);
1231 for (i = 0; i < nLevels; i++) {
1232 Tcl_DStringAppendElement(resultPtr, nameArr[i]);
1233 }
1234 if (nameArr != staticSpace) {
1235 Blt_Free(nameArr);
1236 }
1237 return Tcl_DStringValue(resultPtr);
1238}
1239
1240/*
1241 *----------------------------------------------------------------------
1242 *
1243 * ParseNode5 --
1244 *
1245 * Parses and creates a node based upon the first 3 fields of
1246 * a five field entry. This is the new restore file format.
1247 *
1248 * parentId nodeId pathList dataList tagList
1249 *
1250 * The purpose is to attempt to save and restore the node ids
1251 * embedded in the restore file information. The old format
1252 * could not distinquish between two sibling nodes with the same
1253 * label unless they were both leaves. I'm trying to avoid
1254 * dependencies upon labels.
1255 *
1256 * If you're starting from an empty tree, this obviously should
1257 * work without a hitch. We only need to map the file's root id
1258 * to 0. It's a little more complicated when adding node to an
1259 * already full tree.
1260 *
1261 * First see if the node id isn't already in use. Otherwise, map
1262 * the node id (via a hashtable) to the real node. We'll need it
1263 * later when subsequent entries refer to their parent id.
1264 *
1265 * If a parent id is unknown (the restore file may be out of
1266 * order), then follow plan B and use its path.
1267 *
1268 *----------------------------------------------------------------------
1269 */
1270static Blt_TreeNode
1271ParseNode5(TreeCmd *cmdPtr, char **argv, RestoreData *dataPtr)
1272{
1273 Blt_HashEntry *hPtr;
1274 Blt_TreeNode node, parent;
1275 char **names;
1276 int nNames, isNew;
1277 int parentId, nodeId;
1278
1279 if ((Tcl_GetInt(cmdPtr->interp, argv[0], &parentId) != TCL_OK) ||
1280 (Tcl_GetInt(cmdPtr->interp, argv[1], &nodeId) != TCL_OK) ||
1281 (Tcl_SplitList(cmdPtr->interp, argv[2], &nNames, &names) != TCL_OK)) {
1282 return NULL;
1283 }
1284
1285 if (parentId == -1) { /* Dump marks root's parent as -1. */
1286 node = dataPtr->root;
1287 /* Create a mapping between the old id and the new node */
1288 hPtr = Blt_CreateHashEntry(&dataPtr->idTable, (char *)nodeId,
1289 &isNew);
1290 Blt_SetHashValue(hPtr, node);
1291 Blt_TreeRelabelNode(cmdPtr->tree, node, names[0]);
1292 } else {
1293 /*
1294 * Check if the parent has been translated to another id.
1295 * This can happen when there's a id collision with an
1296 * existing node.
1297 */
1298 hPtr = Blt_FindHashEntry(&dataPtr->idTable, (char *)parentId);
1299 if (hPtr != NULL) {
1300 parent = Blt_GetHashValue(hPtr);
1301 } else {
1302 /* Check if the id already exists in the tree. */
1303 parent = Blt_TreeGetNode(cmdPtr->tree, parentId);
1304 if (parent == NULL) {
1305 /* Parent id doesn't exist (partial restore?).
1306 * Plan B: Use the path to create/find the parent with
1307 * the requested parent id. */
1308 if (nNames > 1) {
1309 int i;
1310
1311 for (i = 1; i < (nNames - 2); i++) {
1312 node = Blt_TreeFindChild(parent, names[i]);
1313 if (node == NULL) {
1314 node = Blt_TreeCreateNode(cmdPtr->tree, parent,
1315 names[i], -1);
1316 }
1317 parent = node;
1318 }
1319 node = Blt_TreeFindChild(parent, names[nNames - 2]);
1320 if (node == NULL) {
1321 node = Blt_TreeCreateNodeWithId(cmdPtr->tree, parent,
1322 names[nNames - 2], parentId, -1);
1323 }
1324 parent = node;
1325 } else {
1326 parent = dataPtr->root;
1327 }
1328 }
1329 }
1330 /* Check if old node id already in use. */
1331 node = NULL;
1332 if (dataPtr->flags & RESTORE_OVERWRITE) {
1333 node = Blt_TreeFindChild(parent, names[nNames - 1]);
1334 /* Create a mapping between the old id and the new node */
1335 hPtr = Blt_CreateHashEntry(&dataPtr->idTable, (char *)nodeId,
1336 &isNew);
1337 Blt_SetHashValue(hPtr, node);
1338 }
1339 if (node == NULL) {
1340 node = Blt_TreeGetNode(cmdPtr->tree, nodeId);
1341 if (node != NULL) {
1342 node = Blt_TreeCreateNode(cmdPtr->tree, parent,
1343 names[nNames - 1], -1);
1344 /* Create a mapping between the old id and the new node */
1345 hPtr = Blt_CreateHashEntry(&dataPtr->idTable, (char *)nodeId,
1346 &isNew);
1347 Blt_SetHashValue(hPtr, node);
1348 } else {
1349 /* Otherwise create a new node with the requested id. */
1350 node = Blt_TreeCreateNodeWithId(cmdPtr->tree, parent,
1351 names[nNames - 1], nodeId, -1);
1352 }
1353 }
1354 }
1355 Blt_Free(names);
1356 return node;
1357}
1358
1359/*
1360 *----------------------------------------------------------------------
1361 *
1362 * ParseNode3 --
1363 *
1364 * Parses and creates a node based upon the first field of
1365 * a three field entry. This is the old restore file format.
1366 *
1367 * pathList dataList tagList
1368 *
1369 *----------------------------------------------------------------------
1370 */
1371static Blt_TreeNode
1372ParseNode3(TreeCmd *cmdPtr, char **argv, RestoreData *dataPtr)
1373{
1374 Blt_TreeNode node, parent;
1375 char **names;
1376 int i;
1377 int nNames;
1378
1379 if (Tcl_SplitList(cmdPtr->interp, argv[0], &nNames, &names) != TCL_OK) {
1380 return NULL;
1381 }
1382 node = parent = dataPtr->root;
1383 /* Automatically create nodes as needed except for the last node. */
1384 for (i = 0; i < (nNames - 1); i++) {
1385 node = Blt_TreeFindChild(parent, names[i]);
1386 if (node == NULL) {
1387 node = Blt_TreeCreateNode(cmdPtr->tree, parent, names[i], -1);
1388 }
1389 parent = node;
1390 }
1391 if (nNames > 0) {
1392 /*
1393 * By default, create duplicate nodes (two sibling nodes with
1394 * the same label), unless the -overwrite flag was set.
1395 */
1396 node = NULL;
1397 if (dataPtr->flags & RESTORE_OVERWRITE) {
1398 node = Blt_TreeFindChild(parent, names[i]);
1399 }
1400 if (node == NULL) {
1401 node = Blt_TreeCreateNode(cmdPtr->tree, parent, names[i], -1);
1402 }
1403 }
1404 Blt_Free(names);
1405 return node;
1406}
1407
1408static int
1409RestoreNode(TreeCmd *cmdPtr, int argc, char **argv, RestoreData *dataPtr)
1410{
1411 Blt_TreeNode node;
1412 Tcl_Obj *valueObjPtr;
1413 char **elemArr;
1414 int nElem, result;
1415 register int i;
1416
1417 if ((argc != 3) && (argc != 5)) {
1418 Tcl_AppendResult(cmdPtr->interp, "line #", Blt_Itoa(nLines),
1419 ": wrong # elements in restore entry", (char *)NULL);
1420 return TCL_ERROR;
1421 }
1422 /* Parse the path name. */
1423 if (argc == 3) {
1424 node = ParseNode3(cmdPtr, argv, dataPtr);
1425 argv++;
1426 } else if (argc == 5) {
1427 node = ParseNode5(cmdPtr, argv, dataPtr);
1428 argv += 3;
1429 } else {
1430 Tcl_AppendResult(cmdPtr->interp, "line #", Blt_Itoa(nLines),
1431 ": wrong # elements in restore entry", (char *)NULL);
1432 return TCL_ERROR;
1433 }
1434 if (node == NULL) {
1435 return TCL_ERROR;
1436 }
1437 /* Parse the key-value list. */
1438 if (Tcl_SplitList(cmdPtr->interp, argv[0], &nElem, &elemArr) != TCL_OK) {
1439 return TCL_ERROR;
1440 }
1441 for (i = 0; i < nElem; i += 2) {
1442 if ((i + 1) < nElem) {
1443 valueObjPtr = Tcl_NewStringObj(elemArr[i + 1], -1);
1444 } else {
1445 valueObjPtr = bltEmptyStringObjPtr;
1446 }
1447 Tcl_IncrRefCount(valueObjPtr);
1448 result = Blt_TreeSetValue(cmdPtr->interp, cmdPtr->tree, node,
1449 elemArr[i], valueObjPtr);
1450 Tcl_DecrRefCount(valueObjPtr);
1451 if (result != TCL_OK) {
1452 Blt_Free(elemArr);
1453 return TCL_ERROR;
1454 }
1455 }
1456 Blt_Free(elemArr);
1457 if (!(dataPtr->flags & RESTORE_NO_TAGS)) {
1458 /* Parse the tag list. */
1459 if (Tcl_SplitList(cmdPtr->interp, argv[1], &nElem, &elemArr)
1460 != TCL_OK) {
1461 return TCL_ERROR;
1462 }
1463 for (i = 0; i < nElem; i++) {
1464 if (AddTag(cmdPtr, node, elemArr[i]) != TCL_OK) {
1465 Blt_Free(elemArr);
1466 return TCL_ERROR;
1467 }
1468 }
1469 Blt_Free(elemArr);
1470 }
1471 return TCL_OK;
1472}
1473
1474/*
1475 *----------------------------------------------------------------------
1476 *
1477 * PrintNode --
1478 *
1479 *----------------------------------------------------------------------
1480 */
1481static void
1482PrintNode(
1483 TreeCmd *cmdPtr,
1484 Blt_TreeNode root,
1485 Blt_TreeNode node,
1486 Tcl_DString *resultPtr)
1487{
1488 Blt_HashEntry *hPtr;
1489 Blt_HashSearch cursor;
1490 char *pathName;
1491 Tcl_DString dString;
1492 Tcl_Obj *valueObjPtr;
1493 register Blt_TreeKey key;
1494 Blt_TreeTagEntry *tPtr;
1495 Blt_TreeKeySearch keyIter;
1496
1497 if (node == root) {
1498 Tcl_DStringAppendElement(resultPtr, "-1");
1499 } else {
1500 Blt_TreeNode parent;
1501
1502 parent = Blt_TreeNodeParent(node);
1503 Tcl_DStringAppendElement(resultPtr, Blt_Itoa(Blt_TreeNodeId(parent)));
1504 }
1505 Tcl_DStringAppendElement(resultPtr, Blt_Itoa(Blt_TreeNodeId(node)));
1506
1507 pathName = GetNodePath(cmdPtr, root, node, TRUE, &dString);
1508 Tcl_DStringAppendElement(resultPtr, pathName);
1509 Tcl_DStringStartSublist(resultPtr);
1510 for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &keyIter); key != NULL;
1511 key = Blt_TreeNextKey(cmdPtr->tree, &keyIter)) {
1512 if (Blt_TreeGetValueByKey((Tcl_Interp *)NULL, cmdPtr->tree, node,
1513 key, &valueObjPtr) == TCL_OK) {
1514 Tcl_DStringAppendElement(resultPtr, key);
1515 Tcl_DStringAppendElement(resultPtr, Tcl_GetString(valueObjPtr));
1516 }
1517 }
1518 Tcl_DStringEndSublist(resultPtr);
1519 Tcl_DStringStartSublist(resultPtr);
1520 for (hPtr = Blt_TreeFirstTag(cmdPtr->tree, &cursor); hPtr != NULL;
1521 hPtr = Blt_NextHashEntry(&cursor)) {
1522 tPtr = Blt_GetHashValue(hPtr);
1523 if (Blt_FindHashEntry(&tPtr->nodeTable, (char *)node) != NULL) {
1524 Tcl_DStringAppendElement(resultPtr, tPtr->tagName);
1525 }
1526 }
1527 Tcl_DStringEndSublist(resultPtr);
1528 Tcl_DStringAppend(resultPtr, "\n", -1);
1529 Tcl_DStringFree(&dString);
1530}
1531
1532/*
1533 *----------------------------------------------------------------------
1534 *
1535 * PrintTraceFlags --
1536 *
1537 *----------------------------------------------------------------------
1538 */
1539static void
1540PrintTraceFlags(unsigned int flags, char *string)
1541{
1542 register char *p;
1543
1544 p = string;
1545 if (flags & TREE_TRACE_READ) {
1546 *p++ = 'r';
1547 }
1548 if (flags & TREE_TRACE_WRITE) {
1549 *p++ = 'w';
1550 }
1551 if (flags & TREE_TRACE_UNSET) {
1552 *p++ = 'u';
1553 }
1554 if (flags & TREE_TRACE_CREATE) {
1555 *p++ = 'c';
1556 }
1557 *p = '\0';
1558}
1559
1560/*
1561 *----------------------------------------------------------------------
1562 *
1563 * GetTraceFlags --
1564 *
1565 *----------------------------------------------------------------------
1566 */
1567static int
1568GetTraceFlags(char *string)
1569{
1570 register char *p;
1571 unsigned int flags;
1572
1573 flags = 0;
1574 for (p = string; *p != '\0'; p++) {
1575 switch (toupper(*p)) {
1576 case 'R':
1577 flags |= TREE_TRACE_READ;
1578 break;
1579 case 'W':
1580 flags |= TREE_TRACE_WRITE;
1581 break;
1582 case 'U':
1583 flags |= TREE_TRACE_UNSET;
1584 break;
1585 case 'C':
1586 flags |= TREE_TRACE_CREATE;
1587 break;
1588 default:
1589 return -1;
1590 }
1591 }
1592 return flags;
1593}
1594
1595/*
1596 *----------------------------------------------------------------------
1597 *
1598 * SetValues --
1599 *
1600 *----------------------------------------------------------------------
1601 */
1602static int
1603SetValues(TreeCmd *cmdPtr, Blt_TreeNode node, int objc, Tcl_Obj *CONST *objv)
1604{
1605 register int i;
1606 char *string;
1607
1608 for (i = 0; i < objc; i += 2) {
1609 string = Tcl_GetString(objv[i]);
1610 if ((i + 1) == objc) {
1611 Tcl_AppendResult(cmdPtr->interp, "missing value for field \"",
1612 string, "\"", (char *)NULL);
1613 return TCL_ERROR;
1614 }
1615 if (Blt_TreeSetValue(cmdPtr->interp, cmdPtr->tree, node, string,
1616 objv[i + 1]) != TCL_OK) {
1617 return TCL_ERROR;
1618 }
1619 }
1620 return TCL_OK;
1621}
1622
1623/*
1624 *----------------------------------------------------------------------
1625 *
1626 * UnsetValues --
1627 *
1628 *----------------------------------------------------------------------
1629 */
1630static int
1631UnsetValues(TreeCmd *cmdPtr, Blt_TreeNode node, int objc, Tcl_Obj *CONST *objv)
1632{
1633 if (objc == 0) {
1634 Blt_TreeKey key;
1635 Blt_TreeKeySearch cursor;
1636
1637 for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &cursor); key != NULL;
1638 key = Blt_TreeNextKey(cmdPtr->tree, &cursor)) {
1639 if (Blt_TreeUnsetValueByKey(cmdPtr->interp, cmdPtr->tree, node,
1640 key) != TCL_OK) {
1641 return TCL_ERROR;
1642 }
1643 }
1644 } else {
1645 register int i;
1646
1647 for (i = 0; i < objc; i ++) {
1648 if (Blt_TreeUnsetValue(cmdPtr->interp, cmdPtr->tree, node,
1649 Tcl_GetString(objv[i])) != TCL_OK) {
1650 return TCL_ERROR;
1651 }
1652 }
1653 }
1654 return TCL_OK;
1655}
1656
1657static int
1658ComparePatternList(Blt_List patternList, char *string, int nocase)
1659{
1660 Blt_ListNode node;
1661 int result, type;
1662 char *pattern;
1663
1664 if (nocase) {
1665 string = Blt_Strdup(string);
1666 strtolower(string);
1667 }
1668 result = FALSE;
1669 for (node = Blt_ListFirstNode(patternList); node != NULL;
1670 node = Blt_ListNextNode(node)) {
1671
1672 type = (int)Blt_ListGetValue(node);
1673 pattern = (char *)Blt_ListGetKey(node);
1674 switch (type) {
1675 case PATTERN_EXACT:
1676 result = (strcmp(string, pattern) == 0);
1677 break;
1678
1679 case PATTERN_GLOB:
1680 result = Tcl_StringMatch(string, pattern);
1681 break;
1682
1683 case PATTERN_REGEXP:
1684 result = Tcl_RegExpMatch((Tcl_Interp *)NULL, string, pattern);
1685 break;
1686 }
1687 }
1688 if (nocase) {
1689 Blt_Free(string);
1690 }
1691 return result;
1692}
1693
1694/*
1695 *----------------------------------------------------------------------
1696 *
1697 * MatchNodeProc --
1698 *
1699 *----------------------------------------------------------------------
1700 */
1701/*ARGSUSED*/
1702static int
1703MatchNodeProc(Blt_TreeNode node, ClientData clientData, int order)
1704{
1705 FindData *dataPtr = clientData;
1706 Tcl_DString dString;
1707 TreeCmd *cmdPtr = dataPtr->cmdPtr;
1708 Tcl_Interp *interp = dataPtr->cmdPtr->interp;
1709 int result, invert;
1710
1711 if ((dataPtr->flags & MATCH_LEAFONLY) && (!Blt_TreeIsLeaf(node))) {
1712 return TCL_OK;
1713 }
1714 if ((dataPtr->maxDepth >= 0) &&
1715 (dataPtr->maxDepth < Blt_TreeNodeDepth(cmdPtr->tree, node))) {
1716 return TCL_OK;
1717 }
1718 result = TRUE;
1719 Tcl_DStringInit(&dString);
1720 if (dataPtr->keyList != NULL) {
1721 Blt_TreeKey key;
1722 Blt_TreeKeySearch cursor;
1723
1724 result = FALSE; /* It's false if no keys match. */
1725 for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &cursor);
1726 key != NULL; key = Blt_TreeNextKey(cmdPtr->tree, &cursor)) {
1727
1728 result = ComparePatternList(dataPtr->keyList, key, 0);
1729 if (!result) {
1730 continue;
1731 }
1732 if (dataPtr->patternList != NULL) {
1733 char *string;
1734 Tcl_Obj *objPtr;
1735
1736 Blt_TreeGetValue(interp, cmdPtr->tree, node, key, &objPtr);
1737 string = (objPtr == NULL) ? "" : Tcl_GetString(objPtr);
1738 result = ComparePatternList(dataPtr->patternList, string,
1739 dataPtr->flags & MATCH_NOCASE);
1740 if (!result) {
1741 continue;
1742 }
1743 }
1744 break;
1745 }
1746 } else if (dataPtr->patternList != NULL) {
1747 char *string;
1748
1749 if (dataPtr->flags & MATCH_PATHNAME) {
1750 string = GetNodePath(cmdPtr, Blt_TreeRootNode(cmdPtr->tree),
1751 node, FALSE, &dString);
1752 } else {
1753 string = Blt_TreeNodeLabel(node);
1754 }
1755 result = ComparePatternList(dataPtr->patternList, string,
1756 dataPtr->flags & MATCH_NOCASE);
1757 }
1758 if ((dataPtr->withTag != NULL) &&
1759 (!Blt_TreeHasTag(cmdPtr->tree, node, dataPtr->withTag))) {
1760 result = FALSE;
1761 }
1762 Tcl_DStringFree(&dString);
1763 invert = (dataPtr->flags & MATCH_INVERT) ? TRUE : FALSE;
1764 if (result != invert) {
1765 Tcl_Obj *objPtr;
1766
1767 if (dataPtr->addTag != NULL) {
1768 if (AddTag(cmdPtr, node, dataPtr->addTag) != TCL_OK) {
1769 return TCL_ERROR;
1770 }
1771 }
1772 objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
1773 Tcl_ListObjAppendElement(interp, dataPtr->listObjPtr, objPtr);
1774 if (dataPtr->objv != NULL) {
1775 dataPtr->objv[dataPtr->objc - 1] = objPtr;
1776 Tcl_IncrRefCount(objPtr);
1777 result = Tcl_EvalObjv(interp, dataPtr->objc, dataPtr->objv, 0);
1778 Tcl_DecrRefCount(objPtr);
1779 dataPtr->objv[dataPtr->objc - 1] = NULL;
1780 if (result != TCL_OK) {
1781 return result;
1782 }
1783 }
1784 dataPtr->nMatches++;
1785 if ((dataPtr->maxMatches > 0) &&
1786 (dataPtr->nMatches >= dataPtr->maxMatches)) {
1787 return TCL_BREAK;
1788 }
1789 }
1790 return TCL_OK;
1791}
1792
1793/*
1794 *----------------------------------------------------------------------
1795 *
1796 * ApplyNodeProc --
1797 *
1798 *----------------------------------------------------------------------
1799 */
1800static int
1801ApplyNodeProc(Blt_TreeNode node, ClientData clientData, int order)
1802{
1803 ApplyData *dataPtr = clientData;
1804 TreeCmd *cmdPtr = dataPtr->cmdPtr;
1805 Tcl_Interp *interp = cmdPtr->interp;
1806 int invert, result;
1807 Tcl_DString dString;
1808
1809 if ((dataPtr->flags & MATCH_LEAFONLY) && (!Blt_TreeIsLeaf(node))) {
1810 return TCL_OK;
1811 }
1812 if ((dataPtr->maxDepth >= 0) &&
1813 (dataPtr->maxDepth < Blt_TreeNodeDepth(cmdPtr->tree, node))) {
1814 return TCL_OK;
1815 }
1816 Tcl_DStringInit(&dString);
1817 result = TRUE;
1818 if (dataPtr->keyList != NULL) {
1819 Blt_TreeKey key;
1820 Blt_TreeKeySearch cursor;
1821
1822 result = FALSE; /* It's false if no keys match. */
1823 for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &cursor);
1824 key != NULL; key = Blt_TreeNextKey(cmdPtr->tree, &cursor)) {
1825
1826 result = ComparePatternList(dataPtr->keyList, key, 0);
1827 if (!result) {
1828 continue;
1829 }
1830 if (dataPtr->patternList != NULL) {
1831 char *string;
1832 Tcl_Obj *objPtr;
1833
1834 Blt_TreeGetValue(interp, cmdPtr->tree, node, key, &objPtr);
1835 string = (objPtr == NULL) ? "" : Tcl_GetString(objPtr);
1836 result = ComparePatternList(dataPtr->patternList, string,
1837 dataPtr->flags & MATCH_NOCASE);
1838 if (!result) {
1839 continue;
1840 }
1841 }
1842 break;
1843 }
1844 } else if (dataPtr->patternList != NULL) {
1845 char *string;
1846
1847 if (dataPtr->flags & MATCH_PATHNAME) {
1848 string = GetNodePath(cmdPtr, Blt_TreeRootNode(cmdPtr->tree),
1849 node, FALSE, &dString);
1850 } else {
1851 string = Blt_TreeNodeLabel(node);
1852 }
1853 result = ComparePatternList(dataPtr->patternList, string,
1854 dataPtr->flags & MATCH_NOCASE);
1855 }
1856 Tcl_DStringFree(&dString);
1857 if ((dataPtr->withTag != NULL) &&
1858 (!Blt_TreeHasTag(cmdPtr->tree, node, dataPtr->withTag))) {
1859 result = FALSE;
1860 }
1861 invert = (dataPtr->flags & MATCH_INVERT) ? 1 : 0;
1862 if (result != invert) {
1863 Tcl_Obj *objPtr;
1864
1865 objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
1866 if (order == TREE_PREORDER) {
1867 dataPtr->preObjv[dataPtr->preObjc - 1] = objPtr;
1868 return Tcl_EvalObjv(interp, dataPtr->preObjc, dataPtr->preObjv, 0);
1869 } else if (order == TREE_POSTORDER) {
1870 dataPtr->postObjv[dataPtr->postObjc - 1] = objPtr;
1871 return Tcl_EvalObjv(interp, dataPtr->postObjc, dataPtr->postObjv,0);
1872 }
1873 }
1874 return TCL_OK;
1875}
1876
1877/*
1878 *----------------------------------------------------------------------
1879 *
1880 * ReleaseTreeObject --
1881 *
1882 *----------------------------------------------------------------------
1883 */
1884static void
1885ReleaseTreeObject(TreeCmd *cmdPtr)
1886{
1887 Blt_HashEntry *hPtr;
1888 Blt_HashSearch cursor;
1889 TraceInfo *tracePtr;
1890 NotifyInfo *notifyPtr;
1891 int i;
1892
1893 Blt_TreeReleaseToken(cmdPtr->tree);
1894 /*
1895 * When the tree token is released, all the traces and
1896 * notification events are automatically removed. But we still
1897 * need to clean up the bookkeeping kept for traces. Clear all
1898 * the tags and trace information.
1899 */
1900 for (hPtr = Blt_FirstHashEntry(&(cmdPtr->traceTable), &cursor);
1901 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
1902 tracePtr = Blt_GetHashValue(hPtr);
1903 Blt_Free(tracePtr);
1904 }
1905 for (hPtr = Blt_FirstHashEntry(&(cmdPtr->notifyTable), &cursor);
1906 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
1907 notifyPtr = Blt_GetHashValue(hPtr);
1908 for (i = 0; i < notifyPtr->objc - 2; i++) {
1909 Tcl_DecrRefCount(notifyPtr->objv[i]);
1910 }
1911 Blt_Free(notifyPtr->objv);
1912 Blt_Free(notifyPtr);
1913 }
1914 cmdPtr->tree = NULL;
1915}
1916
1917/*
1918 *----------------------------------------------------------------------
1919 *
1920 * TreeTraceProc --
1921 *
1922 *----------------------------------------------------------------------
1923 */
1924/*ARGSUSED*/
1925static int
1926TreeTraceProc(
1927 ClientData clientData,
1928 Tcl_Interp *interp,
1929 Blt_TreeNode node, /* Node that has just been updated. */
1930 Blt_TreeKey key, /* Field that's updated. */
1931 unsigned int flags)
1932{
1933 TraceInfo *tracePtr = clientData;
1934 Tcl_DString dsCmd, dsName;
1935 char string[5];
1936 char *qualName;
1937 int result;
1938
1939 Tcl_DStringInit(&dsCmd);
1940 Tcl_DStringAppend(&dsCmd, tracePtr->command, -1);
1941 Tcl_DStringInit(&dsName);
1942 qualName = Blt_GetQualifiedName(
1943 Blt_GetCommandNamespace(interp, tracePtr->cmdPtr->cmdToken),
1944 Tcl_GetCommandName(interp, tracePtr->cmdPtr->cmdToken), &dsName);
1945 Tcl_DStringAppendElement(&dsCmd, qualName);
1946 Tcl_DStringFree(&dsName);
1947 if (node != NULL) {
1948 Tcl_DStringAppendElement(&dsCmd, Blt_Itoa(Blt_TreeNodeId(node)));
1949 } else {
1950 Tcl_DStringAppendElement(&dsCmd, "");
1951 }
1952 Tcl_DStringAppendElement(&dsCmd, key);
1953 PrintTraceFlags(flags, string);
1954 Tcl_DStringAppendElement(&dsCmd, string);
1955 result = Tcl_Eval(interp, Tcl_DStringValue(&dsCmd));
1956 Tcl_DStringFree(&dsCmd);
1957 return result;
1958}
1959
1960/*
1961 *----------------------------------------------------------------------
1962 *
1963 * TreeEventProc --
1964 *
1965 *----------------------------------------------------------------------
1966 */
1967static int
1968TreeEventProc(ClientData clientData, Blt_TreeNotifyEvent *eventPtr)
1969{
1970 TreeCmd *cmdPtr = clientData;
1971 Blt_HashEntry *hPtr;
1972 Blt_HashSearch cursor;
1973 NotifyInfo *notifyPtr;
1974 Blt_TreeNode node;
1975 char *string;
1976
1977 switch (eventPtr->type) {
1978 case TREE_NOTIFY_CREATE:
1979 string = "-create";
1980 break;
1981
1982 case TREE_NOTIFY_DELETE:
1983 node = Blt_TreeGetNode(cmdPtr->tree, eventPtr->inode);
1984 if (node != NULL) {
1985 Blt_TreeClearTags(cmdPtr->tree, node);
1986 }
1987 string = "-delete";
1988 break;
1989
1990 case TREE_NOTIFY_MOVE:
1991 string = "-move";
1992 break;
1993
1994 case TREE_NOTIFY_SORT:
1995 string = "-sort";
1996 break;
1997
1998 case TREE_NOTIFY_RELABEL:
1999 string = "-relabel";
2000 break;
2001
2002 default:
2003 /* empty */
2004 string = "???";
2005 break;
2006 }
2007
2008 for (hPtr = Blt_FirstHashEntry(&(cmdPtr->notifyTable), &cursor);
2009 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
2010 notifyPtr = Blt_GetHashValue(hPtr);
2011 if (notifyPtr->mask & eventPtr->type) {
2012 int result;
2013 Tcl_Obj *flagObjPtr, *nodeObjPtr;
2014
2015 flagObjPtr = Tcl_NewStringObj(string, -1);
2016 nodeObjPtr = Tcl_NewIntObj(eventPtr->inode);
2017 Tcl_IncrRefCount(flagObjPtr);
2018 Tcl_IncrRefCount(nodeObjPtr);
2019 notifyPtr->objv[notifyPtr->objc - 2] = flagObjPtr;
2020 notifyPtr->objv[notifyPtr->objc - 1] = nodeObjPtr;
2021 result = Tcl_EvalObjv(cmdPtr->interp, notifyPtr->objc,
2022 notifyPtr->objv, 0);
2023 Tcl_DecrRefCount(nodeObjPtr);
2024 Tcl_DecrRefCount(flagObjPtr);
2025 if (result != TCL_OK) {
2026 Tcl_BackgroundError(cmdPtr->interp);
2027 return TCL_ERROR;
2028 }
2029 Tcl_ResetResult(cmdPtr->interp);
2030 }
2031 }
2032 return TCL_OK;
2033}
2034
2035
2036
2037/* Tree command operations. */
2038
2039/*
2040 *----------------------------------------------------------------------
2041 *
2042 * ApplyOp --
2043 *
2044 * t0 apply root -precommand {command} -postcommand {command}
2045 *
2046 *----------------------------------------------------------------------
2047 */
2048static int
2049ApplyOp(
2050 TreeCmd *cmdPtr,
2051 Tcl_Interp *interp,
2052 int objc,
2053 Tcl_Obj *CONST *objv)
2054{
2055 int result;
2056 Blt_TreeNode node;
2057 int i;
2058 Tcl_Obj **objArr;
2059 int count;
2060 ApplyData data;
2061 int order;
2062
2063 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
2064 return TCL_ERROR;
2065 }
2066 memset(&data, 0, sizeof(data));
2067 data.maxDepth = -1;
2068 data.cmdPtr = cmdPtr;
2069
2070 /* Process switches */
2071 if (Blt_ProcessObjSwitches(interp, applySwitches, objc - 3, objv + 3,
2072 (char *)&data, 0) < 0) {
2073 return TCL_ERROR;
2074 }
2075 order = 0;
2076 if (data.flags & MATCH_NOCASE) {
2077 Blt_ListNode listNode;
2078
2079 for (listNode = Blt_ListFirstNode(data.patternList); listNode != NULL;
2080 listNode = Blt_ListNextNode(listNode)) {
2081 strtolower((char *)Blt_ListGetKey(listNode));
2082 }
2083 }
2084 if (data.preCmd != NULL) {
2085 char **p;
2086
2087 count = 0;
2088 for (p = data.preCmd; *p != NULL; p++) {
2089 count++;
2090 }
2091 objArr = Blt_Malloc((count + 1) * sizeof(Tcl_Obj *));
2092 for (i = 0; i < count; i++) {
2093 objArr[i] = Tcl_NewStringObj(data.preCmd[i], -1);
2094 Tcl_IncrRefCount(objArr[i]);
2095 }
2096 data.preObjv = objArr;
2097 data.preObjc = count + 1;
2098 order |= TREE_PREORDER;
2099 }
2100 if (data.postCmd != NULL) {
2101 char **p;
2102
2103 count = 0;
2104 for (p = data.postCmd; *p != NULL; p++) {
2105 count++;
2106 }
2107 objArr = Blt_Malloc((count + 1) * sizeof(Tcl_Obj *));
2108 for (i = 0; i < count; i++) {
2109 objArr[i] = Tcl_NewStringObj(data.postCmd[i], -1);
2110 Tcl_IncrRefCount(objArr[i]);
2111 }
2112 data.postObjv = objArr;
2113 data.postObjc = count + 1;
2114 order |= TREE_POSTORDER;
2115 }
2116 result = Blt_TreeApplyDFS(node, ApplyNodeProc, &data, order);
2117 if (data.preObjv != NULL) {
2118 for (i = 0; i < (data.preObjc - 1); i++) {
2119 Tcl_DecrRefCount(data.preObjv[i]);
2120 }
2121 Blt_Free(data.preObjv);
2122 }
2123 if (data.postObjv != NULL) {
2124 for (i = 0; i < (data.postObjc - 1); i++) {
2125 Tcl_DecrRefCount(data.postObjv[i]);
2126 }
2127 Blt_Free(data.postObjv);
2128 }
2129 Blt_FreeSwitches(applySwitches, (char *)&data, 0);
2130 if (result == TCL_ERROR) {
2131 return TCL_ERROR;
2132 }
2133 return TCL_OK;
2134}
2135
2136
2137/*ARGSUSED*/
2138static int
2139AncestorOp(
2140 TreeCmd *cmdPtr,
2141 Tcl_Interp *interp,
2142 int objc, /* Not used. */
2143 Tcl_Obj *CONST *objv)
2144{
2145 int d1, d2, minDepth;
2146 register int i;
2147 Blt_TreeNode ancestor, node1, node2;
2148
2149 if ((GetNode(cmdPtr, objv[2], &node1) != TCL_OK) ||
2150 (GetNode(cmdPtr, objv[3], &node2) != TCL_OK)) {
2151 return TCL_ERROR;
2152 }
2153 if (node1 == node2) {
2154 ancestor = node1;
2155 goto done;
2156 }
2157 d1 = Blt_TreeNodeDepth(cmdPtr->tree, node1);
2158 d2 = Blt_TreeNodeDepth(cmdPtr->tree, node2);
2159 minDepth = MIN(d1, d2);
2160 if (minDepth == 0) { /* One of the nodes is root. */
2161 ancestor = Blt_TreeRootNode(cmdPtr->tree);
2162 goto done;
2163 }
2164 /*
2165 * Traverse back from the deepest node, until the both nodes are
2166 * at the same depth. Check if the ancestor node found is the
2167 * other node.
2168 */
2169 for (i = d1; i > minDepth; i--) {
2170 node1 = Blt_TreeNodeParent(node1);
2171 }
2172 if (node1 == node2) {
2173 ancestor = node2;
2174 goto done;
2175 }
2176 for (i = d2; i > minDepth; i--) {
2177 node2 = Blt_TreeNodeParent(node2);
2178 }
2179 if (node2 == node1) {
2180 ancestor = node1;
2181 goto done;
2182 }
2183
2184 /*
2185 * First find the mutual ancestor of both nodes. Look at each
2186 * preceding ancestor level-by-level for both nodes. Eventually
2187 * we'll find a node that's the parent of both ancestors. Then
2188 * find the first ancestor in the parent's list of subnodes.
2189 */
2190 for (i = minDepth; i > 0; i--) {
2191 node1 = Blt_TreeNodeParent(node1);
2192 node2 = Blt_TreeNodeParent(node2);
2193 if (node1 == node2) {
2194 ancestor = node2;
2195 goto done;
2196 }
2197 }
2198 Tcl_AppendResult(interp, "unknown ancestor", (char *)NULL);
2199 return TCL_ERROR;
2200 done:
2201 Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeNodeId(ancestor));
2202 return TCL_OK;
2203}
2204
2205/*
2206 *----------------------------------------------------------------------
2207 *
2208 * AttachOp --
2209 *
2210 *----------------------------------------------------------------------
2211 */
2212static int
2213AttachOp(
2214 TreeCmd *cmdPtr,
2215 Tcl_Interp *interp,
2216 int objc,
2217 Tcl_Obj *CONST *objv)
2218{
2219 if (objc == 3) {
2220 CONST char *treeName;
2221 CONST char *name;
2222 Blt_Tree token;
2223 Tcl_Namespace *nsPtr;
2224 Tcl_DString dString;
2225 int result;
2226
2227 treeName = Tcl_GetString(objv[2]);
2228 if (Blt_ParseQualifiedName(interp, treeName, &nsPtr, &name)
2229 != TCL_OK) {
2230 Tcl_AppendResult(interp, "can't find namespace in \"", treeName,
2231 "\"", (char *)NULL);
2232 return TCL_ERROR;
2233 }
2234 if (nsPtr == NULL) {
2235 nsPtr = Tcl_GetCurrentNamespace(interp);
2236 }
2237 treeName = Blt_GetQualifiedName(nsPtr, name, &dString);
2238 result = Blt_TreeGetToken(interp, treeName, &token);
2239 Tcl_DStringFree(&dString);
2240 if (result != TCL_OK) {
2241 return TCL_ERROR;
2242 }
2243 ReleaseTreeObject(cmdPtr);
2244 cmdPtr->tree = token;
2245 }
2246 Tcl_SetResult(interp, Blt_TreeName(cmdPtr->tree), TCL_VOLATILE);
2247 return TCL_OK;
2248}
2249
2250/*
2251 *----------------------------------------------------------------------
2252 *
2253 * ChildrenOp --
2254 *
2255 *----------------------------------------------------------------------
2256 */
2257static int
2258ChildrenOp(
2259 TreeCmd *cmdPtr,
2260 Tcl_Interp *interp,
2261 int objc,
2262 Tcl_Obj *CONST *objv)
2263{
2264 Blt_TreeNode node;
2265
2266 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
2267 return TCL_ERROR;
2268 }
2269 if (objc == 3) {
2270 Tcl_Obj *objPtr, *listObjPtr;
2271
2272 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
2273 for (node = Blt_TreeFirstChild(node); node != NULL;
2274 node = Blt_TreeNextSibling(node)) {
2275 objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
2276 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
2277 }
2278 Tcl_SetObjResult(interp, listObjPtr);
2279 } else if (objc == 4) {
2280 int childPos;
2281 int inode, count;
2282
2283 /* Get the node at */
2284 if (Tcl_GetIntFromObj(interp, objv[3], &childPos) != TCL_OK) {
2285 return TCL_ERROR;
2286 }
2287 count = 0;
2288 inode = -1;
2289 for (node = Blt_TreeFirstChild(node); node != NULL;
2290 node = Blt_TreeNextSibling(node)) {
2291 if (count == childPos) {
2292 inode = Blt_TreeNodeId(node);
2293 break;
2294 }
2295 count++;
2296 }
2297 Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
2298 return TCL_OK;
2299 } else if (objc == 5) {
2300 int firstPos, lastPos, count;
2301 Tcl_Obj *objPtr, *listObjPtr;
2302 char *string;
2303
2304 firstPos = lastPos = Blt_TreeNodeDegree(node) - 1;
2305 string = Tcl_GetString(objv[3]);
2306 if ((strcmp(string, "end") != 0) &&
2307 (Tcl_GetIntFromObj(interp, objv[3], &firstPos) != TCL_OK)) {
2308 return TCL_ERROR;
2309 }
2310 string = Tcl_GetString(objv[4]);
2311 if ((strcmp(string, "end") != 0) &&
2312 (Tcl_GetIntFromObj(interp, objv[4], &lastPos) != TCL_OK)) {
2313 return TCL_ERROR;
2314 }
2315
2316 count = 0;
2317 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
2318 for (node = Blt_TreeFirstChild(node); node != NULL;
2319 node = Blt_TreeNextSibling(node)) {
2320 if ((count >= firstPos) && (count <= lastPos)) {
2321 objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
2322 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
2323 }
2324 count++;
2325 }
2326 Tcl_SetObjResult(interp, listObjPtr);
2327 }
2328 return TCL_OK;
2329}
2330
2331
2332static Blt_TreeNode
2333CopyNodes(
2334 CopyData *dataPtr,
2335 Blt_TreeNode node, /* Node to be copied. */
2336 Blt_TreeNode parent) /* New parent for the copied node. */
2337{
2338 Blt_TreeNode newNode; /* Newly created copy. */
2339 char *label;
2340
2341 newNode = NULL;
2342 label = Blt_TreeNodeLabel(node);
2343 if (dataPtr->flags & COPY_OVERWRITE) {
2344 newNode = Blt_TreeFindChild(parent, label);
2345 }
2346 if (newNode == NULL) { /* Create node in new parent. */
2347 newNode = Blt_TreeCreateNode(dataPtr->destTree, parent, label, -1);
2348 }
2349 /* Copy the data fields. */
2350 {
2351 Blt_TreeKey key;
2352 Tcl_Obj *objPtr;
2353 Blt_TreeKeySearch cursor;
2354
2355 for (key = Blt_TreeFirstKey(dataPtr->srcTree, node, &cursor);
2356 key != NULL; key = Blt_TreeNextKey(dataPtr->srcTree, &cursor)) {
2357 if (Blt_TreeGetValueByKey((Tcl_Interp *)NULL, dataPtr->srcTree,
2358 node, key, &objPtr) == TCL_OK) {
2359 Blt_TreeSetValueByKey((Tcl_Interp *)NULL, dataPtr->destTree,
2360 newNode, key, objPtr);
2361 }
2362 }
2363 }
2364 /* Add tags to destination tree command. */
2365 if ((dataPtr->destPtr != NULL) && (dataPtr->flags & COPY_TAGS)) {
2366 Blt_TreeTagEntry *tPtr;
2367 Blt_HashEntry *hPtr, *h2Ptr;
2368 Blt_HashSearch cursor;
2369
2370 for (hPtr = Blt_TreeFirstTag(dataPtr->srcPtr->tree, &cursor);
2371 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
2372 tPtr = Blt_GetHashValue(hPtr);
2373 h2Ptr = Blt_FindHashEntry(&tPtr->nodeTable, (char *)node);
2374 if (h2Ptr != NULL) {
2375 if (AddTag(dataPtr->destPtr, newNode, tPtr->tagName)!= TCL_OK) {
2376 return NULL;
2377 }
2378 }
2379 }
2380 }
2381 if (dataPtr->flags & COPY_RECURSE) {
2382 Blt_TreeNode child;
2383
2384 for (child = Blt_TreeFirstChild(node); child != NULL;
2385 child = Blt_TreeNextSibling(child)) {
2386 if (CopyNodes(dataPtr, child, newNode) == NULL) {
2387 return NULL;
2388 }
2389 }
2390 }
2391 return newNode;
2392}
2393
2394/*
2395 *----------------------------------------------------------------------
2396 *
2397 * CopyOp --
2398 *
2399 * t0 copy node tree node
2400 *
2401 *----------------------------------------------------------------------
2402 */
2403/*ARGSUSED*/
2404static int
2405CopyOp(
2406 TreeCmd *cmdPtr,
2407 Tcl_Interp *interp,
2408 int objc,
2409 Tcl_Obj *CONST *objv)
2410{
2411 TreeCmd *srcPtr, *destPtr;
2412 Blt_Tree srcTree, destTree;
2413 Blt_TreeNode srcNode, destNode;
2414 CopyData data;
2415 int nArgs, nSwitches;
2416 char *string;
2417 Blt_TreeNode root;
2418 register int i;
2419
2420 if (GetNode(cmdPtr, objv[2], &srcNode) != TCL_OK) {
2421 return TCL_ERROR;
2422 }
2423 srcTree = cmdPtr->tree;
2424 srcPtr = cmdPtr;
2425
2426 /* Find the first switch. */
2427 for(i = 3; i < objc; i++) {
2428 string = Tcl_GetString(objv[i]);
2429 if (string[0] == '-') {
2430 break;
2431 }
2432 }
2433 nArgs = i - 2;
2434 nSwitches = objc - i;
2435 if (nArgs < 2) {
2436 string = Tcl_GetString(objv[0]);
2437 Tcl_AppendResult(interp, "must specify source and destination nodes: ",
2438 "should be \"", string,
2439 " copy srcNode ?destTree? destNode ?switches?",
2440 (char *)NULL);
2441 return TCL_ERROR;
2442
2443 }
2444 if (nArgs == 3) {
2445 /*
2446 * The tree name is either the name of a tree command (first choice)
2447 * or an internal tree object.
2448 */
2449 string = Tcl_GetString(objv[3]);
2450 destPtr = GetTreeCmd(cmdPtr->dataPtr, interp, string);
2451 if (destPtr != NULL) {
2452 destTree = destPtr->tree;
2453 } else {
2454 /* Try to get the tree as an internal tree data object. */
2455 if (Blt_TreeGetToken(interp, string, &destTree) != TCL_OK) {
2456 return TCL_ERROR;
2457 }
2458 }
2459 objv++;
2460 } else {
2461 destPtr = cmdPtr;
2462 destTree = destPtr->tree;
2463 }
2464
2465 root = NULL;
2466 if (destPtr == NULL) {
2467 if (GetForeignNode(interp, destTree, objv[3], &destNode) != TCL_OK) {
2468 goto error;
2469 }
2470 } else {
2471 if (GetNode(destPtr, objv[3], &destNode) != TCL_OK) {
2472 goto error;
2473 }
2474 }
2475 if (srcNode == destNode) {
2476 Tcl_AppendResult(interp, "source and destination nodes are the same",
2477 (char *)NULL);
2478 goto error;
2479 }
2480 memset((char *)&data, 0, sizeof(data));
2481 /* Process switches */
2482 if (Blt_ProcessObjSwitches(interp, copySwitches, nSwitches, objv + 4,
2483 (char *)&data, 0) < 0) {
2484 goto error;
2485 }
2486 data.destPtr = destPtr;
2487 data.destTree = destTree;
2488 data.srcPtr = srcPtr;
2489 data.srcTree = srcTree;
2490
2491 if ((srcTree == destTree) && (data.flags & COPY_RECURSE) &&
2492 (Blt_TreeIsAncestor(srcNode, destNode))) {
2493 Tcl_AppendResult(interp, "can't make cyclic copy: ",
2494 "source node is an ancestor of the destination",
2495 (char *)NULL);
2496 goto error;
2497 }
2498
2499 /* Copy nodes to destination. */
2500 root = CopyNodes(&data, srcNode, destNode);
2501 if (root != NULL) {
2502 Tcl_Obj *objPtr;
2503
2504 objPtr = Tcl_NewIntObj(Blt_TreeNodeId(root));
2505 if (data.label != NULL) {
2506 Blt_TreeRelabelNode(data.destTree, root, data.label);
2507 }
2508 Tcl_SetObjResult(interp, objPtr);
2509 }
2510 error:
2511 if (destPtr == NULL) {
2512 Blt_TreeReleaseToken(destTree);
2513 }
2514 return (root == NULL) ? TCL_ERROR : TCL_OK;
2515
2516}
2517
2518/*
2519 *----------------------------------------------------------------------
2520 *
2521 * DepthOp --
2522 *
2523 *----------------------------------------------------------------------
2524 */
2525/*ARGSUSED*/
2526static int
2527DegreeOp(cmdPtr, interp, objc, objv)
2528 TreeCmd *cmdPtr;
2529 Tcl_Interp *interp;
2530 int objc; /* Not used. */
2531 Tcl_Obj *CONST *objv;
2532{
2533 Blt_TreeNode node;
2534 int degree;
2535
2536 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
2537 return TCL_ERROR;
2538 }
2539 degree = Blt_TreeNodeDegree(node);
2540 Tcl_SetIntObj(Tcl_GetObjResult(interp), degree);
2541 return TCL_OK;
2542}
2543
2544/*
2545 *----------------------------------------------------------------------
2546 *
2547 * DeleteOp --
2548 *
2549 * Deletes one or more nodes from the tree. Nodes may be
2550 * specified by their id (a number) or a tag.
2551 *
2552 * Tags have to be handled carefully here. We can't use the
2553 * normal GetTaggedNode, NextTaggedNode, etc. routines because
2554 * they walk hashtables while we're deleting nodes. Also,
2555 * remember that deleting a node recursively deletes all its
2556 * children. If a parent and its children have the same tag, its
2557 * possible that the tag list may contain nodes than no longer
2558 * exist. So save the node indices in a list and then delete
2559 * then in a second pass.
2560 *
2561 *----------------------------------------------------------------------
2562 */
2563static int
2564DeleteOp(
2565 TreeCmd *cmdPtr,
2566 Tcl_Interp *interp,
2567 int objc,
2568 Tcl_Obj *CONST *objv)
2569{
2570 Blt_TreeNode node;
2571 int i;
2572 char *string;
2573
2574 for (i = 2; i < objc; i++) {
2575 string = Tcl_GetString(objv[i]);
2576 if (isdigit(UCHAR(string[0]))) {
2577 if (GetNode(cmdPtr, objv[i], &node) != TCL_OK) {
2578 return TCL_ERROR;
2579 }
2580 DeleteNode(cmdPtr, node);
2581 } else {
2582 Blt_HashEntry *hPtr;
2583 Blt_HashTable *tablePtr;
2584 Blt_HashSearch cursor;
2585 Blt_Chain *chainPtr;
2586 Blt_ChainLink *linkPtr, *nextPtr;
2587 int inode;
2588
2589 if ((strcmp(string, "all") == 0) ||
2590 (strcmp(string, "root") == 0)) {
2591 node = Blt_TreeRootNode(cmdPtr->tree);
2592 DeleteNode(cmdPtr, node);
2593 continue;
2594 }
2595 tablePtr = Blt_TreeTagHashTable(cmdPtr->tree, string);
2596 if (tablePtr == NULL) {
2597 goto error;
2598 }
2599 /*
2600 * Generate a list of tagged nodes. Save the inode instead
2601 * of the node itself since a pruned branch may contain
2602 * more tagged nodes.
2603 */
2604 chainPtr = Blt_ChainCreate();
2605 for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor);
2606 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
2607 node = Blt_GetHashValue(hPtr);
2608 Blt_ChainAppend(chainPtr, (ClientData)Blt_TreeNodeId(node));
2609 }
2610 /*
2611 * Iterate through this list to delete the nodes. By
2612 * side-effect the tag table is deleted and Uids are
2613 * released.
2614 */
2615 for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
2616 linkPtr = nextPtr) {
2617 nextPtr = Blt_ChainNextLink(linkPtr);
2618 inode = (int)Blt_ChainGetValue(linkPtr);
2619 node = Blt_TreeGetNode(cmdPtr->tree, inode);
2620 if (node != NULL) {
2621 DeleteNode(cmdPtr, node);
2622 }
2623 }
2624 Blt_ChainDestroy(chainPtr);
2625 }
2626 }
2627 return TCL_OK;
2628 error:
2629 Tcl_AppendResult(interp, "can't find tag or id \"", string, "\" in ",
2630 Blt_TreeName(cmdPtr->tree), (char *)NULL);
2631 return TCL_ERROR;
2632}
2633
2634/*
2635 *----------------------------------------------------------------------
2636 *
2637 * DepthOp --
2638 *
2639 *----------------------------------------------------------------------
2640 */
2641/*ARGSUSED*/
2642static int
2643DepthOp(
2644 TreeCmd *cmdPtr,
2645 Tcl_Interp *interp,
2646 int objc, /* Not used. */
2647 Tcl_Obj *CONST *objv)
2648{
2649 Blt_TreeNode node;
2650 int depth;
2651
2652 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
2653 return TCL_ERROR;
2654 }
2655 depth = Blt_TreeNodeDepth(cmdPtr->tree, node);
2656 Tcl_SetIntObj(Tcl_GetObjResult(interp), depth);
2657 return TCL_OK;
2658}
2659
2660/*
2661 *----------------------------------------------------------------------
2662 *
2663 * DumpOp --
2664 *
2665 *----------------------------------------------------------------------
2666 */
2667/*ARGSUSED*/
2668static int
2669DumpOp(cmdPtr, interp, objc, objv)
2670 TreeCmd *cmdPtr;
2671 Tcl_Interp *interp;
2672 int objc; /* Not used. */
2673 Tcl_Obj *CONST *objv;
2674{
2675 Blt_TreeNode top;
2676 Tcl_DString dString;
2677 register Blt_TreeNode node;
2678
2679 if (GetNode(cmdPtr, objv[2], &top) != TCL_OK) {
2680 return TCL_ERROR;
2681 }
2682 Tcl_DStringInit(&dString);
2683 for (node = top; node != NULL; node = Blt_TreeNextNode(top, node)) {
2684 PrintNode(cmdPtr, top, node, &dString);
2685 }
2686 Tcl_DStringResult(interp, &dString);
2687 return TCL_OK;
2688}
2689
2690/*
2691 *----------------------------------------------------------------------
2692 *
2693 * DumpfileOp --
2694 *
2695 *----------------------------------------------------------------------
2696 */
2697/*ARGSUSED*/
2698static int
2699DumpfileOp(
2700 TreeCmd *cmdPtr,
2701 Tcl_Interp *interp,
2702 int objc, /* Not used. */
2703 Tcl_Obj *CONST *objv)
2704{
2705 Blt_TreeNode top;
2706 Tcl_Channel channel;
2707 Tcl_DString dString;
2708 char *fileName;
2709 int result;
2710 register Blt_TreeNode node;
2711
2712 if (GetNode(cmdPtr, objv[2], &top) != TCL_OK) {
2713 return TCL_ERROR;
2714 }
2715 fileName = Tcl_GetString(objv[3]);
2716 channel = Tcl_OpenFileChannel(interp, fileName, "w", 0666);
2717 if (channel == NULL) {
2718 return TCL_ERROR;
2719 }
2720 Tcl_DStringInit(&dString);
2721 for (node = top; node != NULL; node = Blt_TreeNextNode(top, node)) {
2722 PrintNode(cmdPtr, top, node, &dString);
2723 }
2724 result = Tcl_Write(channel, Tcl_DStringValue(&dString), -1);
2725 Tcl_Close(interp, channel);
2726 Tcl_DStringFree(&dString);
2727 if (result <= 0) {
2728 return TCL_ERROR;
2729 }
2730 return TCL_OK;
2731}
2732
2733/*
2734 *----------------------------------------------------------------------
2735 *
2736 * ExistsOp --
2737 *
2738 *----------------------------------------------------------------------
2739 */
2740static int
2741ExistsOp(
2742 TreeCmd *cmdPtr,
2743 Tcl_Interp *interp,
2744 int objc,
2745 Tcl_Obj *CONST *objv)
2746{
2747 Blt_TreeNode node;
2748 int bool;
2749
2750 bool = TRUE;
2751 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
2752 bool = FALSE;
2753 } else if (objc == 4) {
2754 Tcl_Obj *valueObjPtr;
2755 char *string;
2756
2757 string = Tcl_GetString(objv[3]);
2758 if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, node,
2759 string, &valueObjPtr) != TCL_OK) {
2760 bool = FALSE;
2761 }
2762 }
2763 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool));
2764 return TCL_OK;
2765}
2766
2767/*
2768 *----------------------------------------------------------------------
2769 *
2770 * FindOp --
2771 *
2772 *----------------------------------------------------------------------
2773 */
2774static int
2775FindOp(
2776 TreeCmd *cmdPtr,
2777 Tcl_Interp *interp,
2778 int objc,
2779 Tcl_Obj *CONST *objv)
2780{
2781 Blt_TreeNode node;
2782 FindData data;
2783 int result;
2784 Tcl_Obj **objArr;
2785
2786 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
2787 return TCL_ERROR;
2788 }
2789 memset(&data, 0, sizeof(data));
2790 data.maxDepth = -1;
2791 data.order = TREE_POSTORDER;
2792 objArr = NULL;
2793
2794 /* Process switches */
2795 if (Blt_ProcessObjSwitches(interp, findSwitches, objc - 3, objv + 3,
2796 (char *)&data, 0) < 0) {
2797 return TCL_ERROR;
2798 }
2799 if (data.maxDepth >= 0) {
2800 data.maxDepth += Blt_TreeNodeDepth(cmdPtr->tree, node);
2801 }
2802 if (data.flags & MATCH_NOCASE) {
2803 Blt_ListNode listNode;
2804
2805 for (listNode = Blt_ListFirstNode(data.patternList); listNode != NULL;
2806 listNode = Blt_ListNextNode(listNode)) {
2807 strtolower((char *)Blt_ListGetKey(listNode));
2808 }
2809 }
2810 if (data.command != NULL) {
2811 int count;
2812 char **p;
2813 register int i;
2814
2815 count = 0;
2816 for (p = data.command; *p != NULL; p++) {
2817 count++;
2818 }
2819 /* Leave room for node Id argument to be appended */
2820 objArr = Blt_Calloc(count + 2, sizeof(Tcl_Obj *));
2821 for (i = 0; i < count; i++) {
2822 objArr[i] = Tcl_NewStringObj(data.command[i], -1);
2823 Tcl_IncrRefCount(objArr[i]);
2824 }
2825 data.objv = objArr;
2826 data.objc = count + 1;
2827 }
2828 data.listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
2829 data.cmdPtr = cmdPtr;
2830 if (data.order == TREE_BREADTHFIRST) {
2831 result = Blt_TreeApplyBFS(node, MatchNodeProc, &data);
2832 } else {
2833 result = Blt_TreeApplyDFS(node, MatchNodeProc, &data, data.order);
2834 }
2835 if (data.command != NULL) {
2836 Tcl_Obj **objPtrPtr;
2837
2838 for (objPtrPtr = objArr; *objPtrPtr != NULL; objPtrPtr++) {
2839 Tcl_DecrRefCount(*objPtrPtr);
2840 }
2841 Blt_Free(objArr);
2842 }
2843 Blt_FreeSwitches(findSwitches, (char *)&data, 0);
2844 if (result == TCL_ERROR) {
2845 return TCL_ERROR;
2846 }
2847 Tcl_SetObjResult(interp, data.listObjPtr);
2848 return TCL_OK;
2849}
2850
2851/*
2852 *----------------------------------------------------------------------
2853 *
2854 * FindChildOp --
2855 *
2856 *----------------------------------------------------------------------
2857 */
2858/*ARGSUSED*/
2859static int
2860FindChildOp(
2861 TreeCmd *cmdPtr,
2862 Tcl_Interp *interp,
2863 int objc, /* Not used. */
2864 Tcl_Obj *CONST *objv)
2865{
2866 Blt_TreeNode node, child;
2867 int inode;
2868
2869 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
2870 return TCL_ERROR;
2871 }
2872 inode = -1;
2873 child = Blt_TreeFindChild(node, Tcl_GetString(objv[3]));
2874 if (child != NULL) {
2875 inode = Blt_TreeNodeId(child);
2876 }
2877 Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
2878 return TCL_OK;
2879}
2880
2881
2882/*
2883 *----------------------------------------------------------------------
2884 *
2885 * FirstChildOp --
2886 *
2887 *----------------------------------------------------------------------
2888 */
2889/*ARGSUSED*/
2890static int
2891FirstChildOp(
2892 TreeCmd *cmdPtr,
2893 Tcl_Interp *interp,
2894 int objc, /* Not used. */
2895 Tcl_Obj *CONST *objv)
2896{
2897 Blt_TreeNode node;
2898 int inode;
2899
2900 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
2901 return TCL_ERROR;
2902 }
2903 inode = -1;
2904 node = Blt_TreeFirstChild(node);
2905 if (node != NULL) {
2906 inode = Blt_TreeNodeId(node);
2907 }
2908 Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
2909 return TCL_OK;
2910}
2911
2912/*
2913 *----------------------------------------------------------------------
2914 *
2915 * GetOp --
2916 *
2917 *----------------------------------------------------------------------
2918 */
2919static int
2920GetOp(
2921 TreeCmd *cmdPtr,
2922 Tcl_Interp *interp,
2923 int objc,
2924 Tcl_Obj *CONST *objv)
2925{
2926 Blt_TreeNode node;
2927
2928 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
2929 return TCL_ERROR;
2930 }
2931 if (objc == 3) {
2932 Blt_TreeKey key;
2933 Tcl_Obj *valueObjPtr, *listObjPtr;
2934 Blt_TreeKeySearch cursor;
2935
2936 /* Add the key-value pairs to a new Tcl_Obj */
2937 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
2938 for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &cursor); key != NULL;
2939 key = Blt_TreeNextKey(cmdPtr->tree, &cursor)) {
2940 if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, node, key,
2941 &valueObjPtr) == TCL_OK) {
2942 Tcl_Obj *objPtr;
2943
2944 objPtr = Tcl_NewStringObj(key, -1);
2945 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
2946 Tcl_ListObjAppendElement(interp, listObjPtr, valueObjPtr);
2947 }
2948 }
2949 Tcl_SetObjResult(interp, listObjPtr);
2950 return TCL_OK;
2951 } else {
2952 Tcl_Obj *valueObjPtr;
2953 char *string;
2954
2955 string = Tcl_GetString(objv[3]);
2956 if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, node, string,
2957 &valueObjPtr) != TCL_OK) {
2958 if (objc == 4) {
2959 Tcl_DString dString;
2960 char *path;
2961
2962 path = GetNodePath(cmdPtr, Blt_TreeRootNode(cmdPtr->tree),
2963 node, FALSE, &dString);
2964 Tcl_AppendResult(interp, "can't find field \"", string,
2965 "\" in \"", path, "\"", (char *)NULL);
2966 Tcl_DStringFree(&dString);
2967 return TCL_ERROR;
2968 }
2969 /* Default to given value */
2970 valueObjPtr = objv[4];
2971 }
2972 Tcl_SetObjResult(interp, valueObjPtr);
2973 }
2974 return TCL_OK;
2975}
2976
2977/*
2978 *----------------------------------------------------------------------
2979 *
2980 * IndexOp --
2981 *
2982 *----------------------------------------------------------------------
2983 */
2984/*ARGSUSED*/
2985static int
2986IndexOp(
2987 TreeCmd *cmdPtr,
2988 Tcl_Interp *interp,
2989 int objc, /* Not used. */
2990 Tcl_Obj *CONST *objv)
2991{
2992 Blt_TreeNode node;
2993 int inode;
2994
2995 inode = -1;
2996 if (GetNode(cmdPtr, objv[2], &node) == TCL_OK) {
2997 inode = Blt_TreeNodeId(node);
2998 } else {
2999 register int i;
3000 int nObjs;
3001 Tcl_Obj **objArr;
3002 Blt_TreeNode parent;
3003 char *string;
3004
3005 if (Tcl_ListObjGetElements(interp, objv[2], &nObjs, &objArr)
3006 != TCL_OK) {
3007 goto done; /* Can't split object. */
3008 }
3009 parent = Blt_TreeRootNode(cmdPtr->tree);
3010 for (i = 0; i < nObjs; i++) {
3011 string = Tcl_GetString(objArr[i]);
3012 if (string[0] == '\0') {
3013 continue;
3014 }
3015 node = Blt_TreeFindChild(parent, string);
3016 if (node == NULL) {
3017 goto done; /* Can't find component */
3018 }
3019 parent = node;
3020 }
3021 inode = Blt_TreeNodeId(node);
3022 }
3023 done:
3024 Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
3025 return TCL_OK;
3026}
3027
3028/*
3029 *----------------------------------------------------------------------
3030 *
3031 * InsertOp --
3032 *
3033 *----------------------------------------------------------------------
3034 */
3035
3036static int
3037InsertOp(
3038 TreeCmd *cmdPtr,
3039 Tcl_Interp *interp,
3040 int objc,
3041 Tcl_Obj *CONST *objv)
3042{
3043 Blt_TreeNode parent, child;
3044 InsertData data;
3045
3046 child = NULL;
3047 if (GetNode(cmdPtr, objv[2], &parent) != TCL_OK) {
3048 return TCL_ERROR;
3049 }
3050 /* Initialize switch flags */
3051 memset(&data, 0, sizeof(data));
3052 data.insertPos = -1; /* Default to append node. */
3053 data.parent = parent;
3054 data.inode = -1;
3055
3056 if (Blt_ProcessObjSwitches(interp, insertSwitches, objc - 3, objv + 3,
3057 (char *)&data, 0) < 0) {
3058 goto error;
3059 }
3060 if (data.inode > 0) {
3061 Blt_TreeNode node;
3062
3063 node = Blt_TreeGetNode(cmdPtr->tree, data.inode);
3064 if (node != NULL) {
3065 Tcl_AppendResult(interp, "can't reissue node id \"",
3066 Blt_Itoa(data.inode), "\": already exists.", (char *)NULL);
3067 goto error;
3068 }
3069 child = Blt_TreeCreateNodeWithId(cmdPtr->tree, parent, data.label,
3070 data.inode, data.insertPos);
3071 } else {
3072 child = Blt_TreeCreateNode(cmdPtr->tree, parent, data.label,
3073 data.insertPos);
3074 }
3075 if (child == NULL) {
3076 Tcl_AppendResult(interp, "can't allocate new node", (char *)NULL);
3077 goto error;
3078 }
3079 if (data.label == NULL) {
3080 char string[200];
3081
3082 sprintf(string, "node%d", Blt_TreeNodeId(child));
3083 Blt_TreeRelabelNode2(child, string);
3084 }
3085 if (data.tags != NULL) {
3086 register char **p;
3087
3088 for (p = data.tags; *p != NULL; p++) {
3089 if (AddTag(cmdPtr, child, *p) != TCL_OK) {
3090 goto error;
3091 }
3092 }
3093 }
3094 if (data.dataPairs != NULL) {
3095 register char **p;
3096 char *key;
3097 Tcl_Obj *objPtr;
3098
3099 for (p = data.dataPairs; *p != NULL; p++) {
3100 key = *p;
3101 p++;
3102 if (*p == NULL) {
3103 Tcl_AppendResult(interp, "missing value for \"", key, "\"",
3104 (char *)NULL);
3105 goto error;
3106 }
3107 objPtr = Tcl_NewStringObj(*p, -1);
3108 if (Blt_TreeSetValue(interp, cmdPtr->tree, child, key, objPtr)
3109 != TCL_OK) {
3110 Tcl_DecrRefCount(objPtr);
3111 goto error;
3112 }
3113 }
3114 }
3115 Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeNodeId(child));
3116 Blt_FreeSwitches(insertSwitches, (char *)&data, 0);
3117 return TCL_OK;
3118
3119 error:
3120 if (child != NULL) {
3121 Blt_TreeDeleteNode(cmdPtr->tree, child);
3122 }
3123 Blt_FreeSwitches(insertSwitches, (char *)&data, 0);
3124 return TCL_ERROR;
3125}
3126
3127/*
3128 *----------------------------------------------------------------------
3129 *
3130 * IsAncestorOp --
3131 *
3132 *----------------------------------------------------------------------
3133 */
3134/*ARGSUSED*/
3135static int
3136IsAncestorOp(
3137 TreeCmd *cmdPtr,
3138 Tcl_Interp *interp,
3139 int objc, /* Not used. */
3140 Tcl_Obj *CONST *objv)
3141{
3142 Blt_TreeNode node1, node2;
3143 int bool;
3144
3145 if ((GetNode(cmdPtr, objv[3], &node1) != TCL_OK) ||
3146 (GetNode(cmdPtr, objv[4], &node2) != TCL_OK)) {
3147 return TCL_ERROR;
3148 }
3149 bool = Blt_TreeIsAncestor(node1, node2);
3150 Tcl_SetIntObj(Tcl_GetObjResult(interp), bool);
3151 return TCL_OK;
3152}
3153
3154/*
3155 *----------------------------------------------------------------------
3156 *
3157 * IsBeforeOp --
3158 *
3159 *----------------------------------------------------------------------
3160 */
3161/*ARGSUSED*/
3162static int
3163IsBeforeOp(
3164 TreeCmd *cmdPtr,
3165 Tcl_Interp *interp,
3166 int objc, /* Not used. */
3167 Tcl_Obj *CONST *objv)
3168{
3169 Blt_TreeNode node1, node2;
3170 int bool;
3171
3172 if ((GetNode(cmdPtr, objv[3], &node1) != TCL_OK) ||
3173 (GetNode(cmdPtr, objv[4], &node2) != TCL_OK)) {
3174 return TCL_ERROR;
3175 }
3176 bool = Blt_TreeIsBefore(node1, node2);
3177 Tcl_SetIntObj(Tcl_GetObjResult(interp), bool);
3178 return TCL_OK;
3179}
3180
3181/*
3182 *----------------------------------------------------------------------
3183 *
3184 * IsLeafOp --
3185 *
3186 *----------------------------------------------------------------------
3187 */
3188/*ARGSUSED*/
3189static int
3190IsLeafOp(
3191 TreeCmd *cmdPtr,
3192 Tcl_Interp *interp,
3193 int objc, /* Not used. */
3194 Tcl_Obj *CONST *objv)
3195{
3196 Blt_TreeNode node;
3197
3198 if (GetNode(cmdPtr, objv[3], &node) != TCL_OK) {
3199 return TCL_ERROR;
3200 }
3201 Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeIsLeaf(node));
3202 return TCL_OK;
3203}
3204
3205/*
3206 *----------------------------------------------------------------------
3207 *
3208 * IsRootOp --
3209 *
3210 *----------------------------------------------------------------------
3211 */
3212/*ARGSUSED*/
3213static int
3214IsRootOp(
3215 TreeCmd *cmdPtr,
3216 Tcl_Interp *interp,
3217 int objc, /* Not used. */
3218 Tcl_Obj *CONST *objv)
3219{
3220 Blt_TreeNode node;
3221 int bool;
3222
3223 if (GetNode(cmdPtr, objv[3], &node) != TCL_OK) {
3224 return TCL_ERROR;
3225 }
3226 bool = (node == Blt_TreeRootNode(cmdPtr->tree));
3227 Tcl_SetIntObj(Tcl_GetObjResult(interp), bool);
3228 return TCL_OK;
3229}
3230
3231/*
3232 *----------------------------------------------------------------------
3233 *
3234 * IsOp --
3235 *
3236 *----------------------------------------------------------------------
3237 */
3238static Blt_OpSpec isOps[] =
3239{
3240 {"ancestor", 1, (Blt_Op)IsAncestorOp, 5, 5, "node1 node2",},
3241 {"before", 1, (Blt_Op)IsBeforeOp, 5, 5, "node1 node2",},
3242 {"leaf", 1, (Blt_Op)IsLeafOp, 4, 4, "node",},
3243 {"root", 1, (Blt_Op)IsRootOp, 4, 4, "node",},
3244};
3245
3246static int nIsOps = sizeof(isOps) / sizeof(Blt_OpSpec);
3247
3248static int
3249IsOp(
3250 TreeCmd *cmdPtr,
3251 Tcl_Interp *interp,
3252 int objc,
3253 Tcl_Obj *CONST *objv)
3254{
3255 Blt_Op proc;
3256 int result;
3257
3258 proc = Blt_GetOpFromObj(interp, nIsOps, isOps, BLT_OP_ARG2, objc, objv, 0);
3259 if (proc == NULL) {
3260 return TCL_ERROR;
3261 }
3262 result = (*proc) (cmdPtr, interp, objc, objv);
3263 return result;
3264}
3265
3266
3267/*
3268 *----------------------------------------------------------------------
3269 *
3270 * KeysOp --
3271 *
3272 * Returns the key names of values for a node or array value.
3273 *
3274 *----------------------------------------------------------------------
3275 */
3276static int
3277KeysOp(
3278 TreeCmd *cmdPtr,
3279 Tcl_Interp *interp,
3280 int objc,
3281 Tcl_Obj *CONST *objv)
3282{
3283 Blt_HashEntry *hPtr;
3284 Blt_HashSearch tagSearch;
3285 Blt_HashTable keyTable;
3286 Blt_TreeKey key;
3287 Blt_TreeKeySearch keyIter;
3288 Blt_TreeNode node;
3289 TagSearch tagIter;
3290 Tcl_Obj *listObjPtr, *objPtr;
3291 register int i;
3292 int isNew;
3293
3294 Blt_InitHashTableWithPool(&keyTable, BLT_ONE_WORD_KEYS);
3295 for (i = 2; i < objc; i++) {
3296 node = FirstTaggedNode(interp, cmdPtr, objv[i], &tagIter);
3297 if (node == NULL) {
3298 return TCL_ERROR;
3299 }
3300 for (/* empty */; node != NULL; node = NextTaggedNode(node, &tagIter)) {
3301 for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &keyIter);
3302 key != NULL; key = Blt_TreeNextKey(cmdPtr->tree, &keyIter)) {
3303 Blt_CreateHashEntry(&keyTable, key, &isNew);
3304 }
3305 }
3306 }
3307 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
3308 for (hPtr = Blt_FirstHashEntry(&keyTable, &tagSearch); hPtr != NULL;
3309 hPtr = Blt_NextHashEntry(&tagSearch)) {
3310 objPtr = Tcl_NewStringObj(Blt_GetHashKey(&keyTable, hPtr), -1);
3311 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
3312 }
3313 Tcl_SetObjResult(interp, listObjPtr);
3314 Blt_DeleteHashTable(&keyTable);
3315 return TCL_OK;
3316}
3317
3318/*
3319 *----------------------------------------------------------------------
3320 *
3321 * LabelOp --
3322 *
3323 *----------------------------------------------------------------------
3324 */
3325static int
3326LabelOp(
3327 TreeCmd *cmdPtr,
3328 Tcl_Interp *interp,
3329 int objc,
3330 Tcl_Obj *CONST *objv)
3331{
3332 Blt_TreeNode node;
3333
3334 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
3335 return TCL_ERROR;
3336 }
3337 if (objc == 4) {
3338 Blt_TreeRelabelNode(cmdPtr->tree, node, Tcl_GetString(objv[3]));
3339 }
3340 Tcl_SetStringObj(Tcl_GetObjResult(interp), Blt_TreeNodeLabel(node), -1);
3341 return TCL_OK;
3342}
3343
3344/*
3345 *----------------------------------------------------------------------
3346 *
3347 * LastChildOp --
3348 *
3349 *----------------------------------------------------------------------
3350 */
3351/*ARGSUSED*/
3352static int
3353LastChildOp(cmdPtr, interp, objc, objv)
3354 TreeCmd *cmdPtr;
3355 Tcl_Interp *interp;
3356 int objc; /* Not used. */
3357 Tcl_Obj *CONST *objv;
3358{
3359 Blt_TreeNode node;
3360 int inode;
3361
3362 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
3363 return TCL_ERROR;
3364 }
3365 inode = -1;
3366 node = Blt_TreeLastChild(node);
3367 if (node != NULL) {
3368 inode = Blt_TreeNodeId(node);
3369 }
3370 Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
3371 return TCL_OK;
3372}
3373
3374/*
3375 *----------------------------------------------------------------------
3376 *
3377 * MoveOp --
3378 *
3379 * The big trick here is to not consider the node to be moved in
3380 * determining it's new location. Ideally, you would temporarily
3381 * pull from the tree and replace it (back in its old location if
3382 * something went wrong), but you could still pick the node by
3383 * its serial number. So here we make lots of checks for the
3384 * node to be moved.
3385 *
3386 *
3387 *----------------------------------------------------------------------
3388 */
3389static int
3390MoveOp(
3391 TreeCmd *cmdPtr,
3392 Tcl_Interp *interp,
3393 int objc,
3394 Tcl_Obj *CONST *objv)
3395{
3396 Blt_TreeNode root, parent, node;
3397 Blt_TreeNode before;
3398 MoveData data;
3399
3400 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
3401 return TCL_ERROR;
3402 }
3403 if (GetNode(cmdPtr, objv[3], &parent) != TCL_OK) {
3404 return TCL_ERROR;
3405 }
3406 root = Blt_TreeRootNode(cmdPtr->tree);
3407 if (node == root) {
3408 Tcl_AppendResult(interp, "can't move root node", (char *)NULL);
3409 return TCL_ERROR;
3410 }
3411 if (parent == node) {
3412 Tcl_AppendResult(interp, "can't move node to self", (char *)NULL);
3413 return TCL_ERROR;
3414 }
3415 data.node = NULL;
3416 data.cmdPtr = cmdPtr;
3417 data.movePos = -1;
3418 /* Process switches */
3419 if (Blt_ProcessObjSwitches(interp, moveSwitches, objc - 4, objv + 4,
3420 (char *)&data, 0) < 0) {
3421 return TCL_ERROR;
3422 }
3423 /* Verify they aren't ancestors. */
3424 if (Blt_TreeIsAncestor(node, parent)) {
3425 Tcl_AppendResult(interp, "can't move node: \"",
3426 Tcl_GetString(objv[2]), (char *)NULL);
3427 Tcl_AppendResult(interp, "\" is an ancestor of \"",
3428 Tcl_GetString(objv[3]), "\"", (char *)NULL);
3429 return TCL_ERROR;
3430 }
3431 before = NULL; /* If before is NULL, this appends the
3432 * node to the parent's child list. */
3433
3434 if (data.node != NULL) { /* -before or -after */
3435 if (Blt_TreeNodeParent(data.node) != parent) {
3436 Tcl_AppendResult(interp, Tcl_GetString(objv[2]),
3437 " isn't the parent of ", Blt_TreeNodeLabel(data.node),
3438 (char *)NULL);
3439 return TCL_ERROR;
3440 }
3441 if (Blt_SwitchChanged(moveSwitches, "-before", (char *)NULL)) {
3442 before = data.node;
3443 if (before == node) {
3444 Tcl_AppendResult(interp, "can't move node before itself",
3445 (char *)NULL);
3446 return TCL_ERROR;
3447 }
3448 } else {
3449 before = Blt_TreeNextSibling(data.node);
3450 if (before == node) {
3451 Tcl_AppendResult(interp, "can't move node after itself",
3452 (char *)NULL);
3453 return TCL_ERROR;
3454 }
3455 }
3456 } else if (data.movePos >= 0) { /* -at */
3457 int count; /* Tracks the current list index. */
3458 Blt_TreeNode child;
3459
3460 /*
3461 * If the node is in the list, ignore it when determining the
3462 * "before" node using the -at index. An index of -1 means to
3463 * append the node to the list.
3464 */
3465 count = 0;
3466 for(child = Blt_TreeFirstChild(parent); child != NULL;
3467 child = Blt_TreeNextSibling(child)) {
3468 if (child == node) {
3469 continue; /* Ignore the node to be moved. */
3470 }
3471 if (count == data.movePos) {
3472 before = child;
3473 break;
3474 }
3475 count++;
3476 }
3477 }
3478 if (Blt_TreeMoveNode(cmdPtr->tree, node, parent, before) != TCL_OK) {
3479 Tcl_AppendResult(interp, "can't move node ", Tcl_GetString(objv[2]),
3480 " to ", Tcl_GetString(objv[3]), (char *)NULL);
3481 return TCL_ERROR;
3482 }
3483 return TCL_OK;
3484}
3485
3486/*
3487 *----------------------------------------------------------------------
3488 *
3489 * NextOp --
3490 *
3491 *----------------------------------------------------------------------
3492 */
3493/*ARGSUSED*/
3494static int
3495NextOp(
3496 TreeCmd *cmdPtr,
3497 Tcl_Interp *interp,
3498 int objc, /* Not used. */
3499 Tcl_Obj *CONST *objv)
3500{
3501 Blt_TreeNode node;
3502 int inode;
3503
3504 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
3505 return TCL_ERROR;
3506 }
3507 inode = -1;
3508 node = Blt_TreeNextNode(Blt_TreeRootNode(cmdPtr->tree), node);
3509 if (node != NULL) {
3510 inode = Blt_TreeNodeId(node);
3511 }
3512 Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
3513 return TCL_OK;
3514}
3515
3516/*
3517 *----------------------------------------------------------------------
3518 *
3519 * NextSiblingOp --
3520 *
3521 *----------------------------------------------------------------------
3522 */
3523/*ARGSUSED*/
3524static int
3525NextSiblingOp(
3526 TreeCmd *cmdPtr,
3527 Tcl_Interp *interp,
3528 int objc, /* Not used. */
3529 Tcl_Obj *CONST *objv)
3530{
3531 Blt_TreeNode node;
3532 int inode;
3533
3534 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
3535 return TCL_ERROR;
3536 }
3537 inode = -1;
3538 node = Blt_TreeNextSibling(node);
3539 if (node != NULL) {
3540 inode = Blt_TreeNodeId(node);
3541 }
3542 Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
3543 return TCL_OK;
3544}
3545
3546/*
3547 *----------------------------------------------------------------------
3548 *
3549 * NotifyCreateOp --
3550 *
3551 * tree0 notify create ?flags? command arg
3552 *----------------------------------------------------------------------
3553 */
3554static int
3555NotifyCreateOp(
3556 TreeCmd *cmdPtr,
3557 Tcl_Interp *interp,
3558 int objc,
3559 Tcl_Obj *CONST *objv)
3560{
3561 NotifyInfo *notifyPtr;
3562 NotifyData data;
3563 char *string;
3564 char idString[200];
3565 int isNew, nArgs;
3566 Blt_HashEntry *hPtr;
3567 int count;
3568 register int i;
3569
3570 count = 0;
3571 for (i = 3; i < objc; i++) {
3572 string = Tcl_GetString(objv[i]);
3573 if (string[0] != '-') {
3574 break;
3575 }
3576 count++;
3577 }
3578 data.mask = 0;
3579 /* Process switches */
3580 if (Blt_ProcessObjSwitches(interp, notifySwitches, count, objv + 3,
3581 (char *)&data, 0) < 0) {
3582 return TCL_ERROR;
3583 }
3584 notifyPtr = Blt_Malloc(sizeof(NotifyInfo));
3585
3586 nArgs = objc - i;
3587
3588 /* Stash away the command in structure and pass that to the notifier. */
3589 notifyPtr->objv = Blt_Malloc((nArgs + 2) * sizeof(Tcl_Obj *));
3590 for (count = 0; i < objc; i++, count++) {
3591 Tcl_IncrRefCount(objv[i]);
3592 notifyPtr->objv[count] = objv[i];
3593 }
3594 notifyPtr->objc = nArgs + 2;
3595 notifyPtr->cmdPtr = cmdPtr;
3596 if (data.mask == 0) {
3597 data.mask = TREE_NOTIFY_ALL;
3598 }
3599 notifyPtr->mask = data.mask;
3600
3601 sprintf(idString, "notify%d", cmdPtr->notifyCounter++);
3602 hPtr = Blt_CreateHashEntry(&(cmdPtr->notifyTable), idString, &isNew);
3603 Blt_SetHashValue(hPtr, notifyPtr);
3604
3605 Tcl_SetStringObj(Tcl_GetObjResult(interp), idString, -1);
3606 return TCL_OK;
3607}
3608
3609/*
3610 *----------------------------------------------------------------------
3611 *
3612 * NotifyDeleteOp --
3613 *
3614 *----------------------------------------------------------------------
3615 */
3616static int
3617NotifyDeleteOp(
3618 TreeCmd *cmdPtr,
3619 Tcl_Interp *interp,
3620 int objc,
3621 Tcl_Obj *CONST *objv)
3622{
3623 NotifyInfo *notifyPtr;
3624 Blt_HashEntry *hPtr;
3625 register int i, j;
3626 char *string;
3627
3628 for (i = 3; i < objc; i++) {
3629 string = Tcl_GetString(objv[i]);
3630 hPtr = Blt_FindHashEntry(&(cmdPtr->notifyTable), string);
3631 if (hPtr == NULL) {
3632 Tcl_AppendResult(interp, "unknown notify name \"", string, "\"",
3633 (char *)NULL);
3634 return TCL_ERROR;
3635 }
3636 notifyPtr = Blt_GetHashValue(hPtr);
3637 Blt_DeleteHashEntry(&(cmdPtr->notifyTable), hPtr);
3638 for (j = 0; j < (notifyPtr->objc - 2); j++) {
3639 Tcl_DecrRefCount(notifyPtr->objv[j]);
3640 }
3641 Blt_Free(notifyPtr->objv);
3642 Blt_Free(notifyPtr);
3643 }
3644 return TCL_OK;
3645}
3646
3647/*
3648 *----------------------------------------------------------------------
3649 *
3650 * NotifyInfoOp --
3651 *
3652 *----------------------------------------------------------------------
3653 */
3654/*ARGSUSED*/
3655static int
3656NotifyInfoOp(
3657 TreeCmd *cmdPtr,
3658 Tcl_Interp *interp,
3659 int objc, /* Not used. */
3660 Tcl_Obj *CONST *objv)
3661{
3662 NotifyInfo *notifyPtr;
3663 Blt_HashEntry *hPtr;
3664 Tcl_DString dString;
3665 char *string;
3666 int i;
3667
3668 string = Tcl_GetString(objv[3]);
3669 hPtr = Blt_FindHashEntry(&(cmdPtr->notifyTable), string);
3670 if (hPtr == NULL) {
3671 Tcl_AppendResult(interp, "unknown notify name \"", string, "\"",
3672 (char *)NULL);
3673 return TCL_ERROR;
3674 }
3675 notifyPtr = Blt_GetHashValue(hPtr);
3676
3677 Tcl_DStringInit(&dString);
3678 Tcl_DStringAppendElement(&dString, string); /* Copy notify Id */
3679 Tcl_DStringStartSublist(&dString);
3680 if (notifyPtr->mask & TREE_NOTIFY_CREATE) {
3681 Tcl_DStringAppendElement(&dString, "-create");
3682 }
3683 if (notifyPtr->mask & TREE_NOTIFY_DELETE) {
3684 Tcl_DStringAppendElement(&dString, "-delete");
3685 }
3686 if (notifyPtr->mask & TREE_NOTIFY_MOVE) {
3687 Tcl_DStringAppendElement(&dString, "-move");
3688 }
3689 if (notifyPtr->mask & TREE_NOTIFY_SORT) {
3690 Tcl_DStringAppendElement(&dString, "-sort");
3691 }
3692 if (notifyPtr->mask & TREE_NOTIFY_RELABEL) {
3693 Tcl_DStringAppendElement(&dString, "-relabel");
3694 }
3695 if (notifyPtr->mask & TREE_NOTIFY_WHENIDLE) {
3696 Tcl_DStringAppendElement(&dString, "-whenidle");
3697 }
3698 Tcl_DStringEndSublist(&dString);
3699 Tcl_DStringStartSublist(&dString);
3700 for (i = 0; i < (notifyPtr->objc - 2); i++) {
3701 Tcl_DStringAppendElement(&dString, Tcl_GetString(notifyPtr->objv[i]));
3702 }
3703 Tcl_DStringEndSublist(&dString);
3704 Tcl_DStringResult(interp, &dString);
3705 return TCL_OK;
3706}
3707
3708/*
3709 *----------------------------------------------------------------------
3710 *
3711 * NotifyNamesOp --
3712 *
3713 *----------------------------------------------------------------------
3714 */
3715/*ARGSUSED*/
3716static int
3717NotifyNamesOp(
3718 TreeCmd *cmdPtr,
3719 Tcl_Interp *interp,
3720 int objc, /* Not used. */
3721 Tcl_Obj *CONST *objv)
3722{
3723 Blt_HashEntry *hPtr;
3724 Blt_HashSearch cursor;
3725 Tcl_Obj *objPtr, *listObjPtr;
3726 char *notifyId;
3727
3728 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
3729 for (hPtr = Blt_FirstHashEntry(&(cmdPtr->notifyTable), &cursor);
3730 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
3731 notifyId = Blt_GetHashKey(&(cmdPtr->notifyTable), hPtr);
3732 objPtr = Tcl_NewStringObj(notifyId, -1);
3733 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
3734 }
3735 Tcl_SetObjResult(interp, listObjPtr);
3736 return TCL_OK;
3737}
3738
3739/*
3740 *----------------------------------------------------------------------
3741 *
3742 * NotifyOp --
3743 *
3744 *----------------------------------------------------------------------
3745 */
3746static Blt_OpSpec notifyOps[] =
3747{
3748 {"create", 1, (Blt_Op)NotifyCreateOp, 4, 0, "?flags? command",},
3749 {"delete", 1, (Blt_Op)NotifyDeleteOp, 3, 0, "notifyId...",},
3750 {"info", 1, (Blt_Op)NotifyInfoOp, 4, 4, "notifyId",},
3751 {"names", 1, (Blt_Op)NotifyNamesOp, 3, 3, "",},
3752};
3753
3754static int nNotifyOps = sizeof(notifyOps) / sizeof(Blt_OpSpec);
3755
3756static int
3757NotifyOp(
3758 TreeCmd *cmdPtr,
3759 Tcl_Interp *interp,
3760 int objc,
3761 Tcl_Obj *CONST *objv)
3762{
3763 Blt_Op proc;
3764 int result;
3765
3766 proc = Blt_GetOpFromObj(interp, nNotifyOps, notifyOps, BLT_OP_ARG2, objc,
3767 objv, 0);
3768 if (proc == NULL) {
3769 return TCL_ERROR;
3770 }
3771 result = (*proc) (cmdPtr, interp, objc, objv);
3772 return result;
3773}
3774
3775
3776/*ARGSUSED*/
3777static int
3778ParentOp(
3779 TreeCmd *cmdPtr,
3780 Tcl_Interp *interp,
3781 int objc, /* Not used. */
3782 Tcl_Obj *CONST *objv)
3783{
3784 Blt_TreeNode node;
3785 int inode;
3786
3787 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
3788 return TCL_ERROR;
3789 }
3790 inode = -1;
3791 node = Blt_TreeNodeParent(node);
3792 if (node != NULL) {
3793 inode = Blt_TreeNodeId(node);
3794 }
3795 Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
3796 return TCL_OK;
3797}
3798
3799/*
3800 *----------------------------------------------------------------------
3801 *
3802 * PathOp --
3803 *
3804 *----------------------------------------------------------------------
3805 */
3806/*ARGSUSED*/
3807static int
3808PathOp(
3809 TreeCmd *cmdPtr,
3810 Tcl_Interp *interp,
3811 int objc, /* Not used. */
3812 Tcl_Obj *CONST *objv)
3813{
3814 Blt_TreeNode node;
3815 Tcl_DString dString;
3816
3817 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
3818 return TCL_ERROR;
3819 }
3820 GetNodePath(cmdPtr, Blt_TreeRootNode(cmdPtr->tree), node, FALSE, &dString);
3821 Tcl_DStringResult(interp, &dString);
3822 return TCL_OK;
3823}
3824
3825
3826static int
3827ComparePositions(Blt_TreeNode *n1Ptr, Blt_TreeNode *n2Ptr)
3828{
3829 if (*n1Ptr == *n2Ptr) {
3830 return 0;
3831 }
3832 if (Blt_TreeIsBefore(*n1Ptr, *n2Ptr)) {
3833 return -1;
3834 }
3835 return 1;
3836}
3837
3838/*
3839 *----------------------------------------------------------------------
3840 *
3841 * PositionOp --
3842 *
3843 *----------------------------------------------------------------------
3844 */
3845/*ARGSUSED*/
3846static int
3847PositionOp(
3848 TreeCmd *cmdPtr,
3849 Tcl_Interp *interp,
3850 int objc, /* Not used. */
3851 Tcl_Obj *CONST *objv)
3852{
3853 PositionData data;
3854 Blt_TreeNode *nodeArr, *nodePtr;
3855 Blt_TreeNode node;
3856 Blt_TreeNode parent, lastParent;
3857 Tcl_Obj *listObjPtr, *objPtr;
3858 int i;
3859 int position;
3860 Tcl_DString dString;
3861 int n;
3862
3863 memset((char *)&data, 0, sizeof(data));
3864 /* Process switches */
3865 n = Blt_ProcessObjSwitches(interp, positionSwitches, objc - 2, objv + 2,
3866 (char *)&data, BLT_SWITCH_OBJV_PARTIAL);
3867 if (n < 0) {
3868 return TCL_ERROR;
3869 }
3870 objc -= n + 2, objv += n + 2;
3871
3872 /* Collect the node ids into an array */
3873 nodeArr = Blt_Malloc((objc + 1) * sizeof(Blt_TreeNode));
3874 for (i = 0; i < objc; i++) {
3875 if (GetNode(cmdPtr, objv[i], &node) != TCL_OK) {
3876 Blt_Free(nodeArr);
3877 return TCL_ERROR;
3878 }
3879 nodeArr[i] = node;
3880 }
3881 nodeArr[i] = NULL;
3882
3883 if (data.sort) { /* Sort the nodes by depth-first order
3884 * if requested. */
3885 qsort((char *)nodeArr, objc, sizeof(Blt_TreeNode),
3886 (QSortCompareProc *)ComparePositions);
3887 }
3888
3889 position = 0; /* Suppress compiler warning. */
3890 lastParent = NULL;
3891 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL);
3892 Tcl_DStringInit(&dString);
3893 for (nodePtr = nodeArr; *nodePtr != NULL; nodePtr++) {
3894 parent = Blt_TreeNodeParent(*nodePtr);
3895 if ((parent != NULL) && (parent == lastParent)) {
3896 /*
3897 * Since we've sorted the nodes already, we can safely
3898 * assume that if two consecutive nodes have the same
3899 * parent, the first node came before the second. If
3900 * this is the case, use the last node as a starting
3901 * point.
3902 */
3903
3904 /*
3905 * Note that we start comparing from the last node,
3906 * not its successor. Some one may give us the same
3907 * node more than once.
3908 */
3909 node = *(nodePtr - 1); /* Can't get here unless there's
3910 * more than one node. */
3911 for(/*empty*/; node != NULL; node = Blt_TreeNextSibling(node)) {
3912 if (node == *nodePtr) {
3913 break;
3914 }
3915 position++;
3916 }
3917 } else {
3918 /* The fallback is to linearly search through the
3919 * parent's list of children, counting the number of
3920 * preceding siblings. Except for nodes with many
3921 * siblings (100+), this should be okay. */
3922 position = Blt_TreeNodePosition(*nodePtr);
3923 }
3924 if (data.sort) {
3925 lastParent = parent; /* Update the last parent. */
3926 }
3927 /*
3928 * Add an element in the form "parent -at position" to the
3929 * list that we're generating.
3930 */
3931 if (data.withId) {
3932 objPtr = Tcl_NewIntObj(Blt_TreeNodeId(*nodePtr));
3933 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
3934 }
3935 if (data.withParent) {
3936 char *string;
3937
3938 Tcl_DStringSetLength(&dString, 0); /* Clear the string. */
3939 string = (parent == NULL) ? "" : Blt_Itoa(Blt_TreeNodeId(parent));
3940 Tcl_DStringAppendElement(&dString, string);
3941 Tcl_DStringAppendElement(&dString, "-at");
3942 Tcl_DStringAppendElement(&dString, Blt_Itoa(position));
3943 objPtr = Tcl_NewStringObj(Tcl_DStringValue(&dString), -1);
3944 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
3945 } else {
3946 objPtr = Tcl_NewIntObj(position);
3947 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
3948 }
3949 }
3950 Tcl_DStringFree(&dString);
3951 Blt_Free(nodeArr);
3952 Tcl_SetObjResult(interp, listObjPtr);
3953 return TCL_OK;
3954}
3955
3956/*
3957 *----------------------------------------------------------------------
3958 *
3959 * PreviousOp --
3960 *
3961 *----------------------------------------------------------------------
3962 */
3963/*ARGSUSED*/
3964static int
3965PreviousOp(
3966 TreeCmd *cmdPtr,
3967 Tcl_Interp *interp,
3968 int objc, /* Not used. */
3969 Tcl_Obj *CONST *objv)
3970{
3971 Blt_TreeNode node;
3972 int inode;
3973
3974 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
3975 return TCL_ERROR;
3976 }
3977 inode = -1;
3978 node = Blt_TreePrevNode(Blt_TreeRootNode(cmdPtr->tree), node);
3979 if (node != NULL) {
3980 inode = Blt_TreeNodeId(node);
3981 }
3982 Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
3983 return TCL_OK;
3984}
3985
3986/*ARGSUSED*/
3987static int
3988PrevSiblingOp(
3989 TreeCmd *cmdPtr,
3990 Tcl_Interp *interp,
3991 int objc, /* Not used. */
3992 Tcl_Obj *CONST *objv)
3993{
3994 Blt_TreeNode node;
3995 int inode;
3996
3997 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
3998 return TCL_ERROR;
3999 }
4000 inode = -1;
4001 node = Blt_TreePrevSibling(node);
4002 if (node != NULL) {
4003 inode = Blt_TreeNodeId(node);
4004 }
4005 Tcl_SetIntObj(Tcl_GetObjResult(interp), inode);
4006 return TCL_OK;
4007}
4008/*
4009 *----------------------------------------------------------------------
4010 *
4011 * RestoreOp --
4012 *
4013 *----------------------------------------------------------------------
4014 */
4015/*ARGSUSED*/
4016static int
4017RestoreOp(
4018 TreeCmd *cmdPtr,
4019 Tcl_Interp *interp,
4020 int objc, /* Not used. */
4021 Tcl_Obj *CONST *objv)
4022{
4023 Blt_TreeNode root;
4024 RestoreData data;
4025 Tcl_DString dString;
4026 char *entry, *eol, *next;
4027 char saved;
4028 int result;
4029
4030 if (GetNode(cmdPtr, objv[2], &root) != TCL_OK) {
4031 return TCL_ERROR;
4032 }
4033 memset((char *)&data, 0, sizeof(data));
4034 Blt_InitHashTable(&data.idTable, BLT_ONE_WORD_KEYS);
4035 data.root = root;
4036
4037 /* Process switches */
4038 if (Blt_ProcessObjSwitches(interp, restoreSwitches, objc - 4, objv + 4,
4039 (char *)&data, 0) < 0) {
4040 return TCL_ERROR;
4041 }
4042 result = TCL_OK;
4043 nLines = 0;
4044 Tcl_DStringInit(&dString);
4045 entry = eol = Tcl_GetString(objv[3]);
4046 next = entry;
4047 while (*eol != '\0') {
4048 /* Find the next end of line. */
4049 for (eol = next; (*eol != '\n') && (*eol != '\0'); eol++) {
4050 /*empty*/
4051 }
4052 /*
4053 * Since we don't own the string (the Tcl_Obj could be shared),
4054 * save the current end-of-line character (it's either a NUL
4055 * or NL) so we can NUL-terminate the line for the call to
4056 * Tcl_SplitList and repair it when we're done.
4057 */
4058 saved = *eol;
4059 *eol = '\0';
4060 next = eol + 1;
4061 nLines++;
4062 if (Tcl_CommandComplete(entry)) {
4063 char **elemArr;
4064 int nElem;
4065
4066 if (Tcl_SplitList(interp, entry, &nElem, &elemArr) != TCL_OK) {
4067 *eol = saved;
4068 return TCL_ERROR;
4069 }
4070 if (nElem > 0) {
4071 result = RestoreNode(cmdPtr, nElem, elemArr, &data);
4072 Blt_Free(elemArr);
4073 if (result != TCL_OK) {
4074 *eol = saved;
4075 break;
4076 }
4077 }
4078 entry = next;
4079 }
4080 *eol = saved;
4081 }
4082 Blt_DeleteHashTable(&data.idTable);
4083 return result;
4084}
4085
4086static int
4087ReadEntry(
4088 Tcl_Interp *interp,
4089 Tcl_Channel channel,
4090 int *argcPtr,
4091 char ***argvPtr)
4092{
4093 Tcl_DString dString;
4094 int result;
4095 char *entry;
4096
4097 if (*argvPtr != NULL) {
4098 Blt_Free(*argvPtr);
4099 *argvPtr = NULL;
4100 }
4101 Tcl_DStringInit(&dString);
4102 entry = NULL;
4103 while (Tcl_Gets(channel, &dString) >= 0) {
4104 nLines++;
4105 Tcl_DStringAppend(&dString, "\n", 1);
4106 entry = Tcl_DStringValue(&dString);
4107 if (Tcl_CommandComplete(entry)) {
4108 result = Tcl_SplitList(interp, entry, argcPtr, argvPtr);
4109 Tcl_DStringFree(&dString);
4110 return result;
4111 }
4112 }
4113 Tcl_DStringFree(&dString);
4114 if (entry == NULL) {
4115 *argcPtr = 0; /* EOF */
4116 return TCL_OK;
4117 }
4118 Tcl_AppendResult(interp, "error reading file: ",
4119 Tcl_PosixError(interp), (char *)NULL);
4120 return TCL_ERROR;
4121}
4122
4123/*
4124 *----------------------------------------------------------------------
4125 *
4126 * RestorefileOp --
4127 *
4128 *----------------------------------------------------------------------
4129 */
4130/*ARGSUSED*/
4131static int
4132RestorefileOp(
4133 TreeCmd *cmdPtr,
4134 Tcl_Interp *interp,
4135 int objc, /* Not used. */
4136 Tcl_Obj *CONST *objv)
4137{
4138 Blt_TreeNode root;
4139 int nElem;
4140 char **elemArr;
4141 char *fileName;
4142 int result;
4143 Tcl_Channel channel;
4144 RestoreData data;
4145
4146 if (GetNode(cmdPtr, objv[2], &root) != TCL_OK) {
4147 return TCL_ERROR;
4148 }
4149 fileName = Tcl_GetString(objv[3]);
4150 channel = Tcl_OpenFileChannel(interp, fileName, "r", 0);
4151 if (channel == NULL) {
4152 return TCL_ERROR;
4153 }
4154 memset((char *)&data, 0, sizeof(data));
4155 Blt_InitHashTable(&data.idTable, BLT_ONE_WORD_KEYS);
4156 data.root = root;
4157
4158 /* Process switches */
4159 if (Blt_ProcessObjSwitches(interp, restoreSwitches, objc - 4, objv + 4,
4160 (char *)&data, 0) < 0) {
4161 Tcl_Close(interp, channel);
4162 return TCL_ERROR;
4163 }
4164 elemArr = NULL;
4165 nLines = 0;
4166 for (;;) {
4167 result = ReadEntry(interp, channel, &nElem, &elemArr);
4168 if ((result != TCL_OK) || (nElem == 0)) {
4169 break;
4170 }
4171 result = RestoreNode(cmdPtr, nElem, elemArr, &data);
4172 if (result != TCL_OK) {
4173 break;
4174 }
4175 }
4176 if (elemArr != NULL) {
4177 Blt_Free(elemArr);
4178 }
4179 Tcl_Close(interp, channel);
4180 return result;
4181}
4182
4183/*
4184 *----------------------------------------------------------------------
4185 *
4186 * RootOp --
4187 *
4188 *----------------------------------------------------------------------
4189 */
4190static int
4191RootOp(
4192 TreeCmd *cmdPtr,
4193 Tcl_Interp *interp,
4194 int objc,
4195 Tcl_Obj *CONST *objv)
4196{
4197 Blt_TreeNode root;
4198
4199 if (objc == 3) {
4200 Blt_TreeNode node;
4201
4202 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
4203 return TCL_ERROR;
4204 }
4205 Blt_TreeChangeRoot(cmdPtr->tree, node);
4206 }
4207 root = Blt_TreeRootNode(cmdPtr->tree);
4208 Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeNodeId(root));
4209 return TCL_OK;
4210}
4211
4212/*
4213 *----------------------------------------------------------------------
4214 *
4215 * SetOp --
4216 *
4217 *----------------------------------------------------------------------
4218 */
4219static int
4220SetOp(
4221 TreeCmd *cmdPtr,
4222 Tcl_Interp *interp,
4223 int objc,
4224 Tcl_Obj *CONST *objv)
4225{
4226 Blt_TreeNode node;
4227 char *string;
4228 TagSearch cursor;
4229
4230 string = Tcl_GetString(objv[2]);
4231 if (isdigit(UCHAR(*string))) {
4232 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
4233 return TCL_ERROR;
4234 }
4235 if (SetValues(cmdPtr, node, objc - 3, objv + 3) != TCL_OK) {
4236 return TCL_ERROR;
4237 }
4238 } else {
4239 node = FirstTaggedNode(interp, cmdPtr, objv[2], &cursor);
4240 if (node == NULL) {
4241 return TCL_ERROR;
4242 }
4243 for (/* empty */; node != NULL; node = NextTaggedNode(node, &cursor)) {
4244 if (SetValues(cmdPtr, node, objc - 3, objv + 3) != TCL_OK) {
4245 return TCL_ERROR;
4246 }
4247 }
4248 }
4249 return TCL_OK;
4250}
4251
4252/*
4253 *----------------------------------------------------------------------
4254 *
4255 * SizeOp --
4256 *
4257 *----------------------------------------------------------------------
4258 */
4259/*ARGSUSED*/
4260static int
4261SizeOp(
4262 TreeCmd *cmdPtr,
4263 Tcl_Interp *interp,
4264 int objc, /* Not used. */
4265 Tcl_Obj *CONST *objv)
4266{
4267 Blt_TreeNode node;
4268
4269 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
4270 return TCL_ERROR;
4271 }
4272 Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeSize(node));
4273 return TCL_OK;
4274}
4275
4276/*
4277 *----------------------------------------------------------------------
4278 *
4279 * TagForgetOp --
4280 *
4281 *----------------------------------------------------------------------
4282 */
4283/*ARGSUSED*/
4284static int
4285TagForgetOp(
4286 TreeCmd *cmdPtr,
4287 Tcl_Interp *interp,
4288 int objc, /* Not used. */
4289 Tcl_Obj *CONST *objv)
4290{
4291 register int i;
4292
4293 for (i = 3; i < objc; i++) {
4294 Blt_TreeForgetTag(cmdPtr->tree, Tcl_GetString(objv[i]));
4295 }
4296 return TCL_OK;
4297}
4298
4299/*
4300 *----------------------------------------------------------------------
4301 *
4302 * TagNamesOp --
4303 *
4304 *----------------------------------------------------------------------
4305 */
4306static int
4307TagNamesOp(
4308 TreeCmd *cmdPtr,
4309 Tcl_Interp *interp,
4310 int objc,
4311 Tcl_Obj *CONST *objv)
4312{
4313 Blt_HashSearch cursor;
4314 Blt_TreeTagEntry *tPtr;
4315 Tcl_Obj *listObjPtr, *objPtr;
4316
4317 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
4318 objPtr = Tcl_NewStringObj("all", -1);
4319 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
4320 if (objc == 3) {
4321 Blt_HashEntry *hPtr;
4322
4323 objPtr = Tcl_NewStringObj("root", -1);
4324 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
4325 for (hPtr = Blt_TreeFirstTag(cmdPtr->tree, &cursor); hPtr != NULL;
4326 hPtr = Blt_NextHashEntry(&cursor)) {
4327 tPtr = Blt_GetHashValue(hPtr);
4328 objPtr = Tcl_NewStringObj(tPtr->tagName, -1);
4329 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
4330 }
4331 } else {
4332 register int i;
4333 Blt_TreeNode node;
4334 Blt_HashEntry *hPtr, *h2Ptr;
4335 Blt_HashTable uniqTable;
4336 int isNew;
4337
4338 Blt_InitHashTable(&uniqTable, BLT_STRING_KEYS);
4339 for (i = 3; i < objc; i++) {
4340 if (GetNode(cmdPtr, objv[i], &node) != TCL_OK) {
4341 goto error;
4342 }
4343 if (node == Blt_TreeRootNode(cmdPtr->tree)) {
4344 Blt_CreateHashEntry(&uniqTable, "root", &isNew);
4345 }
4346 for (hPtr = Blt_TreeFirstTag(cmdPtr->tree, &cursor); hPtr != NULL;
4347 hPtr = Blt_NextHashEntry(&cursor)) {
4348 tPtr = Blt_GetHashValue(hPtr);
4349 h2Ptr = Blt_FindHashEntry(&tPtr->nodeTable, (char *)node);
4350 if (h2Ptr != NULL) {
4351 Blt_CreateHashEntry(&uniqTable, tPtr->tagName, &isNew);
4352 }
4353 }
4354 }
4355 for (hPtr = Blt_FirstHashEntry(&uniqTable, &cursor); hPtr != NULL;
4356 hPtr = Blt_NextHashEntry(&cursor)) {
4357 objPtr = Tcl_NewStringObj(Blt_GetHashKey(&uniqTable, hPtr), -1);
4358 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
4359 }
4360 Blt_DeleteHashTable(&uniqTable);
4361 }
4362 Tcl_SetObjResult(interp, listObjPtr);
4363 return TCL_OK;
4364 error:
4365 Tcl_DecrRefCount(listObjPtr);
4366 return TCL_ERROR;
4367}
4368
4369/*
4370 *----------------------------------------------------------------------
4371 *
4372 * TagNodesOp --
4373 *
4374 *----------------------------------------------------------------------
4375 */
4376static int
4377TagNodesOp(
4378 TreeCmd *cmdPtr,
4379 Tcl_Interp *interp,
4380 int objc,
4381 Tcl_Obj *CONST *objv)
4382{
4383 Blt_HashEntry *hPtr;
4384 Blt_HashSearch cursor;
4385 Blt_HashTable nodeTable;
4386 Blt_TreeNode node;
4387 Tcl_Obj *listObjPtr;
4388 Tcl_Obj *objPtr;
4389 char *string;
4390 int isNew;
4391 register int i;
4392
4393 Blt_InitHashTable(&nodeTable, BLT_ONE_WORD_KEYS);
4394 for (i = 3; i < objc; i++) {
4395 string = Tcl_GetString(objv[i]);
4396 if (strcmp(string, "all") == 0) {
4397 break;
4398 } else if (strcmp(string, "root") == 0) {
4399 Blt_CreateHashEntry(&nodeTable,
4400 (char *)Blt_TreeRootNode(cmdPtr->tree), &isNew);
4401 continue;
4402 } else {
4403 Blt_HashTable *tablePtr;
4404
4405 tablePtr = Blt_TreeTagHashTable(cmdPtr->tree, string);
4406 if (tablePtr != NULL) {
4407 for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor);
4408 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
4409 node = Blt_GetHashValue(hPtr);
4410 Blt_CreateHashEntry(&nodeTable, (char *)node, &isNew);
4411 }
4412 continue;
4413 }
4414 }
4415 Tcl_AppendResult(interp, "can't find a tag \"", string, "\"",
4416 (char *)NULL);
4417 goto error;
4418 }
4419 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
4420 for (hPtr = Blt_FirstHashEntry(&nodeTable, &cursor); hPtr != NULL;
4421 hPtr = Blt_NextHashEntry(&cursor)) {
4422 node = (Blt_TreeNode)Blt_GetHashKey(&nodeTable, hPtr);
4423 objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node));
4424 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
4425 }
4426 Tcl_SetObjResult(interp, listObjPtr);
4427 Blt_DeleteHashTable(&nodeTable);
4428 return TCL_OK;
4429
4430 error:
4431 Blt_DeleteHashTable(&nodeTable);
4432 return TCL_ERROR;
4433}
4434
4435/*
4436 *----------------------------------------------------------------------
4437 *
4438 * TagAddOp --
4439 *
4440 *----------------------------------------------------------------------
4441 */
4442static int
4443TagAddOp(
4444 TreeCmd *cmdPtr,
4445 Tcl_Interp *interp,
4446 int objc,
4447 Tcl_Obj *CONST *objv)
4448{
4449 Blt_TreeNode node;
4450 register int i;
4451 char *string;
4452 TagSearch cursor;
4453
4454 string = Tcl_GetString(objv[3]);
4455 if (isdigit(UCHAR(string[0]))) {
4456 Tcl_AppendResult(interp, "bad tag \"", string,
4457 "\": can't start with a digit", (char *)NULL);
4458 return TCL_ERROR;
4459 }
4460 if ((strcmp(string, "all") == 0) || (strcmp(string, "root") == 0)) {
4461 Tcl_AppendResult(cmdPtr->interp, "can't add reserved tag \"",
4462 string, "\"", (char *)NULL);
4463 return TCL_ERROR;
4464 }
4465 for (i = 4; i < objc; i++) {
4466 node = FirstTaggedNode(interp, cmdPtr, objv[i], &cursor);
4467 if (node == NULL) {
4468 return TCL_ERROR;
4469 }
4470 for (/* empty */; node != NULL; node = NextTaggedNode(node, &cursor)) {
4471 if (AddTag(cmdPtr, node, string) != TCL_OK) {
4472 return TCL_ERROR;
4473 }
4474 }
4475 }
4476 return TCL_OK;
4477}
4478
4479
4480/*
4481 *----------------------------------------------------------------------
4482 *
4483 * TagDeleteOp --
4484 *
4485 *----------------------------------------------------------------------
4486 */
4487static int
4488TagDeleteOp(
4489 TreeCmd *cmdPtr,
4490 Tcl_Interp *interp,
4491 int objc,
4492 Tcl_Obj *CONST *objv)
4493{
4494 char *string;
4495 Blt_HashTable *tablePtr;
4496
4497 string = Tcl_GetString(objv[3]);
4498 if ((strcmp(string, "all") == 0) || (strcmp(string, "root") == 0)) {
4499 Tcl_AppendResult(interp, "can't delete reserved tag \"", string, "\"",
4500 (char *)NULL);
4501 return TCL_ERROR;
4502 }
4503 tablePtr = Blt_TreeTagHashTable(cmdPtr->tree, string);
4504 if (tablePtr != NULL) {
4505 register int i;
4506 Blt_TreeNode node;
4507 TagSearch cursor;
4508 Blt_HashEntry *hPtr;
4509
4510 for (i = 4; i < objc; i++) {
4511 node = FirstTaggedNode(interp, cmdPtr, objv[i], &cursor);
4512 if (node == NULL) {
4513 return TCL_ERROR;
4514 }
4515 for (/* empty */; node != NULL;
4516 node = NextTaggedNode(node, &cursor)) {
4517 hPtr = Blt_FindHashEntry(tablePtr, (char *)node);
4518 if (hPtr != NULL) {
4519 Blt_DeleteHashEntry(tablePtr, hPtr);
4520 }
4521 }
4522 }
4523 }
4524 return TCL_OK;
4525}
4526
4527/*
4528 *----------------------------------------------------------------------
4529 *
4530 * TagDumpOp --
4531 *
4532 *----------------------------------------------------------------------
4533 */
4534static int
4535TagDumpOp(
4536 TreeCmd *cmdPtr,
4537 Tcl_Interp *interp,
4538 int objc,
4539 Tcl_Obj *CONST *objv)
4540{
4541 register Blt_TreeNode root, node;
4542 Tcl_DString dString;
4543 TagSearch cursor;
4544 register int i;
4545
4546 Tcl_DStringInit(&dString);
4547 root = Blt_TreeRootNode(cmdPtr->tree);
4548 for (i = 3; i < objc; i++) {
4549 node = FirstTaggedNode(interp, cmdPtr, objv[i], &cursor);
4550 if (node == NULL) {
4551 Tcl_DStringFree(&dString);
4552 return TCL_ERROR;
4553 }
4554 for (/* empty */; node != NULL; node = NextTaggedNode(node, &cursor)) {
4555 PrintNode(cmdPtr, root, node, &dString);
4556 }
4557 }
4558 Tcl_DStringResult(interp, &dString);
4559 Tcl_DStringFree(&dString);
4560 return TCL_OK;
4561}
4562
4563/*
4564 *----------------------------------------------------------------------
4565 *
4566 * TagOp --
4567 *
4568 *----------------------------------------------------------------------
4569 */
4570static Blt_OpSpec tagOps[] = {
4571 {"add", 1, (Blt_Op)TagAddOp, 5, 0, "tag node...",},
4572 {"delete", 2, (Blt_Op)TagDeleteOp, 5, 0, "tag node...",},
4573 {"dump", 2, (Blt_Op)TagDumpOp, 4, 0, "tag...",},
4574 {"forget", 1, (Blt_Op)TagForgetOp, 4, 0, "tag...",},
4575 {"names", 2, (Blt_Op)TagNamesOp, 3, 0, "?node...?",},
4576 {"nodes", 2, (Blt_Op)TagNodesOp, 4, 0, "tag ?tag...?",},
4577};
4578
4579static int nTagOps = sizeof(tagOps) / sizeof(Blt_OpSpec);
4580
4581static int
4582TagOp(
4583 TreeCmd *cmdPtr,
4584 Tcl_Interp *interp,
4585 int objc,
4586 Tcl_Obj *CONST *objv)
4587{
4588 Blt_Op proc;
4589 int result;
4590
4591 proc = Blt_GetOpFromObj(interp, nTagOps, tagOps, BLT_OP_ARG2, objc, objv,
4592 0);
4593 if (proc == NULL) {
4594 return TCL_ERROR;
4595 }
4596 result = (*proc) (cmdPtr, interp, objc, objv);
4597 return result;
4598}
4599
4600/*
4601 *----------------------------------------------------------------------
4602 *
4603 * TraceCreateOp --
4604 *
4605 *----------------------------------------------------------------------
4606 */
4607/*ARGSUSED*/
4608static int
4609TraceCreateOp(
4610 TreeCmd *cmdPtr,
4611 Tcl_Interp *interp,
4612 int objc, /* Not used. */
4613 Tcl_Obj *CONST *objv)
4614{
4615 Blt_HashEntry *hPtr;
4616 Blt_TreeNode node;
4617 TraceInfo *tracePtr;
4618 char *string, *key, *command;
4619 char *tagName;
4620 char idString[200];
4621 int flags, isNew;
4622 int length;
4623
4624 string = Tcl_GetString(objv[3]);
4625 if (isdigit(UCHAR(*string))) {
4626 if (GetNode(cmdPtr, objv[3], &node) != TCL_OK) {
4627 return TCL_ERROR;
4628 }
4629 tagName = NULL;
4630 } else {
4631 tagName = Blt_Strdup(string);
4632 node = NULL;
4633 }
4634 key = Tcl_GetString(objv[4]);
4635 string = Tcl_GetString(objv[5]);
4636 flags = GetTraceFlags(string);
4637 if (flags < 0) {
4638 Tcl_AppendResult(interp, "unknown flag in \"", string, "\"",
4639 (char *)NULL);
4640 return TCL_ERROR;
4641 }
4642 command = Tcl_GetStringFromObj(objv[6], &length);
4643 /* Stash away the command in structure and pass that to the trace. */
4644 tracePtr = Blt_Malloc(length + sizeof(TraceInfo));
4645 strcpy(tracePtr->command, command);
4646 tracePtr->cmdPtr = cmdPtr;
4647 tracePtr->withTag = tagName;
4648 tracePtr->node = node;
4649 tracePtr->traceToken = Blt_TreeCreateTrace(cmdPtr->tree, node, key, tagName,
4650 flags, TreeTraceProc, tracePtr);
4651
4652 sprintf(idString, "trace%d", cmdPtr->traceCounter++);
4653 hPtr = Blt_CreateHashEntry(&(cmdPtr->traceTable), idString, &isNew);
4654 Blt_SetHashValue(hPtr, tracePtr);
4655
4656 Tcl_SetStringObj(Tcl_GetObjResult(interp), idString, -1);
4657 return TCL_OK;
4658}
4659
4660/*
4661 *----------------------------------------------------------------------
4662 *
4663 * TraceDeleteOp --
4664 *
4665 *----------------------------------------------------------------------
4666 */
4667static int
4668TraceDeleteOp(
4669 TreeCmd *cmdPtr,
4670 Tcl_Interp *interp,
4671 int objc,
4672 Tcl_Obj *CONST *objv)
4673{
4674 TraceInfo *tracePtr;
4675 Blt_HashEntry *hPtr;
4676 register int i;
4677 char *key;
4678
4679 for (i = 3; i < objc; i++) {
4680 key = Tcl_GetString(objv[i]);
4681 hPtr = Blt_FindHashEntry(&(cmdPtr->traceTable), key);
4682 if (hPtr == NULL) {
4683 Tcl_AppendResult(interp, "unknown trace \"", key, "\"",
4684 (char *)NULL);
4685 return TCL_ERROR;
4686 }
4687 tracePtr = Blt_GetHashValue(hPtr);
4688 Blt_DeleteHashEntry(&(cmdPtr->traceTable), hPtr);
4689 Blt_TreeDeleteTrace(tracePtr->traceToken);
4690 if (tracePtr->withTag != NULL) {
4691 Blt_Free(tracePtr->withTag);
4692 }
4693 Blt_Free(tracePtr);
4694 }
4695 return TCL_OK;
4696}
4697
4698/*
4699 *----------------------------------------------------------------------
4700 *
4701 * TraceNamesOp --
4702 *
4703 *----------------------------------------------------------------------
4704 */
4705/*ARGSUSED*/
4706static int
4707TraceNamesOp(
4708 TreeCmd *cmdPtr,
4709 Tcl_Interp *interp,
4710 int objc, /* Not used. */
4711 Tcl_Obj *CONST *objv)
4712{
4713 Blt_HashEntry *hPtr;
4714 Blt_HashSearch cursor;
4715
4716 for (hPtr = Blt_FirstHashEntry(&(cmdPtr->traceTable), &cursor);
4717 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
4718 Tcl_AppendElement(interp, Blt_GetHashKey(&(cmdPtr->traceTable), hPtr));
4719 }
4720 return TCL_OK;
4721}
4722
4723/*
4724 *----------------------------------------------------------------------
4725 *
4726 * TraceInfoOp --
4727 *
4728 *----------------------------------------------------------------------
4729 */
4730/*ARGSUSED*/
4731static int
4732TraceInfoOp(
4733 TreeCmd *cmdPtr,
4734 Tcl_Interp *interp,
4735 int objc, /* Not used. */
4736 Tcl_Obj *CONST *objv)
4737{
4738 TraceInfo *tracePtr;
4739 struct Blt_TreeTraceStruct *tokenPtr;
4740 Blt_HashEntry *hPtr;
4741 Tcl_DString dString;
4742 char string[5];
4743 char *key;
4744
4745 key = Tcl_GetString(objv[3]);
4746 hPtr = Blt_FindHashEntry(&(cmdPtr->traceTable), key);
4747 if (hPtr == NULL) {
4748 Tcl_AppendResult(interp, "unknown trace \"", key, "\"",
4749 (char *)NULL);
4750 return TCL_ERROR;
4751 }
4752 Tcl_DStringInit(&dString);
4753 tracePtr = Blt_GetHashValue(hPtr);
4754 if (tracePtr->withTag != NULL) {
4755 Tcl_DStringAppendElement(&dString, tracePtr->withTag);
4756 } else {
4757 int inode;
4758
4759 inode = Blt_TreeNodeId(tracePtr->node);
4760 Tcl_DStringAppendElement(&dString, Blt_Itoa(inode));
4761 }
4762 tokenPtr = (struct Blt_TreeTraceStruct *)tracePtr->traceToken;
4763 Tcl_DStringAppendElement(&dString, tokenPtr->key);
4764 PrintTraceFlags(tokenPtr->mask, string);
4765 Tcl_DStringAppendElement(&dString, string);
4766 Tcl_DStringAppendElement(&dString, tracePtr->command);
4767 Tcl_DStringResult(interp, &dString);
4768 return TCL_OK;
4769}
4770
4771/*
4772 *----------------------------------------------------------------------
4773 *
4774 * TraceOp --
4775 *
4776 *----------------------------------------------------------------------
4777 */
4778static Blt_OpSpec traceOps[] =
4779{
4780 {"create", 1, (Blt_Op)TraceCreateOp, 7, 7, "node key how command",},
4781 {"delete", 1, (Blt_Op)TraceDeleteOp, 3, 0, "id...",},
4782 {"info", 1, (Blt_Op)TraceInfoOp, 4, 4, "id",},
4783 {"names", 1, (Blt_Op)TraceNamesOp, 3, 3, "",},
4784};
4785
4786static int nTraceOps = sizeof(traceOps) / sizeof(Blt_OpSpec);
4787
4788static int
4789TraceOp(
4790 TreeCmd *cmdPtr,
4791 Tcl_Interp *interp,
4792 int objc,
4793 Tcl_Obj *CONST *objv)
4794{
4795 Blt_Op proc;
4796 int result;
4797
4798 proc = Blt_GetOpFromObj(interp, nTraceOps, traceOps, BLT_OP_ARG2, objc,
4799 objv, 0);
4800 if (proc == NULL) {
4801 return TCL_ERROR;
4802 }
4803 result = (*proc) (cmdPtr, interp, objc, objv);
4804 return result;
4805}
4806
4807/*
4808 *----------------------------------------------------------------------
4809 *
4810 * GetOp --
4811 *
4812 *----------------------------------------------------------------------
4813 */
4814/*ARGSUSED*/
4815static int
4816TypeOp(
4817 TreeCmd *cmdPtr,
4818 Tcl_Interp *interp,
4819 int objc, /* Not used. */
4820 Tcl_Obj *CONST *objv)
4821{
4822 Blt_TreeNode node;
4823 Tcl_Obj *valueObjPtr;
4824 char *string;
4825
4826 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
4827 return TCL_ERROR;
4828 }
4829
4830 string = Tcl_GetString(objv[3]);
4831 if (Blt_TreeGetValue(interp, cmdPtr->tree, node, string, &valueObjPtr)
4832 != TCL_OK) {
4833 return TCL_ERROR;
4834 }
4835 if (valueObjPtr->typePtr != NULL) {
4836 Tcl_SetResult(interp, valueObjPtr->typePtr->name, TCL_VOLATILE);
4837 } else {
4838 Tcl_SetResult(interp, "string", TCL_STATIC);
4839 }
4840 return TCL_OK;
4841}
4842
4843
4844/*
4845 *----------------------------------------------------------------------
4846 *
4847 * UnsetOp --
4848 *
4849 *----------------------------------------------------------------------
4850 */
4851static int
4852UnsetOp(
4853 TreeCmd *cmdPtr,
4854 Tcl_Interp *interp,
4855 int objc,
4856 Tcl_Obj *CONST *objv)
4857{
4858 Blt_TreeNode node;
4859 char *string;
4860
4861 string = Tcl_GetString(objv[2]);
4862 if (isdigit(UCHAR(*string))) {
4863 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
4864 return TCL_ERROR;
4865 }
4866 if (UnsetValues(cmdPtr, node, objc - 3, objv + 3) != TCL_OK) {
4867 return TCL_ERROR;
4868 }
4869 } else {
4870 TagSearch cursor;
4871
4872 node = FirstTaggedNode(interp, cmdPtr, objv[2], &cursor);
4873 if (node == NULL) {
4874 return TCL_ERROR;
4875 }
4876 for (/* empty */; node != NULL; node = NextTaggedNode(node, &cursor)) {
4877 if (UnsetValues(cmdPtr, node, objc - 3, objv + 3) != TCL_OK) {
4878 return TCL_ERROR;
4879 }
4880 }
4881 }
4882 return TCL_OK;
4883}
4884
4885
4886typedef struct {
4887 TreeCmd *cmdPtr;
4888 unsigned int flags;
4889 int type;
4890 int mode;
4891 char *key;
4892 char *command;
4893} SortData;
4894
4895#define SORT_RECURSE (1<<2)
4896#define SORT_DECREASING (1<<3)
4897#define SORT_PATHNAME (1<<4)
4898
4899enum SortTypes { SORT_DICTIONARY, SORT_REAL, SORT_INTEGER, SORT_ASCII,
4900 SORT_COMMAND };
4901
4902enum SortModes { SORT_FLAT, SORT_REORDER };
4903
4904static Blt_SwitchSpec sortSwitches[] =
4905{
4906 {BLT_SWITCH_VALUE, "-ascii", Blt_Offset(SortData, type), 0, 0,
4907 SORT_ASCII},
4908 {BLT_SWITCH_STRING, "-command", Blt_Offset(SortData, command), 0},
4909 {BLT_SWITCH_FLAG, "-decreasing", Blt_Offset(SortData, flags), 0, 0,
4910 SORT_DECREASING},
4911 {BLT_SWITCH_VALUE, "-dictionary", Blt_Offset(SortData, type), 0, 0,
4912 SORT_DICTIONARY},
4913 {BLT_SWITCH_VALUE, "-integer", Blt_Offset(SortData, type), 0, 0,
4914 SORT_INTEGER},
4915 {BLT_SWITCH_STRING, "-key", Blt_Offset(SortData, key), 0},
4916 {BLT_SWITCH_FLAG, "-path", Blt_Offset(SortData, flags), 0, 0,
4917 SORT_PATHNAME},
4918 {BLT_SWITCH_VALUE, "-real", Blt_Offset(SortData, type), 0, 0,
4919 SORT_REAL},
4920 {BLT_SWITCH_VALUE, "-recurse", Blt_Offset(SortData, flags), 0, 0,
4921 SORT_RECURSE},
4922 {BLT_SWITCH_VALUE, "-reorder", Blt_Offset(SortData, mode), 0, 0,
4923 SORT_REORDER},
4924 {BLT_SWITCH_END, NULL, 0, 0}
4925};
4926
4927static SortData sortData;
4928
4929static int
4930CompareNodes(Blt_TreeNode *n1Ptr, Blt_TreeNode *n2Ptr)
4931{
4932 TreeCmd *cmdPtr = sortData.cmdPtr;
4933 char *s1, *s2;
4934 int result;
4935 Tcl_DString dString1, dString2;
4936
4937 s1 = s2 = "";
4938 result = 0;
4939
4940 if (sortData.flags & SORT_PATHNAME) {
4941 Tcl_DStringInit(&dString1);
4942 Tcl_DStringInit(&dString2);
4943 }
4944 if (sortData.key != NULL) {
4945 Tcl_Obj *valueObjPtr;
4946
4947 if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, *n1Ptr,
4948 sortData.key, &valueObjPtr) == TCL_OK) {
4949 s1 = Tcl_GetString(valueObjPtr);
4950 }
4951 if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, *n2Ptr,
4952 sortData.key, &valueObjPtr) == TCL_OK) {
4953 s2 = Tcl_GetString(valueObjPtr);
4954 }
4955 } else if (sortData.flags & SORT_PATHNAME) {
4956 Blt_TreeNode root;
4957
4958 root = Blt_TreeRootNode(cmdPtr->tree);
4959 s1 = GetNodePath(cmdPtr, root, *n1Ptr, FALSE, &dString1);
4960 s2 = GetNodePath(cmdPtr, root, *n2Ptr, FALSE, &dString2);
4961 } else {
4962 s1 = Blt_TreeNodeLabel(*n1Ptr);
4963 s2 = Blt_TreeNodeLabel(*n2Ptr);
4964 }
4965 switch (sortData.type) {
4966 case SORT_ASCII:
4967 result = strcmp(s1, s2);
4968 break;
4969
4970 case SORT_COMMAND:
4971 if (sortData.command == NULL) {
4972 result = Blt_DictionaryCompare(s1, s2);
4973 } else {
4974 Tcl_DString dsCmd, dsName;
4975 char *qualName;
4976
4977 result = 0; /* Hopefully this will be Ok even if the
4978 * Tcl command fails to return the correct
4979 * result. */
4980 Tcl_DStringInit(&dsCmd);
4981 Tcl_DStringAppend(&dsCmd, sortData.command, -1);
4982 Tcl_DStringInit(&dsName);
4983 qualName = Blt_GetQualifiedName(
4984 Blt_GetCommandNamespace(cmdPtr->interp, cmdPtr->cmdToken),
4985 Tcl_GetCommandName(cmdPtr->interp, cmdPtr->cmdToken), &dsName);
4986 Tcl_DStringAppendElement(&dsCmd, qualName);
4987 Tcl_DStringFree(&dsName);
4988 Tcl_DStringAppendElement(&dsCmd, Blt_Itoa(Blt_TreeNodeId(*n1Ptr)));
4989 Tcl_DStringAppendElement(&dsCmd, Blt_Itoa(Blt_TreeNodeId(*n2Ptr)));
4990 Tcl_DStringAppendElement(&dsCmd, s1);
4991 Tcl_DStringAppendElement(&dsCmd, s2);
4992 result = Tcl_GlobalEval(cmdPtr->interp, Tcl_DStringValue(&dsCmd));
4993 Tcl_DStringFree(&dsCmd);
4994
4995 if ((result != TCL_OK) ||
4996 (Tcl_GetInt(cmdPtr->interp,
4997 Tcl_GetStringResult(cmdPtr->interp), &result) != TCL_OK)) {
4998 Tcl_BackgroundError(cmdPtr->interp);
4999 }
5000 Tcl_ResetResult(cmdPtr->interp);
5001 }
5002 break;
5003
5004 case SORT_DICTIONARY:
5005 result = Blt_DictionaryCompare(s1, s2);
5006 break;
5007
5008 case SORT_INTEGER:
5009 {
5010 int i1, i2;
5011
5012 if (Tcl_GetInt(NULL, s1, &i1) == TCL_OK) {
5013 if (Tcl_GetInt(NULL, s2, &i2) == TCL_OK) {
5014 result = i1 - i2;
5015 } else {
5016 result = -1;
5017 }
5018 } else if (Tcl_GetInt(NULL, s2, &i2) == TCL_OK) {
5019 result = 1;
5020 } else {
5021 result = Blt_DictionaryCompare(s1, s2);
5022 }
5023 }
5024 break;
5025
5026 case SORT_REAL:
5027 {
5028 double r1, r2;
5029
5030 if (Tcl_GetDouble(NULL, s1, &r1) == TCL_OK) {
5031 if (Tcl_GetDouble(NULL, s2, &r2) == TCL_OK) {
5032 result = (r1 < r2) ? -1 : (r1 > r2) ? 1 : 0;
5033 } else {
5034 result = -1;
5035 }
5036 } else if (Tcl_GetDouble(NULL, s2, &r2) == TCL_OK) {
5037 result = 1;
5038 } else {
5039 result = Blt_DictionaryCompare(s1, s2);
5040 }
5041 }
5042 break;
5043 }
5044 if (result == 0) {
5045 result = Blt_TreeNodeId(*n1Ptr) - Blt_TreeNodeId(*n2Ptr);
5046 }
5047 if (sortData.flags & SORT_DECREASING) {
5048 result = -result;
5049 }
5050 if (sortData.flags & SORT_PATHNAME) {
5051 Tcl_DStringFree(&dString1);
5052 Tcl_DStringFree(&dString2);
5053 }
5054 return result;
5055}
5056
5057/*
5058 *----------------------------------------------------------------------
5059 *
5060 * SortApplyProc --
5061 *
5062 * Sorts the subnodes at a given node.
5063 *
5064 * Results:
5065 * Always returns TCL_OK.
5066 *
5067 *----------------------------------------------------------------------
5068 */
5069/*ARGSUSED*/
5070static int
5071SortApplyProc(
5072 Blt_TreeNode node,
5073 ClientData clientData,
5074 int order) /* Not used. */
5075{
5076 TreeCmd *cmdPtr = clientData;
5077
5078 if (!Blt_TreeIsLeaf(node)) {
5079 Blt_TreeSortNode(cmdPtr->tree, node, CompareNodes);
5080 }
5081 return TCL_OK;
5082}
5083
5084
5085/*
5086 *----------------------------------------------------------------------
5087 *
5088 * SortOp --
5089 *
5090 *----------------------------------------------------------------------
5091 */
5092static int
5093SortOp(
5094 TreeCmd *cmdPtr,
5095 Tcl_Interp *interp,
5096 int objc,
5097 Tcl_Obj *CONST *objv)
5098{
5099 Blt_TreeNode top;
5100 SortData data;
5101 int result;
5102
5103 if (GetNode(cmdPtr, objv[2], &top) != TCL_OK) {
5104 return TCL_ERROR;
5105 }
5106 /* Process switches */
5107 memset(&data, 0, sizeof(data));
5108 data.cmdPtr = cmdPtr;
5109 if (Blt_ProcessObjSwitches(interp, sortSwitches, objc - 3, objv + 3,
5110 (char *)&data, 0) < 0) {
5111 return TCL_ERROR;
5112 }
5113 if (data.command != NULL) {
5114 data.type = SORT_COMMAND;
5115 }
5116 data.cmdPtr = cmdPtr;
5117 sortData = data;
5118 if (data.mode == SORT_FLAT) {
5119 Blt_TreeNode *p, *nodeArr, node;
5120 int nNodes;
5121 Tcl_Obj *objPtr, *listObjPtr;
5122 int i;
5123
5124 if (data.flags & SORT_RECURSE) {
5125 nNodes = Blt_TreeSize(top);
5126 } else {
5127 nNodes = Blt_TreeNodeDegree(top);
5128 }
5129 nodeArr = Blt_Malloc(nNodes * sizeof(Blt_TreeNode));
5130 assert(nodeArr);
5131 p = nodeArr;
5132 if (data.flags & SORT_RECURSE) {
5133 for(node = top; node != NULL; node = Blt_TreeNextNode(top, node)) {
5134 *p++ = node;
5135 }
5136 } else {
5137 for (node = Blt_TreeFirstChild(top); node != NULL;
5138 node = Blt_TreeNextSibling(node)) {
5139 *p++ = node;
5140 }
5141 }
5142 qsort((char *)nodeArr, nNodes, sizeof(Blt_TreeNode),
5143 (QSortCompareProc *)CompareNodes);
5144 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
5145 for (p = nodeArr, i = 0; i < nNodes; i++, p++) {
5146 objPtr = Tcl_NewIntObj(Blt_TreeNodeId(*p));
5147 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
5148 }
5149 Tcl_SetObjResult(interp, listObjPtr);
5150 Blt_Free(nodeArr);
5151 result = TCL_OK;
5152 } else {
5153 if (data.flags & SORT_RECURSE) {
5154 result = Blt_TreeApply(top, SortApplyProc, cmdPtr);
5155 } else {
5156 result = SortApplyProc(top, cmdPtr, TREE_PREORDER);
5157 }
5158 }
5159 Blt_FreeSwitches(sortSwitches, (char *)&data, 0);
5160 return result;
5161}
5162
5163/*
5164 *----------------------------------------------------------------------
5165 *
5166 * ValuesOp --
5167 *
5168 * Returns the names of values for a node or array value.
5169 *
5170 *----------------------------------------------------------------------
5171 */
5172static int
5173ValuesOp(
5174 TreeCmd *cmdPtr,
5175 Tcl_Interp *interp,
5176 int objc,
5177 Tcl_Obj *CONST *objv)
5178{
5179 Blt_TreeNode node;
5180 Tcl_Obj *listObjPtr;
5181
5182 if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) {
5183 return TCL_ERROR;
5184 }
5185 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
5186 if (objc == 4) {
5187 char *string;
5188
5189 string = Tcl_GetString(objv[3]);
5190 if (Blt_TreeArrayNames(interp, cmdPtr->tree, node, string, listObjPtr)
5191 != TCL_OK) {
5192 return TCL_ERROR;
5193 }
5194 } else {
5195 Blt_TreeKey key;
5196 Tcl_Obj *objPtr;
5197 Blt_TreeKeySearch keyIter;
5198
5199 for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &keyIter); key != NULL;
5200 key = Blt_TreeNextKey(cmdPtr->tree, &keyIter)) {
5201 objPtr = Tcl_NewStringObj(key, -1);
5202 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
5203 }
5204 }
5205 Tcl_SetObjResult(interp, listObjPtr);
5206 return TCL_OK;
5207}
5208
5209
5210/*
5211 * --------------------------------------------------------------
5212 *
5213 * TreeInstObjCmd --
5214 *
5215 * This procedure is invoked to process commands on behalf of
5216 * the tree object.
5217 *
5218 * Results:
5219 * A standard Tcl result.
5220 *
5221 * Side effects:
5222 * See the user documentation.
5223 *
5224 * --------------------------------------------------------------
5225 */
5226static Blt_OpSpec treeOps[] =
5227{
5228 {"ancestor", 2, (Blt_Op)AncestorOp, 4, 4, "node1 node2",},
5229 {"apply", 1, (Blt_Op)ApplyOp, 3, 0, "node ?switches?",},
5230 {"attach", 2, (Blt_Op)AttachOp, 2, 3, "?tree?",},
5231 {"children", 2, (Blt_Op)ChildrenOp, 3, 5, "node ?first? ?last?",},
5232 {"copy", 2, (Blt_Op)CopyOp, 4, 0,
5233 "srcNode ?destTree? destNode ?switches?",},
5234 {"degree", 2, (Blt_Op)DegreeOp, 3, 0, "node",},
5235 {"delete", 2, (Blt_Op)DeleteOp, 3, 0, "node ?node...?",},
5236 {"depth", 3, (Blt_Op)DepthOp, 3, 3, "node",},
5237 {"dump", 4, (Blt_Op)DumpOp, 3, 3, "node",},
5238 {"dumpfile", 5, (Blt_Op)DumpfileOp, 4, 4, "node fileName",},
5239 {"exists", 1, (Blt_Op)ExistsOp, 3, 4, "node ?key?",},
5240 {"find", 4, (Blt_Op)FindOp, 3, 0, "node ?switches?",},
5241 {"findchild", 5, (Blt_Op)FindChildOp, 4, 4, "node name",},
5242 {"firstchild", 3, (Blt_Op)FirstChildOp, 3, 3, "node",},
5243 {"get", 1, (Blt_Op)GetOp, 3, 5, "node ?key? ?defaultValue?",},
5244 {"index", 3, (Blt_Op)IndexOp, 3, 3, "name",},
5245 {"insert", 3, (Blt_Op)InsertOp, 3, 0, "parent ?switches?",},
5246 {"is", 2, (Blt_Op)IsOp, 2, 0, "oper args...",},
5247 {"keys", 1, (Blt_Op)KeysOp, 3, 0, "node ?node...?",},
5248 {"label", 3, (Blt_Op)LabelOp, 3, 4, "node ?newLabel?",},
5249 {"lastchild", 3, (Blt_Op)LastChildOp, 3, 3, "node",},
5250 {"move", 1, (Blt_Op)MoveOp, 4, 0, "node newParent ?switches?",},
5251 {"next", 4, (Blt_Op)NextOp, 3, 3, "node",},
5252 {"nextsibling", 5, (Blt_Op)NextSiblingOp, 3, 3, "node",},
5253 {"notify", 2, (Blt_Op)NotifyOp, 2, 0, "args...",},
5254 {"parent", 3, (Blt_Op)ParentOp, 3, 3, "node",},
5255 {"path", 3, (Blt_Op)PathOp, 3, 3, "node",},
5256 {"position", 2, (Blt_Op)PositionOp, 3, 0, "?switches? node...",},
5257 {"previous", 5, (Blt_Op)PreviousOp, 3, 3, "node",},
5258 {"prevsibling", 5, (Blt_Op)PrevSiblingOp, 3, 3, "node",},
5259 {"restore", 7, (Blt_Op)RestoreOp, 4, 4, "node dataString",},
5260 {"restorefile", 8, (Blt_Op)RestorefileOp, 4, 4, "node fileName",},
5261 {"root", 2, (Blt_Op)RootOp, 2, 3, "?node?",},
5262 {"set", 3, (Blt_Op)SetOp, 3, 0, "node ?key value...?",},
5263 {"size", 2, (Blt_Op)SizeOp, 3, 3, "node",},
5264 {"sort", 2, (Blt_Op)SortOp, 3, 0, "node ?flags...?",},
5265 {"tag", 2, (Blt_Op)TagOp, 3, 0, "args...",},
5266 {"trace", 2, (Blt_Op)TraceOp, 2, 0, "args...",},
5267 {"type", 2, (Blt_Op)TypeOp, 4, 4, "node key",},
5268 {"unset", 3, (Blt_Op)UnsetOp, 3, 0, "node ?key...?",},
5269 {"values", 1, (Blt_Op)ValuesOp, 3, 4, "node ?key?",},
5270};
5271
5272static int nTreeOps = sizeof(treeOps) / sizeof(Blt_OpSpec);
5273
5274static int
5275TreeInstObjCmd(
5276 ClientData clientData, /* Information about the widget. */
5277 Tcl_Interp *interp, /* Interpreter to report errors back to. */
5278 int objc, /* Number of arguments. */
5279 Tcl_Obj *CONST *objv) /* Vector of argument strings. */
5280{
5281 Blt_Op proc;
5282 TreeCmd *cmdPtr = clientData;
5283 int result;
5284
5285 proc = Blt_GetOpFromObj(interp, nTreeOps, treeOps, BLT_OP_ARG1, objc, objv,
5286 BLT_OP_LINEAR_SEARCH);
5287 if (proc == NULL) {
5288 return TCL_ERROR;
5289 }
5290 Tcl_Preserve(cmdPtr);
5291 result = (*proc) (cmdPtr, interp, objc, objv);
5292 Tcl_Release(cmdPtr);
5293 return result;
5294}
5295
5296/*
5297 * ----------------------------------------------------------------------
5298 *
5299 * TreeInstDeleteProc --
5300 *
5301 * Deletes the command associated with the tree. This is
5302 * called only when the command associated with the tree is
5303 * destroyed.
5304 *
5305 * Results:
5306 * None.
5307 *
5308 * ----------------------------------------------------------------------
5309 */
5310static void
5311TreeInstDeleteProc(ClientData clientData)
5312{
5313 TreeCmd *cmdPtr = clientData;
5314
5315 ReleaseTreeObject(cmdPtr);
5316 if (cmdPtr->hashPtr != NULL) {
5317 Blt_DeleteHashEntry(cmdPtr->tablePtr, cmdPtr->hashPtr);
5318 }
5319 Blt_DeleteHashTable(&(cmdPtr->traceTable));
5320 Blt_Free(cmdPtr);
5321}
5322
5323/*
5324 * ----------------------------------------------------------------------
5325 *
5326 * GenerateName --
5327 *
5328 * Generates an unique tree command name. Tree names are in
5329 * the form "treeN", where N is a non-negative integer. Check
5330 * each name generated to see if it is already a tree. We want
5331 * to recycle names if possible.
5332 *
5333 * Results:
5334 * Returns the unique name. The string itself is stored in the
5335 * dynamic string passed into the routine.
5336 *
5337 * ----------------------------------------------------------------------
5338 */
5339static CONST char *
5340GenerateName(
5341 Tcl_Interp *interp,
5342 CONST char *prefix,
5343 CONST char *suffix,
5344 Tcl_DString *resultPtr)
5345{
5346
5347 int n;
5348 Tcl_Namespace *nsPtr;
5349 char string[200];
5350 Tcl_CmdInfo cmdInfo;
5351 Tcl_DString dString;
5352 CONST char *treeName, *name;
5353
5354 /*
5355 * Parse the command and put back so that it's in a consistent
5356 * format.
5357 *
5358 * t1 <current namespace>::t1
5359 * n1::t1 <current namespace>::n1::t1
5360 * ::t1 ::t1
5361 * ::n1::t1 ::n1::t1
5362 */
5363 treeName = NULL; /* Suppress compiler warning. */
5364 for (n = 0; n < INT_MAX; n++) {
5365 Tcl_DStringInit(&dString);
5366 Tcl_DStringAppend(&dString, prefix, -1);
5367 sprintf(string, "tree%d", n);
5368 Tcl_DStringAppend(&dString, string, -1);
5369 Tcl_DStringAppend(&dString, suffix, -1);
5370 treeName = Tcl_DStringValue(&dString);
5371 if (Blt_ParseQualifiedName(interp, treeName, &nsPtr, &name) != TCL_OK) {
5372 Tcl_AppendResult(interp, "can't find namespace in \"", treeName,
5373 "\"", (char *)NULL);
5374 return NULL;
5375 }
5376 if (nsPtr == NULL) {
5377 nsPtr = Tcl_GetCurrentNamespace(interp);
5378 }
5379 treeName = Blt_GetQualifiedName(nsPtr, name, resultPtr);
5380 /*
5381 * Check if the command already exists.
5382 */
5383 if (Tcl_GetCommandInfo(interp, (char *)treeName, &cmdInfo)) {
5384 continue;
5385 }
5386 if (!Blt_TreeExists(interp, treeName)) {
5387 /*
5388 * We want the name of the tree command and the underlying
5389 * tree object to be the same. Check that the free command
5390 * name isn't an already a tree object name.
5391 */
5392 break;
5393 }
5394 }
5395 return treeName;
5396}
5397
5398/*
5399 *----------------------------------------------------------------------
5400 *
5401 * TreeCreateOp --
5402 *
5403 *----------------------------------------------------------------------
5404 */
5405/*ARGSUSED*/
5406static int
5407TreeCreateOp(
5408 ClientData clientData, /* Interpreter-specific data. */
5409 Tcl_Interp *interp,
5410 int objc,
5411 Tcl_Obj *CONST *objv)
5412{
5413 TreeCmdInterpData *dataPtr = clientData;
5414 CONST char *treeName;
5415 Tcl_DString dString;
5416 Blt_Tree token;
5417
5418 treeName = NULL;
5419 if (objc == 3) {
5420 treeName = Tcl_GetString(objv[2]);
5421 }
5422 Tcl_DStringInit(&dString);
5423 if (treeName == NULL) {
5424 treeName = GenerateName(interp, "", "", &dString);
5425 } else {
5426 char *p;
5427
5428 p = strstr(treeName, "#auto");
5429 if (p != NULL) {
5430 *p = '\0';
5431 treeName = GenerateName(interp, treeName, p + 5, &dString);
5432 *p = '#';
5433 } else {
5434 CONST char *name;
5435 Tcl_CmdInfo cmdInfo;
5436 Tcl_Namespace *nsPtr;
5437
5438 nsPtr = NULL;
5439 /*
5440 * Parse the command and put back so that it's in a consistent
5441 * format.
5442 *
5443 * t1 <current namespace>::t1
5444 * n1::t1 <current namespace>::n1::t1
5445 * ::t1 ::t1
5446 * ::n1::t1 ::n1::t1
5447 */
5448 if (Blt_ParseQualifiedName(interp, treeName, &nsPtr, &name)
5449 != TCL_OK) {
5450 Tcl_AppendResult(interp, "can't find namespace in \"", treeName,
5451 "\"", (char *)NULL);
5452 return TCL_ERROR;
5453 }
5454 if (nsPtr == NULL) {
5455 nsPtr = Tcl_GetCurrentNamespace(interp);
5456 }
5457 treeName = Blt_GetQualifiedName(nsPtr, name, &dString);
5458 /*
5459 * Check if the command already exists.
5460 */
5461 if (Tcl_GetCommandInfo(interp, (char *)treeName, &cmdInfo)) {
5462 Tcl_AppendResult(interp, "a command \"", treeName,
5463 "\" already exists", (char *)NULL);
5464 goto error;
5465 }
5466 if (Blt_TreeExists(interp, treeName)) {
5467 Tcl_AppendResult(interp, "a tree \"", treeName,
5468 "\" already exists", (char *)NULL);
5469 goto error;
5470 }
5471 }
5472 }
5473 if (treeName == NULL) {
5474 goto error;
5475 }
5476 if (Blt_TreeCreate(interp, treeName, &token) == TCL_OK) {
5477 int isNew;
5478 TreeCmd *cmdPtr;
5479
5480 cmdPtr = Blt_Calloc(1, sizeof(TreeCmd));
5481 assert(cmdPtr);
5482 cmdPtr->dataPtr = dataPtr;
5483 cmdPtr->tree = token;
5484 cmdPtr->interp = interp;
5485 Blt_InitHashTable(&(cmdPtr->traceTable), BLT_STRING_KEYS);
5486 Blt_InitHashTable(&(cmdPtr->notifyTable), BLT_STRING_KEYS);
5487 cmdPtr->cmdToken = Tcl_CreateObjCommand(interp, (char *)treeName,
5488 (Tcl_ObjCmdProc *)TreeInstObjCmd, cmdPtr, TreeInstDeleteProc);
5489 cmdPtr->tablePtr = &dataPtr->treeTable;
5490 cmdPtr->hashPtr = Blt_CreateHashEntry(cmdPtr->tablePtr, (char *)cmdPtr,
5491 &isNew);
5492 Blt_SetHashValue(cmdPtr->hashPtr, cmdPtr);
5493 Tcl_SetResult(interp, (char *)treeName, TCL_VOLATILE);
5494 Tcl_DStringFree(&dString);
5495 Blt_TreeCreateEventHandler(cmdPtr->tree, TREE_NOTIFY_ALL,
5496 TreeEventProc, cmdPtr);
5497 return TCL_OK;
5498 }
5499 error:
5500 Tcl_DStringFree(&dString);
5501 return TCL_ERROR;
5502}
5503
5504/*
5505 *----------------------------------------------------------------------
5506 *
5507 * TreeDestroyOp --
5508 *
5509 *----------------------------------------------------------------------
5510 */
5511/*ARGSUSED*/
5512static int
5513TreeDestroyOp(
5514 ClientData clientData, /* Interpreter-specific data. */
5515 Tcl_Interp *interp,
5516 int objc,
5517 Tcl_Obj *CONST *objv)
5518{
5519 TreeCmdInterpData *dataPtr = clientData;
5520 TreeCmd *cmdPtr;
5521 char *string;
5522 register int i;
5523
5524 for (i = 2; i < objc; i++) {
5525 string = Tcl_GetString(objv[i]);
5526 cmdPtr = GetTreeCmd(dataPtr, interp, string);
5527 if (cmdPtr == NULL) {
5528 Tcl_AppendResult(interp, "can't find a tree named \"", string,
5529 "\"", (char *)NULL);
5530 return TCL_ERROR;
5531 }
5532 Tcl_DeleteCommandFromToken(interp, cmdPtr->cmdToken);
5533 }
5534 return TCL_OK;
5535}
5536
5537/*
5538 *----------------------------------------------------------------------
5539 *
5540 * TreeNamesOp --
5541 *
5542 *----------------------------------------------------------------------
5543 */
5544/*ARGSUSED*/
5545static int
5546TreeNamesOp(
5547 ClientData clientData, /* Interpreter-specific data. */
5548 Tcl_Interp *interp,
5549 int objc,
5550 Tcl_Obj *CONST *objv)
5551{
5552 TreeCmdInterpData *dataPtr = clientData;
5553 TreeCmd *cmdPtr;
5554 Blt_HashEntry *hPtr;
5555 Blt_HashSearch cursor;
5556 Tcl_Obj *objPtr, *listObjPtr;
5557 Tcl_DString dString;
5558 char *qualName;
5559
5560 Tcl_DStringInit(&dString);
5561 listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
5562 for (hPtr = Blt_FirstHashEntry(&dataPtr->treeTable, &cursor);
5563 hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
5564 cmdPtr = Blt_GetHashValue(hPtr);
5565 qualName = Blt_GetQualifiedName(
5566 Blt_GetCommandNamespace(interp, cmdPtr->cmdToken),
5567 Tcl_GetCommandName(interp, cmdPtr->cmdToken), &dString);
5568 if (objc == 3) {
5569 if (!Tcl_StringMatch(qualName, Tcl_GetString(objv[2]))) {
5570 continue;
5571 }
5572 }
5573 objPtr = Tcl_NewStringObj(qualName, -1);
5574 Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
5575 }
5576 Tcl_SetObjResult(interp, listObjPtr);
5577 Tcl_DStringFree(&dString);
5578 return TCL_OK;
5579}
5580
5581
5582
5583/*
5584 *----------------------------------------------------------------------
5585 *
5586 * TreeObjCmd --
5587 *
5588 *----------------------------------------------------------------------
5589 */
5590static Blt_OpSpec treeCmdOps[] =
5591{
5592 {"create", 1, (Blt_Op)TreeCreateOp, 2, 3, "?name?",},
5593 {"destroy", 1, (Blt_Op)TreeDestroyOp, 3, 0, "name...",},
5594 {"names", 1, (Blt_Op)TreeNamesOp, 2, 3, "?pattern?...",},
5595};
5596
5597static int nCmdOps = sizeof(treeCmdOps) / sizeof(Blt_OpSpec);
5598
5599/*ARGSUSED*/
5600static int
5601TreeObjCmd(
5602 ClientData clientData, /* Interpreter-specific data. */
5603 Tcl_Interp *interp,
5604 int objc,
5605 Tcl_Obj *CONST *objv)
5606{
5607 Blt_Op proc;
5608
5609 proc = Blt_GetOpFromObj(interp, nCmdOps, treeCmdOps, BLT_OP_ARG1, objc,
5610 objv, 0);
5611 if (proc == NULL) {
5612 return TCL_ERROR;
5613 }
5614 return (*proc) (clientData, interp, objc, objv);
5615}
5616
5617/*
5618 * -----------------------------------------------------------------------
5619 *
5620 * TreeInterpDeleteProc --
5621 *
5622 * This is called when the interpreter hosting the "tree" command
5623 * is deleted.
5624 *
5625 * Results:
5626 * None.
5627 *
5628 * Side effects:
5629 * Removes the hash table managing all tree names.
5630 *
5631 * ------------------------------------------------------------------------
5632 */
5633/* ARGSUSED */
5634static void
5635TreeInterpDeleteProc(
5636 ClientData clientData, /* Interpreter-specific data. */
5637 Tcl_Interp *interp)
5638{
5639 TreeCmdInterpData *dataPtr = clientData;
5640
5641 /* All tree instances should already have been destroyed when
5642 * their respective Tcl commands were deleted. */
5643 Blt_DeleteHashTable(&dataPtr->treeTable);
5644 Tcl_DeleteAssocData(interp, TREE_THREAD_KEY);
5645 Blt_Free(dataPtr);
5646}
5647
5648/*ARGSUSED*/
5649static int
5650CompareDictionaryCmd(
5651 ClientData clientData, /* Not used. */
5652 Tcl_Interp *interp,
5653 int objc,
5654 Tcl_Obj *CONST *objv)
5655{
5656 int result;
5657 char *s1, *s2;
5658
5659 s1 = Tcl_GetString(objv[1]);
5660 s2 = Tcl_GetString(objv[2]);
5661 result = Blt_DictionaryCompare(s1, s2);
5662 result = (result > 0) ? -1 : (result < 0) ? 1 : 0;
5663 Tcl_SetIntObj(Tcl_GetObjResult(interp), result);
5664 return TCL_OK;
5665}
5666
5667/*ARGSUSED*/
5668static int
5669ExitCmd(
5670 ClientData clientData, /* Not used. */
5671 Tcl_Interp *interp,
5672 int objc,
5673 Tcl_Obj *CONST *objv)
5674{
5675 int code;
5676
5677 if (Tcl_GetIntFromObj(interp, objv[1], &code) != TCL_OK) {
5678 return TCL_ERROR;
5679 }
5680#ifdef TCL_THREADS
5681 Tcl_Exit(code);
5682#else
5683 exit(code);
5684#endif
5685 /*NOTREACHED*/
5686 return TCL_OK;
5687}
5688
5689/*
5690 * -----------------------------------------------------------------------
5691 *
5692 * Blt_TreeInit --
5693 *
5694 * This procedure is invoked to initialize the "tree" command.
5695 *
5696 * Results:
5697 * None.
5698 *
5699 * Side effects:
5700 * Creates the new command and adds a new entry into a global Tcl
5701 * associative array.
5702 *
5703 * ------------------------------------------------------------------------
5704 */
5705int
5706Blt_TreeInit(Tcl_Interp *interp)
5707{
5708 TreeCmdInterpData *dataPtr; /* Interpreter-specific data. */
5709 static Blt_ObjCmdSpec cmdSpec = {
5710 "tree", TreeObjCmd,
5711 };
5712 static Blt_ObjCmdSpec compareSpec = {
5713 "compare", CompareDictionaryCmd,
5714 };
5715 static Blt_ObjCmdSpec exitSpec = {
5716 "exit", ExitCmd,
5717 };
5718 if (Blt_InitObjCmd(interp, "blt::util", &compareSpec) == NULL) {
5719 return TCL_ERROR;
5720 }
5721 if (Blt_InitObjCmd(interp, "blt::util", &exitSpec) == NULL) {
5722 return TCL_ERROR;
5723 }
5724
5725 dataPtr = GetTreeCmdInterpData(interp);
5726 cmdSpec.clientData = dataPtr;
5727 if (Blt_InitObjCmd(interp, "blt", &cmdSpec) == NULL) {
5728 return TCL_ERROR;
5729 }
5730 return TCL_OK;
5731}
5732
5733int
5734Blt_TreeCmdGetToken(
5735 Tcl_Interp *interp,
5736 CONST char *string,
5737 Blt_Tree *treePtr)
5738{
5739 TreeCmdInterpData *dataPtr;
5740 TreeCmd *cmdPtr;
5741
5742 dataPtr = GetTreeCmdInterpData(interp);
5743 cmdPtr = GetTreeCmd(dataPtr, interp, string);
5744 if (cmdPtr == NULL) {
5745 Tcl_AppendResult(interp, "can't find a tree associated with \"",
5746 string, "\"", (char *)NULL);
5747 return TCL_ERROR;
5748 }
5749 *treePtr = cmdPtr->tree;
5750 return TCL_OK;
5751}
5752
5753/* Dump tree to dbm */
5754/* Convert node data to datablock */
5755
5756#endif /* NO_TREE */
5757
Note: See TracBrowser for help on using the repository browser.