Index: trunk/src/helpers/dialog.c
===================================================================
--- trunk/src/helpers/dialog.c	(revision 82)
+++ trunk/src/helpers/dialog.c	(revision 83)
@@ -6,4 +6,9 @@
  *
  *      See dlghCreateDlg for details.
+ *
+ *      In addition, this has dlghMessageBox (a WinMessageBox
+ *      replacement) and some helper functions for simulating
+ *      dialog behavior in regular window procs (see
+ *      dlghSetPrevFocus and others).
  *
  *      Usage: All PM programs.
@@ -82,4 +87,7 @@
  *      used, even though the prototype only
  *      declares DIALOGDATA.
+ *
+ *      This only exists while the dialog is being
+ *      created and is not stored with the new dialog.
  */
 
Index: trunk/src/helpers/semaphores.c
===================================================================
--- trunk/src/helpers/semaphores.c	(revision 82)
+++ trunk/src/helpers/semaphores.c	(revision 83)
@@ -396,6 +396,7 @@
 
                 // check if this thread has a reader entry already
-                if (pReader = (PREADERTREENODE)treeFindEQID(&pMutex->ReaderThreadsTree,
-                                                            tidMyself)) // ID to look for
+                if (pReader = (PREADERTREENODE)treeFind(pMutex->ReaderThreadsTree,
+                                                        tidMyself,  // ID to look for
+                                                        treeCompareKeys))
                 {
                     // yes:
@@ -418,11 +419,11 @@
                         // store the thread ID as the tree ID to
                         // sort by (so we can find by TID)
-                        pReader->Tree.id = tidMyself;
+                        pReader->Tree.ulKey = tidMyself;
                         // set requests count to 1
                         pReader->cRequests = 1;
 
-                        treeInsertID(&pMutex->ReaderThreadsTree,
-                                     (TREE*)pReader,
-                                     FALSE);
+                        treeInsert(&pMutex->ReaderThreadsTree,
+                                   (TREE*)pReader,
+                                   treeCompareKeys);
                         (pMutex->cReaderThreads)++;
                     }
@@ -466,6 +467,7 @@
 
             // find the READERTREENODE for our TID
