diff options
Diffstat (limited to 'blt/src/bltTreeCmd.c')
-rw-r--r-- | blt/src/bltTreeCmd.c | 5793 |
1 files changed, 5793 insertions, 0 deletions
diff --git a/blt/src/bltTreeCmd.c b/blt/src/bltTreeCmd.c new file mode 100644 index 00000000000..3ba34538394 --- /dev/null +++ b/blt/src/bltTreeCmd.c @@ -0,0 +1,5793 @@ +/* + * + * bltTreeCmd.c -- + * + * Copyright 1998-1999 Lucent Technologies, Inc. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that the copyright notice and warranty + * disclaimer appear in supporting documentation, and that the names + * of Lucent Technologies or any of their entities not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. + * + * Lucent Technologies disclaims all warranties with regard to this + * software, including all implied warranties of merchantability and + * fitness. In no event shall Lucent Technologies be liable for any + * special, indirect or consequential damages or any damages + * whatsoever resulting from loss of use, data or profits, whether in + * an action of contract, negligence or other tortuous action, arising + * out of or in connection with the use or performance of this + * software. + * + * The "tree" data object was created by George A. Howlett. + */ + +/* + tree create t0 t1 t2 + tree names + t0 destroy + -or- + tree destroy t0 + tree copy tree@node tree@node -recurse -tags + + tree move node after|before|into t2@node + + $t apply -recurse $root command arg arg + + $t attach treename + + $t children $n + t0 copy node1 node2 node3 node4 node5 destName + $t delete $n... + $t depth $n + $t dump $root + $t dumpfile $root fileName + $t dup $t2 + $t find $root -name pat -name pattern + $t firstchild $n + $t get $n $key + $t get $n $key(abc) + $t index $n + $t insert $parent $switches? + $t isancestor $n1 $n2 + $t isbefore $n1 $n2 + $t isleaf $n + $t lastchild $n + $t move $n1 after|before|into $n2 + $t next $n + $t nextsibling $n + $t path $n1 $n2 $n3... + $t parent $n + $t previous $n + $t prevsibling $n + $t restore $root data -overwrite + $t root ?$n? + + $t set $n $key $value ?$key $value? + $t size $n + $t slink $n $t2@$node ??? + $t sort -recurse $root + + $t tag delete tag1 tag2 tag3... + $t tag names + $t tag nodes $tag + $t tag set $n tag1 tag2 tag3... + $t tag unset $n tag1 tag2 tag3... + + $t trace create $n $key how command + $t trace delete id1 id2 id3... + $t trace names + $t trace info $id + + $t unset $n key1 key2 key3... + + $t notify create -oncreate -ondelete -onmove command + $t notify create -oncreate -ondelete -onmove -onsort command arg arg arg + $t notify delete id1 id2 id3 + $t notify names + $t notify info id + + for { set n [$t firstchild $node] } { $n >= 0 } { + set n [$t nextsibling $n] } { + } + foreach n [$t children $node] { + + } + set n [$t next $node] + set n [$t previous $node] + +*/ + +#include <bltInt.h> + +#ifndef NO_TREE + +#include <bltTree.h> +#include <bltHash.h> +#include "bltSwitch.h" +#include <ctype.h> + +#define TREE_THREAD_KEY "BLT Tree Command Data" +#define TREE_MAGIC ((unsigned int) 0x46170277) + +enum TagTypes { TAG_TYPE_NONE, TAG_TYPE_ALL, TAG_TYPE_TAG }; + +typedef struct { + Blt_HashTable treeTable; /* Hash table of trees keyed by address. */ + Tcl_Interp *interp; +} TreeCmdInterpData; + +typedef struct { + Tcl_Interp *interp; + Tcl_Command cmdToken; /* Token for tree's Tcl command. */ + Blt_Tree tree; /* Token holding internal tree. */ + + Blt_HashEntry *hashPtr; + Blt_HashTable *tablePtr; + + Blt_TreeTagTable *tagTablePtr; + + TreeCmdInterpData *dataPtr; /* */ + + int traceCounter; /* Used to generate trace id strings. */ + Blt_HashTable traceTable; /* Table of active traces. Maps trace ids + * back to their TraceInfo records. */ + + int notifyCounter; /* Used to generate notify id strings. */ + Blt_HashTable notifyTable; /* Table of event handlers. Maps notify ids + * back to their NotifyInfo records. */ +} TreeCmd; + +typedef struct { + TreeCmd *cmdPtr; + Blt_TreeNode node; + + Blt_TreeTrace traceToken; + + char *withTag; /* If non-NULL, the event or trace was + * specified with this tag. In this + * case, the standard handler will + * check if the particular node has + * this tag. */ + + char command[1]; /* Command prefix for the trace or notify + * Tcl callback. Extra arguments will be + * appended to the end. Extra space will + * be allocated for the length of the string + */ +} TraceInfo; + +typedef struct { + TreeCmd *cmdPtr; + int mask; + Tcl_Obj **objv; + int objc; + Blt_TreeNode node; /* Node affected by event. */ + Blt_TreeTrace notifyToken; +} NotifyInfo; + + +typedef struct { + int mask; +} NotifyData; + +static Blt_SwitchSpec notifySwitches[] = +{ + {BLT_SWITCH_FLAG, "-create", Blt_Offset(NotifyData, mask), 0, 0, + TREE_NOTIFY_CREATE}, + {BLT_SWITCH_FLAG, "-delete", Blt_Offset(NotifyData, mask), 0, 0, + TREE_NOTIFY_DELETE}, + {BLT_SWITCH_FLAG, "-move", Blt_Offset(NotifyData, mask), 0, 0, + TREE_NOTIFY_MOVE}, + {BLT_SWITCH_FLAG, "-sort", Blt_Offset(NotifyData, mask), 0, 0, + TREE_NOTIFY_SORT}, + {BLT_SWITCH_FLAG, "-relabel", Blt_Offset(NotifyData, mask), 0, 0, + TREE_NOTIFY_RELABEL}, + {BLT_SWITCH_FLAG, "-allevents", Blt_Offset(NotifyData, mask), 0, 0, + TREE_NOTIFY_ALL}, + {BLT_SWITCH_FLAG, "-whenidle", Blt_Offset(NotifyData, mask), 0, 0, + TREE_NOTIFY_WHENIDLE}, + {BLT_SWITCH_END, NULL, 0, 0} +}; + +static Blt_SwitchParseProc StringToChild; +#define INSERT_BEFORE (ClientData)0 +#define INSERT_AFTER (ClientData)1 +static Blt_SwitchCustom beforeSwitch = +{ + StringToChild, (Blt_SwitchFreeProc *)NULL, INSERT_BEFORE, +}; +static Blt_SwitchCustom afterSwitch = +{ + StringToChild, (Blt_SwitchFreeProc *)NULL, INSERT_AFTER, +}; + +typedef struct { + char *label; + int insertPos; + int inode; + char **tags; + char **dataPairs; + Blt_TreeNode parent; +} InsertData; + +static Blt_SwitchSpec insertSwitches[] = +{ + {BLT_SWITCH_CUSTOM, "-after", Blt_Offset(InsertData, insertPos), 0, + &afterSwitch}, + {BLT_SWITCH_INT_NONNEGATIVE, "-at", Blt_Offset(InsertData, insertPos), 0}, + {BLT_SWITCH_CUSTOM, "-before", Blt_Offset(InsertData, insertPos), 0, + &beforeSwitch}, + {BLT_SWITCH_LIST, "-data", Blt_Offset(InsertData, dataPairs), 0}, + {BLT_SWITCH_STRING, "-label", Blt_Offset(InsertData, label), 0}, + {BLT_SWITCH_INT_NONNEGATIVE, "-node", Blt_Offset(InsertData, inode), 0}, + {BLT_SWITCH_LIST, "-tags", Blt_Offset(InsertData, tags), 0}, + {BLT_SWITCH_END, NULL, 0, 0} +}; + +#define PATTERN_EXACT (1) +#define PATTERN_GLOB (2) +#define PATTERN_MASK (0x3) +#define PATTERN_NONE (0) +#define PATTERN_REGEXP (3) +#define MATCH_INVERT (1<<8) +#define MATCH_LEAFONLY (1<<4) +#define MATCH_NOCASE (1<<5) +#define MATCH_PATHNAME (1<<6) + +typedef struct { + TreeCmd *cmdPtr; /* Tree to examine. */ + Tcl_Obj *listObjPtr; /* List to accumulate the indices of + * matching nodes. */ + Tcl_Obj **objv; /* Command converted into an array of + * Tcl_Obj's. */ + int objc; /* Number of Tcl_Objs in above array. */ + + int nMatches; /* Current number of matches. */ + + unsigned int flags; /* See flags definitions above. */ + + /* Integer options. */ + int maxMatches; /* If > 0, stop after this many matches. */ + int maxDepth; /* If > 0, don't descend more than this + * many levels. */ + int order; /* Order of search: Can be either + * TREE_PREORDER, TREE_POSTORDER, + * TREE_INORDER, TREE_BREADTHFIRST. */ + /* String options. */ + char *pattern; /* If non-NULL, pattern to use when + * comparing node names. */ + char *addTag; /* If non-NULL, tag added to selected nodes. */ + + char **command; /* Command split into a Tcl list. */ + + char *key; /* */ + char *withTag; + +} FindData; + +static Blt_SwitchParseProc StringToOrder; +static Blt_SwitchCustom orderSwitch = +{ + StringToOrder, (Blt_SwitchFreeProc *)NULL, (ClientData)0, +}; + + +static Blt_SwitchSpec findSwitches[] = +{ + {BLT_SWITCH_STRING, "-addtag", Blt_Offset(FindData, addTag), 0}, + {BLT_SWITCH_INT_NONNEGATIVE, "-count", Blt_Offset(FindData, maxMatches), 0}, + {BLT_SWITCH_INT_NONNEGATIVE, "-depth", Blt_Offset(FindData, maxDepth), 0}, + {BLT_SWITCH_STRING, "-exact", Blt_Offset(FindData, pattern), 0}, + {BLT_SWITCH_LIST, "-exec", Blt_Offset(FindData, command), 0}, + {BLT_SWITCH_STRING, "-glob", Blt_Offset(FindData, pattern), 0}, + {BLT_SWITCH_FLAG, "-invert", Blt_Offset(FindData, flags), 0, 0, + MATCH_INVERT}, + {BLT_SWITCH_STRING, "-key", Blt_Offset(FindData, key), 0}, + {BLT_SWITCH_FLAG, "-leafonly", Blt_Offset(FindData, flags), 0, 0, + MATCH_LEAFONLY}, + {BLT_SWITCH_FLAG, "-nocase", Blt_Offset(FindData, flags), 0, 0, + MATCH_NOCASE}, + {BLT_SWITCH_CUSTOM, "-order", Blt_Offset(FindData, order), 0, &orderSwitch}, + {BLT_SWITCH_FLAG, "-path", Blt_Offset(FindData, flags), 0, 0, + MATCH_PATHNAME}, + {BLT_SWITCH_STRING, "-regexp", Blt_Offset(FindData, pattern), 0}, + {BLT_SWITCH_STRING, "-tag", Blt_Offset(FindData, withTag), 0}, + {BLT_SWITCH_END, NULL, 0, 0} +}; + +static Blt_SwitchParseProc StringToNode; +static Blt_SwitchCustom nodeSwitch = +{ + StringToNode, (Blt_SwitchFreeProc *)NULL, (ClientData)0, +}; + +typedef struct { + TreeCmd *cmdPtr; /* Tree to move nodes. */ + Blt_TreeNode node; + int insertPos; +} MoveData; + +static Blt_SwitchSpec moveSwitches[] = +{ + {BLT_SWITCH_CUSTOM, "-after", Blt_Offset(MoveData, node), 0, &nodeSwitch}, + {BLT_SWITCH_INT_NONNEGATIVE, "-at", Blt_Offset(MoveData, insertPos), 0}, + {BLT_SWITCH_CUSTOM, "-before", Blt_Offset(MoveData, node), 0, &nodeSwitch}, + {BLT_SWITCH_END, NULL, 0, 0} +}; + +typedef struct { + Blt_TreeNode srcNode, destNode; + Blt_Tree srcTree, destTree; + TreeCmd *srcPtr, *destPtr; + unsigned int flags; + char *label; +} CopyData; + +#define COPY_RECURSE (1<<0) +#define COPY_TAGS (1<<1) +#define COPY_OVERWRITE (1<<2) + +static Blt_SwitchSpec copySwitches[] = +{ + {BLT_SWITCH_STRING, "-label", Blt_Offset(CopyData, label), 0}, + {BLT_SWITCH_FLAG, "-recurse", Blt_Offset(CopyData, flags), 0, 0, + COPY_RECURSE}, + {BLT_SWITCH_FLAG, "-tags", Blt_Offset(CopyData, flags), 0, 0, + COPY_TAGS}, + {BLT_SWITCH_FLAG, "-overwrite", Blt_Offset(CopyData, flags), 0, 0, + COPY_OVERWRITE}, + {BLT_SWITCH_END, NULL, 0, 0} +}; + +typedef struct { + TreeCmd *cmdPtr; /* Tree to examine. */ + Tcl_Obj **preObjv; /* Command converted into an array of + * Tcl_Obj's. */ + int preObjc; /* Number of Tcl_Objs in above array. */ + + Tcl_Obj **postObjv; /* Command converted into an array of + * Tcl_Obj's. */ + int postObjc; /* Number of Tcl_Objs in above array. */ + + unsigned int flags; /* See flags definitions above. */ + + int maxDepth; /* If > 0, don't descend more than this + * many levels. */ + /* String options. */ + char *pattern; /* If non-NULL, pattern to use when + * comparing node names. */ + char **preCmd; /* Pre-command split into a Tcl list. */ + char **postCmd; /* Post-command split into a Tcl list. */ + + char *key; /* */ + char *withTag; +} ApplyData; + +static Blt_SwitchSpec applySwitches[] = +{ + {BLT_SWITCH_LIST, "-precommand", Blt_Offset(ApplyData, preCmd), 0}, + {BLT_SWITCH_LIST, "-postcommand", Blt_Offset(ApplyData, postCmd), 0}, + {BLT_SWITCH_INT_NONNEGATIVE, "-depth", Blt_Offset(ApplyData, maxDepth), 0}, + {BLT_SWITCH_STRING, "-exact", Blt_Offset(ApplyData, pattern), 0}, + {BLT_SWITCH_STRING, "-glob", Blt_Offset(ApplyData, pattern), 0}, + {BLT_SWITCH_FLAG, "-invert", Blt_Offset(ApplyData, flags), 0, 0, + MATCH_INVERT}, + {BLT_SWITCH_STRING, "-key", Blt_Offset(ApplyData, key), 0}, + {BLT_SWITCH_FLAG, "-leafonly", Blt_Offset(ApplyData, flags), 0, 0, + MATCH_LEAFONLY}, + {BLT_SWITCH_FLAG, "-nocase", Blt_Offset(ApplyData, flags), 0, 0, + MATCH_NOCASE}, + {BLT_SWITCH_FLAG, "-path", Blt_Offset(ApplyData, flags), 0, 0, + MATCH_PATHNAME}, + {BLT_SWITCH_STRING, "-regexp", Blt_Offset(ApplyData, pattern), 0}, + {BLT_SWITCH_STRING, "-tag", Blt_Offset(ApplyData, withTag), 0}, + {BLT_SWITCH_END, NULL, 0, 0} +}; + +typedef struct { + unsigned int flags; + Blt_HashTable idTable; + Blt_TreeNode root; +} RestoreData; + +#define RESTORE_NO_TAGS (1<<0) +#define RESTORE_OVERWRITE (1<<1) + +static Blt_SwitchSpec restoreSwitches[] = +{ + {BLT_SWITCH_FLAG, "-notags", Blt_Offset(RestoreData, flags), 0, 0, + RESTORE_NO_TAGS}, + {BLT_SWITCH_FLAG, "-overwrite", Blt_Offset(RestoreData, flags), 0, 0, + RESTORE_OVERWRITE}, + {BLT_SWITCH_END, NULL, 0, 0} +}; + +static Blt_SwitchParseProc StringToFormat; +static Blt_SwitchCustom formatSwitch = +{ + StringToFormat, (Blt_SwitchFreeProc *)NULL, (ClientData)0, +}; + +typedef struct { + int sort; /* If non-zero, sort the nodes. */ + int withParent; /* If non-zero, add the parent node id + * to the output of the command.*/ + int withId; /* If non-zero, echo the node id in the + * output of the command. */ +} PositionData; + +#define POSITION_SORTED (1<<0) + +static Blt_SwitchSpec positionSwitches[] = +{ + {BLT_SWITCH_FLAG, "-sort", Blt_Offset(PositionData, sort), 0, 0, + POSITION_SORTED}, + {BLT_SWITCH_CUSTOM, "-format", 0, 0, &formatSwitch}, + {BLT_SWITCH_END, NULL, 0, 0} +}; + + +static Tcl_InterpDeleteProc TreeInterpDeleteProc; +static Blt_TreeApplyProc MatchNodeProc, SortApplyProc; +static Blt_TreeApplyProc ApplyNodeProc; +static Blt_TreeTraceProc TreeTraceProc; +static Tcl_CmdDeleteProc TreeInstDeleteProc; +static Blt_TreeCompareNodesProc CompareNodes; + +static Tcl_ObjCmdProc TreeObjCmd; +static Tcl_ObjCmdProc CompareDictionaryCmd; +static Tcl_ObjCmdProc ExitCmd; +static Blt_TreeNotifyEventProc TreeEventProc; + +static int GetNode _ANSI_ARGS_((TreeCmd *cmdPtr, Tcl_Obj *objPtr, + Blt_TreeNode *nodePtr)); + +static int nLines; + + + +/* + *---------------------------------------------------------------------- + * + * StringToChild -- + * + * Convert a string represent a node number into its integer + * value. + * + * Results: + * The return value is a standard Tcl result. + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +StringToChild(clientData, interp, switchName, string, record, offset) + ClientData clientData; /* Flag indicating if the node is + * considered before or after the + * insertion position. */ + Tcl_Interp *interp; /* Interpreter to send results back to */ + char *switchName; /* Not used. */ + char *string; /* String representation */ + char *record; /* Structure record */ + int offset; /* Offset to field in structure */ +{ + InsertData *dataPtr = (InsertData *)record; + Blt_TreeNode node; + + node = Blt_TreeFindChild(dataPtr->parent, string); + if (node == NULL) { + Tcl_AppendResult(interp, "can't find a child named \"", string, + "\" in \"", Blt_TreeNodeLabel(dataPtr->parent), "\"", + (char *)NULL); + return TCL_ERROR; + } + dataPtr->insertPos = Blt_TreeNodeDegree(node); + if (clientData == INSERT_AFTER) { + dataPtr->insertPos++; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * StringToNode -- + * + * Convert a string represent a node number into its integer + * value. + * + * Results: + * The return value is a standard Tcl result. + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +StringToNode(clientData, interp, switchName, string, record, offset) + ClientData clientData; /* Not used. */ + Tcl_Interp *interp; /* Interpreter to send results back to */ + char *switchName; /* Not used. */ + char *string; /* String representation */ + char *record; /* Structure record */ + int offset; /* Offset to field in structure */ +{ + MoveData *dataPtr = (MoveData *)record; + Blt_TreeNode node; + Tcl_Obj *objPtr; + TreeCmd *cmdPtr = dataPtr->cmdPtr; + + objPtr = Tcl_NewStringObj(string, -1); + if (GetNode(cmdPtr, objPtr, &node) != TCL_OK) { + return TCL_ERROR; + } + dataPtr->node = node; + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * StringToOrder -- + * + * Convert a string represent a node number into its integer + * value. + * + * Results: + * The return value is a standard Tcl result. + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +StringToOrder(clientData, interp, switchName, string, record, offset) + ClientData clientData; /* Not used. */ + Tcl_Interp *interp; /* Interpreter to send results back to */ + char *switchName; /* Not used. */ + char *string; /* String representation */ + char *record; /* Structure record */ + int offset; /* Offset to field in structure */ +{ + int *orderPtr = (int *)(record + offset); + char c; + + c = string[0]; + if ((c == 'b') && (strcmp(string, "breadthfirst") == 0)) { + *orderPtr = TREE_BREADTHFIRST; + } else if ((c == 'i') && (strcmp(string, "inorder") == 0)) { + *orderPtr = TREE_INORDER; + } else if ((c == 'p') && (strcmp(string, "preorder") == 0)) { + *orderPtr = TREE_PREORDER; + } else if ((c == 'p') && (strcmp(string, "postorder") == 0)) { + *orderPtr = TREE_POSTORDER; + } else { + Tcl_AppendResult(interp, "bad order \"", string, + "\": should be breadthfirst, inorder, preorder, or postorder", + (char *)NULL); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * StringToOrder -- + * + * Convert a string represent a node number into its integer + * value. + * + * Results: + * The return value is a standard Tcl result. + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +StringToFormat(clientData, interp, switchName, string, record, offset) + ClientData clientData; /* Not used. */ + Tcl_Interp *interp; /* Interpreter to send results back to */ + char *switchName; /* Not used. */ + char *string; /* String representation */ + char *record; /* Structure record */ + int offset; /* Offset to field in structure */ +{ + PositionData *dataPtr = (PositionData *)record; + + if (strcmp(string, "position") == 0) { + dataPtr->withParent = FALSE; + dataPtr->withId = FALSE; + } else if (strcmp(string, "id+position") == 0) { + dataPtr->withParent = FALSE; + dataPtr->withId = TRUE; + } else if (strcmp(string, "parent-at-position") == 0) { + dataPtr->withParent = TRUE; + dataPtr->withId = FALSE; + } else if (strcmp(string, "id+parent-at-position") == 0) { + dataPtr->withParent = TRUE; + dataPtr->withId = TRUE; + } else { + Tcl_AppendResult(interp, "bad format \"", string, + "\": should be position, parent-at-position, id+position, or id+parent-at-position", + (char *)NULL); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * GetTreeCmdInterpData -- + * + *---------------------------------------------------------------------- + */ +static TreeCmdInterpData * +GetTreeCmdInterpData(interp) + Tcl_Interp *interp; +{ + TreeCmdInterpData *dataPtr; + Tcl_InterpDeleteProc *proc; + + dataPtr = (TreeCmdInterpData *) + Tcl_GetAssocData(interp, TREE_THREAD_KEY, &proc); + if (dataPtr == NULL) { + dataPtr = Blt_Malloc(sizeof(TreeCmdInterpData)); + assert(dataPtr); + dataPtr->interp = interp; + Tcl_SetAssocData(interp, TREE_THREAD_KEY, TreeInterpDeleteProc, + dataPtr); + Blt_InitHashTable(&dataPtr->treeTable, BLT_ONE_WORD_KEYS); + } + return dataPtr; +} + +/* + *---------------------------------------------------------------------- + * + * GetTreeCmd -- + * + * Find the tree command associated with the Tcl command "string". + * + * We have to do multiple lookups to get this right. + * + * The first step is to generate a canonical command name. If an + * unqualified command name (i.e. no namespace qualifier) is + * given, we should search first the current namespace and then + * the global one. Most Tcl commands (like Tcl_GetCmdInfo) look + * only at the global namespace. + * + * Next check if the string is + * a) a Tcl command and + * b) really is a command for a tree object. + * Tcl_GetCommandInfo will get us the objClientData field that + * should be a cmdPtr. We can verify that by searching our hashtable + * of cmdPtr addresses. + * + * Results: + * A pointer to the tree command. If no associated tree command + * can be found, NULL is returned. It's up to the calling routines + * to generate an error message. + * + *---------------------------------------------------------------------- + */ +static TreeCmd * +GetTreeCmd(dataPtr, interp, string) + TreeCmdInterpData *dataPtr; + Tcl_Interp *interp; + char *string; +{ + char *name; + Tcl_Namespace *nsPtr; + Tcl_CmdInfo cmdInfo; + Blt_HashEntry *hPtr; + Tcl_DString dString; + char *treeName; + int result; + + /* Put apart the tree name and put is back together in a standard + * format. */ + if (Blt_ParseQualifiedName(interp, string, &nsPtr, &name) != TCL_OK) { + return NULL; /* No such parent namespace. */ + } + if (nsPtr == NULL) { + nsPtr = Tcl_GetCurrentNamespace(interp); + } + /* Rebuild the fully qualified name. */ + treeName = Blt_GetQualifiedName(nsPtr, name, &dString); + result = Tcl_GetCommandInfo(interp, treeName, &cmdInfo); + Tcl_DStringFree(&dString); + + if (!result) { + return NULL; + } + hPtr = Blt_FindHashEntry(&dataPtr->treeTable, + (char *)(cmdInfo.objClientData)); + if (hPtr == NULL) { + return NULL; + } + return Blt_GetHashValue(hPtr); +} + + +/* + *---------------------------------------------------------------------- + * + * ForgetTag -- + * + *---------------------------------------------------------------------- + */ +static int +ForgetTag(cmdPtr, tagName) + TreeCmd *cmdPtr; + char *tagName; +{ + if ((strcmp(tagName, "all") != 0) || (strcmp(tagName, "root") != 0)) { + Blt_TreeForgetTag(cmdPtr->tagTablePtr, tagName); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * AddTag -- + * + *---------------------------------------------------------------------- + */ +static int +AddTag(cmdPtr, node, tagName) + TreeCmd *cmdPtr; + Blt_TreeNode node; + char *tagName; +{ + if ((strcmp(tagName, "all") == 0) || (strcmp(tagName, "root") == 0)) { + Tcl_AppendResult(cmdPtr->interp, "can't add reserved tag \"", + tagName, "\"", (char *)NULL); + return TCL_ERROR; + } + Blt_TreeAddTag(cmdPtr->tagTablePtr, node, tagName); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * HasTag -- + * + *---------------------------------------------------------------------- + */ +static int +HasTag(cmdPtr, node, tagName) + TreeCmd *cmdPtr; + Blt_TreeNode node; + char *tagName; +{ + if ((strcmp(tagName, "root") == 0) && + (node == Blt_TreeRootNode(cmdPtr->tree))) { + return TRUE; + } + if (strcmp(tagName, "all") == 0) { + return TRUE; + } + return Blt_TreeHasTag(cmdPtr->tagTablePtr, node, tagName); +} + + +static Blt_TreeNode +ParseModifiers(interp, tree, node, modifiers) + Tcl_Interp *interp; + Blt_Tree tree; + Blt_TreeNode node; + char *modifiers; +{ + char *p, *np; + + p = modifiers; + do { + p += 2; /* Skip the initial "->" */ + np = strstr(p, "->"); + if (np != NULL) { + *np = '\0'; + } + if ((*p == 'p') && (strcmp(p, "parent") == 0)) { + node = Blt_TreeNodeParent(node); + } else if ((*p == 'f') && (strcmp(p, "firstchild") == 0)) { + node = Blt_TreeFirstChild(node); + } else if ((*p == 'l') && (strcmp(p, "lastchild") == 0)) { + node = Blt_TreeLastChild(node); + } else if ((*p == 'n') && (strcmp(p, "next") == 0)) { + node = Blt_TreeNextNode(Blt_TreeRootNode(tree), node); + } else if ((*p == 'n') && (strcmp(p, "nextsibling") == 0)) { + node = Blt_TreeNextSibling(node); + } else if ((*p == 'p') && (strcmp(p, "previous") == 0)) { + node = Blt_TreePrevNode(Blt_TreeRootNode(tree), node); + } else if ((*p == 'p') && (strcmp(p, "prevsibling") == 0)) { + node = Blt_TreePrevSibling(node); + } else if (isdigit(UCHAR(*p))) { + int inode; + + if (Tcl_GetInt(interp, p, &inode) != TCL_OK) { + node = NULL; + } else { + node = Blt_TreeGetNode(tree, inode); + } + } else { + char *endp; + + if (np != NULL) { + endp = np - 1; + } else { + endp = p + strlen(p) - 1; + } + if ((*p == '"') && (*endp == '"')) { + *endp = '\0'; + node = Blt_TreeFindChild(node, p + 1); + *endp = '"'; + } else { + node = Blt_TreeFindChild(node, p); + } + } + if (node == NULL) { + goto error; + } + if (np != NULL) { + *np = '-'; /* Repair the string */ + } + p = np; + } while (np != NULL); + return node; + error: + if (np != NULL) { + *np = '-'; /* Repair the string */ + } + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * GetForeignNode -- + * + *---------------------------------------------------------------------- + */ +static int +GetForeignNode(interp, tree, objPtr, nodePtr) + Tcl_Interp *interp; + Blt_Tree tree; + Tcl_Obj *objPtr; + Blt_TreeNode *nodePtr; +{ + char c; + Blt_TreeNode node; + char *string; + char *p; + + node = NULL; + string = Tcl_GetString(objPtr); + c = string[0]; + + /* + * Check if modifiers are present. + */ + p = strstr(string, "->"); + if (isdigit(UCHAR(c))) { + int inode; + + if (p != NULL) { + char save; + int result; + + save = *p; + *p = '\0'; + result = Tcl_GetInt(interp, string, &inode); + *p = save; + if (result != TCL_OK) { + return TCL_ERROR; + } + } else { + if (Tcl_GetIntFromObj(interp, objPtr, &inode) != TCL_OK) { + return TCL_ERROR; + } + } + node = Blt_TreeGetNode(tree, inode); + if (p != NULL) { + node = ParseModifiers(interp, tree, node, p); + } + if (node != NULL) { + *nodePtr = node; + return TCL_OK; + } + } + Tcl_AppendResult(interp, "can't find tag or id \"", string, "\" in ", + Blt_TreeName(tree), (char *)NULL); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * GetNode -- + * + *---------------------------------------------------------------------- + */ +static int +GetNode(cmdPtr, objPtr, nodePtr) + TreeCmd *cmdPtr; + Tcl_Obj *objPtr; + Blt_TreeNode *nodePtr; +{ + Tcl_Interp *interp = cmdPtr->interp; + Blt_Tree tree = cmdPtr->tree; + char c; + Blt_TreeNode node; + char *string; + char *p; + + node = NULL; + string = Tcl_GetString(objPtr); + c = string[0]; + + /* + * Check if modifiers are present. + */ + p = strstr(string, "->"); + if (isdigit(UCHAR(c))) { + int inode; + + if (p != NULL) { + char save; + int result; + + save = *p; + *p = '\0'; + result = Tcl_GetInt(interp, string, &inode); + *p = save; + if (result != TCL_OK) { + return TCL_ERROR; + } + } else { + if (Tcl_GetIntFromObj(interp, objPtr, &inode) != TCL_OK) { + return TCL_ERROR; + } + } + node = Blt_TreeGetNode(tree, inode); + if (p != NULL) { + node = ParseModifiers(interp, tree, node, p); + } + if (node != NULL) { + *nodePtr = node; + return TCL_OK; + } + } else if (cmdPtr != NULL) { + char save; + + save = '\0'; /* Suppress compiler warning. */ + if (p != NULL) { + save = *p; + *p = '\0'; + } + if (strcmp(string, "all") == 0) { + if (Blt_TreeSize(Blt_TreeRootNode(tree)) > 1) { + Tcl_AppendResult(interp, "more than one node tagged as \"", + string, "\"", (char *)NULL); + if (p != NULL) { + *p = save; + } + return TCL_ERROR; + } + node = Blt_TreeRootNode(tree); + } else if (strcmp(string, "root") == 0) { + node = Blt_TreeRootNode(tree); + } else { + Blt_HashTable *tablePtr; + Blt_HashSearch cursor; + Blt_HashEntry *hPtr; + int result; + + node = NULL; + result = TCL_ERROR; + tablePtr = Blt_TreeTagHashTable(cmdPtr->tagTablePtr, string); + if (tablePtr == NULL) { + Tcl_AppendResult(interp, "can't find tag or id \"", string, + "\" in ", Blt_TreeName(cmdPtr->tree), (char *)NULL); + } else if (tablePtr->numEntries > 1) { + Tcl_AppendResult(interp, "more than one node tagged as \"", + string, "\"", (char *)NULL); + } else { + hPtr = Blt_FirstHashEntry(tablePtr, &cursor); + node = Blt_GetHashValue(hPtr); + result = TCL_OK; + } + if (result == TCL_ERROR) { + if (p != NULL) { + *p = save; + } + return TCL_ERROR; + } + } + if (p != NULL) { + *p = save; + node = ParseModifiers(interp, tree, node, p); + } + if (node != NULL) { + *nodePtr = node; + return TCL_OK; + } + } + Tcl_AppendResult(interp, "can't find tag or id \"", string, "\" in ", + Blt_TreeName(tree), (char *)NULL); + return TCL_ERROR; +} + +typedef struct { + int tagType; + Blt_TreeNode root; + Blt_HashSearch cursor; +} TagSearch; + +/* + *---------------------------------------------------------------------- + * + * FirstTaggedNode -- + * + * Returns the id of the first node tagged by the given tag in + * objPtr. It basically hides much of the cumbersome special + * case details. For example, the special tags "root" and "all" + * always exist, so they don't have entries in the tag hashtable. + * If it's a hashed tag (not "root" or "all"), we have to save + * the place of where we are in the table for the next call to + * NextTaggedNode. + * + *---------------------------------------------------------------------- + */ +static Blt_TreeNode +FirstTaggedNode(interp, cmdPtr, objPtr, cursorPtr) + Tcl_Interp *interp; + TreeCmd *cmdPtr; + Tcl_Obj *objPtr; + TagSearch *cursorPtr; +{ + Blt_TreeNode node, root; + char *string; + + node = NULL; + string = Tcl_GetString(objPtr); + + root = Blt_TreeRootNode(cmdPtr->tree); + string = Tcl_GetString(objPtr); + cursorPtr->tagType = TAG_TYPE_NONE; + cursorPtr->root = root; + + /* Process strings with modifiers or digits as simple ids, not + * tags. */ + if ((strstr(string, "->") != NULL) || (isdigit(UCHAR(*string)))) { + if (GetNode(cmdPtr, objPtr, &node) != TCL_OK) { + return NULL; + } + return node; + } + if (strcmp(string, "all") == 0) { + cursorPtr->tagType = TAG_TYPE_ALL; + return root; + } else if (strcmp(string, "root") == 0) { + return root; + } else { + Blt_HashTable *tablePtr; + + tablePtr = Blt_TreeTagHashTable(cmdPtr->tagTablePtr, string); + if (tablePtr != NULL) { + Blt_HashEntry *hPtr; + + hPtr = Blt_FirstHashEntry(tablePtr, &(cursorPtr->cursor)); + node = Blt_GetHashValue(hPtr); + cursorPtr->tagType = TAG_TYPE_TAG; + return node; + } + } + Tcl_AppendResult(interp, "can't find tag or id \"", string, "\" in ", + Blt_TreeName(cmdPtr->tree), (char *)NULL); + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * NextTaggedNode -- + * + *---------------------------------------------------------------------- + */ +static Blt_TreeNode +NextTaggedNode(node, cursorPtr) + Blt_TreeNode node; + TagSearch *cursorPtr; +{ + if (cursorPtr->tagType == TAG_TYPE_ALL) { + return Blt_TreeNextNode(cursorPtr->root, node); + } + if (cursorPtr->tagType == TAG_TYPE_TAG) { + Blt_HashEntry *hPtr; + + hPtr = Blt_NextHashEntry(&(cursorPtr->cursor)); + if (hPtr == NULL) { + return NULL; + } + return Blt_GetHashValue(hPtr); + } + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * DeleteNode -- + * + *---------------------------------------------------------------------- + */ +static void +DeleteNode(cmdPtr, node) + TreeCmd *cmdPtr; + Blt_TreeNode node; +{ + Blt_TreeNode root; + + Blt_TreeClearTags(cmdPtr->tagTablePtr, node); + root = Blt_TreeRootNode(cmdPtr->tree); + if (node == root) { + Blt_TreeNode next; + /* Don't delete the root node. Simply clean out the tree. */ + for (node = Blt_TreeFirstChild(node); node != NULL; node = next) { + next = Blt_TreeNextSibling(node); + Blt_TreeDeleteNode(cmdPtr->tree, node); + } + } else if (Blt_TreeIsAncestor(root, node)) { + Blt_TreeDeleteNode(cmdPtr->tree, node); + } +} + +/* + *---------------------------------------------------------------------- + * + * GetNodePath -- + * + *---------------------------------------------------------------------- + */ +static char * +GetNodePath(cmdPtr, root, node, rootFlag, resultPtr) + TreeCmd *cmdPtr; + Blt_TreeNode root, node; + int rootFlag; /* If non-zero, indicates to include + * the root name in the path */ + Tcl_DString *resultPtr; +{ + char **nameArr; /* Used to stack the component names. */ + char *staticSpace[64]; + register int i; + int nLevels; + + nLevels = Blt_TreeNodeDepth(cmdPtr->tree, node) - + Blt_TreeNodeDepth(cmdPtr->tree, root); + if (rootFlag) { + nLevels++; + } + if (nLevels > 64) { + nameArr = Blt_Malloc(nLevels * sizeof(char *)); + assert(nameArr); + } else { + nameArr = staticSpace; + } + for (i = nLevels; i > 0; i--) { + /* Save the name of each ancestor in the name array. + * Note that we ignore the root. */ + nameArr[i - 1] = Blt_TreeNodeLabel(node); + node = Blt_TreeNodeParent(node); + } + /* Append each the names in the array. */ + Tcl_DStringInit(resultPtr); + for (i = 0; i < nLevels; i++) { + Tcl_DStringAppendElement(resultPtr, nameArr[i]); + } + if (nameArr != staticSpace) { + Blt_Free(nameArr); + } + return Tcl_DStringValue(resultPtr); +} + +/* + *---------------------------------------------------------------------- + * + * GetKeys -- + * + * Tree values are stored in a list last-to-first. Create an + * array to hold the pointer so that we can return them in + * reverse order. + * + *---------------------------------------------------------------------- + */ +static Blt_TreeKey * +GetKeys(tree, node) + Blt_Tree tree; + Blt_TreeNode node; +{ + int nValues; + Blt_TreeKeySearch keyIter; + register Blt_TreeKey key, *keys; + + /* Find out how may values there are. Note that we have to iterate + * through the list because some values may be private. */ + nValues = 0; + for (key = Blt_TreeFirstKey(tree, node, &keyIter); key != NULL; + key = Blt_TreeNextKey(tree, &keyIter)) { + nValues++; + } + /* Create a temporary array and store the keys in reverse order. */ + keys = Blt_Malloc(sizeof(Blt_TreeKey) * (nValues + 1)); + assert(keys); + keys[nValues] = NULL; + for (key = Blt_TreeFirstKey(tree, node, &keyIter); key != NULL; + key = Blt_TreeNextKey(tree, &keyIter)) { + nValues--; + keys[nValues] = key; + } + return keys; +} + +/* + *---------------------------------------------------------------------- + * + * ParseNode5 -- + * + * Parses and creates a node based upon the first 3 fields of + * a five field entry. This is the new restore file format. + * + * parentId nodeId pathList dataList tagList + * + * The purpose is to attempt to save and restore the node ids + * embedded in the restore file information. The old format + * could not distinquish between two sibling nodes with the same + * label unless they were both leaves. I'm trying to avoid + * dependencies upon labels. + * + * If you're starting from an empty tree, this obviously should + * work without a hitch. We only need to map the file's root id + * to 0. It's a little more complicated when adding node to an + * already full tree. + * + * First see if the node id isn't already in use. Otherwise, map + * the node id (via a hashtable) to the real node. We'll need it + * later when subsequent entries refer to their parent id. + * + * If a parent id is unknown (the restore file may be out of + * order), then follow plan B and use its path. + * + *---------------------------------------------------------------------- + */ +static Blt_TreeNode +ParseNode5(cmdPtr, argc, argv, dataPtr) + TreeCmd *cmdPtr; + int argc; + char **argv; + RestoreData *dataPtr; +{ + Blt_HashEntry *hPtr; + Blt_TreeNode node, parent; + char **names; + int nNames, isNew; + int parentId, nodeId; + + if ((Tcl_GetInt(cmdPtr->interp, argv[0], &parentId) != TCL_OK) || + (Tcl_GetInt(cmdPtr->interp, argv[1], &nodeId) != TCL_OK) || + (Tcl_SplitList(cmdPtr->interp, argv[2], &nNames, &names) != TCL_OK)) { + return NULL; + } + + node = parent = dataPtr->root; + if (parentId == -1) { /* Dump marks root's parent as -1. */ + node = dataPtr->root; + /* Create a mapping between the old id and the new node */ + hPtr = Blt_CreateHashEntry(&dataPtr->idTable, (char *)nodeId, + &isNew); + Blt_SetHashValue(hPtr, node); + Blt_TreeRelabelNode(cmdPtr->tree, node, names[0]); + } else { + /* + * Check if the parent has been translated to another id. + * This can happen when there's a id collision with an + * existing node. + */ + hPtr = Blt_FindHashEntry(&dataPtr->idTable, (char *)parentId); + if (hPtr != NULL) { + parent = Blt_GetHashValue(hPtr); + } else { + /* Check if the id already exists in the tree. */ + parent = Blt_TreeGetNode(cmdPtr->tree, parentId); + if (parent == NULL) { + /* Parent id doesn't exist (partial restore?). + * Plan B: Use the path to create/find the parent with + * the requested parent id. */ + if (nNames > 1) { + int i; + + for (i = 1; i < (nNames - 2); i++) { + node = Blt_TreeFindChild(parent, names[i]); + if (node == NULL) { + node = Blt_TreeCreateNode(cmdPtr->tree, parent, + names[i], -1); + } + parent = node; + } + node = Blt_TreeFindChild(parent, names[nNames - 2]); + if (node == NULL) { + node = Blt_TreeCreateNodeWithId(cmdPtr->tree, parent, + names[nNames - 2], parentId, -1); + } + parent = node; + } else { + parent = dataPtr->root; + } + } + } + /* Check if old node id already in use. */ + node = NULL; + if (dataPtr->flags & RESTORE_OVERWRITE) { + node = Blt_TreeFindChild(parent, names[nNames - 1]); + /* Create a mapping between the old id and the new node */ + hPtr = Blt_CreateHashEntry(&dataPtr->idTable, (char *)nodeId, + &isNew); + Blt_SetHashValue(hPtr, node); + } + if (node == NULL) { + node = Blt_TreeGetNode(cmdPtr->tree, nodeId); + if (node != NULL) { + node = Blt_TreeCreateNode(cmdPtr->tree, parent, + names[nNames - 1], -1); + /* Create a mapping between the old id and the new node */ + hPtr = Blt_CreateHashEntry(&dataPtr->idTable, (char *)nodeId, + &isNew); + Blt_SetHashValue(hPtr, node); + } else { + /* Otherwise create a new node with the requested id. */ + node = Blt_TreeCreateNodeWithId(cmdPtr->tree, parent, + names[nNames - 1], nodeId, -1); + } + } + } + Blt_Free(names); + return node; +} + +/* + *---------------------------------------------------------------------- + * + * ParseNode3 -- + * + * Parses and creates a node based upon the first field of + * a three field entry. This is the old restore file format. + * + * pathList dataList tagList + * + *---------------------------------------------------------------------- + */ +static Blt_TreeNode +ParseNode3(cmdPtr, argc, argv, dataPtr) + TreeCmd *cmdPtr; + int argc; + char **argv; + RestoreData *dataPtr; +{ + Blt_TreeNode node, parent; + char **names; + int i; + int nNames; + + if (Tcl_SplitList(cmdPtr->interp, argv[0], &nNames, &names) != TCL_OK) { + return NULL; + } + node = parent = dataPtr->root; + /* Automatically create nodes as needed except for the last node. */ + for (i = 0; i < (nNames - 1); i++) { + node = Blt_TreeFindChild(parent, names[i]); + if (node == NULL) { + node = Blt_TreeCreateNode(cmdPtr->tree, parent, names[i], -1); + } + parent = node; + } + if (nNames > 0) { + /* + * By default, create duplicate nodes (two sibling nodes with + * the same label), unless the -overwrite flag was set. + */ + node = NULL; + if (dataPtr->flags & RESTORE_OVERWRITE) { + node = Blt_TreeFindChild(parent, names[i]); + } + if (node == NULL) { + node = Blt_TreeCreateNode(cmdPtr->tree, parent, names[i], -1); + } + } + Blt_Free(names); + return node; +} + +static int +RestoreNode(cmdPtr, argc, argv, dataPtr) + TreeCmd *cmdPtr; + int argc; + char **argv; + RestoreData *dataPtr; +{ + Blt_TreeNode node; + Tcl_Obj *valueObjPtr, *emptyStringObjPtr; + char **elemArr; + int nElem; + register int i; + + if ((argc != 3) && (argc != 5)) { + Tcl_AppendResult(cmdPtr->interp, "line #", Blt_Itoa(nLines), + ": wrong # elements in restore entry", (char *)NULL); + return TCL_ERROR; + } + emptyStringObjPtr = Tcl_NewStringObj("", -1); + /* Parse the path name. */ + if (argc == 3) { + node = ParseNode3(cmdPtr, argc, argv, dataPtr); + argc--, argv++; + } else if (argc == 5) { + node = ParseNode5(cmdPtr, argc, argv, dataPtr); + argc -= 3, argv += 3; + } else { + Tcl_AppendResult(cmdPtr->interp, "line #", Blt_Itoa(nLines), + ": wrong # elements in restore entry", (char *)NULL); + return TCL_ERROR; + } + if (node == NULL) { + return TCL_ERROR; + } + /* Parse the key-value list. */ + if (Tcl_SplitList(cmdPtr->interp, argv[0], &nElem, &elemArr) != TCL_OK) { + return TCL_ERROR; + } + for (i = 0; i < nElem; i += 2) { + if ((i + 1) < nElem) { + valueObjPtr = Tcl_NewStringObj(elemArr[i + 1], -1); + } else { + valueObjPtr = emptyStringObjPtr; + } + if (Blt_TreeSetValue(cmdPtr->interp, cmdPtr->tree, node, elemArr[i], + valueObjPtr) != TCL_OK) { + Blt_Free(elemArr); + return TCL_ERROR; + } + } + Blt_Free(elemArr); + if (!(dataPtr->flags & RESTORE_NO_TAGS)) { + /* Parse the tag list. */ + if (Tcl_SplitList(cmdPtr->interp, argv[1], &nElem, &elemArr) + != TCL_OK) { + return TCL_ERROR; + } + for (i = 0; i < nElem; i++) { + AddTag(cmdPtr, node, elemArr[i]); + } + Blt_Free(elemArr); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * PrintNode -- + * + *---------------------------------------------------------------------- + */ +static void +PrintNode(cmdPtr, root, node, resultPtr) + TreeCmd *cmdPtr; + Blt_TreeNode root, node; + Tcl_DString *resultPtr; +{ + Blt_HashEntry *hPtr; + Blt_HashSearch cursor; + char *pathName; + Tcl_DString dString; + Tcl_Obj *valueObjPtr; + register Blt_TreeKey *key; + Blt_TreeKey *keys; + Blt_TreeTag *tagPtr; + + if (node == root) { + Tcl_DStringAppendElement(resultPtr, "-1"); + } else { + Blt_TreeNode parent; + + parent = Blt_TreeNodeParent(node); + Tcl_DStringAppendElement(resultPtr, Blt_Itoa(Blt_TreeNodeId(parent))); + } + Tcl_DStringAppendElement(resultPtr, Blt_Itoa(Blt_TreeNodeId(node))); + + pathName = GetNodePath(cmdPtr, root, node, TRUE, &dString); + Tcl_DStringAppendElement(resultPtr, pathName); + Tcl_DStringStartSublist(resultPtr); + keys = GetKeys(cmdPtr->tree, node); + for (key = keys; *key != NULL; key++) { + if (Blt_TreeGetValueByKey((Tcl_Interp *)NULL, cmdPtr->tree, node, + *key, &valueObjPtr) == TCL_OK) { + Tcl_DStringAppendElement(resultPtr, *key); + Tcl_DStringAppendElement(resultPtr, Tcl_GetString(valueObjPtr)); + } + } + Blt_Free(keys); + Tcl_DStringEndSublist(resultPtr); + Tcl_DStringStartSublist(resultPtr); + for (hPtr = Blt_FirstHashEntry(&cmdPtr->tagTablePtr->table, &cursor); + hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { + tagPtr = Blt_GetHashValue(hPtr); + if (Blt_FindHashEntry(&tagPtr->nodeTable, (char *)node) != NULL) { + Tcl_DStringAppendElement(resultPtr, tagPtr->tagName); + } + } + Tcl_DStringEndSublist(resultPtr); + Tcl_DStringAppend(resultPtr, "\n", -1); + Tcl_DStringFree(&dString); +} + +/* + *---------------------------------------------------------------------- + * + * PrintTraceFlags -- + * + *---------------------------------------------------------------------- + */ +static void +PrintTraceFlags(flags, string) + unsigned int flags; + char *string; +{ + register char *p; + + p = string; + if (flags & TREE_TRACE_READ) { + *p++ = 'r'; + } + if (flags & TREE_TRACE_WRITE) { + *p++ = 'w'; + } + if (flags & TREE_TRACE_UNSET) { + *p++ = 'u'; + } + if (flags & TREE_TRACE_CREATE) { + *p++ = 'c'; + } + *p = '\0'; +} + +/* + *---------------------------------------------------------------------- + * + * GetTraceFlags -- + * + *---------------------------------------------------------------------- + */ +static int +GetTraceFlags(string) + char *string; +{ + register char *p; + unsigned int flags; + + flags = 0; + for (p = string; *p != '\0'; p++) { + switch (toupper(*p)) { + case 'R': + flags |= TREE_TRACE_READ; + break; + case 'W': + flags |= TREE_TRACE_WRITE; + break; + case 'U': + flags |= TREE_TRACE_UNSET; + break; + case 'C': + flags |= TREE_TRACE_CREATE; + break; + default: + return -1; + } + } + return flags; +} + +/* + *---------------------------------------------------------------------- + * + * SetValues -- + * + *---------------------------------------------------------------------- + */ +static int +SetValues(cmdPtr, node, objc, objv) + TreeCmd *cmdPtr; + Blt_TreeNode node; + int objc; + Tcl_Obj *CONST *objv; +{ + register int i; + char *string; + + for (i = 0; i < objc; i += 2) { + string = Tcl_GetString(objv[i]); + if ((i + 1) == objc) { + Tcl_AppendResult(cmdPtr->interp, "missing value for field \"", + string, "\"", (char *)NULL); + return TCL_ERROR; + } + if (Blt_TreeSetValue(cmdPtr->interp, cmdPtr->tree, node, string, + objv[i + 1]) != TCL_OK) { + return TCL_ERROR; + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * UnsetValues -- + * + *---------------------------------------------------------------------- + */ +static int +UnsetValues(cmdPtr, node, objc, objv) + TreeCmd *cmdPtr; + Blt_TreeNode node; + int objc; + Tcl_Obj *CONST *objv; +{ + if (objc == 0) { + Blt_TreeKey key; + Blt_TreeKeySearch keyIter; + + for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &keyIter); key != NULL; + key = Blt_TreeNextKey(cmdPtr->tree, &keyIter)) { + if (Blt_TreeUnsetValueByKey(cmdPtr->interp, NULL, node, key) + != TCL_OK) { + return TCL_ERROR; + } + } + } else { + register int i; + + for (i = 0; i < objc; i ++) { + if (Blt_TreeUnsetValue(cmdPtr->interp, NULL, node, + Tcl_GetString(objv[i])) != TCL_OK) { + return TCL_ERROR; + } + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * MatchNodeProc -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +MatchNodeProc(node, clientData, order) + Blt_TreeNode node; + ClientData clientData; + int order; +{ + FindData *dataPtr = clientData; + Tcl_DString dString; + TreeCmd *cmdPtr = dataPtr->cmdPtr; + Tcl_Interp *interp = dataPtr->cmdPtr->interp; + int result, invert, patternFlags; + char *string; + + if ((dataPtr->flags & MATCH_LEAFONLY) && (!Blt_TreeIsLeaf(node))) { + return TCL_OK; + } + if ((dataPtr->maxDepth >= 0) && + (dataPtr->maxDepth < Blt_TreeNodeDepth(cmdPtr->tree, node))) { + return TCL_OK; + } + result = TRUE; + patternFlags = (dataPtr->flags & PATTERN_MASK); + Tcl_DStringInit(&dString); + string = NULL; + if (dataPtr->key != NULL) { + Tcl_Obj *objPtr; + + if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, node, + dataPtr->key, &objPtr) == TCL_OK) { + string = Tcl_GetString(objPtr); + } else { + result = FALSE; + } + } else { + if (dataPtr->flags & MATCH_PATHNAME) { + string = GetNodePath(cmdPtr, Blt_TreeRootNode(cmdPtr->tree), + node, FALSE, &dString); + } else { + string = Blt_TreeNodeLabel(node); + } + } + if ((string != NULL) && (patternFlags != PATTERN_NONE)) { + if (dataPtr->flags & MATCH_NOCASE) { + string = Blt_Strdup(string); + strtolower(string); + } + switch (patternFlags) { + case PATTERN_EXACT: + result = (strcmp(string, dataPtr->pattern) == 0); + break; + + case PATTERN_GLOB: + result = Tcl_StringMatch(string, dataPtr->pattern); + break; + + case PATTERN_REGEXP: + result = Tcl_RegExpMatch(interp, string, dataPtr->pattern); + break; + } + if (dataPtr->flags & MATCH_NOCASE) { + Blt_Free(string); + } + } + if ((dataPtr->withTag != NULL) && + (!HasTag(cmdPtr, node, dataPtr->withTag))) { + result = FALSE; + } + Tcl_DStringFree(&dString); + invert = (dataPtr->flags & MATCH_INVERT) ? TRUE : FALSE; + if (result != invert) { + Tcl_Obj *objPtr; + + if (dataPtr->addTag != NULL) { + if (AddTag(cmdPtr, node, dataPtr->addTag) != TCL_OK) { + return TCL_ERROR; + } + } + objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node)); + Tcl_ListObjAppendElement(interp, dataPtr->listObjPtr, objPtr); + if (dataPtr->objv != NULL) { + dataPtr->objv[dataPtr->objc - 1] = objPtr; + result = Tcl_EvalObjv(interp, dataPtr->objc, dataPtr->objv, 0); + if (result != TCL_OK) { + return result; + } + } + dataPtr->nMatches++; + if ((dataPtr->maxMatches > 0) && + (dataPtr->nMatches >= dataPtr->maxMatches)) { + return TCL_BREAK; + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * ApplyNodeProc -- + * + *---------------------------------------------------------------------- + */ +static int +ApplyNodeProc(node, clientData, order) + Blt_TreeNode node; + ClientData clientData; + int order; +{ + ApplyData *dataPtr = clientData; + TreeCmd *cmdPtr = dataPtr->cmdPtr; + Tcl_Interp *interp = cmdPtr->interp; + char *string; + int invert, result; + unsigned int patternFlags; + Tcl_DString dString; + + if ((dataPtr->flags & MATCH_LEAFONLY) && (!Blt_TreeIsLeaf(node))) { + return TCL_OK; + } + if ((dataPtr->maxDepth >= 0) && + (dataPtr->maxDepth < Blt_TreeNodeDepth(cmdPtr->tree, node))) { + return TCL_OK; + } + Tcl_DStringInit(&dString); + result = TRUE; + patternFlags = (dataPtr->flags & PATTERN_MASK); + string = NULL; + if (dataPtr->key != NULL) { + Tcl_Obj *valueObjPtr; + + if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, node, + dataPtr->key, &valueObjPtr) == TCL_OK) { + string = Tcl_GetString(valueObjPtr); + } else { + result = FALSE; + } + } else { + if (dataPtr->flags & MATCH_PATHNAME) { + string = GetNodePath(cmdPtr, Blt_TreeRootNode(cmdPtr->tree), node, + FALSE, &dString); + } else { + string = Blt_TreeNodeLabel(node); + } + } + if (patternFlags != PATTERN_NONE) { + if (dataPtr->flags & MATCH_NOCASE) { + string = Blt_Strdup(string); + strtolower(string); + } + switch (patternFlags) { + case PATTERN_EXACT: + result = (strcmp(string, dataPtr->pattern) == 0); + break; + + case PATTERN_GLOB: + result = Tcl_StringMatch(string, dataPtr->pattern); + break; + + case PATTERN_REGEXP: + result = Tcl_RegExpMatch(interp, string, dataPtr->pattern); + break; + } + if (dataPtr->flags & MATCH_NOCASE) { + Blt_Free(string); + } + } + Tcl_DStringFree(&dString); + if ((dataPtr->withTag != NULL) && + (!HasTag(cmdPtr, node, dataPtr->withTag))) { + result = FALSE; + } + invert = (dataPtr->flags & MATCH_INVERT) ? 1 : 0; + if (result != invert) { + Tcl_Obj *objPtr; + + objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node)); + if (order == TREE_PREORDER) { + dataPtr->preObjv[dataPtr->preObjc - 1] = objPtr; + return Tcl_EvalObjv(interp, dataPtr->preObjc, dataPtr->preObjv, 0); + } else if (order == TREE_POSTORDER) { + dataPtr->postObjv[dataPtr->postObjc - 1] = objPtr; + return Tcl_EvalObjv(interp, dataPtr->postObjc, dataPtr->postObjv,0); + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * ReleaseTreeObject -- + * + *---------------------------------------------------------------------- + */ +static void +ReleaseTreeObject(cmdPtr) + TreeCmd *cmdPtr; +{ + Blt_HashEntry *hPtr; + Blt_HashSearch cursor; + TraceInfo *tracePtr; + NotifyInfo *notifyPtr; + int i; + + Blt_TreeReleaseToken(cmdPtr->tree); + /* + * When the tree token is released, all the traces and + * notification events are automatically removed. But we still + * need to clean up the bookkeeping kept for traces. Clear all + * the tags and trace information. + */ + for (hPtr = Blt_FirstHashEntry(&(cmdPtr->traceTable), &cursor); + hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { + tracePtr = Blt_GetHashValue(hPtr); + Blt_Free(tracePtr); + } + for (hPtr = Blt_FirstHashEntry(&(cmdPtr->notifyTable), &cursor); + hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { + notifyPtr = Blt_GetHashValue(hPtr); + for (i = 0; i < notifyPtr->objc - 2; i++) { + Tcl_DecrRefCount(notifyPtr->objv[i]); + } + Blt_Free(notifyPtr->objv); + Blt_Free(notifyPtr); + } + if (cmdPtr->tagTablePtr != NULL) { + Blt_TreeReleaseTagTable(cmdPtr->tagTablePtr); + cmdPtr->tagTablePtr = NULL; + } + cmdPtr->tree = NULL; +} + +/* + *---------------------------------------------------------------------- + * + * TreeTraceProc -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +TreeTraceProc(clientData, interp, node, key, flags) + ClientData clientData; + Tcl_Interp *interp; + Blt_TreeNode node; /* Node that has just been updated. */ + Blt_TreeKey key; /* Field that's updated. */ + unsigned int flags; +{ + TraceInfo *tracePtr = clientData; + Tcl_DString dsCmd, dsName; + char string[5]; + char *qualName; + int result; + + /* + * If the trace was set on a tag, check that this node has the tag. + * There's a special check for the "all" tag. We don't want to + * actually tag all nodes in the table, so we do a separate test. + */ + if ((tracePtr->withTag != NULL) && + (!HasTag(tracePtr->cmdPtr, node, tracePtr->withTag))) { + return TCL_OK; + } + Tcl_DStringInit(&dsCmd); + Tcl_DStringAppend(&dsCmd, tracePtr->command, -1); + Tcl_DStringInit(&dsName); + qualName = Blt_GetQualifiedName( + Blt_GetCommandNamespace(interp, tracePtr->cmdPtr->cmdToken), + Tcl_GetCommandName(interp, tracePtr->cmdPtr->cmdToken), &dsName); + Tcl_DStringAppendElement(&dsCmd, qualName); + Tcl_DStringFree(&dsName); + if (node != NULL) { + Tcl_DStringAppendElement(&dsCmd, Blt_Itoa(Blt_TreeNodeId(node))); + } else { + Tcl_DStringAppendElement(&dsCmd, ""); + } + Tcl_DStringAppendElement(&dsCmd, key); + PrintTraceFlags(flags, string); + Tcl_DStringAppendElement(&dsCmd, string); + result = Tcl_Eval(interp, Tcl_DStringValue(&dsCmd)); + Tcl_DStringFree(&dsCmd); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * TreeEventProc -- + * + *---------------------------------------------------------------------- + */ +static int +TreeEventProc(clientData, eventPtr) + ClientData clientData; + Blt_TreeNotifyEvent *eventPtr; +{ + TreeCmd *cmdPtr = clientData; + Blt_HashEntry *hPtr; + Blt_HashSearch cursor; + NotifyInfo *notifyPtr; + Blt_TreeNode node; + char *string; + + switch (eventPtr->type) { + case TREE_NOTIFY_CREATE: + string = "-create"; + break; + + case TREE_NOTIFY_DELETE: + node = Blt_TreeGetNode(cmdPtr->tree, eventPtr->inode); + if (node != NULL) { + Blt_TreeClearTags(cmdPtr->tagTablePtr, node); + } + string = "-delete"; + break; + + case TREE_NOTIFY_MOVE: + string = "-move"; + break; + + case TREE_NOTIFY_SORT: + string = "-sort"; + break; + + case TREE_NOTIFY_RELABEL: + string = "-relabel"; + break; + + default: + /* empty */ + string = "???"; + break; + } + + for (hPtr = Blt_FirstHashEntry(&(cmdPtr->notifyTable), &cursor); + hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { + notifyPtr = Blt_GetHashValue(hPtr); + if (notifyPtr->mask & eventPtr->type) { + int result; + Tcl_Obj *flagObjPtr, *nodeObjPtr; + + flagObjPtr = Tcl_NewStringObj(string, -1); + nodeObjPtr = Tcl_NewIntObj(eventPtr->inode); + Tcl_IncrRefCount(flagObjPtr); + notifyPtr->objv[notifyPtr->objc - 2] = flagObjPtr; + notifyPtr->objv[notifyPtr->objc - 1] = nodeObjPtr; + Tcl_IncrRefCount(nodeObjPtr); + result = Tcl_EvalObjv(cmdPtr->interp, notifyPtr->objc, + notifyPtr->objv, 0); + Tcl_DecrRefCount(nodeObjPtr); + Tcl_DecrRefCount(flagObjPtr); + if (result != TCL_OK) { + Tcl_BackgroundError(cmdPtr->interp); + return TCL_ERROR; + } + Tcl_ResetResult(cmdPtr->interp); + } + } + return TCL_OK; +} + + +/* Tree command operations. */ + +/* + *---------------------------------------------------------------------- + * + * ApplyOp -- + * + * t0 apply root -precommand {command} -postcommand {command} + * + *---------------------------------------------------------------------- + */ +static int +ApplyOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + int result; + Blt_TreeNode node; + int i; + Tcl_Obj **objArr; + int count; + ApplyData data; + int order; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + memset(&data, 0, sizeof(data)); + data.maxDepth = -1; + data.cmdPtr = cmdPtr; + + /* Process switches */ + if (Blt_ProcessObjSwitches(interp, applySwitches, objc - 3, objv + 3, + (char *)&data, 0) < 0) { + return TCL_ERROR; + } + order = 0; + if (data.preCmd != NULL) { + char **p; + + count = 0; + for (p = data.preCmd; *p != NULL; p++) { + count++; + } + objArr = Blt_Malloc((count + 1) * sizeof(Tcl_Obj *)); + for (i = 0; i < count; i++) { + objArr[i] = Tcl_NewStringObj(data.preCmd[i], -1); + Tcl_IncrRefCount(objArr[i]); + } + data.preObjv = objArr; + data.preObjc = count + 1; + order |= TREE_PREORDER; + } + if (data.postCmd != NULL) { + char **p; + + count = 0; + for (p = data.postCmd; *p != NULL; p++) { + count++; + } + objArr = Blt_Malloc((count + 1) * sizeof(Tcl_Obj *)); + for (i = 0; i < count; i++) { + objArr[i] = Tcl_NewStringObj(data.postCmd[i], -1); + Tcl_IncrRefCount(objArr[i]); + } + data.postObjv = objArr; + data.postObjc = count + 1; + order |= TREE_POSTORDER; + } + result = Blt_TreeApplyDFS(node, ApplyNodeProc, &data, order); + if (data.preObjv != NULL) { + for (i = 0; i < (data.preObjc - 1); i++) { + Tcl_DecrRefCount(data.preObjv[i]); + } + Blt_Free(data.preObjv); + } + if (data.postObjv != NULL) { + for (i = 0; i < (data.postObjc - 1); i++) { + Tcl_DecrRefCount(data.postObjv[i]); + } + Blt_Free(data.postObjv); + } + Blt_FreeSwitches(applySwitches, (char *)&data, 0); + if (result == TCL_ERROR) { + return TCL_ERROR; + } + return TCL_OK; +} + + +/*ARGSUSED*/ +static int +AncestorOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + int d1, d2, minDepth; + register int i; + Blt_TreeNode ancestor, node1, node2; + + if ((GetNode(cmdPtr, objv[2], &node1) != TCL_OK) || + (GetNode(cmdPtr, objv[3], &node2) != TCL_OK)) { + return TCL_ERROR; + } + if (node1 == node2) { + ancestor = node1; + goto done; + } + d1 = Blt_TreeNodeDepth(cmdPtr->tree, node1); + d2 = Blt_TreeNodeDepth(cmdPtr->tree, node2); + minDepth = MIN(d1, d2); + if (minDepth == 0) { /* One of the nodes is root. */ + ancestor = Blt_TreeRootNode(cmdPtr->tree); + goto done; + } + /* + * Traverse back from the deepest node, until the both nodes are + * at the same depth. Check if the ancestor node found is the + * other node. + */ + for (i = d1; i > minDepth; i--) { + node1 = Blt_TreeNodeParent(node1); + } + if (node1 == node2) { + ancestor = node2; + goto done; + } + for (i = d2; i > minDepth; i--) { + node2 = Blt_TreeNodeParent(node2); + } + if (node2 == node1) { + ancestor = node1; + goto done; + } + + /* + * First find the mutual ancestor of both nodes. Look at each + * preceding ancestor level-by-level for both nodes. Eventually + * we'll find a node that's the parent of both ancestors. Then + * find the first ancestor in the parent's list of subnodes. + */ + for (i = minDepth; i > 0; i--) { + node1 = Blt_TreeNodeParent(node1); + node2 = Blt_TreeNodeParent(node2); + if (node1 == node2) { + ancestor = node2; + goto done; + } + } + Tcl_AppendResult(interp, "unknown ancestor", (char *)NULL); + return TCL_ERROR; + done: + Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeNodeId(ancestor)); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * AttachOp -- + * + *---------------------------------------------------------------------- + */ +static int +AttachOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + if (objc == 3) { + char *treeName; + char *name; + Blt_Tree token; + Tcl_Namespace *nsPtr; + Tcl_DString dString; + int result; + + treeName = Tcl_GetString(objv[2]); + if (Blt_ParseQualifiedName(interp, treeName, &nsPtr, &name) + != TCL_OK) { + Tcl_AppendResult(interp, "can't find namespace in \"", treeName, + "\"", (char *)NULL); + return TCL_ERROR; + } + if (nsPtr == NULL) { + nsPtr = Tcl_GetCurrentNamespace(interp); + } + treeName = Blt_GetQualifiedName(nsPtr, name, &dString); + result = Blt_TreeGetToken(interp, treeName, &token); + Tcl_DStringFree(&dString); + if (result != TCL_OK) { + return TCL_ERROR; + } + ReleaseTreeObject(cmdPtr); + cmdPtr->tree = token; + cmdPtr->tagTablePtr = Blt_TreeNewTagTable(); + } + Tcl_SetObjResult(interp, Tcl_NewStringObj(Blt_TreeName(cmdPtr->tree), -1)); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * ChildrenOp -- + * + *---------------------------------------------------------------------- + */ +static int +ChildrenOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + if (objc == 3) { + Tcl_Obj *objPtr, *listObjPtr; + + listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + for (node = Blt_TreeFirstChild(node); node != NULL; + node = Blt_TreeNextSibling(node)) { + objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node)); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + Tcl_SetObjResult(interp, listObjPtr); + } else if (objc == 4) { + int childPos; + int inode, count; + + /* Get the node at */ + if (Tcl_GetIntFromObj(interp, objv[3], &childPos) != TCL_OK) { + return TCL_ERROR; + } + count = 0; + inode = -1; + for (node = Blt_TreeFirstChild(node); node != NULL; + node = Blt_TreeNextSibling(node)) { + if (count == childPos) { + inode = Blt_TreeNodeId(node); + break; + } + count++; + } + Tcl_SetIntObj(Tcl_GetObjResult(interp), inode); + return TCL_OK; + } else if (objc == 5) { + int firstPos, lastPos, count; + Tcl_Obj *objPtr, *listObjPtr; + char *string; + + firstPos = lastPos = Blt_TreeNodeDegree(node) - 1; + string = Tcl_GetString(objv[3]); + if ((strcmp(string, "end") != 0) && + (Tcl_GetIntFromObj(interp, objv[3], &firstPos) != TCL_OK)) { + return TCL_ERROR; + } + string = Tcl_GetString(objv[4]); + if ((strcmp(string, "end") != 0) && + (Tcl_GetIntFromObj(interp, objv[4], &lastPos) != TCL_OK)) { + return TCL_ERROR; + } + + count = 0; + listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + for (node = Blt_TreeFirstChild(node); node != NULL; + node = Blt_TreeNextSibling(node)) { + if ((count >= firstPos) && (count <= lastPos)) { + objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node)); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + count++; + } + Tcl_SetObjResult(interp, listObjPtr); + } + return TCL_OK; +} + + +static Blt_TreeNode +CopyNodes(dataPtr, node, parent) + CopyData *dataPtr; + Blt_TreeNode node; /* Node to be copied. */ + Blt_TreeNode parent; /* New parent for the copied node. */ +{ + Blt_TreeNode newNode; /* Newly created copy. */ + char *label; + + newNode = NULL; + label = Blt_TreeNodeLabel(node); + if (dataPtr->flags & COPY_OVERWRITE) { + newNode = Blt_TreeFindChild(parent, label); + } + if (newNode == NULL) { /* Create node in new parent. */ + newNode = Blt_TreeCreateNode(dataPtr->destTree, parent, label, -1); + } + /* Copy the data fields. */ + { + Blt_TreeKey key; + Tcl_Obj *objPtr; + Blt_TreeKeySearch keyIter; + + for (key = Blt_TreeFirstKey(dataPtr->srcTree, node, &keyIter); + key != NULL; key = Blt_TreeNextKey(dataPtr->srcTree, &keyIter)) { + if (Blt_TreeGetValueByKey((Tcl_Interp *)NULL, dataPtr->srcTree, + node, key, &objPtr) == TCL_OK) { + Blt_TreeSetValueByKey((Tcl_Interp *)NULL, dataPtr->destTree, + newNode, key, objPtr); + } + } + } + /* Add tags to destination tree command. */ + if ((dataPtr->destPtr != NULL) && (dataPtr->flags & COPY_TAGS)) { + Blt_TreeTag *tagPtr; + Blt_HashEntry *hPtr, *h2Ptr; + Blt_HashSearch hashIter; + + for (hPtr = + Blt_FirstHashEntry(&dataPtr->srcPtr->tagTablePtr->table, &hashIter); + hPtr != NULL; hPtr = Blt_NextHashEntry(&hashIter)) { + tagPtr = Blt_GetHashValue(hPtr); + h2Ptr = Blt_FindHashEntry(&(tagPtr->nodeTable), (char *)node); + if (h2Ptr != NULL) { + AddTag(dataPtr->destPtr, newNode, tagPtr->tagName); + } + } + } + if (dataPtr->flags & COPY_RECURSE) { + Blt_TreeNode child; + + for (child = Blt_TreeFirstChild(node); child != NULL; + child = Blt_TreeNextSibling(child)) { + if (CopyNodes(dataPtr, child, newNode) == NULL) { + return NULL; + } + } + } + return newNode; +} + +/* + *---------------------------------------------------------------------- + * + * CopyOp -- + * + * t0 copy node tree node + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +CopyOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + TreeCmd *srcPtr, *destPtr; + Blt_Tree srcTree, destTree; + Blt_TreeNode srcNode, destNode; + CopyData data; + int nArgs, nSwitches; + char *string; + Blt_TreeNode root; + register int i; + + if (GetNode(cmdPtr, objv[2], &srcNode) != TCL_OK) { + return TCL_ERROR; + } + srcTree = cmdPtr->tree; + srcPtr = cmdPtr; + + /* Find the first switch. */ + for(i = 3; i < objc; i++) { + string = Tcl_GetString(objv[i]); + if (string[0] == '-') { + break; + } + } + nArgs = i - 2; + nSwitches = objc - i; + if (nArgs < 2) { + string = Tcl_GetString(objv[0]); + Tcl_AppendResult(interp, "must specify source and destination nodes: ", + "should be \"", string, + " copy srcNode ?destTree? destNode ?switches?", + (char *)NULL); + return TCL_ERROR; + + } + if (nArgs == 3) { + /* + * The tree name is either the name of a tree command (first choice) + * or an internal tree object. + */ + string = Tcl_GetString(objv[3]); + destPtr = GetTreeCmd(cmdPtr->dataPtr, interp, string); + if (destPtr != NULL) { + destTree = destPtr->tree; + } else { + /* Try to get the tree as an internal tree data object. */ + if (Blt_TreeGetToken(interp, string, &destTree) != TCL_OK) { + return TCL_ERROR; + } + } + objv++, objc--; + } else { + destPtr = cmdPtr; + destTree = destPtr->tree; + } + + root = NULL; + if (destPtr == NULL) { + if (GetForeignNode(interp, destTree, objv[3], &destNode) != TCL_OK) { + goto error; + } + } else { + if (GetNode(destPtr, objv[3], &destNode) != TCL_OK) { + goto error; + } + } + if (srcNode == destNode) { + Tcl_AppendResult(interp, "source and destination nodes are the same", + (char *)NULL); + goto error; + } + memset((char *)&data, 0, sizeof(data)); + /* Process switches */ + if (Blt_ProcessObjSwitches(interp, copySwitches, nSwitches, objv + 4, + (char *)&data, 0) < 0) { + goto error; + } + data.destPtr = destPtr; + data.destTree = destTree; + data.srcPtr = srcPtr; + data.srcTree = srcTree; + + if ((srcTree == destTree) && (data.flags & COPY_RECURSE) && + (Blt_TreeIsAncestor(srcNode, destNode))) { + Tcl_AppendResult(interp, "can't make cyclic copy: ", + "source node is an ancestor of the destination", + (char *)NULL); + goto error; + } + + /* Copy nodes to destination. */ + root = CopyNodes(&data, srcNode, destNode, data.label); + if (root != NULL) { + Tcl_Obj *objPtr; + + objPtr = Tcl_NewIntObj(Blt_TreeNodeId(root)); + if (data.label != NULL) { + Blt_TreeRelabelNode(data.destTree, root, data.label); + } + Tcl_SetObjResult(interp, objPtr); + } + error: + if (destPtr == NULL) { + Blt_TreeReleaseToken(destTree); + } + return (root == NULL) ? TCL_ERROR : TCL_OK; + +} + +/* + *---------------------------------------------------------------------- + * + * DepthOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +DegreeOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + int degree; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + degree = Blt_TreeNodeDegree(node); + Tcl_SetIntObj(Tcl_GetObjResult(interp), degree); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * DeleteOp -- + * + * Deletes one or more nodes from the tree. Nodes may be + * specified by their id (a number) or a tag. + * + * Tags have to be handled carefully here. We can't use the + * normal GetTaggedNode, NextTaggedNode, etc. routines because + * they walk hashtables while we're deleting nodes. Also, + * remember that deleting a node recursively deletes all its + * children. If a parent and its children have the same tag, its + * possible that the tag list may contain nodes than no longer + * exist. So save the node indices in a list and then delete + * then in a second pass. + * + *---------------------------------------------------------------------- + */ +static int +DeleteOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + int i; + char *string; + + for (i = 2; i < objc; i++) { + string = Tcl_GetString(objv[i]); + if (isdigit(UCHAR(string[0]))) { + if (GetNode(cmdPtr, objv[i], &node) != TCL_OK) { + return TCL_ERROR; + } + DeleteNode(cmdPtr, node); + } else { + Blt_HashEntry *hPtr; + Blt_HashTable *tablePtr; + Blt_HashSearch cursor; + Blt_Chain *chainPtr; + Blt_ChainLink *linkPtr, *nextPtr; + int inode; + + if ((strcmp(string, "all") == 0) || + (strcmp(string, "root") == 0)) { + node = Blt_TreeRootNode(cmdPtr->tree); + DeleteNode(cmdPtr, node); + continue; + } + tablePtr = Blt_TreeTagHashTable(cmdPtr->tagTablePtr, string); + if (tablePtr == NULL) { + goto error; + } + /* + * Generate a list of tagged nodes. Save the inode instead + * of the node itself since a pruned branch may contain + * more tagged nodes. + */ + chainPtr = Blt_ChainCreate(); + for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); + hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { + node = Blt_GetHashValue(hPtr); + Blt_ChainAppend(chainPtr, (ClientData)Blt_TreeNodeId(node)); + } + /* + * Iterate through this list to delete the nodes. By + * side-effect the tag table is deleted and Uids are + * released. + */ + for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL; + linkPtr = nextPtr) { + nextPtr = Blt_ChainNextLink(linkPtr); + inode = (int)Blt_ChainGetValue(linkPtr); + node = Blt_TreeGetNode(cmdPtr->tree, inode); + if (node != NULL) { + DeleteNode(cmdPtr, node); + } + } + Blt_ChainDestroy(chainPtr); + } + } + return TCL_OK; + error: + Tcl_AppendResult(interp, "can't find tag or id \"", string, "\" in ", + Blt_TreeName(cmdPtr->tree), (char *)NULL); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * DepthOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +DepthOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + int depth; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + depth = Blt_TreeNodeDepth(cmdPtr->tree, node); + Tcl_SetIntObj(Tcl_GetObjResult(interp), depth); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * DumpOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +DumpOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode top; + Tcl_DString dString; + register Blt_TreeNode node; + + if (GetNode(cmdPtr, objv[2], &top) != TCL_OK) { + return TCL_ERROR; + } + Tcl_DStringInit(&dString); + for (node = top; node != NULL; node = Blt_TreeNextNode(top, node)) { + PrintNode(cmdPtr, top, node, &dString); + } + Tcl_DStringResult(interp, &dString); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * DumpfileOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +DumpfileOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode top; + Tcl_Channel channel; + Tcl_DString dString; + char *fileName; + int result; + register Blt_TreeNode node; + + if (GetNode(cmdPtr, objv[2], &top) != TCL_OK) { + return TCL_ERROR; + } + fileName = Tcl_GetString(objv[3]); + channel = Tcl_OpenFileChannel(interp, fileName, "w", 0666); + if (channel == NULL) { + return TCL_ERROR; + } + Tcl_DStringInit(&dString); + for (node = top; node != NULL; node = Blt_TreeNextNode(top, node)) { + PrintNode(cmdPtr, top, node, &dString); + } + result = Tcl_Write(channel, Tcl_DStringValue(&dString), -1); + Tcl_Close(interp, channel); + Tcl_DStringFree(&dString); + if (result <= 0) { + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * ExistsOp -- + * + *---------------------------------------------------------------------- + */ +static int +ExistsOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + int bool; + + bool = TRUE; + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + bool = FALSE; + } else if (objc == 4) { + Tcl_Obj *valueObjPtr; + char *string; + + string = Tcl_GetString(objv[3]); + if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, node, + string, &valueObjPtr) != TCL_OK) { + bool = FALSE; + } + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(bool)); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * FindOp -- + * + *---------------------------------------------------------------------- + */ +static int +FindOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + FindData data; + int count, result; + register int i; + Tcl_Obj **objArr; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + memset(&data, 0, sizeof(data)); + data.maxDepth = -1; + data.order = TREE_POSTORDER; + data.flags = 0; + objArr = NULL; + /* Process switches */ + if (Blt_ProcessObjSwitches(interp, findSwitches, objc - 3, objv + 3, + (char *)&data, 0) < 0) { + return TCL_ERROR; + } + count = 0; + if (Blt_SwitchChanged(findSwitches, "-glob", (char *)NULL)) { + count++; + data.flags &= ~PATTERN_MASK; + data.flags |= PATTERN_GLOB; + } + if (Blt_SwitchChanged(findSwitches, "-regexp", (char *)NULL)) { + count++; + data.flags &= ~PATTERN_MASK; + data.flags |= PATTERN_REGEXP; + } + if (Blt_SwitchChanged(findSwitches, "-exact", (char *)NULL)) { + count++; + data.flags &= ~PATTERN_MASK; + data.flags |= PATTERN_EXACT; + } + if ((data.flags & MATCH_NOCASE) && (data.pattern != NULL)) { + strtolower(data.pattern); + } + if (data.maxDepth >= 0) { + data.maxDepth += Blt_TreeNodeDepth(cmdPtr->tree, node); + } + if (count > 1) { + /* Two many patterns supplied. */ + } + if (data.command != NULL) { + char **p; + + count = 0; + for (p = data.command; *p != NULL; p++) { + count++; + } + objArr = Blt_Malloc((count + 1) * sizeof(Tcl_Obj *)); + for (i = 0; i < count; i++) { + objArr[i] = Tcl_NewStringObj(data.command[i], -1); + Tcl_IncrRefCount(objArr[i]); + } + data.objv = objArr; + data.objc = count + 1; + } + data.listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + data.cmdPtr = cmdPtr; + if (data.order == TREE_BREADTHFIRST) { + result = Blt_TreeApplyBFS(node, MatchNodeProc, &data); + } else { + result = Blt_TreeApplyDFS(node, MatchNodeProc, &data, data.order); + } + if (data.command != NULL) { + for (i = 0; i < count; i++) { + Tcl_DecrRefCount(objArr[i]); + } + Blt_Free(objArr); + } + Blt_FreeSwitches(findSwitches, (char *)&data, 0); + if (result == TCL_ERROR) { + return TCL_ERROR; + } + Tcl_SetObjResult(interp, data.listObjPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * FindChildOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +FindChildOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node, child; + int inode; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + inode = -1; + child = Blt_TreeFindChild(node, Tcl_GetString(objv[3])); + if (child != NULL) { + inode = Blt_TreeNodeId(child); + } + Tcl_SetIntObj(Tcl_GetObjResult(interp), inode); + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * FirstChildOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +FirstChildOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + int inode; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + inode = -1; + node = Blt_TreeFirstChild(node); + if (node != NULL) { + inode = Blt_TreeNodeId(node); + } + Tcl_SetIntObj(Tcl_GetObjResult(interp), inode); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * GetOp -- + * + *---------------------------------------------------------------------- + */ +static int +GetOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + if (objc == 3) { + Blt_TreeKey *key; + Tcl_Obj *valueObjPtr, *listObjPtr; + Blt_TreeKey *keys; + + keys = GetKeys(cmdPtr->tree, node); + + /* Add the key-value pairs to a new Tcl_Obj */ + listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + for (key = keys; *key != NULL; key++) { + if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, node, *key, + &valueObjPtr) == TCL_OK) { + Tcl_Obj *objPtr; + + objPtr = Tcl_NewStringObj(*key, -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + Tcl_ListObjAppendElement(interp, listObjPtr, valueObjPtr); + } + } + Tcl_SetObjResult(interp, listObjPtr); + Blt_Free(keys); + return TCL_OK; + } else { + Tcl_Obj *valueObjPtr; + char *string; + + string = Tcl_GetString(objv[3]); + if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, node, string, + &valueObjPtr) != TCL_OK) { + if (objc == 4) { + Tcl_DString dString; + char *path; + + path = GetNodePath(cmdPtr, Blt_TreeRootNode(cmdPtr->tree), + node, FALSE, &dString); + Tcl_AppendResult(interp, "can't find field \"", string, + "\" in \"", path, (char *)NULL); + Tcl_DStringFree(&dString); + return TCL_ERROR; + } + /* Default to given value */ + valueObjPtr = objv[4]; + } + Tcl_SetObjResult(interp, valueObjPtr); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * IndexOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +IndexOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + int inode; + + inode = -1; + if (GetNode(cmdPtr, objv[2], &node) == TCL_OK) { + inode = Blt_TreeNodeId(node); + } else { + register int i; + int nObjs; + Tcl_Obj **objArr; + Blt_TreeNode parent; + char *string; + + if (Tcl_ListObjGetElements(interp, objv[2], &nObjs, &objArr) + != TCL_OK) { + goto done; /* Can't split object. */ + } + parent = Blt_TreeRootNode(cmdPtr->tree); + for (i = 0; i < nObjs; i++) { + string = Tcl_GetString(objArr[i]); + if (string[0] == '\0') { + continue; + } + node = Blt_TreeFindChild(parent, string); + if (node == NULL) { + goto done; /* Can't find component */ + } + parent = node; + } + inode = Blt_TreeNodeId(node); + } + done: + Tcl_SetIntObj(Tcl_GetObjResult(interp), inode); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * InsertOp -- + * + *---------------------------------------------------------------------- + */ + +static int +InsertOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode parent, child; + InsertData data; + + child = NULL; + if (GetNode(cmdPtr, objv[2], &parent) != TCL_OK) { + return TCL_ERROR; + } + /* Initialize switch flags */ + memset(&data, 0, sizeof(data)); + data.insertPos = -1; /* Default to append node. */ + data.parent = parent; + data.inode = -1; + + if (Blt_ProcessObjSwitches(interp, insertSwitches, objc - 3, objv + 3, + (char *)&data, 0) < 0) { + goto error; + } + if (data.inode > 0) { + Blt_TreeNode node; + + node = Blt_TreeGetNode(cmdPtr->tree, data.inode); + if (node != NULL) { + Tcl_AppendResult(interp, "can't reissue node id \"", + Blt_Itoa(data.inode), "\": already exists.", (char *)NULL); + goto error; + } + child = Blt_TreeCreateNodeWithId(cmdPtr->tree, parent, data.label, + data.inode, data.insertPos); + } else { + child = Blt_TreeCreateNode(cmdPtr->tree, parent, data.label, + data.insertPos); + } + if (child == NULL) { + Tcl_AppendResult(interp, "can't allocate new node", (char *)NULL); + goto error; + } + if (data.tags != NULL) { + register char **p; + + for (p = data.tags; *p != NULL; p++) { + if (AddTag(cmdPtr, child, *p) != TCL_OK) { + goto error; + } + } + } + if (data.dataPairs != NULL) { + register char **p; + char *key; + Tcl_Obj *objPtr; + + for (p = data.dataPairs; *p != NULL; p++) { + key = *p; + p++; + if (*p == NULL) { + Tcl_AppendResult(interp, "missing value for \"", key, "\"", + (char *)NULL); + goto error; + } + objPtr = Tcl_NewStringObj(*p, -1); + if (Blt_TreeSetValue(interp, cmdPtr->tree, child, key, objPtr) + != TCL_OK) { + goto error; + } + } + } + Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeNodeId(child)); + Blt_FreeSwitches(insertSwitches, (char *)&data, 0); + return TCL_OK; + + error: + if (child != NULL) { + Blt_TreeDeleteNode(cmdPtr->tree, child); + } + Blt_FreeSwitches(insertSwitches, (char *)&data, 0); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * IsAncestorOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +IsAncestorOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node1, node2; + int bool; + + if ((GetNode(cmdPtr, objv[3], &node1) != TCL_OK) || + (GetNode(cmdPtr, objv[4], &node2) != TCL_OK)) { + return TCL_ERROR; + } + bool = Blt_TreeIsAncestor(node1, node2); + Tcl_SetIntObj(Tcl_GetObjResult(interp), bool); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * IsBeforeOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +IsBeforeOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node1, node2; + int bool; + + if ((GetNode(cmdPtr, objv[3], &node1) != TCL_OK) || + (GetNode(cmdPtr, objv[4], &node2) != TCL_OK)) { + return TCL_ERROR; + } + bool = Blt_TreeIsBefore(node1, node2); + Tcl_SetIntObj(Tcl_GetObjResult(interp), bool); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * IsLeafOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +IsLeafOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + + if (GetNode(cmdPtr, objv[3], &node) != TCL_OK) { + return TCL_ERROR; + } + Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeIsLeaf(node)); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * IsRootOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +IsRootOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + int bool; + + if (GetNode(cmdPtr, objv[3], &node) != TCL_OK) { + return TCL_ERROR; + } + bool = (node == Blt_TreeRootNode(cmdPtr->tree)); + Tcl_SetIntObj(Tcl_GetObjResult(interp), bool); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * IsOp -- + * + *---------------------------------------------------------------------- + */ +static Blt_OpSpec isOps[] = +{ + {"ancestor", 1, (Blt_Op)IsAncestorOp, 5, 5, "node1 node2",}, + {"before", 1, (Blt_Op)IsBeforeOp, 5, 5, "node1 node2",}, + {"leaf", 1, (Blt_Op)IsLeafOp, 4, 4, "node",}, + {"root", 1, (Blt_Op)IsRootOp, 4, 4, "node",}, +}; + +static int nIsOps = sizeof(isOps) / sizeof(Blt_OpSpec); + +static int +IsOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_Op proc; + int result; + + proc = Blt_GetOpFromObj(interp, nIsOps, isOps, BLT_OP_ARG2, objc, objv, 0); + if (proc == NULL) { + return TCL_ERROR; + } + result = (*proc) (cmdPtr, interp, objc, objv); + return result; +} + + +/* + *---------------------------------------------------------------------- + * + * KeysOp -- + * + * Returns the key names of values for a node or array value. + * + *---------------------------------------------------------------------- + */ +static int +KeysOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_HashEntry *hPtr; + Blt_HashSearch hashIter; + Blt_HashTable keyTable; + Blt_TreeKey key; + Blt_TreeKeySearch keyIter; + Blt_TreeNode node; + TagSearch tagIter; + Tcl_Obj *listObjPtr, *objPtr; + register int i; + int isNew; + + Blt_InitHashTableWithPool(&keyTable, BLT_ONE_WORD_KEYS); + for (i = 2; i < objc; i++) { + node = FirstTaggedNode(interp, cmdPtr, objv[i], &tagIter); + if (node == NULL) { + return TCL_ERROR; + } + for (/* empty */; node != NULL; + node = NextTaggedNode(node, &tagIter)) { + for (key = Blt_TreeFirstKey(cmdPtr->tree, node, &keyIter); + key != NULL; key = Blt_TreeNextKey(cmdPtr->tree, &keyIter)) { + Blt_CreateHashEntry(&keyTable, key, &isNew); + } + } + } + listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + for (hPtr = Blt_FirstHashEntry(&keyTable, &hashIter); hPtr != NULL; + hPtr = Blt_NextHashEntry(&hashIter)) { + objPtr = Tcl_NewStringObj(Blt_GetHashKey(&keyTable, hPtr), -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + Tcl_SetObjResult(interp, listObjPtr); + Blt_DeleteHashTable(&keyTable); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * LabelOp -- + * + *---------------------------------------------------------------------- + */ +static int +LabelOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + if (objc == 4) { + Blt_TreeRelabelNode(cmdPtr->tree, node, Tcl_GetString(objv[3])); + } + Tcl_SetStringObj(Tcl_GetObjResult(interp), Blt_TreeNodeLabel(node), -1); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * LastChildOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +LastChildOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + int inode; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + inode = -1; + node = Blt_TreeLastChild(node); + if (node != NULL) { + inode = Blt_TreeNodeId(node); + } + Tcl_SetIntObj(Tcl_GetObjResult(interp), inode); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * MoveOp -- + * + * The big trick here is to not consider the node to be moved in + * determining it's new location. Ideally, you would temporarily + * pull from the tree and replace it (back in its old location if + * something went wrong), but you could still pick the node by + * its serial number. So here we make lots of checks for the + * node to be moved. + * + * + *---------------------------------------------------------------------- + */ +static int +MoveOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode root, parent, node; + Blt_TreeNode before; + MoveData data; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + if (GetNode(cmdPtr, objv[3], &parent) != TCL_OK) { + return TCL_ERROR; + } + root = Blt_TreeRootNode(cmdPtr->tree); + if (node == root) { + Tcl_AppendResult(interp, "can't move root node", (char *)NULL); + return TCL_ERROR; + } + if (parent == node) { + Tcl_AppendResult(interp, "can't move node to self", (char *)NULL); + return TCL_ERROR; + } + data.node = NULL; + data.cmdPtr = cmdPtr; + data.insertPos = -1; + /* Process switches */ + if (Blt_ProcessObjSwitches(interp, moveSwitches, objc - 4, objv + 4, + (char *)&data, 0) < 0) { + return TCL_ERROR; + } + /* Verify they aren't ancestors. */ + if (Blt_TreeIsAncestor(node, parent)) { + Tcl_AppendResult(interp, "can't move node: \"", + Tcl_GetString(objv[2]), (char *)NULL); + Tcl_AppendResult(interp, "\" is an ancestor of \"", + Tcl_GetString(objv[3]), "\"", (char *)NULL); + return TCL_ERROR; + } + before = NULL; /* If before is NULL, this appends the + * node to the parent's child list. */ + + if (data.node != NULL) { /* -before or -after */ + if (Blt_TreeNodeParent(data.node) != parent) { + Tcl_AppendResult(interp, Tcl_GetString(objv[2]), + " isn't the parent of ", Blt_TreeNodeLabel(data.node), + (char *)NULL); + return TCL_ERROR; + } + if (Blt_SwitchChanged(moveSwitches, "-before", (char *)NULL)) { + before = data.node; + if (before == node) { + Tcl_AppendResult(interp, "can't move node before itself", + (char *)NULL); + return TCL_ERROR; + } + } else { + before = Blt_TreeNextSibling(data.node); + if (before == node) { + Tcl_AppendResult(interp, "can't move node after itself", + (char *)NULL); + return TCL_ERROR; + } + } + } else if (data.insertPos >= 0) { /* -at */ + int count; /* Tracks the current list index. */ + Blt_TreeNode child; + + /* + * If the node is in the list, ignore it when determining the + * "before" node using the -at index. An index of -1 means to + * append the node to the list. + */ + count = 0; + for(child = Blt_TreeFirstChild(parent); child != NULL; + child = Blt_TreeNextSibling(child)) { + if (child == node) { + continue; /* Ignore the node to be moved. */ + } + if (count == data.insertPos) { + before = child; + break; + } + count++; + } + } + if (Blt_TreeMoveNode(cmdPtr->tree, node, parent, before) != TCL_OK) { + Tcl_AppendResult(interp, "can't move node ", Tcl_GetString(objv[2]), + " to ", Tcl_GetString(objv[3]), (char *)NULL); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NextOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +NextOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + int inode; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + inode = -1; + node = Blt_TreeNextNode(Blt_TreeRootNode(cmdPtr->tree), node); + if (node != NULL) { + inode = Blt_TreeNodeId(node); + } + Tcl_SetIntObj(Tcl_GetObjResult(interp), inode); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NextSiblingOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +NextSiblingOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + int inode; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + inode = -1; + node = Blt_TreeNextSibling(node); + if (node != NULL) { + inode = Blt_TreeNodeId(node); + } + Tcl_SetIntObj(Tcl_GetObjResult(interp), inode); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NotifyCreateOp -- + * + * tree0 notify create ?flags? command arg + *---------------------------------------------------------------------- + */ +static int +NotifyCreateOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + NotifyInfo *notifyPtr; + NotifyData data; + char *string; + char idString[200]; + int isNew, nArgs; + Blt_HashEntry *hPtr; + int count; + register int i; + + count = 0; + for (i = 3; i < objc; i++) { + string = Tcl_GetString(objv[i]); + if (string[0] != '-') { + break; + } + count++; + } + data.mask = 0; + /* Process switches */ + if (Blt_ProcessObjSwitches(interp, notifySwitches, count, objv + 3, + (char *)&data, 0) < 0) { + return TCL_ERROR; + } + notifyPtr = Blt_Malloc(sizeof(NotifyInfo)); + + nArgs = objc - i; + + /* Stash away the command in structure and pass that to the notifier. */ + notifyPtr->objv = Blt_Malloc((nArgs + 2) * sizeof(Tcl_Obj *)); + for (count = 0; i < objc; i++, count++) { + Tcl_IncrRefCount(objv[i]); + notifyPtr->objv[count] = objv[i]; + } + notifyPtr->objc = nArgs + 2; + notifyPtr->cmdPtr = cmdPtr; + if (data.mask == 0) { + data.mask = TREE_NOTIFY_ALL; + } + notifyPtr->mask = data.mask; + + sprintf(idString, "notify%d", cmdPtr->notifyCounter++); + hPtr = Blt_CreateHashEntry(&(cmdPtr->notifyTable), idString, &isNew); + Blt_SetHashValue(hPtr, notifyPtr); + + Tcl_SetStringObj(Tcl_GetObjResult(interp), idString, -1); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NotifyDeleteOp -- + * + *---------------------------------------------------------------------- + */ +static int +NotifyDeleteOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + NotifyInfo *notifyPtr; + Blt_HashEntry *hPtr; + register int i, j; + char *string; + + for (i = 3; i < objc; i++) { + string = Tcl_GetString(objv[i]); + hPtr = Blt_FindHashEntry(&(cmdPtr->notifyTable), string); + if (hPtr == NULL) { + Tcl_AppendResult(interp, "unknown notify name \"", string, "\"", + (char *)NULL); + return TCL_ERROR; + } + notifyPtr = Blt_GetHashValue(hPtr); + Blt_DeleteHashEntry(&(cmdPtr->notifyTable), hPtr); + for (j = 0; j < (notifyPtr->objc - 2); j++) { + Tcl_DecrRefCount(notifyPtr->objv[j]); + } + Blt_Free(notifyPtr->objv); + Blt_Free(notifyPtr); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NotifyInfoOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +NotifyInfoOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + NotifyInfo *notifyPtr; + Blt_HashEntry *hPtr; + Tcl_DString dString; + char *string; + int i; + + string = Tcl_GetString(objv[3]); + hPtr = Blt_FindHashEntry(&(cmdPtr->notifyTable), string); + if (hPtr == NULL) { + Tcl_AppendResult(interp, "unknown notify name \"", string, "\"", + (char *)NULL); + return TCL_ERROR; + } + notifyPtr = Blt_GetHashValue(hPtr); + + Tcl_DStringInit(&dString); + Tcl_DStringAppendElement(&dString, string); /* Copy notify Id */ + Tcl_DStringStartSublist(&dString); + if (notifyPtr->mask & TREE_NOTIFY_CREATE) { + Tcl_DStringAppendElement(&dString, "-create"); + } + if (notifyPtr->mask & TREE_NOTIFY_DELETE) { + Tcl_DStringAppendElement(&dString, "-delete"); + } + if (notifyPtr->mask & TREE_NOTIFY_MOVE) { + Tcl_DStringAppendElement(&dString, "-move"); + } + if (notifyPtr->mask & TREE_NOTIFY_SORT) { + Tcl_DStringAppendElement(&dString, "-sort"); + } + if (notifyPtr->mask & TREE_NOTIFY_RELABEL) { + Tcl_DStringAppendElement(&dString, "-relabel"); + } + if (notifyPtr->mask & TREE_NOTIFY_WHENIDLE) { + Tcl_DStringAppendElement(&dString, "-whenidle"); + } + Tcl_DStringEndSublist(&dString); + Tcl_DStringStartSublist(&dString); + for (i = 0; i < (notifyPtr->objc - 2); i++) { + Tcl_DStringAppendElement(&dString, Tcl_GetString(notifyPtr->objv[i])); + } + Tcl_DStringEndSublist(&dString); + Tcl_DStringResult(interp, &dString); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NotifyNamesOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +NotifyNamesOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_HashEntry *hPtr; + Blt_HashSearch cursor; + Tcl_Obj *objPtr, *listObjPtr; + char *notifyId; + + listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + for (hPtr = Blt_FirstHashEntry(&(cmdPtr->notifyTable), &cursor); + hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { + notifyId = Blt_GetHashKey(&(cmdPtr->notifyTable), hPtr); + objPtr = Tcl_NewStringObj(notifyId, -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NotifyOp -- + * + *---------------------------------------------------------------------- + */ +static Blt_OpSpec notifyOps[] = +{ + {"create", 1, (Blt_Op)NotifyCreateOp, 4, 0, "?flags? command",}, + {"delete", 1, (Blt_Op)NotifyDeleteOp, 3, 0, "notifyId...",}, + {"info", 1, (Blt_Op)NotifyInfoOp, 4, 4, "notifyId",}, + {"names", 1, (Blt_Op)NotifyNamesOp, 3, 3, "",}, +}; + +static int nNotifyOps = sizeof(notifyOps) / sizeof(Blt_OpSpec); + +static int +NotifyOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_Op proc; + int result; + + proc = Blt_GetOpFromObj(interp, nNotifyOps, notifyOps, BLT_OP_ARG2, objc, + objv, 0); + if (proc == NULL) { + return TCL_ERROR; + } + result = (*proc) (cmdPtr, interp, objc, objv); + return result; +} + + +/*ARGSUSED*/ +static int +ParentOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + int inode; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + inode = -1; + node = Blt_TreeNodeParent(node); + if (node != NULL) { + inode = Blt_TreeNodeId(node); + } + Tcl_SetIntObj(Tcl_GetObjResult(interp), inode); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * PathOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +PathOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + Tcl_DString dString; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + GetNodePath(cmdPtr, Blt_TreeRootNode(cmdPtr->tree), node, FALSE, &dString); + Tcl_DStringResult(interp, &dString); + return TCL_OK; +} + + +static int +ComparePositions(n1Ptr, n2Ptr) + Blt_TreeNode *n1Ptr, *n2Ptr; +{ + if (*n1Ptr == *n2Ptr) { + return 0; + } + if (Blt_TreeIsBefore(*n1Ptr, *n2Ptr)) { + return -1; + } + return 1; +} + +/* + *---------------------------------------------------------------------- + * + * PositionOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +PositionOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + PositionData data; + Blt_TreeNode *nodeArr, *nodePtr; + Blt_TreeNode node; + Blt_TreeNode parent, lastParent; + Tcl_Obj *listObjPtr, *objPtr; + int i; + int position; + Tcl_DString dString; + int n; + + memset((char *)&data, 0, sizeof(data)); + /* Process switches */ + n = Blt_ProcessObjSwitches(interp, positionSwitches, objc - 2, objv + 2, + (char *)&data, BLT_SWITCH_OBJV_PARTIAL); + if (n < 0) { + return TCL_ERROR; + } + objc -= n + 2, objv += n + 2; + + /* Collect the node ids into an array */ + nodeArr = Blt_Malloc((objc + 1) * sizeof(Blt_TreeNode)); + for (i = 0; i < objc; i++) { + if (GetNode(cmdPtr, objv[i], &node) != TCL_OK) { + Blt_Free(nodeArr); + return TCL_ERROR; + } + nodeArr[i] = node; + } + nodeArr[i] = NULL; + + if (data.sort) { /* Sort the nodes by depth-first order + * if requested. */ + qsort((char *)nodeArr, objc, sizeof(Blt_TreeNode), + (QSortCompareProc *)ComparePositions); + } + + position = 0; /* Suppress compiler warning. */ + lastParent = NULL; + listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **)NULL); + Tcl_DStringInit(&dString); + for (nodePtr = nodeArr; *nodePtr != NULL; nodePtr++) { + parent = Blt_TreeNodeParent(*nodePtr); + if ((parent != NULL) && (parent == lastParent)) { + /* + * Since we've sorted the nodes already, we can safely + * assume that if two consecutive nodes have the same + * parent, the first node came before the second. If + * this is the case, use the last node as a starting + * point. + */ + + /* + * Note that we start comparing from the last node, + * not its successor. Some one may give us the same + * node more than once. + */ + node = *(nodePtr - 1); /* Can't get here unless there's + * more than one node. */ + for(/*empty*/; node != NULL; node = Blt_TreeNextSibling(node)) { + if (node == *nodePtr) { + break; + } + position++; + } + } else { + /* The fallback is to linearly search through the + * parent's list of children, counting the number of + * preceding siblings. Except for nodes with many + * siblings (100+), this should be okay. */ + position = Blt_TreeNodePosition(*nodePtr); + } + if (data.sort) { + lastParent = parent; /* Update the last parent. */ + } + /* + * Add an element in the form "parent -at position" to the + * list that we're generating. + */ + if (data.withId) { + objPtr = Tcl_NewIntObj(Blt_TreeNodeId(*nodePtr)); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + if (data.withParent) { + char *string; + + Tcl_DStringSetLength(&dString, 0); /* Clear the string. */ + string = (parent == NULL) ? "" : Blt_Itoa(Blt_TreeNodeId(parent)); + Tcl_DStringAppendElement(&dString, string); + Tcl_DStringAppendElement(&dString, "-at"); + Tcl_DStringAppendElement(&dString, Blt_Itoa(position)); + objPtr = Tcl_NewStringObj(Tcl_DStringValue(&dString), -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } else { + objPtr = Tcl_NewIntObj(position); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + } + Tcl_DStringFree(&dString); + Blt_Free(nodeArr); + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * PreviousOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +PreviousOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + int inode; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + inode = -1; + node = Blt_TreePrevNode(Blt_TreeRootNode(cmdPtr->tree), node); + if (node != NULL) { + inode = Blt_TreeNodeId(node); + } + Tcl_SetIntObj(Tcl_GetObjResult(interp), inode); + return TCL_OK; +} + +/*ARGSUSED*/ +static int +PrevSiblingOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + int inode; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + inode = -1; + node = Blt_TreePrevSibling(node); + if (node != NULL) { + inode = Blt_TreeNodeId(node); + } + Tcl_SetIntObj(Tcl_GetObjResult(interp), inode); + return TCL_OK; +} +/* + *---------------------------------------------------------------------- + * + * RestoreOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +RestoreOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode root; + RestoreData data; + Tcl_DString dString; + char *entry, *eol, *next; + char saved; + int result; + + if (GetNode(cmdPtr, objv[2], &root) != TCL_OK) { + return TCL_ERROR; + } + memset((char *)&data, 0, sizeof(data)); + Blt_InitHashTable(&data.idTable, BLT_ONE_WORD_KEYS); + data.root = root; + + /* Process switches */ + if (Blt_ProcessObjSwitches(interp, restoreSwitches, objc - 4, objv + 4, + (char *)&data, 0) < 0) { + return TCL_ERROR; + } + result = TCL_OK; + nLines = 0; + Tcl_DStringInit(&dString); + entry = eol = Tcl_GetString(objv[3]); + next = entry; + while (*eol != '\0') { + /* Find end-of-line */ + for (eol = next; (*eol != '\n') && (*eol != '\0'); eol++) { + /*empty*/ + } + /* + * Since we don't own the string (the Tcl_Obj could be shared), + * save the current end-of-line character (it's either a NUL + * or NL) so we can NUL-terminate the line for the call to + * Tcl_SplitList and repair it when we're done. + */ + saved = *eol; + *eol = '\0'; + next = eol + 1; + nLines++; + if (Tcl_CommandComplete(entry)) { + char **elemArr; + int nElem; + + if (Tcl_SplitList(interp, entry, &nElem, &elemArr) != TCL_OK) { + return TCL_ERROR; + } + if (nElem > 0) { + result = RestoreNode(cmdPtr, nElem, elemArr, &data); + Blt_Free(elemArr); + if (result != TCL_OK) { + *eol = saved; + break; + } + } + entry = next; + } + *eol = saved; + } + Blt_DeleteHashTable(&data.idTable); + return result; +} + +static int +ReadEntry(interp, channel, argcPtr, argvPtr) + Tcl_Interp *interp; + Tcl_Channel channel; + int *argcPtr; + char ***argvPtr; +{ + Tcl_DString dString; + int result; + char *entry; + + if (*argvPtr != NULL) { + Blt_Free(*argvPtr); + *argvPtr = NULL; + } + Tcl_DStringInit(&dString); + entry = NULL; + while (Tcl_Gets(channel, &dString) >= 0) { + nLines++; + Tcl_DStringAppend(&dString, "\n", 1); + entry = Tcl_DStringValue(&dString); + if (Tcl_CommandComplete(entry)) { + result = Tcl_SplitList(interp, entry, argcPtr, argvPtr); + Tcl_DStringFree(&dString); + return result; + } + } + Tcl_DStringFree(&dString); + if (entry == NULL) { + *argcPtr = 0; /* EOF */ + return TCL_OK; + } + Tcl_AppendResult(interp, "error reading file: ", + Tcl_PosixError(interp), (char *)NULL); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * RestorefileOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +RestorefileOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode root; + int nElem; + char **elemArr; + char *fileName; + int result; + Tcl_Channel channel; + RestoreData data; + + if (GetNode(cmdPtr, objv[2], &root) != TCL_OK) { + return TCL_ERROR; + } + fileName = Tcl_GetString(objv[3]); + channel = Tcl_OpenFileChannel(interp, fileName, "r", 0); + if (channel == NULL) { + return TCL_ERROR; + } + memset((char *)&data, 0, sizeof(data)); + Blt_InitHashTable(&data.idTable, BLT_ONE_WORD_KEYS); + data.root = root; + + /* Process switches */ + if (Blt_ProcessObjSwitches(interp, restoreSwitches, objc - 4, objv + 4, + (char *)&data, 0) < 0) { + Tcl_Close(interp, channel); + return TCL_ERROR; + } + elemArr = NULL; + nLines = 0; + for (;;) { + result = ReadEntry(interp, channel, &nElem, &elemArr); + if ((result != TCL_OK) || (nElem == 0)) { + break; + } + result = RestoreNode(cmdPtr, nElem, elemArr, &data); + if (result != TCL_OK) { + break; + } + } + if (elemArr != NULL) { + Blt_Free(elemArr); + } + Tcl_Close(interp, channel); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * RootOp -- + * + *---------------------------------------------------------------------- + */ +static int +RootOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode root; + + if (objc == 3) { + Blt_TreeNode node; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + Blt_TreeChangeRoot(cmdPtr->tree, node); + } + root = Blt_TreeRootNode(cmdPtr->tree); + Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeNodeId(root)); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * SetOp -- + * + *---------------------------------------------------------------------- + */ +static int +SetOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + char *string; + TagSearch cursor; + + string = Tcl_GetString(objv[2]); + if (isdigit(UCHAR(*string))) { + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + if (SetValues(cmdPtr, node, objc - 3, objv + 3) != TCL_OK) { + return TCL_ERROR; + } + } else { + node = FirstTaggedNode(interp, cmdPtr, objv[2], &cursor); + if (node == NULL) { + return TCL_ERROR; + } + for (/* empty */; node != NULL; node = NextTaggedNode(node, &cursor)) { + if (SetValues(cmdPtr, node, objc - 3, objv + 3) != TCL_OK) { + return TCL_ERROR; + } + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * SizeOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +SizeOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + Tcl_SetIntObj(Tcl_GetObjResult(interp), Blt_TreeSize(node)); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TagForgetOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +TagForgetOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + register int i; + + for (i = 3; i < objc; i++) { + ForgetTag(cmdPtr, Tcl_GetString(objv[i])); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TagNamesOp -- + * + *---------------------------------------------------------------------- + */ +static int +TagNamesOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_HashSearch cursor; + Blt_TreeTag *tagPtr; + Tcl_Obj *listObjPtr, *objPtr; + + listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + objPtr = Tcl_NewStringObj("all", -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + if (objc == 3) { + Blt_HashEntry *hPtr; + + objPtr = Tcl_NewStringObj("root", -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + for (hPtr = Blt_FirstHashEntry(&cmdPtr->tagTablePtr->table, &cursor); + hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { + tagPtr = Blt_GetHashValue(hPtr); + objPtr = Tcl_NewStringObj(tagPtr->tagName, -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + } else { + register int i; + Blt_TreeNode node; + Blt_HashEntry *hPtr, *h2Ptr; + Blt_HashTable tagTable; + int isNew; + + Blt_InitHashTable(&tagTable, BLT_STRING_KEYS); + for (i = 3; i < objc; i++) { + if (GetNode(cmdPtr, objv[i], &node) != TCL_OK) { + goto error; + } + if (node == Blt_TreeRootNode(cmdPtr->tree)) { + Blt_CreateHashEntry(&tagTable, "root", &isNew); + } + for (hPtr = Blt_FirstHashEntry(&cmdPtr->tagTablePtr->table, + &cursor); + hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { + tagPtr = Blt_GetHashValue(hPtr); + h2Ptr = Blt_FindHashEntry(&tagPtr->nodeTable, (char *)node); + if (h2Ptr != NULL) { + Blt_CreateHashEntry(&tagTable, tagPtr->tagName, &isNew); + } + } + } + for (hPtr = Blt_FirstHashEntry(&tagTable, &cursor); hPtr != NULL; + hPtr = Blt_NextHashEntry(&cursor)) { + objPtr = Tcl_NewStringObj(Blt_GetHashKey(&tagTable, hPtr), -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + Blt_DeleteHashTable(&tagTable); + } + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; + error: + Tcl_DecrRefCount(listObjPtr); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * TagNodesOp -- + * + *---------------------------------------------------------------------- + */ +static int +TagNodesOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_HashEntry *hPtr; + Blt_HashSearch cursor; + Blt_HashTable nodeTable; + Blt_TreeNode node; + Tcl_Obj *listObjPtr; + Tcl_Obj *objPtr; + char *string; + int isNew; + register int i; + + Blt_InitHashTable(&nodeTable, BLT_ONE_WORD_KEYS); + for (i = 3; i < objc; i++) { + string = Tcl_GetString(objv[i]); + if (strcmp(string, "all") == 0) { + break; + } else if (strcmp(string, "root") == 0) { + Blt_CreateHashEntry(&nodeTable, + (char *)Blt_TreeRootNode(cmdPtr->tree), &isNew); + continue; + } else { + Blt_HashTable *tablePtr; + + tablePtr = Blt_TreeTagHashTable(cmdPtr->tagTablePtr, string); + if (tablePtr != NULL) { + for (hPtr = Blt_FirstHashEntry(tablePtr, &cursor); + hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { + node = Blt_GetHashValue(hPtr); + Blt_CreateHashEntry(&nodeTable, (char *)node, &isNew); + } + continue; + } + } + Tcl_AppendResult(interp, "can't find a tag \"", string, "\"", + (char *)NULL); + goto error; + } + listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + for (hPtr = Blt_FirstHashEntry(&nodeTable, &cursor); hPtr != NULL; + hPtr = Blt_NextHashEntry(&cursor)) { + node = (Blt_TreeNode)Blt_GetHashKey(&nodeTable, hPtr); + objPtr = Tcl_NewIntObj(Blt_TreeNodeId(node)); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + Tcl_SetObjResult(interp, listObjPtr); + Blt_DeleteHashTable(&nodeTable); + return TCL_OK; + + error: + Blt_DeleteHashTable(&nodeTable); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * TagAddOp -- + * + *---------------------------------------------------------------------- + */ +static int +TagAddOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + register int i; + char *string; + TagSearch cursor; + + string = Tcl_GetString(objv[3]); + if (isdigit(UCHAR(string[0]))) { + Tcl_AppendResult(interp, "bad tag \"", string, + "\": can't start with a digit", (char *)NULL); + return TCL_ERROR; + } + if ((strcmp(string, "all") == 0) || (strcmp(string, "root") == 0)) { + Tcl_AppendResult(cmdPtr->interp, "can't add reserved tag \"", + string, "\"", (char *)NULL); + return TCL_ERROR; + } + for (i = 4; i < objc; i++) { + node = FirstTaggedNode(interp, cmdPtr, objv[i], &cursor); + if (node == NULL) { + return TCL_ERROR; + } + for (/* empty */; node != NULL; node = NextTaggedNode(node, &cursor)) { + if (AddTag(cmdPtr, node, string) != TCL_OK) { + return TCL_ERROR; + } + } + } + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * TagDeleteOp -- + * + *---------------------------------------------------------------------- + */ +static int +TagDeleteOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + char *string; + Blt_HashTable *tablePtr; + + string = Tcl_GetString(objv[3]); + if ((strcmp(string, "all") == 0) || (strcmp(string, "root") == 0)) { + Tcl_AppendResult(interp, "can't delete reserved tag \"", string, "\"", + (char *)NULL); + return TCL_ERROR; + } + tablePtr = Blt_TreeTagHashTable(cmdPtr->tagTablePtr, string); + if (tablePtr != NULL) { + register int i; + Blt_TreeNode node; + TagSearch cursor; + Blt_HashEntry *hPtr; + + for (i = 4; i < objc; i++) { + node = FirstTaggedNode(interp, cmdPtr, objv[i], &cursor); + if (node == NULL) { + return TCL_ERROR; + } + for (/* empty */; node != NULL; + node = NextTaggedNode(node, &cursor)) { + hPtr = Blt_FindHashEntry(tablePtr, (char *)node); + if (hPtr != NULL) { + Blt_DeleteHashEntry(tablePtr, hPtr); + } + } + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TagDumpOp -- + * + *---------------------------------------------------------------------- + */ +static int +TagDumpOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + register Blt_TreeNode root, node; + Tcl_DString dString; + TagSearch cursor; + register int i; + + Tcl_DStringInit(&dString); + root = Blt_TreeRootNode(cmdPtr->tree); + for (i = 3; i < objc; i++) { + node = FirstTaggedNode(interp, cmdPtr, objv[i], &cursor); + if (node == NULL) { + return TCL_ERROR; + } + for (/* empty */; node != NULL; node = NextTaggedNode(node, &cursor)) { + PrintNode(cmdPtr, root, node, &dString); + } + } + Tcl_DStringResult(interp, &dString); + Tcl_DStringFree(&dString); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TagOp -- + * + *---------------------------------------------------------------------- + */ +static Blt_OpSpec tagOps[] = { + {"add", 1, (Blt_Op)TagAddOp, 5, 0, "tag node...",}, + {"delete", 2, (Blt_Op)TagDeleteOp, 5, 0, "tag node...",}, + {"dump", 2, (Blt_Op)TagDumpOp, 4, 0, "tag...",}, + {"forget", 1, (Blt_Op)TagForgetOp, 4, 0, "tag...",}, + {"names", 2, (Blt_Op)TagNamesOp, 3, 0, "?node...?",}, + {"nodes", 2, (Blt_Op)TagNodesOp, 4, 0, "tag ?tag...?",}, +}; + +static int nTagOps = sizeof(tagOps) / sizeof(Blt_OpSpec); + +static int +TagOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_Op proc; + int result; + + proc = Blt_GetOpFromObj(interp, nTagOps, tagOps, BLT_OP_ARG2, objc, objv, + 0); + if (proc == NULL) { + return TCL_ERROR; + } + result = (*proc) (cmdPtr, interp, objc, objv); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * TraceCreateOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +TraceCreateOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_HashEntry *hPtr; + Blt_TreeNode node; + TraceInfo *tracePtr; + char *string, *key, *command; + char *tagName; + char idString[200]; + int flags, isNew; + + string = Tcl_GetString(objv[3]); + if (isdigit(UCHAR(*string))) { + if (GetNode(cmdPtr, objv[3], &node) != TCL_OK) { + return TCL_ERROR; + } + tagName = NULL; + } else { + tagName = Blt_Strdup(string); + node = NULL; + } + key = Tcl_GetString(objv[4]); + string = Tcl_GetString(objv[5]); + flags = GetTraceFlags(string); + if (flags < 0) { + Tcl_AppendResult(interp, "unknown flag in \"", string, "\"", + (char *)NULL); + return TCL_ERROR; + } + command = Tcl_GetString(objv[6]); + /* Stash away the command in structure and pass that to the trace. */ + tracePtr = Blt_Malloc(strlen(command) + sizeof(TraceInfo)); + strcpy(tracePtr->command, command); + tracePtr->cmdPtr = cmdPtr; + tracePtr->withTag = tagName; + tracePtr->node = node; + tracePtr->traceToken = Blt_TreeCreateTrace(cmdPtr->tree, node, key, + flags, TreeTraceProc, tracePtr); + + sprintf(idString, "trace%d", cmdPtr->traceCounter++); + hPtr = Blt_CreateHashEntry(&(cmdPtr->traceTable), idString, &isNew); + Blt_SetHashValue(hPtr, tracePtr); + + Tcl_SetStringObj(Tcl_GetObjResult(interp), idString, -1); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TraceDeleteOp -- + * + *---------------------------------------------------------------------- + */ +static int +TraceDeleteOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + TraceInfo *tracePtr; + Blt_HashEntry *hPtr; + register int i; + char *key; + + for (i = 3; i < objc; i++) { + key = Tcl_GetString(objv[i]); + hPtr = Blt_FindHashEntry(&(cmdPtr->traceTable), key); + if (hPtr == NULL) { + Tcl_AppendResult(interp, "unknown trace \"", key, "\"", + (char *)NULL); + return TCL_ERROR; + } + tracePtr = Blt_GetHashValue(hPtr); + Blt_DeleteHashEntry(&(cmdPtr->traceTable), hPtr); + Blt_TreeDeleteTrace(tracePtr->traceToken); + if (tracePtr->withTag != NULL) { + Blt_Free(tracePtr->withTag); + } + Blt_Free(tracePtr); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TraceNamesOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +TraceNamesOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_HashEntry *hPtr; + Blt_HashSearch cursor; + + for (hPtr = Blt_FirstHashEntry(&(cmdPtr->traceTable), &cursor); + hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { + Tcl_AppendElement(interp, Blt_GetHashKey(&(cmdPtr->traceTable), hPtr)); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TraceInfoOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +TraceInfoOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + TraceInfo *tracePtr; + struct Blt_TreeTraceStruct *tokenPtr; + Blt_HashEntry *hPtr; + Tcl_DString dString; + char string[5]; + char *key; + + key = Tcl_GetString(objv[3]); + hPtr = Blt_FindHashEntry(&(cmdPtr->traceTable), key); + if (hPtr == NULL) { + Tcl_AppendResult(interp, "unknown trace \"", key, "\"", + (char *)NULL); + return TCL_ERROR; + } + Tcl_DStringInit(&dString); + tracePtr = Blt_GetHashValue(hPtr); + if (tracePtr->withTag != NULL) { + Tcl_DStringAppendElement(&dString, tracePtr->withTag); + } else { + int inode; + + inode = Blt_TreeNodeId(tracePtr->node); + Tcl_DStringAppendElement(&dString, Blt_Itoa(inode)); + } + tokenPtr = (struct Blt_TreeTraceStruct *)tracePtr->traceToken; + Tcl_DStringAppendElement(&dString, tokenPtr->key); + PrintTraceFlags(tokenPtr->mask, string); + Tcl_DStringAppendElement(&dString, string); + Tcl_DStringAppendElement(&dString, tracePtr->command); + Tcl_DStringResult(interp, &dString); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TraceOp -- + * + *---------------------------------------------------------------------- + */ +static Blt_OpSpec traceOps[] = +{ + {"create", 1, (Blt_Op)TraceCreateOp, 7, 7, "node key how command",}, + {"delete", 1, (Blt_Op)TraceDeleteOp, 3, 0, "id...",}, + {"info", 1, (Blt_Op)TraceInfoOp, 4, 4, "id",}, + {"names", 1, (Blt_Op)TraceNamesOp, 3, 3, "",}, +}; + +static int nTraceOps = sizeof(traceOps) / sizeof(Blt_OpSpec); + +static int +TraceOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_Op proc; + int result; + + proc = Blt_GetOpFromObj(interp, nTraceOps, traceOps, BLT_OP_ARG2, objc, + objv, 0); + if (proc == NULL) { + return TCL_ERROR; + } + result = (*proc) (cmdPtr, interp, objc, objv); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * GetOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +TypeOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; /* Not used. */ + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + Tcl_Obj *valueObjPtr; + char *string; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + + string = Tcl_GetString(objv[3]); + if (Blt_TreeGetValue(interp, cmdPtr->tree, node, string, &valueObjPtr) + != TCL_OK) { + return TCL_ERROR; + } + if (valueObjPtr->typePtr != NULL) { + Tcl_SetObjResult(interp, + Tcl_NewStringObj(valueObjPtr->typePtr->name, -1)); + } else { + Tcl_SetObjResult(interp, Tcl_NewStringObj("string", -1)); + } + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * UnsetOp -- + * + *---------------------------------------------------------------------- + */ +static int +UnsetOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + char *string; + + string = Tcl_GetString(objv[2]); + if (isdigit(UCHAR(*string))) { + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + if (UnsetValues(cmdPtr, node, objc - 3, objv + 3) != TCL_OK) { + return TCL_ERROR; + } + } else { + TagSearch cursor; + + node = FirstTaggedNode(interp, cmdPtr, objv[2], &cursor); + if (node == NULL) { + return TCL_ERROR; + } + for (/* empty */; node != NULL; node = NextTaggedNode(node, &cursor)) { + if (UnsetValues(cmdPtr, node, objc - 3, objv + 3) != TCL_OK) { + return TCL_ERROR; + } + } + } + return TCL_OK; +} + + +typedef struct { + TreeCmd *cmdPtr; + unsigned int flags; + int type; + int mode; + char *key; + char *command; +} SortData; + +#define SORT_RECURSE (1<<2) +#define SORT_DECREASING (1<<3) +#define SORT_PATHNAME (1<<4) + +enum SortTypes { SORT_DICTIONARY, SORT_REAL, SORT_INTEGER, SORT_ASCII, + SORT_COMMAND }; + +enum SortModes { SORT_FLAT, SORT_REORDER }; + +static Blt_SwitchSpec sortSwitches[] = +{ + {BLT_SWITCH_VALUE, "-ascii", Blt_Offset(SortData, type), 0, 0, + SORT_ASCII}, + {BLT_SWITCH_STRING, "-command", Blt_Offset(SortData, command), 0}, + {BLT_SWITCH_FLAG, "-decreasing", Blt_Offset(SortData, flags), 0, 0, + SORT_DECREASING}, + {BLT_SWITCH_VALUE, "-dictionary", Blt_Offset(SortData, type), 0, 0, + SORT_DICTIONARY}, + {BLT_SWITCH_VALUE, "-integer", Blt_Offset(SortData, type), 0, 0, + SORT_INTEGER}, + {BLT_SWITCH_STRING, "-key", Blt_Offset(SortData, key), 0}, + {BLT_SWITCH_FLAG, "-path", Blt_Offset(SortData, flags), 0, 0, + SORT_PATHNAME}, + {BLT_SWITCH_VALUE, "-real", Blt_Offset(SortData, type), 0, 0, + SORT_REAL}, + {BLT_SWITCH_VALUE, "-recurse", Blt_Offset(SortData, flags), 0, 0, + SORT_RECURSE}, + {BLT_SWITCH_VALUE, "-reorder", Blt_Offset(SortData, mode), 0, 0, + SORT_REORDER}, + {BLT_SWITCH_END, NULL, 0, 0} +}; + +static SortData sortData; + +static int +CompareNodes(n1Ptr, n2Ptr) + Blt_TreeNode *n1Ptr, *n2Ptr; +{ + TreeCmd *cmdPtr = sortData.cmdPtr; + char *s1, *s2; + int result; + Tcl_DString dString1, dString2; + + s1 = s2 = ""; + result = 0; + + if (sortData.flags & SORT_PATHNAME) { + Tcl_DStringInit(&dString1); + Tcl_DStringInit(&dString2); + } + if (sortData.key != NULL) { + Tcl_Obj *valueObjPtr; + + if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, *n1Ptr, + sortData.key, &valueObjPtr) == TCL_OK) { + s1 = Tcl_GetString(valueObjPtr); + } + if (Blt_TreeGetValue((Tcl_Interp *)NULL, cmdPtr->tree, *n2Ptr, + sortData.key, &valueObjPtr) == TCL_OK) { + s2 = Tcl_GetString(valueObjPtr); + } + } else if (sortData.flags & SORT_PATHNAME) { + Blt_TreeNode root; + + root = Blt_TreeRootNode(cmdPtr->tree); + s1 = GetNodePath(cmdPtr, root, *n1Ptr, FALSE, &dString1); + s2 = GetNodePath(cmdPtr, root, *n2Ptr, FALSE, &dString2); + } else { + s1 = Blt_TreeNodeLabel(*n1Ptr); + s2 = Blt_TreeNodeLabel(*n2Ptr); + } + switch (sortData.type) { + case SORT_ASCII: + result = strcmp(s1, s2); + break; + + case SORT_COMMAND: + if (sortData.command == NULL) { + result = Blt_DictionaryCompare(s1, s2); + } else { + Tcl_DString dsCmd, dsName; + char *qualName; + + result = 0; /* Hopefully this will be Ok even if the + * Tcl command fails to return the correct + * result. */ + Tcl_DStringInit(&dsCmd); + Tcl_DStringAppend(&dsCmd, sortData.command, -1); + Tcl_DStringInit(&dsName); + qualName = Blt_GetQualifiedName( + Blt_GetCommandNamespace(cmdPtr->interp, cmdPtr->cmdToken), + Tcl_GetCommandName(cmdPtr->interp, cmdPtr->cmdToken), &dsName); + Tcl_DStringAppendElement(&dsCmd, qualName); + Tcl_DStringFree(&dsName); + Tcl_DStringAppendElement(&dsCmd, Blt_Itoa(Blt_TreeNodeId(*n1Ptr))); + Tcl_DStringAppendElement(&dsCmd, Blt_Itoa(Blt_TreeNodeId(*n2Ptr))); + Tcl_DStringAppendElement(&dsCmd, s1); + Tcl_DStringAppendElement(&dsCmd, s2); + result = Tcl_GlobalEval(cmdPtr->interp, Tcl_DStringValue(&dsCmd)); + Tcl_DStringFree(&dsCmd); + + if ((result != TCL_OK) || + (Tcl_GetInt(cmdPtr->interp, + Tcl_GetStringResult(cmdPtr->interp), &result) != TCL_OK)) { + Tcl_BackgroundError(cmdPtr->interp); + } + Tcl_ResetResult(cmdPtr->interp); + } + break; + + case SORT_DICTIONARY: + result = Blt_DictionaryCompare(s1, s2); + break; + + case SORT_INTEGER: + { + int i1, i2; + + if (Tcl_GetInt(NULL, s1, &i1) == TCL_OK) { + if (Tcl_GetInt(NULL, s2, &i2) == TCL_OK) { + result = i1 - i2; + } else { + result = -1; + } + } else if (Tcl_GetInt(NULL, s2, &i2) == TCL_OK) { + result = 1; + } else { + result = Blt_DictionaryCompare(s1, s2); + } + } + break; + + case SORT_REAL: + { + double r1, r2; + + if (Tcl_GetDouble(NULL, s1, &r1) == TCL_OK) { + if (Tcl_GetDouble(NULL, s2, &r2) == TCL_OK) { + result = (r1 < r2) ? -1 : (r1 > r2) ? 1 : 0; + } else { + result = -1; + } + } else if (Tcl_GetDouble(NULL, s2, &r2) == TCL_OK) { + result = 1; + } else { + result = Blt_DictionaryCompare(s1, s2); + } + } + break; + } + if (result == 0) { + result = Blt_TreeNodeId(*n1Ptr) - Blt_TreeNodeId(*n2Ptr); + } + if (sortData.flags & SORT_DECREASING) { + result = -result; + } + if (sortData.flags & SORT_PATHNAME) { + Tcl_DStringFree(&dString1); + Tcl_DStringFree(&dString2); + } + return result; +} + +/* + *---------------------------------------------------------------------- + * + * SortApplyProc -- + * + * Sorts the subnodes at a given node. + * + * Results: + * Always returns TCL_OK. + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +SortApplyProc(node, clientData, order) + Blt_TreeNode node; + ClientData clientData; + int order; /* Not used. */ +{ + TreeCmd *cmdPtr = clientData; + + if (!Blt_TreeIsLeaf(node)) { + Blt_TreeSortNode(cmdPtr->tree, node, CompareNodes); + } + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * SortOp -- + * + *---------------------------------------------------------------------- + */ +static int +SortOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode top; + SortData data; + int result; + + if (GetNode(cmdPtr, objv[2], &top) != TCL_OK) { + return TCL_ERROR; + } + result = TCL_ERROR; + /* Process switches */ + memset(&data, 0, sizeof(data)); + data.cmdPtr = cmdPtr; + if (Blt_ProcessObjSwitches(interp, sortSwitches, objc - 3, objv + 3, + (char *)&data, 0) < 0) { + return TCL_ERROR; + } + if (data.command != NULL) { + data.type = SORT_COMMAND; + } + data.cmdPtr = cmdPtr; + sortData = data; + if (data.mode == SORT_FLAT) { + Blt_TreeNode *p, *nodeArr, node; + int nNodes; + Tcl_Obj *objPtr, *listObjPtr; + int i; + + if (data.flags & SORT_RECURSE) { + nNodes = Blt_TreeSize(top); + } else { + nNodes = Blt_TreeNodeDegree(top); + } + nodeArr = Blt_Malloc(nNodes * sizeof(Blt_TreeNode)); + assert(nodeArr); + p = nodeArr; + if (data.flags & SORT_RECURSE) { + for(node = top; node != NULL; node = Blt_TreeNextNode(top, node)) { + *p++ = node; + } + } else { + for (node = Blt_TreeFirstChild(top); node != NULL; + node = Blt_TreeNextSibling(node)) { + *p++ = node; + } + } + qsort((char *)nodeArr, nNodes, sizeof(Blt_TreeNode), + (QSortCompareProc *)CompareNodes); + listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + for (p = nodeArr, i = 0; i < nNodes; i++, p++) { + objPtr = Tcl_NewIntObj(Blt_TreeNodeId(*p)); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + Tcl_SetObjResult(interp, listObjPtr); + Blt_Free(nodeArr); + result = TCL_OK; + } else { + if (data.flags & SORT_RECURSE) { + result = Blt_TreeApply(top, SortApplyProc, cmdPtr); + } else { + result = SortApplyProc(top, cmdPtr, TREE_PREORDER); + } + } + Blt_FreeSwitches(sortSwitches, (char *)&data, 0); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * ValuesOp -- + * + * Returns the names of values for a node or array value. + * + *---------------------------------------------------------------------- + */ +static int +ValuesOp(cmdPtr, interp, objc, objv) + TreeCmd *cmdPtr; + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_TreeNode node; + Tcl_Obj *listObjPtr; + + if (GetNode(cmdPtr, objv[2], &node) != TCL_OK) { + return TCL_ERROR; + } + listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + if (objc == 4) { + char *string; + + string = Tcl_GetString(objv[3]); + if (Blt_TreeArrayNames(interp, cmdPtr->tree, node, string, listObjPtr) + != TCL_OK) { + return TCL_ERROR; + } + } else { + Blt_TreeKey *key, *keys; + Tcl_Obj *objPtr; + + keys = GetKeys(cmdPtr->tree, node); + for (key = keys; *key != NULL; key++) { + objPtr = Tcl_NewStringObj(*key, -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + Blt_Free(keys); + } + Tcl_SetObjResult(interp, listObjPtr); + return TCL_OK; +} + + +/* + * -------------------------------------------------------------- + * + * TreeInstObjCmd -- + * + * This procedure is invoked to process commands on behalf of + * the tree object. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + * -------------------------------------------------------------- + */ +static Blt_OpSpec treeOps[] = +{ + {"ancestor", 2, (Blt_Op)AncestorOp, 4, 4, "node1 node2",}, + {"apply", 1, (Blt_Op)ApplyOp, 3, 0, "node ?switches?",}, + {"attach", 2, (Blt_Op)AttachOp, 2, 3, "?tree?",}, + {"children", 2, (Blt_Op)ChildrenOp, 3, 5, "node ?first? ?last?",}, + {"copy", 2, (Blt_Op)CopyOp, 4, 0, + "srcNode ?destTree? destNode ?switches?",}, + {"degree", 2, (Blt_Op)DegreeOp, 3, 0, "node",}, + {"delete", 2, (Blt_Op)DeleteOp, 3, 0, "node ?node...?",}, + {"depth", 3, (Blt_Op)DepthOp, 3, 3, "node",}, + {"dump", 4, (Blt_Op)DumpOp, 3, 3, "node",}, + {"dumpfile", 5, (Blt_Op)DumpfileOp, 4, 4, "node fileName",}, + {"exists", 1, (Blt_Op)ExistsOp, 3, 4, "node ?key?",}, + {"find", 4, (Blt_Op)FindOp, 3, 0, "node ?switches?",}, + {"findchild", 5, (Blt_Op)FindChildOp, 4, 4, "node name",}, + {"firstchild", 3, (Blt_Op)FirstChildOp, 3, 3, "node",}, + {"get", 1, (Blt_Op)GetOp, 3, 5, "node ?key? ?defaultValue?",}, + {"index", 3, (Blt_Op)IndexOp, 3, 3, "name",}, + {"insert", 3, (Blt_Op)InsertOp, 3, 0, "parent ?switches?",}, + {"is", 2, (Blt_Op)IsOp, 2, 0, "oper args...",}, + {"keys", 1, (Blt_Op)KeysOp, 3, 0, "node ?node...?",}, + {"label", 3, (Blt_Op)LabelOp, 3, 4, "node ?newLabel?",}, + {"lastchild", 3, (Blt_Op)LastChildOp, 3, 3, "node",}, + {"move", 1, (Blt_Op)MoveOp, 4, 0, "node newParent ?switches?",}, + {"next", 4, (Blt_Op)NextOp, 3, 3, "node",}, + {"nextsibling", 5, (Blt_Op)NextSiblingOp, 3, 3, "node",}, + {"notify", 2, (Blt_Op)NotifyOp, 2, 0, "args...",}, + {"parent", 3, (Blt_Op)ParentOp, 3, 3, "node",}, + {"path", 3, (Blt_Op)PathOp, 3, 3, "node",}, + {"position", 2, (Blt_Op)PositionOp, 3, 0, "?switches? node...",}, + {"previous", 5, (Blt_Op)PreviousOp, 3, 3, "node",}, + {"prevsibling", 5, (Blt_Op)PrevSiblingOp, 3, 3, "node",}, + {"restore", 7, (Blt_Op)RestoreOp, 4, 4, "node dataString",}, + {"restorefile", 8, (Blt_Op)RestorefileOp, 4, 4, "node fileName",}, + {"root", 2, (Blt_Op)RootOp, 2, 3, "?node?",}, + {"set", 3, (Blt_Op)SetOp, 3, 0, "node ?key value...?",}, + {"size", 2, (Blt_Op)SizeOp, 3, 3, "node",}, + {"sort", 2, (Blt_Op)SortOp, 3, 0, "node ?flags...?",}, + {"tag", 2, (Blt_Op)TagOp, 3, 0, "args...",}, + {"trace", 2, (Blt_Op)TraceOp, 2, 0, "args...",}, + {"type", 2, (Blt_Op)TypeOp, 4, 4, "node key",}, + {"unset", 3, (Blt_Op)UnsetOp, 3, 0, "node ?key...?",}, + {"values", 1, (Blt_Op)ValuesOp, 3, 4, "node ?key?",}, +}; + +static int nTreeOps = sizeof(treeOps) / sizeof(Blt_OpSpec); + +static int +TreeInstObjCmd(clientData, interp, objc, objv) + ClientData clientData; /* Information about the widget. */ + Tcl_Interp *interp; /* Interpreter to report errors back to. */ + int objc; /* Number of arguments. */ + Tcl_Obj *CONST *objv; /* Vector of argument strings. */ +{ + Blt_Op proc; + TreeCmd *cmdPtr = clientData; + int result; + + proc = Blt_GetOpFromObj(interp, nTreeOps, treeOps, BLT_OP_ARG1, objc, objv, + BLT_OP_LINEAR_SEARCH); + if (proc == NULL) { + return TCL_ERROR; + } + Tcl_Preserve(cmdPtr); + result = (*proc) (cmdPtr, interp, objc, objv); + Tcl_Release(cmdPtr); + return result; +} + +/* + * ---------------------------------------------------------------------- + * + * TreeInstDeleteProc -- + * + * Deletes the command associated with the tree. This is + * called only when the command associated with the tree is + * destroyed. + * + * Results: + * None. + * + * ---------------------------------------------------------------------- + */ +static void +TreeInstDeleteProc(clientData) + ClientData clientData; +{ + TreeCmd *cmdPtr = clientData; + + ReleaseTreeObject(cmdPtr); + if (cmdPtr->hashPtr != NULL) { + Blt_DeleteHashEntry(cmdPtr->tablePtr, cmdPtr->hashPtr); + } + Blt_DeleteHashTable(&(cmdPtr->traceTable)); + Blt_Free(cmdPtr); +} + +/* + * ---------------------------------------------------------------------- + * + * GenerateName -- + * + * Generates an unique tree command name. Tree names are in + * the form "treeN", where N is a non-negative integer. Check + * each name generated to see if it is already a tree. We want + * to recycle names if possible. + * + * Results: + * Returns the unique name. The string itself is stored in the + * dynamic string passed into the routine. + * + * ---------------------------------------------------------------------- + */ +static char * +GenerateName(interp, prefix, suffix, resultPtr) + Tcl_Interp *interp; + char *prefix, *suffix; + Tcl_DString *resultPtr; +{ + + int n; + Tcl_Namespace *nsPtr; + char string[200]; + Tcl_CmdInfo cmdInfo; + Tcl_DString dString; + char *treeName, *name; + + /* + * Parse the command and put back so that it's in a consistent + * format. + * + * t1 <current namespace>::t1 + * n1::t1 <current namespace>::n1::t1 + * ::t1 ::t1 + * ::n1::t1 ::n1::t1 + */ + treeName = NULL; /* Suppress compiler warning. */ + for (n = 0; n < INT_MAX; n++) { + Tcl_DStringInit(&dString); + Tcl_DStringAppend(&dString, prefix, -1); + sprintf(string, "tree%d", n); + Tcl_DStringAppend(&dString, string, -1); + Tcl_DStringAppend(&dString, suffix, -1); + treeName = Tcl_DStringValue(&dString); + if (Blt_ParseQualifiedName(interp, treeName, &nsPtr, &name) != TCL_OK) { + Tcl_AppendResult(interp, "can't find namespace in \"", treeName, + "\"", (char *)NULL); + return NULL; + } + if (nsPtr == NULL) { + nsPtr = Tcl_GetCurrentNamespace(interp); + } + treeName = Blt_GetQualifiedName(nsPtr, name, resultPtr); + /* + * Check if the command already exists. + */ + if (Tcl_GetCommandInfo(interp, treeName, &cmdInfo)) { + continue; + } + if (!Blt_TreeExists(interp, treeName)) { + /* + * We want the name of the tree command and the underlying + * tree object to be the same. Check that the free command + * name isn't an already a tree object name. + */ + break; + } + } + return treeName; +} + +/* + *---------------------------------------------------------------------- + * + * TreeCreateOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +TreeCreateOp(clientData, interp, objc, objv) + ClientData clientData; /* Interpreter-specific data. */ + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + TreeCmdInterpData *dataPtr = clientData; + char *treeName; + Tcl_DString dString; + Blt_Tree token; + + treeName = NULL; + if (objc == 3) { + treeName = Tcl_GetString(objv[2]); + } + Tcl_DStringInit(&dString); + if (treeName == NULL) { + treeName = GenerateName(interp, "", "", &dString); + } else { + char *p; + + p = strstr(treeName, "#auto"); + if (p != NULL) { + *p = '\0'; + treeName = GenerateName(interp, treeName, p + 5, &dString); + *p = '#'; + } else { + char *name; + Tcl_CmdInfo cmdInfo; + Tcl_Namespace *nsPtr; + + nsPtr = NULL; + /* + * Parse the command and put back so that it's in a consistent + * format. + * + * t1 <current namespace>::t1 + * n1::t1 <current namespace>::n1::t1 + * ::t1 ::t1 + * ::n1::t1 ::n1::t1 + */ + if (Blt_ParseQualifiedName(interp, treeName, &nsPtr, &name) + != TCL_OK) { + Tcl_AppendResult(interp, "can't find namespace in \"", treeName, + "\"", (char *)NULL); + return TCL_ERROR; + } + if (nsPtr == NULL) { + nsPtr = Tcl_GetCurrentNamespace(interp); + } + treeName = Blt_GetQualifiedName(nsPtr, name, &dString); + /* + * Check if the command already exists. + */ + if (Tcl_GetCommandInfo(interp, treeName, &cmdInfo)) { + Tcl_AppendResult(interp, "a command \"", treeName, + "\" already exists", (char *)NULL); + goto error; + } + if (Blt_TreeExists(interp, treeName)) { + Tcl_AppendResult(interp, "a tree \"", treeName, + "\" already exists", (char *)NULL); + goto error; + } + } + } + if (treeName == NULL) { + goto error; + } + if (Blt_TreeCreate(interp, treeName, &token) == TCL_OK) { + int isNew; + TreeCmd *cmdPtr; + + cmdPtr = Blt_Calloc(1, sizeof(TreeCmd)); + assert(cmdPtr); + cmdPtr->dataPtr = dataPtr; + cmdPtr->tree = token; + cmdPtr->interp = interp; + Blt_InitHashTable(&(cmdPtr->traceTable), BLT_STRING_KEYS); + Blt_InitHashTable(&(cmdPtr->notifyTable), BLT_STRING_KEYS); + cmdPtr->tagTablePtr = Blt_TreeNewTagTable(); + cmdPtr->cmdToken = Tcl_CreateObjCommand(interp, treeName, + (Tcl_ObjCmdProc *)TreeInstObjCmd, cmdPtr, TreeInstDeleteProc); + cmdPtr->tablePtr = &dataPtr->treeTable; + cmdPtr->hashPtr = Blt_CreateHashEntry(cmdPtr->tablePtr, (char *)cmdPtr, + &isNew); + Blt_SetHashValue(cmdPtr->hashPtr, cmdPtr); + Tcl_SetResult(interp, treeName, TCL_VOLATILE); + Tcl_DStringFree(&dString); + Blt_TreeCreateEventHandler(cmdPtr->tree, TREE_NOTIFY_ALL, + TreeEventProc, cmdPtr); + return TCL_OK; + } + error: + Tcl_DStringFree(&dString); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * TreeDestroyOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +TreeDestroyOp(clientData, interp, objc, objv) + ClientData clientData; /* Interpreter-specific data. */ + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + TreeCmdInterpData *dataPtr = clientData; + TreeCmd *cmdPtr; + char *string; + register int i; + + for (i = 2; i < objc; i++) { + string = Tcl_GetString(objv[i]); + cmdPtr = GetTreeCmd(dataPtr, interp, string); + if (cmdPtr == NULL) { + Tcl_AppendResult(interp, "can't find a tree named \"", string, + "\"", (char *)NULL); + return TCL_ERROR; + } + Tcl_DeleteCommandFromToken(interp, cmdPtr->cmdToken); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TreeNamesOp -- + * + *---------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static int +TreeNamesOp(clientData, interp, objc, objv) + ClientData clientData; /* Interpreter-specific data. */ + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + TreeCmdInterpData *dataPtr = clientData; + TreeCmd *cmdPtr; + Blt_HashEntry *hPtr; + Blt_HashSearch cursor; + Tcl_Obj *objPtr, *listObjPtr; + Tcl_DString dString; + char *qualName; + + Tcl_DStringInit(&dString); + listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + for (hPtr = Blt_FirstHashEntry(&dataPtr->treeTable, &cursor); + hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { + cmdPtr = Blt_GetHashValue(hPtr); + qualName = Blt_GetQualifiedName( + Blt_GetCommandNamespace(interp, cmdPtr->cmdToken), + Tcl_GetCommandName(interp, cmdPtr->cmdToken), &dString); + if (objc == 3) { + if (!Tcl_StringMatch(qualName, Tcl_GetString(objv[2]))) { + continue; + } + } + objPtr = Tcl_NewStringObj(qualName, -1); + Tcl_ListObjAppendElement(interp, listObjPtr, objPtr); + } + Tcl_SetObjResult(interp, listObjPtr); + Tcl_DStringFree(&dString); + return TCL_OK; +} + + + +/* + *---------------------------------------------------------------------- + * + * TreeObjCmd -- + * + *---------------------------------------------------------------------- + */ +static Blt_OpSpec treeCmdOps[] = +{ + {"create", 1, (Blt_Op)TreeCreateOp, 2, 3, "?name?",}, + {"destroy", 1, (Blt_Op)TreeDestroyOp, 3, 0, "name...",}, + {"names", 1, (Blt_Op)TreeNamesOp, 2, 3, "?pattern?...",}, +}; + +static int nCmdOps = sizeof(treeCmdOps) / sizeof(Blt_OpSpec); + +/*ARGSUSED*/ +static int +TreeObjCmd(clientData, interp, objc, objv) + ClientData clientData; /* Interpreter-specific data. */ + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + Blt_Op proc; + + proc = Blt_GetOpFromObj(interp, nCmdOps, treeCmdOps, BLT_OP_ARG1, objc, + objv, 0); + if (proc == NULL) { + return TCL_ERROR; + } + return (*proc) (clientData, interp, objc, objv); +} + +/* + * ----------------------------------------------------------------------- + * + * TreeInterpDeleteProc -- + * + * This is called when the interpreter hosting the "tree" command + * is deleted. + * + * Results: + * None. + * + * Side effects: + * Removes the hash table managing all tree names. + * + * ------------------------------------------------------------------------ + */ +/* ARGSUSED */ +static void +TreeInterpDeleteProc(clientData, interp) + ClientData clientData; /* Interpreter-specific data. */ + Tcl_Interp *interp; +{ + TreeCmdInterpData *dataPtr = clientData; + + /* All tree instances should already have been destroyed when + * their respective Tcl commands were deleted. */ + Blt_DeleteHashTable(&dataPtr->treeTable); + Tcl_DeleteAssocData(interp, TREE_THREAD_KEY); + Blt_Free(dataPtr); +} + +/*ARGSUSED*/ +static int +CompareDictionaryCmd(clientData, interp, objc, objv) + ClientData clientData; /* Not used. */ + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + int result; + char *s1, *s2; + + s1 = Tcl_GetString(objv[1]); + s2 = Tcl_GetString(objv[2]); + result = Blt_DictionaryCompare(s1, s2); + result = (result > 0) ? -1 : (result < 0) ? 1 : 0; + Tcl_SetIntObj(Tcl_GetObjResult(interp), result); + return TCL_OK; +} + +/*ARGSUSED*/ +static int +ExitCmd(clientData, interp, objc, objv) + ClientData clientData; /* Not used. */ + Tcl_Interp *interp; + int objc; + Tcl_Obj *CONST *objv; +{ + int code; + + if (Tcl_GetIntFromObj(interp, objv[1], &code) != TCL_OK) { + return TCL_ERROR; + } +#ifdef TCL_THREADS + Tcl_Exit(code); +#else + exit(code); +#endif + /*NOTREACHED*/ + return TCL_OK; +} + +/* + * ----------------------------------------------------------------------- + * + * Blt_TreeInit -- + * + * This procedure is invoked to initialize the "tree" command. + * + * Results: + * None. + * + * Side effects: + * Creates the new command and adds a new entry into a global Tcl + * associative array. + * + * ------------------------------------------------------------------------ + */ +int +Blt_TreeInit(interp) + Tcl_Interp *interp; +{ + TreeCmdInterpData *dataPtr; /* Interpreter-specific data. */ + static Blt_ObjCmdSpec cmdSpec = { + "tree", TreeObjCmd, + }; + static Blt_ObjCmdSpec compareSpec = { + "compare", CompareDictionaryCmd, + }; + static Blt_ObjCmdSpec exitSpec = { + "exit", ExitCmd, + }; + if (Blt_InitObjCmd(interp, "blt::util", &compareSpec) == NULL) { + return TCL_ERROR; + } + if (Blt_InitObjCmd(interp, "blt::util", &exitSpec) == NULL) { + return TCL_ERROR; + } + + dataPtr = GetTreeCmdInterpData(interp); + cmdSpec.clientData = dataPtr; + if (Blt_InitObjCmd(interp, "blt", &cmdSpec) == NULL) { + return TCL_ERROR; + } + return TCL_OK; +} + +int +Blt_TreeCmdGetToken(interp, string, treePtr) + Tcl_Interp *interp; + char *string; + Blt_Tree *treePtr; +{ + TreeCmdInterpData *dataPtr; + TreeCmd *cmdPtr; + + dataPtr = GetTreeCmdInterpData(interp); + cmdPtr = GetTreeCmd(dataPtr, interp, string); + if (cmdPtr == NULL) { + Tcl_AppendResult(interp, "can't find a tree associated with \"", + string, "\"", (char *)NULL); + return TCL_ERROR; + } + *treePtr = cmdPtr->tree; + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Blt_TreeCmdGetTagTable -- + * + *---------------------------------------------------------------------- + */ +int +Blt_TreeCmdGetTagTable(interp, treeName, dataPtrPtr) + Tcl_Interp *interp; + char *treeName; + Blt_TreeTagTable **dataPtrPtr; +{ + TreeCmdInterpData *dataPtr; + TreeCmd *cmdPtr; + + dataPtr = GetTreeCmdInterpData(interp); + cmdPtr = GetTreeCmd(dataPtr, interp, treeName); + if (cmdPtr == NULL) { + return TCL_ERROR; + } + cmdPtr->tagTablePtr->refCount++; + *dataPtrPtr = cmdPtr->tagTablePtr; + return TCL_OK; +} + +/* Dump tree to dbm */ +/* Convert node data to datablock */ + +#endif /* NO_TREE */ + |