-            if (    (pReader = (PREADERTREENODE)treeFindEQID(&pMutex->ReaderThreadsTree,
-                                                             tidMyself)) // ID to look for
+            if (    (pReader = (PREADERTREENODE)treeFind(pMutex->ReaderThreadsTree,
+                                                         tidMyself,  // ID to look for
+                                                         treeCompareKeys))
                  && (pReader->cRequests)
                )
@@ -535,6 +537,7 @@
 
             // find the READERTREENODE for our TID
-            if (    (!(pReader = (PREADERTREENODE)treeFindEQID(&pMutex->ReaderThreadsTree,
-                                                               tidMyself)))          // ID to look for
+            if (    (!(pReader = (PREADERTREENODE)treeFind(pMutex->ReaderThreadsTree,
+                                                           tidMyself,          // ID to look for
+                                                           treeCompareKeys)))
                  || (pReader->cRequests == 0)
                )
@@ -603,6 +606,7 @@
                 // check if current TID holds read request also
                 PREADERTREENODE pReader
-                    = (PREADERTREENODE)treeFindEQID(&pMutex->ReaderThreadsTree,
-                                                    tidMyself);
+                    = (PREADERTREENODE)treeFind(pMutex->ReaderThreadsTree,
+                                                tidMyself,
+                                                treeCompareKeys);
                             // != NULL if this TID has a reader already
 
Index: trunk/src/helpers/tree.c
===================================================================
--- trunk/src/helpers/tree.c	(revision 82)
+++ trunk/src/helpers/tree.c	(revision 83)
@@ -4,7 +4,4 @@
  *      contains helper functions for maintaining 'Red-Black' balanced
  *      binary trees.
- *      See explanations below.
- *
- *      This file is all new with V0.9.5 (2000-09-29) [umoeller].
  *
  *      Usage: All C programs; not OS/2-specific.
@@ -13,111 +10,54 @@
  *      --  tree*    tree helper functions
  *
- *      This has been taken from the Standard Function Library (SFL)
- *      by iMatix Corporation and changed to user the "id" member for
- *      tree sorting/comparison. This implementation is released
- *      under the GPL.
- *
  *      <B>Introduction</B>
  *
- *      Binary trees are different from linked lists in that items
- *      are not simply linked sequentially, but instead put into
- *      a tree-like structure.
- *
- *      While lists have "next" and "previous" pointers, trees have
- *      "left" and "right" pointers which keep the tree sorted at
- *      all times.
- *
- *      Per definition, in our trees, if you follow the "left" pointer,
- *      you will reach an item which is "greater than" the current node.
- *      Reversely, following the "right" pointer will lead you to a
- *      node which is "less than" the current node.
- *
- *      What is considered "less" or "greater" is determined by a
- *      comparison callback to be supplied by the caller.
- *
- *      For this, the functions here use the TREE structure. You can
- *      easily see that this has the "left" and "right" members,
- *      which make up the tree.
- *
- *      In addition, each tree has a "tree root" item, from which all
- *      other tree nodes can be reached by following the "left" and
- *      "right" pointers.
- *
- *      The implementation here has the following characteristics:
- *
- *      -- We have "binary" trees. That is, there are only "left" and
- *         "right" pointers.
- *
- *      -- The tree is always "balanced". The tree gets completely
- *         reordered when items are added/removed to ensure that
- *         all paths through the tree are approximately the same
- *         length. This avoids the "worst case" scenario that some
- *         paths grow terribly long while others remain short, which
- *         can make searching very inefficient.
- *
- *      -- The tree nodes are marked as either "red" or "black", which
- *         is an algorithm to allow the implementation of 2-3-4 trees
- *         using a binary tree only. I don't fully understand how this
- *         works, but essentially, "red" nodes represent a 3 or 4 node,
- *         while "black" nodes are plain binary nodes.
- *
- *      As much as I understand about all this, red-black balanced
- *      binary trees are the most efficient tree algorithm known to
- *      mankind. As long as you are sure that trees are more efficient
- *      in your situation than a linked list in the first place (see
- *      below for comparisons), use the functions in here.
- *
- *      <B>Using binary trees</B>
- *
- *      You can use any structure as elements in a tree, provided
- *      that the first member in the structure is a TREE structure
- *      (i.e. it has the left, right, parent, id, and colour members).
- *      The tree functions don't care what follows, since they do
- *      not manage any memory themselves.
- *
- *      So the implementation here is slightly different from the
- *      linked lists in linklist.c, because the LISTNODE structs
- *      only have pointers to the data. By contrast, the TREE structs
- *      are expected to contain the data themselves. See treeInsertID()
- *      for a sample.
- *
- *      Initialize the root of the tree with treeInit(). Then
- *      add nodes to the tree with treeInsertID() and remove nodes
- *      with treeDelete().
- *
- *      You can test whether a tree is empty by comparing its
- *      root with TREE_NULL.
- *
- *      Most functions in here come in two flavors.
- *
- *      -- You can provide a comparison function and use the "Node"
- *         flavors of these functions. This is useful, for example,
- *         if you are storing strings. You can then write a short
- *         comparison function which does a strcmp() on the data
- *         of tree nodes.
- *
- *         The order of nodes in the tree is determined by calling a
- *         node comparison function provided by the caller
- *         (which you must write). This must be declared as:
- *
- +              int TREEENTRY fnMyCompareNodes(TREE *t1, TREE *t2);
- *
- *         It obviously receives two TREE pointers, which it must
- *         compare and return:
- *
- +               0: tree1 == tree2
- +              -1: tree1 < tree2
- +              +1: tree1 > tree2
- *
- *      -- The "ID" functions (e.g. treeInsertID) do not require
- *         a comparison function, but will use the "id" member of
- *         the TREE structure instead. If this flavor is used, an
- *         internal comparison function is used for comparing the
- *         "id" fields, which are assumed to be plain ULONGs.
+ *      While linked lists have "next" and "previous" pointers (which
+ *      makes them one-dimensional), trees have a two-dimensional layout:
+ *      each tree node has one "parent" and two "children" which are
+ *      called "left" and "right". The "left" pointer will always lead
+ *      to a tree node that is "less than" its parent node, while the
+ *      "right" pointer will lead to a node that is "greater than"
+ *      its parent. What is considered "less" or "greater" for sorting
+ *      is determined by a comparison callback to be supplied by the
+ *      tree functions' caller. The "leafs" of the tree will have
+ *      null left and right pointers.
+ *
+ *      For this, the functions here use the TREE structure. The most
+ *      important member here is the "ulKey" field which is used for
+ *      sorting (passed to the compare callbacks). Since the tree
+ *      functions do no memory allocation, the caller can easily
+ *      use an extended TREE structure with additional fields as
+ *      long as the first member is the TREE structure. See below.
+ *
+ *      Each tree must have a "root" item, from which all other tree
+ *      nodes can eventually be reached by following the "left" and
+ *      "right" pointers. The root node is the only node whose
+ *      parent is null.
  *
  *      <B>Trees vs. linked lists</B>
  *
  *      Compared to linked lists (as implemented by linklist.c),
- *      trees allow for much faster searching.
+ *      trees allow for much faster searching, since they are
+ *      always sorted.
+ *
+ *      Take an array of numbers, and assume you'd need to find
+ *      the array node with the specified number.
+ *
+ *      With a (sorted) linked list, this would look like:
+ *
+ +          4  -->  7  -->  16  -->  20  -->  37  -->  38  -->  43
+ *
+ *      Searching for "43" would need 6 iterations.
+ *
+ *      With a binary tree, this would instead look like:
+ *
+ +                       20
+ +                     /    \
+ +                    7      38
+ +                   / \    /  \
+ +                  4  16  37   43
+ +                 / \ / \ / \ / \
+ *
+ *      Searching for "43" would need 2 iterations only.
  *
  *      Assuming a linked list contains N items, then searching a
@@ -131,9 +71,7 @@
  *      average.
  *
- *      Example: You need to build a list of files, and you
- *      will search the list frequently according to the file
- *      handles. This would make the handle an ideal "id" field.
- *
  *      Differences compared to linklist.c:
+ *
+ *      -- A tree is always sorted.
  *
  *      -- Trees are considerably slower when inserting and removing
@@ -142,11 +80,168 @@
  *         nodes because the tree is always sorted.
  *
- *      -- If you are not using the "ID" flavors, you must supply a
- *         comparison function to allow the tree functions to sort the tree.
- *
  *      -- As opposed to a LISTNODE, the TREE structure (which
  *         represents a tree node) does not contain a data pointer,
  *         as said above. The caller must do all memory management.
  *
+ *      <B>Background</B>
+ *
+ *      Now, a "red-black balanced binary tree" means the following:
+ *
+ *      -- We have "binary" trees. That is, there are only "left" and
+ *         "right" pointers. (Other algorithms allow tree nodes to
+ *         have more than two children, but binary trees are usually
+ *         more efficient.)
+ *
+ *      -- The tree is always "balanced". The tree gets reordered
+ *         when items are added/removed to ensure that all paths
+ *         through the tree are approximately the same length.
+ *         This avoids the "worst case" scenario that some paths
+ *         grow terribly long while others remain short ("degenerated"
+ *         trees), which can make searching very inefficient:
+ *
+ +                  4
+ +                 / \
+ +                    7
+ +                   / \
+ +                      16
+ +                     / \
+ +                        20
+ +                       / \
+ +                          37
+ +                         / \
+ +                            43
+ +                           /  \
+ *
+ *      -- Fully balanced trees can be quite expensive because on
+ *         every insertion or deletion, the tree nodes must be reordered.
+ *         By contrast, "Red-black" binary balanced trees contain
+ *         an additional bit in each node which marks the node as
+ *         either red or black. This bit is used only for efficient
+ *         rebalancing when inserting or deleting nodes.
+ *
+ *         The color of each node is instrumental in determining the
+ *         balance of the tree. During insert and delete operations,
+ *         nodes may be rotated to maintain tree balance.
+ *
+ *         I don't fully understand why this works, but if you really
+ *         care, this is explained at
+ *         "http://www.eli.sdsu.edu/courses/fall96/cs660/notes/redBlack/redBlack.html".
+ *
+ *      In other words, as opposed to regular binary trees, RB trees
+ *      are not _fully_ balanced, but they are _mostly_ balanced. With
+ *      respect to efficiency, RB trees are thus a good compromise:
+ *
+ *      -- Completely unbalanced trees are efficient when inserting,
+ *         but can have a terrible worst case when searching.
+ *
+ *      -- RB trees are still acceptably efficient when inserting
+ *         and quite efficient when searching.
+ *         A RB tree with n internal nodes has a height of at most
+ *         2lg(n+1). Both average and worst-case search time is O(lg n).
+ *
+ *      -- Fully balanced binary trees are inefficient when inserting
+ *         but most efficient when searching.
+ *
+ *      So as long as you are sure that trees are more efficient
+ *      in your situation than a linked list in the first place, use
+ *      these RB trees instead of linked lists.
+ *
+ *      <B>Using binary trees</B>
+ *
+ *      You can use any structure as elements in a tree, provided
+ *      that the first member in the structure is a TREE structure
+ *      (i.e. it has the left, right, parent, and color members).
+ *      Each TREE node has a ulKey field which is used for
+ *      comparing tree nodes and thus determines the location of
+ *      the node in the tree.
+ *
+ *      The tree functions don't care what follows in each TREE
+ *      node since they do not manage any memory themselves.
+ *      So the implementation here is slightly different from the
+ *      linked lists in linklist.c, because the LISTNODE structs
+ *      only have pointers to the data. By contrast, the TREE structs
+ *      are expected to contain the data themselves.
+ *
+ *      Initialize the root of the tree with treeInit(). Then
+ *      add nodes to the tree with treeInsert() and remove nodes
+ *      with treeDelete(). See below for a sample.
+ *
+ *      You can test whether a tree is empty by comparing its
+ *      root with LEAF.
+ *
+ *      For most tree* functions, you must specify a comparison
+ *      function which will always receive two "key" parameters
+ *      to compare. This must be declared as
+ +
+ +          int TREEENTRY fnCompare(ULONG ul1, ULONG ul2);
+ *
+ *      This will receive two TREE.ulKey members (whose meaning
+ *      is defined by your implementation) and must return
+ *
+ *      -- something < 0: ul1 < ul2
+ *      -- 0: ul1 == ul2
+ *      -- something > 1: ul1 > ul2
+ *
+ *      <B>Example</B>
+ *
+ *      A good example where trees are efficient would be the
+ *      case where you have "keyword=value" string pairs and
+ *      you frequently need to search for "keyword" to find
+ *      a "value". So "keyword" would be an ideal candidate for
+ *      the TREE.key field.
+ *
+ *      You'd then define your own tree nodes like this:
+ *
+ +          typedef struct _MYTREENODE
+ +          {
+ +              TREE        Tree;       // regular tree node, which has
+ +                                      // the ULONG "key" field; we'd
+ +                                      // use this as a const char*
+ +                                      // pointer to the keyword string
+ +              // here come the additional fields
+ +              // (whatever you need for your data)
+ +              const char  *pcszValue;
+ +
+ +          } MYTREENODE, *PMYTREENODE;
+ *
+ *      Initialize the tree root:
+ *
+ +          TREE *root;
+ +          treeInit(&root);
+ *
+ *      To add a new "keyword=value" pair, do this:
+ *
+ +          PMYTREENODE AddNode(TREE **root,
+ +                              const char *pcszKeyword,
+ +                              const char *pcszValue)
+ +          {
+ +              PMYTREENODE p = (PMYTREENODE)malloc(sizeof(MYTREENODE));
+ +              p.Tree.ulKey = (ULONG)pcszKeyword;
+ +              p.pcszValue = pcszValue;
+ +              treeInsert(root,                // tree's root
+ +                         p,                   // new tree node
+ +                         fnCompare);          // comparison func
+ +              return (p);
+ +          }
+ *
+ *      Your comparison func receives two ulKey values to compare,
+ *      which in this case would be the typecast string pointers:
+ *
+ +          int TREEENTRY fnCompare(ULONG ul1, ULONG ul2)
+ +          {
+ +              return (strcmp((const char*)ul1,
+ +                             (const char*)ul2));
+ +          }
+ *
+ *      You can then use treeFind to very quickly find a node
+ *      with a specified ulKey member.
+ *
+ *      This file was new with V0.9.5 (2000-09-29) [umoeller].
+ *      With V0.9.13, all the code has been replaced with the public
+ *      domain code found at http://epaperpress.com/sortsearch/index.html
+ *      ("A compact guide to searching and sorting") by Thomas Niemann.
+ *      The old implementation from the Standard Function Library (SFL)
+ *      turned out to be buggy for large trees (more than 100 nodes).
+ *
  *@@added V0.9.5 (2000-09-29) [umoeller]
  *@@header "helpers\tree.h"
@@ -154,9 +249,8 @@
 
 /*
- *      Written:    97/11/18  Jonathan Schultz <jonathan@imatix.com>
- *      Revised:    98/12/08  Jonathan Schultz <jonathan@imatix.com>
- *
- *      Copyright (C) 1991-99 iMatix Corporation.
- *      Copyright (C) 2000 Ulrich Mller.
+ *      Original coding by Thomas Niemann, placed in the public domain
+ *      (see http://epaperpress.com/sortsearch/index.html).
+ *
+ *      This implementation Copyright (C) 2001 Ulrich Mller.
  *      This file is part of the "XWorkplace helpers" source package.
  *      This is free software; you can redistribute it and/or modify
@@ -178,40 +272,39 @@
 #include "helpers\tree.h"
 
-//  Constants
-TREE    TREE_EMPTY = {TREE_NULL, TREE_NULL, NULL, BLACK};
-
-//  Internal function prototypes
-static void insert_fixup(TREE **root, TREE *tree);
-static void rotate_left(TREE **root, TREE *tree);
-static void rotate_right(TREE **root, TREE *tree);
-static void delete_fixup(TREE **root, TREE *tree);
+#define LEAF &sentinel           // all leafs are sentinels
+static TREE sentinel = { LEAF, LEAF, 0, BLACK};
+
+/*
+A binary search tree is a red-black tree if:
+
+1. Every node is either red or black.
+2. Every leaf (nil) is black.
+3. If a node is red, then both its children are black.
+4. Every simple path from a node to a descendant leaf contains the same
+   number of black nodes.
+*/
 
 /*
  *@@ treeInit:
- *      initializes an empty tree. The data on the
- *      tree will be invalid, and no memory will be
- *      freed.
- *
- *      Usage:
- +          TREE *TreeRoot;
- +          treeInit(&TreeRoot);
+ *      initializes the root of a tree.
+ *
  */
 
 void treeInit(TREE **root)
 {
-    *root = TREE_NULL;
-}
-
-/*
- * fnCompareIDs:
- *
- *added V0.9.9 (2001-02-06) [umoeller]
- */
-
-int TREEENTRY fnCompareIDs(unsigned long id1, unsigned long id2)
-{
-    if (id1 < id2)
+    *root = LEAF;
+}
+
+/*
+ *@@ treeCompareKeys:
+ *      standard comparison func if the TREE.ulKey
+ *      field really is a ULONG.
+ */
+
+int TREEENTRY treeCompareKeys(unsigned long  ul1, unsigned long ul2)
+{
+    if (ul1 < ul2)
         return -1;
-    if (id1 > id2)
+    if (ul1 > ul2)
         return +1;
     return (0);
@@ -219,750 +312,604 @@
 
 /*
- *@@ treeInsertID:
- *      inserts a node into an existing tree.
- *
- *      Note: A tree node MUST contain a TREE structure
- *      at the beginning for the tree functions to work.
- *      So to create a tree node with usable data, do this:
- *
- +          typedef _MYTREENODE
- +          {
- +              // TREE must be at beginning
- +              TREE        Tree;
- +              // now use whatever you want
- +              CHAR        szMyExtraData[100];
- +          } MYTREENODE, *PMYTREENODE;
- *
- *      When calling the tree functions, manually cast your
- *      MYTREENODE pointers to (TREE*).
- *
- *      This function initialises the node pointers and colour
- *      in the TREE structure to correct values, so the caller
- *      does not have to worry about those.
- *
- *      However you must initialize the TREE.id member correctly
- *      so that your comparison function can compare on that
- *      to find the correct place in the tree to insert the node.
- *
- *      Usage:
- +          TREE *TreeRoot;
- +          treeInit(&TreeRoot);
- +
- +          PMYTREENODE pTreeItem = malloc(sizeof(MYTREENODE));
- +          pTreeItem->Tree.id = 1;
- +
- +          treeInsertID(&TreeRoot,
- +                       (TREE*)pTreeItem,
- +                       FALSE);
- *
- *      Returns:
- *
- *      -- TREE_OK: OK, item inserted.
- *
- *      -- TREE_DUPLICATE: if (fAllowDuplicates == FALSE), this is
- *          returned if a tree item with the specified ID already
- *          exists.
- *
- *@@changed V0.9.9 (2001-02-06) [umoeller]: removed comparison func
- */
-
-int treeInsertID(TREE **root,             // in: root of tree
-                 TREE *tree,              // in: new tree node
-                 BOOL fAllowDuplicates)   // in: whether duplicates with the same ID are allowed
+ *@@ rotateLeft:
+ *      private function during rebalancing.
+ */
+
+static void rotateLeft(TREE **root,
+                       TREE *x)
+{
+   /**************************
+    *  rotate node x to left *
+    **************************/
+
+    TREE *y = x->right;
+
+    // establish x->right link
+    x->right = y->left;
+    if (y->left != LEAF)
+        y->left->parent = x;
+
+    // establish y->parent link
+    if (y != LEAF)
+        y->parent = x->parent;
+    if (x->parent)
+    {
+        if (x == x->parent->left)
+            x->parent->left = y;
+        else
+            x->parent->right = y;
+    }
+    else
+        *root = y;
+
+    // link x and y
+    y->left = x;
+    if (x != LEAF)
+        x->parent = y;
+}
+
+/*
+ *@@ rotateRight:
+ *      private function during rebalancing.
+ */
+
+static void rotateRight(TREE **root,
+                        TREE *x)
+{
+
+   /****************************
+    *  rotate node x to right  *
+    ****************************/
+
+    TREE *y = x->left;
+
+    // establish x->left link
+    x->left = y->right;
+    if (y->right != LEAF)
+        y->right->parent = x;
+
+    // establish y->parent link
+    if (y != LEAF)
+        y->parent = x->parent;
+    if (x->parent)
+    {
+        if (x == x->parent->right)
+            x->parent->right = y;
+        else
+            x->parent->left = y;
+    }
+    else
+        *root = y;
+
+    // link x and y
+    y->right = x;
+    if (x != LEAF)
+        x->parent = y;
+}
+
+/*
+ *@@ insertFixup:
+ *      private function during rebalancing.
+ */
+
+static void insertFixup(TREE **root,
+                        TREE *x)
+{
+   /*************************************
+    *  maintain Red-Black tree balance  *
+    *  after inserting node x           *
+    *************************************/
+
+    // check Red-Black properties
+    while (    x != *root
+            && x->parent->color == RED
+          )
+    {
+        // we have a violation
+        if (x->parent == x->parent->parent->left)
+        {
+            TREE *y = x->parent->parent->right;
+            if (y->color == RED)
+            {
+                // uncle is RED
+                x->parent->color = BLACK;
+                y->color = BLACK;
+                x->parent->parent->color = RED;
+                x = x->parent->parent;
+            }
+            else
+            {
+                // uncle is BLACK
+                if (x == x->parent->right)
+                {
+                    // make x a left child
+                    x = x->parent;
+                    rotateLeft(root,
+                               x);
+                }
+
+                // recolor and rotate
+                x->parent->color = BLACK;
+                x->parent->parent->color = RED;
+                rotateRight(root,
+                            x->parent->parent);
+            }
+        }
+        else
+        {
+            // mirror image of above code
+            TREE *y = x->parent->parent->left;
+            if (y->color == RED)
+            {
+                // uncle is RED
+                x->parent->color = BLACK;
+                y->color = BLACK;
+                x->parent->parent->color = RED;
+                x = x->parent->parent;
+            }
+            else
+            {
+                // uncle is BLACK
+                if (x == x->parent->left)
+                {
+                    x = x->parent;
+                    rotateRight(root,
+                                x);
+                }
+                x->parent->color = BLACK;
+                x->parent->parent->color = RED;
+                rotateLeft(root,
+                           x->parent->parent);
+            }
+        }
+    }
+    (*root)->color = BLACK;
+}
+
+/*
+ *@@ treeInsert:
+ *      inserts a new tree node into the specified
+ *      tree, using the specified comparison function
+ *      for sorting.
+ *
+ *      "x" specifies the new tree node which must
+ *      have been allocated by the caller. x->ulKey
+ *      must already contain the node's key (data).
+ *      This function will then set the parent,
+ *      left, right, and color members.
+ *
+ *      Returns 0 if no error. Might return
+ *      STATUS_DUPLICATE_KEY if a node with the
+ *      same ulKey already exists.
+ */
+
+int treeInsert(TREE **root,                     // in: root of the tree
+               TREE *x,                         // in: new node to insert
+               FNTREE_COMPARE *pfnCompare)      // in: comparison func
 {
     TREE    *current,
             *parent;
-    int     last_comp = 0;
-
-    // find where node belongs
+
+    unsigned long key = x->ulKey;
+
+    // find future parent
     current = *root;
-    parent  = NULL;
-    while (current != TREE_NULL)
-    {
-        parent  = current;
-        last_comp = fnCompareIDs(tree->id, current->id);
-        switch (last_comp)
-        {
-            case -1: current = current->left;  break;
-            case  1: current = current->right; break;
-            default:
-                if (fAllowDuplicates)
-                    current = current->left;
-                else
-                    return TREE_DUPLICATE;
-        }
+    parent = 0;
+
+    while (current != LEAF)
+    {
+        int iResult;
+        if (0 == (iResult = pfnCompare(key, current->ulKey))) // if (compEQ(key, current->key))
+            return STATUS_DUPLICATE_KEY;
+        parent = current;
+        current = (iResult < 0)    // compLT(key, current->key)
+                    ? current->left
+                    : current->right;
     }
 
     // set up new node
-    ((TREE*)tree)->parent = parent;
-    ((TREE*)tree)->left   = TREE_NULL;
-    ((TREE*)tree)->right  = TREE_NULL;
-    ((TREE*)tree)->colour = RED;
+    /* if ((x = malloc (sizeof(*x))) == 0)
+        return STATUS_MEM_EXHAUSTED; */
+    x->parent = parent;
+    x->left = LEAF;
+    x->right = LEAF;
+    x->color = RED;
+    // x->key = key;
+    // x->rec = *rec;
 
     // insert node in tree
     if (parent)
-        switch (last_comp)
-        {
-            case  1: parent->right = tree;  break;
-            default: parent->left  = tree;
-        }
+    {
+        if (pfnCompare(key, parent->ulKey) < 0) // (compLT(key, parent->key))
+            parent->left = x;
+        else
+            parent->right = x;
+    }
     else
-        *root = tree;
-
-    insert_fixup(root, tree);
-    return(TREE_OK);
-}
-
-/*
- *@@ treeInsertNode:
- *      similar to treeInsertID, but this uses
- *      a comparision function which compares
- *      nodes.
- */
-
-int treeInsertNode(TREE **root,             // in: root of tree
-                   TREE *tree,              // in: new tree node
-                   FNTREE_COMPARE_NODES *comp,  // in: comparison function
-                   BOOL fAllowDuplicates)   // in: whether duplicates with the same ID are allowed
-{
-    TREE
-       *current,
-       *parent;
-    int
-        last_comp = 0;
-
-    // find where node belongs
-    current = *root;
-    parent  = NULL;
-    while (current != TREE_NULL)
-    {
-        parent  = current;
-        last_comp = comp(tree, current);
-        switch (last_comp)
-        {
-            case -1: current = current->left;  break;
-            case  1: current = current->right; break;
-            default: if (fAllowDuplicates)
-                         current = current->left;
-                     else
-                         return TREE_DUPLICATE;
-
-        }
-    }
-
-    // set up new node
-    ((TREE*)tree)->parent = parent;
-    ((TREE*)tree)->left   = TREE_NULL;
-    ((TREE*)tree)->right  = TREE_NULL;
-    ((TREE*)tree)->colour = RED;
-
-    // insert node in tree
-    if (parent)
-        switch (last_comp)
-        {
-            case  1: parent->right = tree;  break;
-            default: parent->left  = tree;
-        }
+        *root = x;
+
+    insertFixup(root,
+                x);
+    // lastFind = NULL;
+
+    return STATUS_OK;
+}
+
+/*
+ *@@ deleteFixup:
+ *
+ */
+
+static void deleteFixup(TREE **root,
+                        TREE *tree)
+{
+    TREE    *s;
+
+    while (    tree != *root
+            && tree->color == BLACK
+          )
+    {
+        if (tree == tree->parent->left)
+        {
+            s = tree->parent->right;
+            if (s->color == RED)
+            {
+                s->color = BLACK;
+                tree->parent->color = RED;
+                rotateLeft(root, tree->parent);
+                s = tree->parent->right;
+            }
+            if (    (s->left->color == BLACK)
+                 && (s->right->color == BLACK)
+               )
+            {
+                s->color = RED;
+                tree = tree->parent;
+            }
+            else
+            {
+                if (s->right->color == BLACK)
+                {
+                    s->left->color = BLACK;
+                    s->color = RED;
+                    rotateRight(root, s);
+                    s = tree->parent->right;
+                }
+                s->color = tree->parent->color;
+                tree->parent->color = BLACK;
+                s->right->color = BLACK;
+                rotateLeft(root, tree->parent);
+                tree = *root;
+            }
+        }
+        else
+        {
+            s = tree->parent->left;
+            if (s->color == RED)
+            {
+                s->color = BLACK;
+                tree->parent->color = RED;
+                rotateRight(root, tree->parent);
+                s = tree->parent->left;
+            }
+            if (    (s->right->color == BLACK)
+                 && (s->left->color == BLACK)
+               )
+            {
+                s->color = RED;
+                tree = tree->parent;
+            }
+            else
+            {
+                if (s->left->color == BLACK)
+                {
+                    s->right->color = BLACK;
+                    s->color = RED;
+                    rotateLeft(root, s);
+                    s = tree->parent->left;
+                }
+                s->color = tree->parent->color;
+                tree->parent->color = BLACK;
+                s->left->color = BLACK;
+                rotateRight (root, tree->parent);
+                tree = *root;
+            }
+        }
+    }
+    tree->color = BLACK;
+
+   /*************************************
+    *  maintain Red-Black tree balance  *
+    *  after deleting node x            *
+    *************************************/
+
+    /* while (    x != *root
+            && x->color == BLACK
+          )
+    {
+        if (x == x->parent->left)
+        {
+            TREE *w = x->parent->right;
+            if (w->color == RED)
+            {
+                w->color = BLACK;
+                x->parent->color = RED;
+                rotateLeft(root,
+                           x->parent);
+                w = x->parent->right;
+            }
+            if (    w->left->color == BLACK
+                 && w->right->color == BLACK
+               )
+            {
+                w->color = RED;
+                x = x->parent;
+            }
+            else
+            {
+                if (w->right->color == BLACK)
+                {
+                    w->left->color = BLACK;
+                    w->color = RED;
+                    rotateRight(root,
+                                w);
+                    w = x->parent->right;
+                }
+                w->color = x->parent->color;
+                x->parent->color = BLACK;
+                w->right->color = BLACK;
+                rotateLeft(root,
+                           x->parent);
+                x = *root;
+            }
+        }
+        else
+        {
+            TREE *w = x->parent->left;
+            if (w->color == RED)
+            {
+                w->color = BLACK;
+                x->parent->color = RED;
+                rotateRight(root,
+                            x->parent);
+                w = x->parent->left;
+            }
+            if (    w->right->color == BLACK
+                 && w->left->color == BLACK
+               )
+            {
+                w->color = RED;
+                x = x->parent;
+            }
+            else
+            {
+                if (w->left->color == BLACK)
+                {
+                    w->right->color = BLACK;
+                    w->color = RED;
+                    rotateLeft(root,
+                               w);
+                    w = x->parent->left;
+                }
+                w->color = x->parent->color;
+                x->parent->color = BLACK;
+                w->left->color = BLACK;
+                rotateRight(root,
+                            x->parent);
+                x = *root;
+            }
+        }
+    }
+    x->color = BLACK; */
+}
+
+/*
+ *@@ treeDelete:
+ *      removes the specified node from the tree.
+ *      Does not free() the node though.
+ *
+ *      Returns 0 if the node was deleted or
+ *      STATUS_INVALID_NODE if not.
+ */
+
+int treeDelete(TREE **root,         // in: root of the tree
+               TREE *tree)          // in: tree node to delete
+{
+    TREE        *y,
+                *d;
+    nodeColor   color;
+
+    if (    (!tree)
+         || (tree == LEAF)
+       )
+        return STATUS_INVALID_NODE;
+
+    if (    (tree->left  == LEAF)
+         || (tree->right == LEAF)
+       )
+        // d has a TREE_NULL node as a child
+        d = tree;
     else
-        *root = tree;
-
-    insert_fixup(root, tree);
-    return(TREE_OK);
-}
-
-/*
- * insert_fixup:
- *      maintains the Red-Black tree balance after a node has been inserted.
- *
- *      Private function.
- */
-
-static void insert_fixup(TREE **root,
-                         TREE *tree)
-{
-    TREE *uncle;
-
-    // check red-black properties
-    while ((tree != *root)
-       &&  (tree->parent->colour == RED))
-    {
-        // we have a violation
-        if (tree->parent == tree->parent->parent->left)
-        {
-            uncle = tree->parent->parent->right;
-            if (uncle->colour == RED)
-            {
-                // uncle is RED
-                tree ->parent->colour = BLACK;
-                uncle->colour = BLACK;
-                tree ->parent->parent->colour = RED;
-
-                tree = tree->parent->parent;
-            }
+    {
+        // find tree successor with a TREE_NULL node as a child
+        d = tree->right;
+        while (d->left != LEAF)
+            d = d->left;
+    }
+
+    // y is d's only child, if there is one, else TREE_NULL
+    if (d->left != LEAF)
+        y = d->left;
+    else
+        y = d->right;
+
+    // remove d from the parent chain
+    if (y != LEAF)
+        y->parent = d->parent;
+    if (d->parent)
+    {
+        if (d == d->parent->left)
+            d->parent->left  = y;
+        else
+            d->parent->right = y;
+    }
+    else
+        *root = y;
+
+    color = d->color;
+
+    if (d != tree)
+    {
+        // move the data from d to tree; we do this by
+        // linking d into the structure in the place of tree
+        d->left   = tree->left;
+        d->right  = tree->right;
+        d->parent = tree->parent;
+        d->color  = tree->color;
+
+        if (d->parent)
+        {
+            if (tree == d->parent->left)
+                d->parent->left  = d;
             else
-            {
-                // uncle is BLACK
-                if (tree == tree->parent->right)
-                {
-                    // make tree a left child
-                    tree = tree->parent;
-                    rotate_left (root, tree);
-                }
-
-                // recolor and rotate
-                tree->parent->colour = BLACK;
-                tree->parent->parent->colour = RED;
-                rotate_right (root, tree->parent->parent);
-            }
+                d->parent->right = d;
         }
         else
-        {
-            // mirror image of above code
-            uncle = tree->parent->parent->left;
-            if (uncle->colour == RED)
-            {
-                // uncle is RED
-                tree ->parent->colour = BLACK;
-                uncle->colour = BLACK;
-                tree ->parent->parent->colour = RED;
-
-                tree = tree->parent->parent;
-            }
+            *root = d;
+
+        if (d->left != LEAF)
+            d->left->parent = d;
+
+        if (d->right != LEAF)
+            d->right->parent = d;
+    }
+
+    if (    (y != LEAF)
+         && (color == BLACK)
+       )
+        deleteFixup(root,
+                    y);
+
+    return (STATUS_OK);
+
+    /* TREE    *x,
+            *y; */
+            // *z;
+
+   /*****************************
+    *  delete node z from tree  *
+    *****************************/
+
+    // find node in tree
+    /* if (lastFind && compEQ(lastFind->key, key))
+        // if we just found node, use pointer
+        z = lastFind;
+    else {
+        z = *root;
+        while(z != LEAF)
+        {
+            int iResult = pfnCompare(key, z->key);
+            if (iResult == 0)
+            // if(compEQ(key, z->key))
+                break;
             else
-            {
-                // uncle is BLACK
-                if (tree == tree->parent->left)
-                {
-                    tree = tree->parent;
-                    rotate_right (root, tree);
-                }
-                tree->parent->colour = BLACK;
-                tree->parent->parent->colour = RED;
-                rotate_left (root, tree->parent->parent);
-            }
-        }
-    }
-    (*root)->colour = BLACK;
-}
-
-/*
- * rotate_left:
- *      rotates tree to left.
- *
- *      Private function.
- */
-
-static void rotate_left(TREE **root,
-                        TREE *tree)
-{
-    TREE *other = tree->right;
-
-    // establish tree->right link
-    tree->right = other->left;
-    if (other->left != TREE_NULL)
-        other->left->parent = tree;
-
-    // establish other->parent link
-    if (other != TREE_NULL)
-        other->parent = tree->parent;
-
-    if (tree->parent)
-    {
-        if (tree == tree->parent->left)
-            tree->parent->left  = other;
+                z = (iResult < 0) // compLT(key, z->key)
+                    ? z->left
+                    : z->right;
+        }
+        if (z == LEAF)
+            return STATUS_KEY_NOT_FOUND;
+    }
+
+    if (    z->left == LEAF
+         || z->right == LEAF
+       )
+    {
+        // y has a LEAF node as a child
+        y = z;
+    }
+    else
+    {
+        // find tree successor with a LEAF node as a child
+        y = z->right;
+        while (y->left != LEAF)
+            y = y->left;
+    }
+
+    // x is y's only child
+    if (y->left != LEAF)
+        x = y->left;
+    else
+        x = y->right;
+
+    // remove y from the parent chain
+    x->parent = y->parent;
+    if (y->parent)
+        if (y == y->parent->left)
+            y->parent->left = x;
         else
-            tree->parent->right = other;
-    }
+            y->parent->right = x;
     else
-        *root = other;
-
-    // link tree and other
-    other->left = tree;
-    if (tree != TREE_NULL)
-        tree->parent = other;
-}
-
-/*
- * rotate_right:
- *      rotates tree to right.
- *
- *      Private function.
- */
-
-static void rotate_right(TREE **root,
-                         TREE *tree)
-{
-    TREE *other;
-
-    other = tree->left;
-
-    // establish tree->left link
-    tree->left = other->right;
-    if (other->right != TREE_NULL)
-        other->right->parent = tree;
-
-    // establish other->parent link
-    if (other != TREE_NULL)
-        other->parent = tree->parent;
-
-    if (tree->parent)
-    {
-        if (tree == tree->parent->right)
-            tree->parent->right = other;
+        *root = x;
+
+    // y is about to be deleted...
+
+    if (y != z)
+    {
+        // now, the original code simply copied the data
+        // from y to z... we can't safely do that since
+        // we don't know about the real data in the
+        // caller's TREE structure
+        z->ulKey = y->ulKey;
+        // z->rec = y->rec;         // hope this works...
+                                    // the original implementation used rec
+                                    // for the node's data
+
+        if (cbStruct > sizeof(TREE))
+        {
+            memcpy(((char*)&z) + sizeof(TREE),
+                   ((char*)&y) + sizeof(TREE),
+                   cbStruct - sizeof(TREE));
+        }
+    }
+
+    if (y->color == BLACK)
+        deleteFixup(root,
+                    x);
+
+    // free(y);
+    // lastFind = NULL;
+
+    return STATUS_OK; */
+}
+
+/*
+ *@@ treeFind:
+ *      finds the tree node with the specified key.
+ */
+
+TREE* treeFind(TREE *root,                    // in: root of the tree
+               unsigned long key,             // in: key to find
+               FNTREE_COMPARE *pfnCompare)    // in: comparison func
+{
+   /*******************************
+    *  find node containing data  *
+    *******************************/
+
+    TREE *current = root;
+    while (current != LEAF)
+    {
+        int iResult;
+        if (0 == (iResult = pfnCompare(key, current->ulKey)))
+            return (current);
         else
-            tree->parent->left  = other;
-    }
-    else
-        *root = other;
-
-    // link tree and other
-    other->right = tree;
-    if (tree != TREE_NULL)
-        tree->parent = other;
-}
-
-/*
- *@@ treeDelete:
- *      deletes a node from a tree. Does not deallocate any memory.
- *
- *      Returns:
- *
- *      -- TREE_OK: node deleted.
- *      -- TREE_INVALID_NODE: tree node not found.
- */
-
-int treeDelete(TREE **root,     // in: root of tree
-               TREE *tree)      // in: tree node to delete
-{
-    int irc = TREE_OK;
-
-    TREE
-       *youngest, *descendent;
-    TREE_COLOUR
-        colour;
-
-    if (    (!tree)
-         || (tree == TREE_NULL)
-       )
-        return TREE_INVALID_NODE;
-
-    if (    (((TREE*)tree)->left  == TREE_NULL)
-         || (((TREE*)tree)->right == TREE_NULL)
-       )
-        // descendent has a TREE_NULL node as a child
-        descendent = tree;
-    else
-  {
-        // find tree successor with a TREE_NULL node as a child
-        descendent = ((TREE*)tree)->right;
-        while (descendent->left != TREE_NULL)
-            descendent = descendent->left;
-  }
-
-    // youngest is descendent's only child, if there is one, else TREE_NULL
-    if (descendent->left != TREE_NULL)
-        youngest = descendent->left;
-    else
-        youngest = descendent->right;
-
-    // remove descendent from the parent chain
-    if (youngest != TREE_NULL)
-        youngest->parent = descendent->parent;
-    if (descendent->parent)
-    {
-        if (descendent == descendent->parent->left)
-            descendent->parent->left  = youngest;
-        else
-            descendent->parent->right = youngest;
-    }
-    else
-        *root = youngest;
-
-    colour = descendent->colour;
-
-    if (descendent != (TREE *) tree)
-    {
-        // Conceptually what we are doing here is moving the data from
-        // descendent to tree.  In fact we do this by linking descendent
-        // into the structure in the place of tree.
-        descendent->left   = ((TREE*)tree)->left;
-        descendent->right  = ((TREE*)tree)->right;
-        descendent->parent = ((TREE*)tree)->parent;
-        descendent->colour = ((TREE*)tree)->colour;
-
-        if (descendent->parent)
-        {
-            if (tree == descendent->parent->left)
-                descendent->parent->left  = descendent;
-            else
-                descendent->parent->right = descendent;
-        }
-        else
-            *root = descendent;
-
-        if (descendent->left != TREE_NULL)
-            descendent->left->parent = descendent;
-
-        if (descendent->right != TREE_NULL)
-            descendent->right->parent = descendent;
-    }
-
-    if (    (youngest != TREE_NULL)
-        &&  (colour   == BLACK))
-        delete_fixup (root, youngest);
-
-    return (irc);
-}
-
-/*
- *@@ delete_fixup:
- *      maintains Red-Black tree balance after deleting a node.
- *
- *      Private function.
- */
-
-static void delete_fixup(TREE **root,
-                         TREE *tree)
-{
-    TREE
-       *sibling;
-
-    while (tree != *root && tree->colour == BLACK)
-    {
-        if (tree == tree->parent->left)
-        {
-            sibling = tree->parent->right;
-            if (sibling->colour == RED)
-            {
-                sibling->colour = BLACK;
-                tree->parent->colour = RED;
-                rotate_left (root, tree->parent);
-                sibling = tree->parent->right;
-            }
-            if ((sibling->left->colour == BLACK)
-            &&  (sibling->right->colour == BLACK))
-            {
-                sibling->colour = RED;
-                tree = tree->parent;
-            }
-            else
-            {
-                if (sibling->right->colour == BLACK)
-                {
-                    sibling->left->colour = BLACK;
-                    sibling->colour = RED;
-                    rotate_right (root, sibling);
-                    sibling = tree->parent->right;
-                }
-                sibling->colour = tree->parent->colour;
-                tree->parent->colour = BLACK;
-                sibling->right->colour = BLACK;
-                rotate_left (root, tree->parent);
-                tree = *root;
-            }
-        }
-        else
-        {
-            sibling = tree->parent->left;
-            if (sibling->colour == RED)
-            {
-                sibling->colour = BLACK;
-                tree->parent->colour = RED;
-                rotate_right (root, tree->parent);
-                sibling = tree->parent->left;
-            }
-            if ((sibling->right->colour == BLACK)
-            &&  (sibling->left->colour == BLACK))
-            {
-                sibling->colour = RED;
-                tree = tree->parent;
-            }
-            else
-            {
-                if (sibling->left->colour == BLACK)
-                {
-                    sibling->right->colour = BLACK;
-                    sibling->colour = RED;
-                    rotate_left (root, sibling);
-                    sibling = tree->parent->left;
-                }
-                sibling->colour = tree->parent->colour;
-                tree->parent->colour = BLACK;
-                sibling->left->colour = BLACK;
-                rotate_right (root, tree->parent);
-                tree = *root;
-            }
-        }
-    }
-    tree->colour = BLACK;
-}
-
-/*
- *@@ treeFindEQID:
- *      finds a node with ID exactly matching that provided.
- *      Returns NULL if not found.
- */
-
-void* treeFindEQID(TREE **root,
-                   unsigned long id)
-{
-    TREE    *current = *root,
-            *found = NULL;
-
-    while (current != TREE_NULL)
-        switch (fnCompareIDs(current->id, id))
-        {
-            case -1: current = current->right; break;
-            case  1: current = current->left;  break;
-            default: found = current;           //  In case of duplicates,
-                     current = current->left;  //  get the first one.
-        }
-
-    return found;
-}
-
-/*
- *@@ treeFindGEID:
- *      finds a node with ID greater than or equal to provided.
- *      To find a tree node, your comparison function must
- *      compare the tree node IDs.
- */
-
-void* treeFindGEID(TREE **root,
-                   unsigned long idFind)
-{
-    TREE    *current = *root,
-            *found;
-
-    found = NULL;
-    while (current != TREE_NULL)
-        switch (fnCompareIDs(current->id, idFind))
-        {
-            case -1: current = current->right; break;
-            default: found = current;
-                     current = current->left;
-        }
-
-    return found;
-}
-
-/*
- *@@ treeFindEQNode:
- *      finds a node with ID exactly matching that provided.
- *      To find a tree node, your comparison function must
- *      compare the tree nodes.
- */
-
-void* treeFindEQNode(TREE **root,
-                     TREE *nodeFind,
-                     FNTREE_COMPARE_NODES *comp)
-{
-    TREE    *current = *root,
-            *found;
-
-    found = NULL;
-    while (current != TREE_NULL)
-        switch (comp(current, nodeFind))
-        {
-            case -1: current = current->right; break;
-            case  1: current = current->left;  break;
-            default: found = current;           //  In case of duplicates,
-                     current = current->left;  //  get the first one.
-        }
-
-    return found;
-}
-
-/*
- *@@ treeFindGENode:
- *      finds a node with ID greater than or equal to provided.
- *      To find a tree node, your comparison function must
- *      compare the tree nodes.
- */
-
-void* treeFindGENode(TREE **root,
-                     TREE *nodeFind,
-                     FNTREE_COMPARE_NODES *comp)
-{
-    TREE    *current = *root,
-            *found;
-
-    found = NULL;
-    while (current != TREE_NULL)
-        switch (comp(current, nodeFind))
-        {
-            case -1: current = current->right; break;
-            default: found = current;
-                     current = current->left;
-        }
-
-    return found;
-}
-
-/*
- *@@ treeFindLTNode:
- *      finds a node with Node less than provided.
- *      To find a tree node, your comparison function must
- *      compare the tree nodes.
- */
-
-void* treeFindLTNode(TREE **root,
-                     TREE *nodeFind,
-                     FNTREE_COMPARE_NODES *comp)
-{
-    TREE    *current = *root,
-            *found;
-
-    found = NULL;
-    while (current != TREE_NULL)
-        switch (comp(current, nodeFind))
-        {
-            case -1: found = current;
-                     current = current->right; break;
-            default: current = current->left;
-        }
-
-    return found;
-}
-
-/*
- *@@ treeFindLENode:
- *      finds a node with Node less than or equal to provided.
- *      To find a tree node, your comparison function must
- *      compare the tree nodes.
- */
-
-void* treeFindLENode(TREE **root,
-                     TREE *nodeFind,
-                     FNTREE_COMPARE_NODES *comp)
-{
-    TREE    *current = *root,
-            *found;
-
-    found = NULL;
-    while (current != TREE_NULL)
-        switch (comp(current, nodeFind))
-        {
-            case 1 : current = current->left;  break;
-            default: found = current;
-                     current = current->right;
-        }
-
-    return found;
-}
-
-/*
- *@@ treeFindGTNode:
- *      finds a node with Node greater than provided.
- *      To find a tree node, your comparison function must
- *      compare the tree nodes.
- */
-
-void* treeFindGTNode(TREE **root,
-                     TREE *nodeFind,
-                     FNTREE_COMPARE_NODES *comp)
-{
-    TREE    *current = *root,
-            *found;
-
-    found = NULL;
-    while (current != TREE_NULL)
-        switch (comp(current, nodeFind))
-        {
-            case 1 : found = current;
-                     current = current->left; break;
-            default: current = current->right;
-        }
-
-    return found;
-}
-
-/*
- *@@ treeFindEQID:
- *      finds a node with data exactly matching that provided.
- *      To find a tree node, your comparison function must
- *      compare a tree member with external data.
- *
- *      This is useful for finding a tree item from a string ID.
- *
- *      Make sure to use treeInsertNode and compare according
- *      to a string member, and then write a second compare
- *      function for this function which compares the string
- *      member to an external string.
- */
-
-void* treeFindEQData(TREE **root,
-                     void *pData,
-                     FNTREE_COMPARE_DATA *comp)
-{
-    TREE    *current = *root,
-            *found = NULL;
-
-    while (current != TREE_NULL)
-        switch (comp(current, pData))
-        {
-            case -1: current = current->right; break;
-            case  1: current = current->left;  break;
-            default: found = current;           //  In case of duplicates,
-                     current = current->left;  //  get the first one.
-        }
-
-    return found;
-}
-
-/*
- *@@ treeTraverse:
- *      traverses the specified tree, calling a processing function
- *      for each tree node.
- *
- *      The processing function ("process") must be declared as
- *      follows:
- *
- +          void fnProcess(TREE *t,         // current tree node
- +                         void *pUser);    // user data
- *
- *      and will receive the "pUser" parameter, which you can use
- *      as a data pointer to some structure for whatever you like.
- *
- *      WARNING: This function recurses and can use up a lot of
- *      stack. For very deep trees, traverse the tree using
- *      treeFirst and treeNext instead. See treeNext for a sample.
- *
- *      "method" specifies in which order the nodes are traversed.
- *      This can be:
- *
- *      -- 1: current node first, then left node, then right node.
- *      -- 2: left node first, then right node, then current node.
- *      -- 0 or other: left node first, then current node, then right node.
- *           This is the sorted order.
- */
-
-void treeTraverse(TREE *tree,               // in: root of tree
-                  TREE_PROCESS *process,    // in: callback for each node
-                  void *pUser,              // in: user param for callback
-                  int method)               // in: traversal mode
-{
-    if (    (!tree)
-         || (tree == TREE_NULL))
-        return;
-
-    if (method == 1)
-    {
-        process(tree, pUser);
-        treeTraverse (((TREE*)tree)->left,  process, pUser, method);
-        treeTraverse (((TREE*)tree)->right, process, pUser, method);
-    }
-    else if (method == 2)
-    {
-        treeTraverse (((TREE*)tree)->left,  process, pUser, method);
-        treeTraverse (((TREE*)tree)->right, process, pUser, method);
-        process(tree, pUser);
-    }
-    else
-    {
-        treeTraverse (((TREE*)tree)->left,  process, pUser, method);
-        process(tree, pUser);
-        treeTraverse (((TREE*)tree)->right, process, pUser, method);
-    }
+        {
+            current = (iResult < 0) // compLT (key, current->key)
+                ? current->left
+                : current->right;
+        }
+    }
+
+    return 0;
 }
 
@@ -974,19 +921,18 @@
  */
 
-void* treeFirst(TREE *tree)
-{
-    TREE
-       *current;
-
-    if (    (!tree)
-         || (tree == TREE_NULL)
+TREE* treeFirst(TREE *r)
+{
+    TREE    *p;
+
+    if (    (!r)
+         || (r == LEAF)
        )
         return NULL;
 
-    current = tree;
-    while (current->left != TREE_NULL)
-        current = current->left;
-
-    return current;
+    p = r;
+    while (p->left != LEAF)
+        p = p->left;
+
+    return p;
 }
 
@@ -996,18 +942,17 @@
  */
 
-void* treeLast(TREE *tree)
-{
-    TREE
-       *current;
-
-    if (    (!tree)
-         || (tree == TREE_NULL))
+TREE* treeLast(TREE *r)
+{
+    TREE    *p;
+
+    if (    (!r)
+         || (r == LEAF))
         return NULL;
 
-    current = tree;
-    while (current->right != TREE_NULL)
-        current = current->right;
-
-    return current;
+    p = r;
+    while (p->right != LEAF)
+        p = p->right;
+
+    return p;
 }
 
@@ -1016,6 +961,5 @@
  *      finds and returns the next node in a tree.
  *
- *      Example for traversing a whole tree if you don't
- *      want to use treeTraverse:
+ *      Example for traversing a whole tree:
  *
  +          TREE    *TreeRoot;
@@ -1031,31 +975,30 @@
  */
 
-void* treeNext(TREE *tree)
-{
-    TREE
-       *current,
-       *child;
-
-    if (    (!tree)
-         || (tree == TREE_NULL)
+TREE* treeNext(TREE *r)
+{
+    TREE    *p,
+            *child;
+
+    if (    (!r)
+         || (r == LEAF)
        )
         return NULL;
 
-    current = tree;
-    if (current->right != TREE_NULL)
-        return treeFirst (current->right);
+    p = r;
+    if (p->right != LEAF)
+        return treeFirst (p->right);
     else
     {
-        current = tree;
-        child   = TREE_NULL;
-        while (    (current->parent)
-                && (current->right == child)
+        p = r;
+        child   = LEAF;
+        while (    (p->parent)
+                && (p->right == child)
               )
         {
-            child = current;
-            current = current->parent;
-        }
-        if (current->right != child)
-            return current;
+            child = p;
+            p = p->parent;
+        }
+        if (p->right != child)
+            return p;
         else
             return NULL;
@@ -1068,29 +1011,28 @@
  */
 
-void* treePrev(TREE *tree)
-{
-    TREE
-       *current,
-       *child;
-
-    if (    (!tree)
-         || (tree == TREE_NULL))
+TREE* treePrev(TREE *r)
+{
+    TREE    *p,
+            *child;
+
+    if (    (!r)
+         || (r == LEAF))
         return NULL;
 
-    current = tree;
-    if (current->left != TREE_NULL)
-        return treeLast (current->left);
+    p = r;
+    if (p->left != LEAF)
+        return treeLast (p->left);
     else
     {
-        current = tree;
-        child   = TREE_NULL;
-        while ((current->parent)
-           &&  (current->left == child))
-        {
-            child = current;
-            current = current->parent;
-        }
-        if (current->left != child)
-            return current;
+        p = r;
+        child   = LEAF;
+        while ((p->parent)
+           &&  (p->left == child))
+        {
+            child = p;
+            p = p->parent;
+        }
+        if (p->left != child)
+            return p;
         else
             return NULL;
@@ -1125,5 +1067,5 @@
  +          // add stuff to the tree
  +          TREE    *pNewNode = malloc(...);
- +          treeInsertID(&G_TreeRoot, pNewNode, FALSE)
+ +          treeInsert(&G_TreeRoot, pNewNode, fnCompare)
  +
  +          // now delete all nodes
@@ -1186,3 +1128,22 @@
 }
 
-
+/* void main(int argc, char **argv) {
+    int maxnum, ct;
+    recType rec;
+    keyType key;
+    statusEnum status;
+
+    maxnum = atoi(argv[1]);
+
+    printf("maxnum = %d\n", maxnum);
+    for (ct = maxnum; ct; ct--) {
+        key = rand() % 9 + 1;
+        if ((status = find(key, &rec)) == STATUS_OK) {
+            status = delete(key);
+            if (status) printf("fail: status = %d\n", status);
+        } else {
+            status = insert(key, &rec);
+            if (status) printf("fail: status = %d\n", status);
+        }
+    }
+} */
Index: trunk/src/helpers/winh.c
===================================================================
--- trunk/src/helpers/winh.c	(revision 82)
+++ trunk/src/helpers/winh.c	(revision 83)
@@ -120,4 +120,22 @@
 
     /*
+     *@@ winhSendDlgItemMsg:
+     *      wrapper for WinSendDlgItemMsg.
+     *
+     *      If WINH_STANDARDWRAPPERS is #defined before
+     *      including win.h, all WinSendMsg calls are
+     *      redefined to use this wrapper instead. This
+     *      reduces the amount of external fixups required
+     *      for loading the module.
+     *
+     *@@added V0.9.13 (2001-06-27) [umoeller]
+     */
+
+    MRESULT winhSendDlgItemMsg(HWND hwnd, ULONG id, ULONG msg, MPARAM mp1, MPARAM mp2)
+    {
+        return ((WinSendDlgItemMsg)(hwnd, id, msg, mp1, mp2));
+    }
+
+    /*
      *@@ winhPostMsg:
      *      wrapper for WinPostMsg.
@@ -240,46 +258,4 @@
  *
  ********************************************************************/
-
-/*
- *@@ winhSetDlgItemChecked:
- *      checks a check box.
- *
- *      This has been turned into a real function
- *      because this is used hundreds of times in
- *      XWP, and each WinSendDlgItemMsg in each
- *      macro produced a fixup relocation record.
- *
- *@@added V0.9.12 (2001-05-18) [umoeller]
- */
-
-BOOL winhSetDlgItemChecked(HWND hwnd,       // in: dialog
-                           SHORT id,        // in: dialog item ID
-                           SHORT bCheck)    // in: 0, 1, or (for tri-state) 2
-{
-    return ((BOOL)WinSendDlgItemMsg(hwnd,
-                                    id,
-                                    BM_SETCHECK,
-                                    MPFROMSHORT(bCheck),
-                                    MPNULL));
-}
-
-/*
- *@@ winhIsDlgItemChecked:
- *      returns the current check state of the
- *      specified check box, which can be 0, 1,
- *      or (for tri-state checkboxes) 2.
- *
- *@@added V0.9.12 (2001-05-18) [umoeller]
- */
-
-SHORT winhIsDlgItemChecked(HWND hwnd,       // in: dialog
-                           SHORT id)        // in: dialog item ID
-{
-    return (SHORT1FROMMR(WinSendDlgItemMsg(hwnd,
-                                           id,
-                                           BM_QUERYCHECK,
-                                           MPNULL,
-                                           MPNULL)));
-}
 
 /*
Index: trunk/src/helpers/xml.c
===================================================================
--- trunk/src/helpers/xml.c	(revision 82)
+++ trunk/src/helpers/xml.c	(revision 83)
@@ -276,5 +276,5 @@
 
 /*
- *@@ CompareNodeBaseNodes:
+ *@@ CompareXStrings:
  *      tree comparison func for NodeBases.
  *      This works for all trees which contain structures
@@ -293,25 +293,9 @@
  */
 
-int TREEENTRY CompareNodeBaseNodes(TREE *t1,
-                                  TREE *t2)
-{
-    PNODEBASE   p1 = (PNODEBASE)t1,
-                p2 = (PNODEBASE)t2;
-    return (strhcmp(p1->strNodeName.psz, p2->strNodeName.psz));
-}
-
-/*
- *@@ CompareNodeBaseNodes:
- *      tree comparison func for element declarations.
- *      Used to find nodes in _DOMDOCTYPENODE.ElementDeclsTree.
- *
- *@@added V0.9.9 (2001-02-16) [umoeller]
- */
-
-int TREEENTRY CompareNodeBaseData(TREE *t1,
-                                 void *pData)
-{
-    PNODEBASE     p1 = (PNODEBASE)t1;
-    return (strhcmp(p1->strNodeName.psz, (const char*)pData));
+int TREEENTRY CompareXStrings(ULONG ul1,
+                              ULONG ul2)
+{
+    return (strhcmp(((PXSTRING)ul1)->psz,
+                    ((PXSTRING)ul1)->psz));
 }
 
@@ -343,10 +327,13 @@
         pNewNode->ulNodeType = ulNodeType;
 
+        pNewNode->Tree.ulKey = (ULONG)&pNewNode->strNodeName;
+
         xstrInit(&pNewNode->strNodeName, 0);
         if (pcszNodeName)
+        {
             xstrcpy(&pNewNode->strNodeName,
                     pcszNodeName,
-                    ulNodeNameLength);  // if 0, xstrcpy will do strlen()
-
+                    ulNodeNameLength);
+        }
 
         *ppNew = pNewNode;
@@ -514,5 +501,5 @@
         lstClear(&llDeleteNodes);
 
-        xstrClear(&pNode->strNodeName);
+        xstrFree(((PXSTRING*)&pNode->Tree.ulKey));
         free(pNode);
     }
@@ -587,9 +574,7 @@
                 // attribute:
                 // add to parent's attributes list
-                if (treeInsertNode(&pParentNode->AttributesMap,
-                                   &pNewNode->NodeBase.Tree,
-                                   CompareNodeBaseNodes,
-                                   FALSE)      // no duplicates
-                        == TREE_DUPLICATE)
+                if (treeInsert(&pParentNode->AttributesMap,
+                               &pNewNode->NodeBase.Tree,
+                               CompareXStrings))
                     arc = ERROR_DOM_DUPLICATE_ATTRIBUTE;
                                 // shouldn't happen, because expat takes care of this
@@ -837,8 +822,8 @@
         PDOMDOCTYPENODE pNew = NULL;
         arc = xmlCreateDomNode((PDOMNODE)pDocumentNode,
-                            DOMNODE_DOCUMENT_TYPE,
-                            NULL,
-                            0,
-                            (PDOMNODE*)&pNew);
+                               DOMNODE_DOCUMENT_TYPE,
+                               pcszDoctypeName,
+                               0,
+                               (PDOMNODE*)&pNew);
 
         if (!arc)
@@ -854,15 +839,4 @@
             pNew->fHasInternalSubset = fHasInternalSubset;
 
-            if (pcszDoctypeName)
-            {
-                ULONG ul = strlen(pcszDoctypeName);
-                if (ul)
-                {
-                    xstrcpy(&pDocumentNode->DomNode.NodeBase.strNodeName,
-                            pcszDoctypeName,
-                            ul);
-                }
-            }
-
             treeInit(&pNew->ElementDeclsTree);
             treeInit(&pNew->AttribDeclBasesTree);
@@ -909,9 +883,8 @@
         case XML_CTYPE_NAME:   // that's easy
             pParticle->NodeBase.ulNodeType = ELEMENTPARTICLE_NAME;
-            xstrInitCopy(&pParticle->NodeBase.strNodeName, pModel->name, 0);
-            treeInsertNode(ppElementNamesTree,
-                           &pParticle->NodeBase.Tree,
-                           CompareNodeBaseNodes,
-                           TRUE);       // allow duplicates here
+            xstrcpy(&pParticle->NodeBase.strNodeName, pModel->name, 0);
+            treeInsert(ppElementNamesTree,
+                       &pParticle->NodeBase.Tree,
+                       CompareXStrings);
         break;
 
@@ -1097,14 +1070,14 @@
                 case ELEMENTPARTICLE_SEQ:
                 {
-                    const char *pcszNewElementName
-                        = pNewElement->NodeBase.strNodeName.psz;
+                    PXSTRING pstrNewElementName
+                        = &pNewElement->NodeBase.strNodeName;
 
                     // for all these, we first need to check if
                     // the element is allowed at all
                     PCMELEMENTPARTICLE pParticle
-                        = (PCMELEMENTPARTICLE)treeFindEQData(
-                                         &pParentElementDecl->ParticleNamesTree,
-                                         (void*)pcszNewElementName,
-                                         CompareNodeBaseData);
+                        = (PCMELEMENTPARTICLE)treeFind(
+                                         pParentElementDecl->ParticleNamesTree,
+                                         (ULONG)pstrNewElementName,
+                                         CompareXStrings);
                     if (!pParticle)
                         // not found: then this element is not allowed within this
@@ -1112,5 +1085,5 @@
                         xmlSetError(pDom,
                                     ERROR_DOM_INVALID_SUBELEMENT,
-                                    pcszNewElementName,
+                                    pstrNewElementName->psz,
                                     TRUE);
                     else
@@ -1249,8 +1222,8 @@
                 // enumeration: then check if it has one of the
                 // allowed values
-                PNODEBASE pValue = (PNODEBASE)treeFindEQData(
-                                                &pAttribDecl->ValuesTree,
-                                                (void*)pAttrib->pstrNodeValue->psz,
-                                                CompareNodeBaseData);
+                PNODEBASE pValue = (PNODEBASE)treeFind(
+                                                pAttribDecl->ValuesTree,
+                                                (ULONG)pAttrib->pstrNodeValue,
+                                                CompareXStrings);
                 if (!pValue)
                     xmlSetError(pDom,
@@ -1297,9 +1270,9 @@
         {
             // for all others , we need to find the attribute
-            PSZ pszAttrNameThis = pDeclThis->NodeBase.strNodeName.psz;
-            PDOMNODE pAttrNode = (PDOMNODE)treeFindEQData(
-                                            &pNewElement->AttributesMap,
-                                            (void*)pszAttrNameThis,
-                                            CompareNodeBaseData);
+            PXSTRING pstrAttrNameThis = &pDeclThis->NodeBase.strNodeName;
+            PDOMNODE pAttrNode = (PDOMNODE)treeFind(
+                                            pNewElement->AttributesMap,
+                                            (ULONG)pstrAttrNameThis,
+                                            CompareXStrings);
 
             // now switch again
@@ -1311,5 +1284,5 @@
                         xmlSetError(pDom,
                                     ERROR_DOM_REQUIRED_ATTRIBUTE_MISSING,
-                                    pszAttrNameThis,
+                                    pstrAttrNameThis->psz,
                                     TRUE);
                 break;
@@ -1871,9 +1844,7 @@
             {
                 // add this to the doctype's declarations tree
-                if (treeInsertNode(&pDocType->ElementDeclsTree,
-                                   (TREE*)pNew,
-                                   CompareNodeBaseNodes,
-                                   FALSE)
-                        == TREE_DUPLICATE)
+                if (treeInsert(&pDocType->ElementDeclsTree,
+                               (TREE*)pNew,
+                               CompareXStrings))
                     // element already declared:
                     // according to the XML specs, this is a validity
@@ -1906,8 +1877,7 @@
                                    &pNew);
     if (!arc)
-        treeInsertNode(&pDecl->ValuesTree,
-                       (TREE*)pNew,
-                       CompareNodeBaseNodes,
-                       FALSE);
+        treeInsert(&pDecl->ValuesTree,
+                   (TREE*)pNew,
+                   CompareXStrings);
 
     return (arc);
@@ -1978,9 +1948,13 @@
             if (!pThis)
             {
-                // cache didn't match: look up attributes tree then
-                pThis = (PCMATTRIBUTEDECLBASE)treeFindEQData(
-                                    &pDocType->AttribDeclBasesTree,
-                                    (void*)pcszElementName,
-                                    CompareNodeBaseData);
+                // cache didn't match: look up attributes tree then...
+                // note: cheap trick, we need an XSTRING for treeFind
+                // but don't want malloc, so we use xstrInitSet
+                XSTRING strElementName;
+                xstrInitSet(&strElementName, (PSZ)pcszElementName);
+                pThis = (PCMATTRIBUTEDECLBASE)treeFind(
+                                    pDocType->AttribDeclBasesTree,
+                                    (ULONG)&strElementName,
+                                    CompareXStrings);
 
                 if (!pThis)
@@ -1990,6 +1964,6 @@
                     pDom->arcDOM = xmlCreateNodeBase(ATTRIBUTE_DECLARATION_BASE,
                                                      sizeof(CMATTRIBUTEDECLBASE),
-                                                     pcszElementName,
-                                                     0,
+                                                     strElementName.psz,
+                                                     strElementName.ulLength,
                                                      (PNODEBASE*)&pThis);
                     if (!pDom->arcDOM)
@@ -1998,8 +1972,7 @@
                         treeInit(&pThis->AttribDeclsTree);
 
-                        treeInsertNode(&pDocType->AttribDeclBasesTree,
-                                       (TREE*)pThis,
-                                       CompareNodeBaseNodes,
-                                       FALSE);
+                        treeInsert(&pDocType->AttribDeclBasesTree,
+                                   (TREE*)pThis,
+                                   CompareXStrings);
                     }
                 }
@@ -2085,9 +2058,7 @@
                                 pNew->ulConstraint = CMAT_IMPLIED;
 
-                        if (treeInsertNode(&pThis->AttribDeclsTree,
-                                           (TREE*)pNew,
-                                           CompareNodeBaseNodes,
-                                           FALSE)
-                                == TREE_DUPLICATE)
+                        if (treeInsert(&pThis->AttribDeclsTree,
+                                       (TREE*)pNew,
+                                       CompareXStrings))
                             xmlSetError(pDom,
                                         ERROR_DOM_DUPLICATE_ATTRIBUTE_DECL,
@@ -2431,8 +2402,38 @@
 
 PCMELEMENTDECLNODE xmlFindElementDecl(PXMLDOM pDom,
-                                      const XSTRING *pstrElementName)
+                                      const XSTRING *pcstrElementName)
 {
     PCMELEMENTDECLNODE pElementDecl = NULL;
 
+    PDOMDOCTYPENODE pDocTypeNode = pDom->pDocTypeNode;
+    if (    (pDocTypeNode)
+         && (pcstrElementName)
+         && (pcstrElementName->ulLength)
+       )
+    {
+        pElementDecl = (PCMELEMENTDECLNODE)treeFind(
+                                      pDocTypeNode->ElementDeclsTree,
+                                      (ULONG)pcstrElementName,
+                                      CompareXStrings);
+    }
+
+    return (pElementDecl);
+}
+
+/*
+ *@@ xmlFindAttribDeclBase:
+ *      returns the _CMATTRIBUTEDECLBASE for the specified
+ *      element name, or NULL if none exists.
+ *
+ *      To find a specific attribute declaration from both
+ *      an element and an attribute name, use xmlFindAttribDecl
+ *      instead.
+ *
+ *@@added V0.9.9 (2001-02-16) [umoeller]
+ */
+
+PCMATTRIBUTEDECLBASE xmlFindAttribDeclBase(PXMLDOM pDom,
+                                           const XSTRING *pstrElementName)
+{
     PDOMDOCTYPENODE pDocTypeNode = pDom->pDocTypeNode;
     if (    (pDocTypeNode)
@@ -2441,43 +2442,11 @@
        )
     {
-        pElementDecl = (PCMELEMENTDECLNODE)treeFindEQData(
-                                      &pDocTypeNode->ElementDeclsTree,
-                                      (void*)pstrElementName->psz,
-                                      CompareNodeBaseData);
-    }
-
-    return (pElementDecl);
-}
-
-/*
- *@@ xmlFindAttribDeclBase:
- *      returns the _CMATTRIBUTEDECLBASE for the specified
- *      element name, or NULL if none exists.
- *
- *      To find a specific attribute declaration from both
- *      an element and an attribute name, use xmlFindAttribDecl
- *      instead.
- *
- *@@added V0.9.9 (2001-02-16) [umoeller]
- */
-
-PCMATTRIBUTEDECLBASE xmlFindAttribDeclBase(PXMLDOM pDom,
-                                           const XSTRING *pstrElementName)
-{
-    PCMATTRIBUTEDECLBASE pAttribDeclBase = NULL;
-
-    PDOMDOCTYPENODE pDocTypeNode = pDom->pDocTypeNode;
-    if (    (pDocTypeNode)
-         && (pstrElementName)
-         && (pstrElementName->ulLength)
-       )
-    {
-        pAttribDeclBase = (PCMATTRIBUTEDECLBASE)treeFindEQData(
-                                        &pDocTypeNode->AttribDeclBasesTree,
-                                        (void*)pstrElementName->psz,
-                                        CompareNodeBaseData);
-    }
-
-    return (pAttribDeclBase);
+        return ((PCMATTRIBUTEDECLBASE)treeFind(
+                                        pDocTypeNode->AttribDeclBasesTree,
+                                        (ULONG)pstrElementName,
+                                        CompareXStrings));
+    }
+
+    return (NULL);
 }
 
@@ -2498,5 +2467,4 @@
                                             // must be NULL on the first call
 {
-    PCMATTRIBUTEDECL pAttribDecl = NULL;
     if (pstrElementName && pstrAttribName)
     {
@@ -2507,12 +2475,12 @@
         if (*ppAttribDeclBase)
         {
-            pAttribDecl = (PCMATTRIBUTEDECL)treeFindEQData(
-                                         &((**ppAttribDeclBase).AttribDeclsTree),
-                                         (void*)pstrAttribName->psz,
-                                         CompareNodeBaseData);
-        }
-    }
-
-    return (pAttribDecl);
+            return ((PCMATTRIBUTEDECL)treeFind(
+                                         ((**ppAttribDeclBase).AttribDeclsTree),
+                                         (ULONG)pstrAttribName,
+                                         CompareXStrings));
+        }
+    }
+
+    return (NULL);
 }
 
@@ -2671,7 +2639,12 @@
                                const char *pcszAttribName)
 {
-    PDOMNODE pAttrNode = (PDOMNODE)treeFindEQData(&pElement->AttributesMap,
-                                                  (void*)pcszAttribName,
-                                                  CompareNodeBaseData);
+    XSTRING str;
+    PDOMNODE pAttrNode;
+    xstrInitSet(&str, (PSZ)pcszAttribName);
+    // note, cheap trick: no malloc here, but we need
+    // an XSTRING for treeFind
+    pAttrNode = (PDOMNODE)treeFind(pElement->AttributesMap,
+                                   (ULONG)&str,
+                                   CompareXStrings);
     if (pAttrNode)
         return (pAttrNode->pstrNodeValue);
