Index: trunk/src/helpers/cctl_chart.c
===================================================================
--- trunk/src/helpers/cctl_chart.c	(revision 62)
+++ trunk/src/helpers/cctl_chart.c	(revision 63)
@@ -340,6 +340,5 @@
                         // by halving each color component
                         gpihManipulateRGB(&ab.lColor,
-                                          1,        // multiplier
-                                          2);       // divisor
+                                          .5);       // factor
 
                     // set the area (fill) color
Index: trunk/src/helpers/configsys.c
===================================================================
--- trunk/src/helpers/configsys.c	(revision 62)
+++ trunk/src/helpers/configsys.c	(revision 63)
@@ -163,4 +163,5 @@
  *@@changed V0.9.7 (2001-01-15) [umoeller]: moved this here from stringh.c; renamed from strhFindKey
  *@@changed V0.9.7 (2001-01-15) [umoeller]: now checking for tabs too
+ *@@changed V0.9.11 (2001-04-25) [umoeller]: this never found lines which had leading spaces, fixed
  */
 
@@ -180,15 +181,19 @@
         p = strhistr(p, pcszKey);
 
-        if ((p) && (p >= pcszSearchIn))
+        if (    (p)
+             && (p >= pcszSearchIn)
+           )
         {
             // make sure the key is at the beginning of a line
             // by going backwards until we find a char != " "
             const char *pStartOfLine = p;
-            while (     (   (*pStartOfLine == ' ')
-                         || (*pStartOfLine == '\t')       // allow tabs too
-                        )
-                     && (pStartOfLine > pcszSearchIn)
+
+            // fixed this V0.9.11 (2001-04-25) [umoeller]
+            while (    (pStartOfLine > pcszSearchIn)
+                    && (    (*(pStartOfLine - 1) == ' ')
+                         || (*(pStartOfLine - 1) == '\t')       // allow tabs too
+                       )
                   )
-                pStartOfLine--;
+                --pStartOfLine;
 
             // if we're at the beginning of a line or
@@ -368,5 +373,6 @@
                         ulOldParamOfs,
                         ulToReplace,
-                        &strReplaceWith);
+                        strReplaceWith.psz,
+                        strReplaceWith.ulLength);      // adjusted V0.9.11 (2001-04-22) [umoeller]
 
                 xstrClear(&strReplaceWith);
@@ -1006,3 +1012,20 @@
 }
 
-
+// testcase
+
+/* int main()
+{
+    PSZ pszContents = NULL;
+    if (0 == csysLoadConfigSys(NULL, &pszContents))
+    {
+        CHAR    szValue[1000];
+        PSZ pszValue = csysGetParameter(pszContents,
+                                        "IFS=",
+                                        szValue,
+                                        sizeof(szValue));
+        if (pszValue)
+            printf("Value: %s", szValue);
+    }
+
+    return (0);
+} */
Index: trunk/src/helpers/dosh.c
===================================================================
--- trunk/src/helpers/dosh.c	(revision 62)
+++ trunk/src/helpers/dosh.c	(revision 63)
@@ -346,8 +346,8 @@
                     BOOL fSkipRemoveables) // in: if TRUE, only non-removeable disks will be returned
 {
-    CHAR szName[5] = "";
-    ULONG ulLogicalDrive = 1, // start with drive A:
-          ulFound = 0;        // found drives count
-    APIRET arc             = NO_ERROR; // return code
+    CHAR    szName[5] = "";
+    ULONG   ulLogicalDrive = 1, // start with drive A:
+            ulFound = 0;        // found drives count
+    APIRET  arc = NO_ERROR; // return code
 
     if (fSkipRemoveables)
@@ -379,10 +379,4 @@
                           1,
                           &dataLen);
-
-        /* _Pmpf(("  ul = %d, Drive %c: arc = %d nonRemoveable = %d",
-                    ulLogicalDrive,
-                    G_acDriveLetters[ulLogicalDrive],
-                    arc,
-                    nonRemovable)); */
 
         if (    // fixed disk and non-removeable
@@ -471,4 +465,5 @@
  *@@changed V0.9.4 (2000-08-03) [umoeller]: more network fixes
  *@@changed V0.9.9 (2001-03-19) [pr]: validate drive number
+ *@@changed V0.9.11 (2001-04-23) [umoeller]: added an extra check for floppies
  */
 
@@ -517,15 +512,20 @@
             // sigh...
         case ERROR_NOT_SUPPORTED: // 50
-        {
             // this is returned by file systems which don't
             // support DASD DosOpen;
             // use some other method then, this isn't likely
             // to fail -- V0.9.1 (2000-02-09) [umoeller]
-            FSALLOCATE  fsa;
-            arc = DosQueryFSInfo(ulLogicalDrive,
-                                 FSIL_ALLOC,
-                                 &fsa,
-                                 sizeof(fsa));
-        break; }
+
+            // but don't do this for floppies
+            // V0.9.11 (2001-04-23) [umoeller]
+            if (ulLogicalDrive > 2)
+            {
+                FSALLOCATE  fsa;
+                arc = DosQueryFSInfo(ulLogicalDrive,
+                                     FSIL_ALLOC,
+                                     &fsa,
+                                     sizeof(fsa));
+            }
+        break;
 
         case NO_ERROR:
@@ -554,7 +554,7 @@
             paramsize = 0,
             datasize = 0;
-    APIRET    rc = NO_ERROR;
-    USHORT    data,
-              param;
+    APIRET  rc = NO_ERROR;
+    USHORT  data,
+            param;
 
     name[0] = doshQueryBootDrive();
@@ -802,4 +802,5 @@
  *
  *@@added V0.9.0 [umoeller]
+ *@@changed V0.9.11 (2001-04-22) [umoeller]: this copied even with errors, fixed
  */
 
@@ -820,9 +821,12 @@
                          sizeof(FSInfoBuf)); // depends
 
-    #ifdef __OS2V2X__
-        strcpy(pszVolumeLabel, FSInfoBuf.szVolLabel);
-    #else
-        strcpy(pszVolumeLabel, FSInfoBuf.vol.szVolLabel);
-    #endif
+    if (!arc)       // V0.9.11 (2001-04-22) [umoeller]
+    {
+        #ifdef __OS2V2X__
+            strcpy(pszVolumeLabel, FSInfoBuf.szVolLabel);
+        #else
+            strcpy(pszVolumeLabel, FSInfoBuf.vol.szVolLabel);
+        #endif
+    }
 
     return (arc);
@@ -1023,5 +1027,5 @@
  *      --  FILE_HIDDEN
  *
- *      This returns the APIRET of DosQueryPathAttr.
+ *      This returns the APIRET of DosQueryPathInfo.
  *      *pulAttr is only valid if NO_ERROR is
  *      returned.
Index: trunk/src/helpers/encodings.c
===================================================================
--- trunk/src/helpers/encodings.c	(revision 62)
+++ trunk/src/helpers/encodings.c	(revision 63)
@@ -37,4 +37,6 @@
 typedef struct _ENCODINGTABLE
 {
+    XWPENCODINGID   EncodingID;
+
     unsigned short  cEntries;
     unsigned short  ausEntries[1];        // variable size
@@ -69,5 +71,5 @@
 long encRegisterEncoding(PXWPENCODINGMAP pEncodingMap,
                          unsigned long cArrayEntries,    // count of array items
-                         XWPENCODINGID EncodingID)
+                         XWPENCODINGID EncodingID)       // enum from encodings\base.h
 {
     long lrc = 0;
Index: trunk/src/helpers/gpih.c
===================================================================
--- trunk/src/helpers/gpih.c	(revision 62)
+++ trunk/src/helpers/gpih.c	(revision 63)
@@ -196,30 +196,61 @@
 
 /*
+ * HackColor:
+ *
+ */
+
+VOID HackColor(PBYTE pb, double dFactor)
+{
+    ULONG ul = (ULONG)((double)(*pb) * dFactor);
+    if (ul > 255)
+        *pb = 255;
+    else
+        *pb = (BYTE)ul;
+}
+
+/*
  *@@ gpihManipulateRGB:
  *      this changes an RGB color value
  *      by multiplying each color component
- *      (red, green, blue) with bMultiplier
- *      and dividing it by bDivisor afterwards.
+ *      (red, green, blue) with dFactor.
  *
  *      Each color component is treated separately,
- *      so if overflows occur (because bMultiplier
+ *      so if overflows occur (because dFactor
  *      is > 1), this does not affect the other
  *      components.
  *
- *      Example: if you pass a brigt red
- *      (0xFF0000) to this func, this will
- *      be 0x7F0000 if bMultiplier and
- *      bDivisor are 1 and 2.
+ *@@changed V0.9.11 (2001-04-25) [umoeller]: changed prototype to use a double now
  */
 
 VOID gpihManipulateRGB(PLONG plColor,       // in/out: RGB color
-                       BYTE bMultiplier,    // in: multiplier
-                       BYTE bDivisor)       // in: divisor
+                       double dFactor)      // in: factor (> 1 makes brigher, < 1 makes darker)
 {
     PBYTE   pb = (PBYTE)plColor;
+
     // in memory, the bytes are blue, green, red, unused
-    *pb++ = (BYTE)((LONG)(*pb) * bMultiplier / bDivisor); // blue
-    *pb++ = (BYTE)((LONG)(*pb) * bMultiplier / bDivisor); // green
-    *pb++ = (BYTE)((LONG)(*pb) * bMultiplier / bDivisor); // red
+
+    // blue
+    ULONG   ul = (ULONG)(   (double)(*pb) * dFactor
+                        );
+    if (ul > 255)
+        *pb = 255;
+    else
+        *pb = (BYTE)ul;
+
+    // green
+    ul = (ULONG)(   (double)(*(++pb)) * dFactor
+                );
+    if (ul > 255)
+        *pb = 255;
+    else
+        *pb = (BYTE)ul;
+
+    // red
+    ul = (ULONG)(   (double)(*(++pb)) * dFactor
+                );
+    if (ul > 255)
+        *pb = 255;
+    else
+        *pb = (BYTE)ul;
 }
 
Index: trunk/src/helpers/xml.c
===================================================================
--- trunk/src/helpers/xml.c	(revision 62)
+++ trunk/src/helpers/xml.c	(revision 63)
@@ -142,394 +142,7 @@
 /* ******************************************************************
  *
- *   Generic methods
+ *   Error handling
  *
  ********************************************************************/
-
-/*
- *@@ CompareNodeBaseNodes:
- *      tree comparison func for NodeBases.
- *      This works for all trees which contain structures
- *      whose first item is a _NODEBASE because NODEBASE's first
- *      member is a TREE.
- *
- *      Used in two places:
- *
- *      --  to insert _CMELEMENTDECLNODE nodes into
- *          _DOMDOCTYPENODE.ElementDeclsTree;
- *
- *      --  to insert _CMELEMENTPARTICLE nodes into
- *          _CMELEMENTDECLNODE.ElementNamesTree.
- *
- *@@added V0.9.9 (2001-02-16) [umoeller]
- */
-
-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));
-}
-
-/*
- *@@ xmlCreateNodeBase:
- *      creates a new NODEBASE node.
- *
- *      Gets called from xmlCreateDomNode also to create
- *      a DOMNODE, since that in turn has a NODEBASE.
- *
- *@@added V0.9.9 (2001-02-16) [umoeller]
- */
-
-APIRET xmlCreateNodeBase(NODEBASETYPE ulNodeType,     // in: node type
-                         ULONG cb,                    // in: size of struct
-                         const char *pcszNodeName,    // in: node name or NULL
-                         ULONG ulNodeNameLength,      // in: node name length
-                                                      // or 0 to run strlen(pcszNodeName)
-                         PNODEBASE *ppNew)            // out: new node
-{
-    APIRET      arc = NO_ERROR;
-    PNODEBASE   pNewNode = (PNODEBASE)malloc(cb);
-
-    if (!pNewNode)
-        arc = ERROR_NOT_ENOUGH_MEMORY;
-    else
-    {
-        memset(pNewNode, 0, cb);
-        pNewNode->ulNodeType = ulNodeType;
-
-        xstrInit(&pNewNode->strNodeName, 0);
-        if (pcszNodeName)
-            xstrcpy(&pNewNode->strNodeName,
-                    pcszNodeName,
-                    ulNodeNameLength);  // if 0, xstrcpy will do strlen()
-
-
-        *ppNew = pNewNode;
-    }
-
-    return (arc);
-}
-
-/*
- *@@ xmlDeleteNode:
- *      deletes a NODEBASE and frees memory that was
- *      associated with its members.
- *
- *      NOTE: If you call this for derived types, call
- *      this LAST, after you have cleared additional
- *      members. After calling this, pNode is no longer
- *      valid.
- *
- *@@added V0.9.9 (2001-02-16) [umoeller]
- */
-
-VOID xmlDeleteNode(PNODEBASE pNode)
-{
-    if (pNode)
-    {
-        PLISTNODE   pNodeThis;
-        PDOMNODE    pDomNode = NULL;
-
-        LINKLIST    llDeleteNodes;          // list that nodes to be deleted
-                                            // can be appended to
-        PLISTNODE   pDelNode;
-        lstInit(&llDeleteNodes, FALSE);
-
-        // now handle special types and their allocations
-        switch (pNode->ulNodeType)
-        {
-            case DOMNODE_ELEMENT:
-            {
-                PDOMNODE pAttrib;
-
-                pDomNode = (PDOMNODE)pNode;
-
-                // delete all attribute nodes
-                pAttrib = (PDOMNODE)treeFirst(pDomNode->AttributesMap);
-                while (pAttrib)
-                {
-                    // call delete recursively
-                    xmlDeleteNode((PNODEBASE)pAttrib);
-                            // this will remove pAttrib from pNode's attrib
-                            // tree and rebalance the tree; treeNext will
-                            // still work #### noooooo
-                    pAttrib = (PDOMNODE)treeNext((TREE*)pAttrib);
-                }
-            break; }
-
-            case DOMNODE_ATTRIBUTE:
-            case DOMNODE_TEXT:
-            case DOMNODE_PROCESSING_INSTRUCTION:
-            case DOMNODE_COMMENT:
-            case DOMNODE_DOCUMENT:
-                pDomNode = (PDOMNODE)pNode;
-            break;
-
-            case DOMNODE_DOCUMENT_TYPE:
-            {
-                PDOMDOCTYPENODE pDocType = (PDOMDOCTYPENODE)pNode;
-                PCMELEMENTDECLNODE pElDecl;
-                PCMATTRIBUTEDECLBASE pAttrDeclBase;
-
-                pDomNode = (PDOMNODE)pNode;
-
-                pElDecl = (PCMELEMENTDECLNODE)treeFirst(pDocType->ElementDeclsTree);
-                while (pElDecl)
-                {
-                    lstAppendItem(&llDeleteNodes, pElDecl);
-                    pElDecl = (PCMELEMENTDECLNODE)treeNext((TREE*)pElDecl);
-                }
-
-                pAttrDeclBase = (PCMATTRIBUTEDECLBASE)treeFirst(pDocType->AttribDeclBasesTree);
-                while (pAttrDeclBase)
-                {
-                    lstAppendItem(&llDeleteNodes, pAttrDeclBase);
-                    pAttrDeclBase = (PCMATTRIBUTEDECLBASE)treeNext((TREE*)pAttrDeclBase);
-                }
-
-                xstrClear(&pDocType->strPublicID);
-                xstrClear(&pDocType->strSystemID);
-            break; }
-
-            case ELEMENTPARTICLE_EMPTY:
-            case ELEMENTPARTICLE_ANY:
-            case ELEMENTPARTICLE_MIXED:
-            case ELEMENTPARTICLE_CHOICE:
-            case ELEMENTPARTICLE_SEQ:
-            case ELEMENTPARTICLE_NAME:
-            {
-                PCMELEMENTPARTICLE pp = (PCMELEMENTPARTICLE)pNode;
-                if (pp->pllSubNodes)
-                {
-                    pDelNode = lstQueryFirstNode(pp->pllSubNodes);
-                    while (pDelNode)
-                    {
-                        PCMELEMENTPARTICLE
-                                pParticle = (PCMELEMENTPARTICLE)pDelNode->pItemData;
-                        xmlDeleteNode((PNODEBASE)pParticle);
-                        //  treeDelete(pp->         ###
-                        pDelNode = pDelNode->pNext;
-                    }
-                }
-            break; }
-
-            // case ATTRIBUTE_DECLARATION_ENUM:     // this is a plain NODEBASE
-
-            case ATTRIBUTE_DECLARATION:
-            {
-                PCMATTRIBUTEDECL pDecl = (PCMATTRIBUTEDECL)pNode;
-            break; }
-
-            case ATTRIBUTE_DECLARATION_BASE:
-            break;
-        }
-
-        if (pDomNode)
-        {
-            // recurse into child nodes
-            while (pNodeThis = lstQueryFirstNode(&pDomNode->llChildren))
-                // recurse!!
-                xmlDeleteNode((PNODEBASE)(pNodeThis->pItemData));
-                            // this updates llChildren
-
-            if (pDomNode->pParentNode)
-            {
-                // node has a parent:
-                if (pNode->ulNodeType == DOMNODE_ATTRIBUTE)
-                    // this is an attribute:
-                    // remove from parent's attributes map
-                    treeDelete(&pDomNode->pParentNode->AttributesMap,
-                               (TREE*)pNode);
-                else
-                    // remove this node from the parent's list
-                    // of child nodes before deleting this node
-                    lstRemoveItem(&pDomNode->pParentNode->llChildren,
-                                  pNode);
-
-                pDomNode->pParentNode = NULL;
-            }
-
-            xstrFree(pDomNode->pstrNodeValue);
-            lstClear(&pDomNode->llChildren);
-        }
-
-        pDelNode = lstQueryFirstNode(&llDeleteNodes);
-        while (pDelNode)
-        {
-            PNODEBASE pNodeBase = (PNODEBASE)pDelNode->pItemData;
-            xmlDeleteNode(pNodeBase);
-            pDelNode = pDelNode->pNext;
-        }
-
-        lstClear(&llDeleteNodes);
-
-        xstrClear(&pNode->strNodeName);
-        free(pNode);
-    }
-}
-
-/*
- *@@ xmlCreateDomNode:
- *      creates a new DOMNODE with the specified
- *      type and parent. Other than that, the
- *      node fields are zeroed.
- *
- *      If pParentNode is specified (which is required,
- *      unless you are creating a document node),
- *      its children list is automatically updated
- *      (unless this is an attribute node, which updates
- *      the attributes map).
- *
- *      This returns the following errors:
- *
- *      --  ERROR_NOT_ENOUGH_MEMORY
- *
- *      --  ERROR_DOM_NOT_SUPPORTED: invalid ulNodeType
- *          specified.
- *
- *      --  ERROR_DOM_WRONG_DOCUMENT: cannot find the
- *          document for this node. This happens if you do
- *          not have a document node at the root of your tree.
- */
-
-APIRET xmlCreateDomNode(PDOMNODE pParentNode,        // in: parent node or NULL if root
-                        NODEBASETYPE ulNodeType,     // in: DOMNODE_* type
-                        const char *pcszNodeName,    // in: node name or NULL
-                        ULONG ulNodeNameLength,      // in: node name length
-                                                     // or 0 to run strlen(pcszNodeName)
-                        PDOMNODE *ppNew)             // out: new node
-{
-    PDOMNODE pNewNode = NULL;
-    APIRET  arc = NO_ERROR;
-
-    ULONG   cb = 0;
-
-    switch (ulNodeType)
-    {
-        case DOMNODE_DOCUMENT:
-            cb = sizeof(DOMDOCUMENTNODE);
-        break;
-
-        case DOMNODE_DOCUMENT_TYPE:
-            cb = sizeof(DOMDOCTYPENODE);
-        break;
-
-        default:
-            cb = sizeof(DOMNODE);
-        break;
-    }
-
-    arc = xmlCreateNodeBase(ulNodeType,
-                            cb,
-                            pcszNodeName,
-                            ulNodeNameLength,
-                            (PNODEBASE*)&pNewNode);
-    if (arc == NO_ERROR)
-    {
-        pNewNode->pParentNode = pParentNode;
-
-        if (pParentNode)
-        {
-            // parent specified:
-            // check if this is an attribute
-            if (ulNodeType == DOMNODE_ATTRIBUTE)
-            {
-                // attribute:
-                // add to parent's attributes list
-                if (treeInsertNode(&pParentNode->AttributesMap,
-                                   &pNewNode->NodeBase.Tree,
-                                   CompareNodeBaseNodes,
-                                   FALSE)      // no duplicates
-                        == TREE_DUPLICATE)
-                    arc = ERROR_DOM_DUPLICATE_ATTRIBUTE;
-                                // shouldn't happen, because expat takes care of this
-            }
-            else
-                // append this new node to the parent's
-                // list of child nodes
-                lstAppendItem(&pParentNode->llChildren,
-                              pNewNode);
-
-            if (!arc)
-            {
-                // set document pointer...
-                // if the parent node has a document pointer,
-                // we can copy that
-                if (pParentNode->pDocumentNode)
-                    pNewNode->pDocumentNode = pParentNode->pDocumentNode;
-                else
-                    // parent has no document pointer: then it is probably
-                    // the document itself... check
-                    if (pParentNode->NodeBase.ulNodeType == DOMNODE_DOCUMENT)
-                        pNewNode->pDocumentNode = pParentNode;
-                    else
-                        arc = ERROR_DOM_NO_DOCUMENT;
-            }
-        }
-
-        lstInit(&pNewNode->llChildren, FALSE);
-        treeInit(&pNewNode->AttributesMap);
-    }
-
-    if (!arc)
-        *ppNew = pNewNode;
-    else
-        if (pNewNode)
-            free(pNewNode);
-
-    return (arc);
-}
-
-/*
- *@@ xmlGetFirstChild:
- *      returns the first child node of pDomNode.
- *      See _DOMNODE for what a "child" can be for the
- *      various node types.
- *
- *@@added V0.9.9 (2001-02-14) [umoeller]
- */
-
-PDOMNODE xmlGetFirstChild(PDOMNODE pDomNode)
-{
-    PLISTNODE pListNode = lstQueryFirstNode(&pDomNode->llChildren);
-    if (pListNode)
-        return ((PDOMNODE)pListNode->pItemData);
-
-    return (0);
-}
-
-/*
- *@@ xmlGetLastChild:
- *      returns the last child node of pDomNode.
- *      See _DOMNODE for what a "child" can be for the
- *      various node types.
- *
- *@@added V0.9.9 (2001-02-14) [umoeller]
- */
-
-PDOMNODE xmlGetLastChild(PDOMNODE pDomNode)
-{
-    PLISTNODE pListNode = lstQueryLastNode(&pDomNode->llChildren);
-    if (pListNode)
-        return ((PDOMNODE)pListNode->pItemData);
-
-    return (0);
-}
 
 /*
@@ -584,5 +197,20 @@
         "Invalid attribute value",
         "Required attribute is missing",
-        "Subelement in empty element"
+        "Subelement in empty element",
+
+        "Parsing error",
+        "Validity error",
+
+        "DOM node type not supported",
+        "No DOM document",
+        "No DOM element",
+        "Duplicate doctype",
+        "Root element doesn't match doctype name",
+        "DOM integrity error",
+        "Duplicate attribute",
+
+        "Validation error: Undeclared element name",
+        "Element declaration outside doctype",
+        "Attlist declaration outside doctype"
     };
 
@@ -630,5 +258,362 @@
 /* ******************************************************************
  *
- *   Specific DOM node methods
+ *   Most basic node management
+ *
+ ********************************************************************/
+
+/*
+ *@@ CompareNodeBaseNodes:
+ *      tree comparison func for NodeBases.
+ *      This works for all trees which contain structures
+ *      whose first item is a _NODEBASE because NODEBASE's first
+ *      member is a TREE.
+ *
+ *      Used in two places:
+ *
+ *      --  to insert _CMELEMENTDECLNODE nodes into
+ *          _DOMDOCTYPENODE.ElementDeclsTree;
+ *
+ *      --  to insert _CMELEMENTPARTICLE nodes into
+ *          _CMELEMENTDECLNODE.ElementNamesTree.
+ *
+ *@@added V0.9.9 (2001-02-16) [umoeller]
+ */
+
+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));
+}
+
+/*
+ *@@ xmlCreateNodeBase:
+ *      creates a new NODEBASE node.
+ *
+ *      Gets called from xmlCreateDomNode also to create
+ *      a DOMNODE, since that in turn has a NODEBASE.
+ *
+ *@@added V0.9.9 (2001-02-16) [umoeller]
+ */
+
+APIRET xmlCreateNodeBase(NODEBASETYPE ulNodeType,     // in: node type
+                         ULONG cb,                    // in: size of struct
+                         const char *pcszNodeName,    // in: node name or NULL
+                         ULONG ulNodeNameLength,      // in: node name length
+                                                      // or 0 to run strlen(pcszNodeName)
+                         PNODEBASE *ppNew)            // out: new node
+{
+    APIRET      arc = NO_ERROR;
+    PNODEBASE   pNewNode = (PNODEBASE)malloc(cb);
+
+    if (!pNewNode)
+        arc = ERROR_NOT_ENOUGH_MEMORY;
+    else
+    {
+        memset(pNewNode, 0, cb);
+        pNewNode->ulNodeType = ulNodeType;
+
+        xstrInit(&pNewNode->strNodeName, 0);
+        if (pcszNodeName)
+            xstrcpy(&pNewNode->strNodeName,
+                    pcszNodeName,
+                    ulNodeNameLength);  // if 0, xstrcpy will do strlen()
+
+
+        *ppNew = pNewNode;
+    }
+
+    return (arc);
+}
+
+/*
+ *@@ xmlDeleteNode:
+ *      deletes a NODEBASE and frees memory that was
+ *      associated with its members.
+ *
+ *      NOTE: If you call this for derived types, call
+ *      this LAST, after you have cleared additional
+ *      members. After calling this, pNode is no longer
+ *      valid.
+ *
+ *@@added V0.9.9 (2001-02-16) [umoeller]
+ */
+
+VOID xmlDeleteNode(PNODEBASE pNode)
+{
+    if (pNode)
+    {
+        PLISTNODE   pNodeThis;
+        PDOMNODE    pDomNode = NULL;
+
+        LINKLIST    llDeleteNodes;          // list that nodes to be deleted
+                                            // can be appended to
+        PLISTNODE   pDelNode;
+        lstInit(&llDeleteNodes, FALSE);
+
+        // now handle special types and their allocations
+        switch (pNode->ulNodeType)
+        {
+            case DOMNODE_ELEMENT:
+            {
+                PDOMNODE pAttrib;
+
+                pDomNode = (PDOMNODE)pNode;
+
+                // delete all attribute nodes
+                pAttrib = (PDOMNODE)treeFirst(pDomNode->AttributesMap);
+                while (pAttrib)
+                {
+                    // call delete recursively
+                    xmlDeleteNode((PNODEBASE)pAttrib);
+                            // this will remove pAttrib from pNode's attrib
+                            // tree and rebalance the tree; treeNext will
+                            // still work #### noooooo
+                    pAttrib = (PDOMNODE)treeNext((TREE*)pAttrib);
+                }
+            break; }
+
+            case DOMNODE_ATTRIBUTE:
+            case DOMNODE_TEXT:
+            case DOMNODE_PROCESSING_INSTRUCTION:
+            case DOMNODE_COMMENT:
+            case DOMNODE_DOCUMENT:
+                pDomNode = (PDOMNODE)pNode;
+            break;
+
+            case DOMNODE_DOCUMENT_TYPE:
+            {
+                PDOMDOCTYPENODE pDocType = (PDOMDOCTYPENODE)pNode;
+                PCMELEMENTDECLNODE pElDecl;
+                PCMATTRIBUTEDECLBASE pAttrDeclBase;
+
+                pDomNode = (PDOMNODE)pNode;
+
+                pElDecl = (PCMELEMENTDECLNODE)treeFirst(pDocType->ElementDeclsTree);
+                while (pElDecl)
+                {
+                    lstAppendItem(&llDeleteNodes, pElDecl);
+                    pElDecl = (PCMELEMENTDECLNODE)treeNext((TREE*)pElDecl);
+                }
+
+                pAttrDeclBase = (PCMATTRIBUTEDECLBASE)treeFirst(pDocType->AttribDeclBasesTree);
+                while (pAttrDeclBase)
+                {
+                    lstAppendItem(&llDeleteNodes, pAttrDeclBase);
+                    pAttrDeclBase = (PCMATTRIBUTEDECLBASE)treeNext((TREE*)pAttrDeclBase);
+                }
+
+                xstrClear(&pDocType->strPublicID);
+                xstrClear(&pDocType->strSystemID);
+            break; }
+
+            case ELEMENTPARTICLE_EMPTY:
+            case ELEMENTPARTICLE_ANY:
+            case ELEMENTPARTICLE_MIXED:
+            case ELEMENTPARTICLE_CHOICE:
+            case ELEMENTPARTICLE_SEQ:
+            case ELEMENTPARTICLE_NAME:
+            {
+                PCMELEMENTPARTICLE pp = (PCMELEMENTPARTICLE)pNode;
+                if (pp->pllSubNodes)
+                {
+                    pDelNode = lstQueryFirstNode(pp->pllSubNodes);
+                    while (pDelNode)
+                    {
+                        PCMELEMENTPARTICLE
+                                pParticle = (PCMELEMENTPARTICLE)pDelNode->pItemData;
+                        xmlDeleteNode((PNODEBASE)pParticle);
+                        //  treeDelete(pp->         ###
+                        pDelNode = pDelNode->pNext;
+                    }
+                }
+            break; }
+
+            // case ATTRIBUTE_DECLARATION_ENUM:     // this is a plain NODEBASE
+
+            case ATTRIBUTE_DECLARATION:
+            {
+                PCMATTRIBUTEDECL pDecl = (PCMATTRIBUTEDECL)pNode;
+            break; }
+
+            case ATTRIBUTE_DECLARATION_BASE:
+            break;
+        }
+
+        if (pDomNode)
+        {
+            // recurse into child nodes
+            while (pNodeThis = lstQueryFirstNode(&pDomNode->llChildren))
+                // recurse!!
+                xmlDeleteNode((PNODEBASE)(pNodeThis->pItemData));
+                            // this updates llChildren
+
+            if (pDomNode->pParentNode)
+            {
+                // node has a parent:
+                if (pNode->ulNodeType == DOMNODE_ATTRIBUTE)
+                    // this is an attribute:
+                    // remove from parent's attributes map
+                    treeDelete(&pDomNode->pParentNode->AttributesMap,
+                               (TREE*)pNode);
+                else
+                    // remove this node from the parent's list
+                    // of child nodes before deleting this node
+                    lstRemoveItem(&pDomNode->pParentNode->llChildren,
+                                  pNode);
+
+                pDomNode->pParentNode = NULL;
+            }
+
+            xstrFree(pDomNode->pstrNodeValue);
+            lstClear(&pDomNode->llChildren);
+        }
+
+        pDelNode = lstQueryFirstNode(&llDeleteNodes);
+        while (pDelNode)
+        {
+            PNODEBASE pNodeBase = (PNODEBASE)pDelNode->pItemData;
+            xmlDeleteNode(pNodeBase);
+            pDelNode = pDelNode->pNext;
+        }
+
+        lstClear(&llDeleteNodes);
+
+        xstrClear(&pNode->strNodeName);
+        free(pNode);
+    }
+}
+
+/*
+ *@@ xmlCreateDomNode:
+ *      creates a new DOMNODE with the specified
+ *      type and parent. Other than that, the
+ *      node fields are zeroed.
+ *
+ *      If pParentNode is specified (which is required,
+ *      unless you are creating a document node),
+ *      its children list is automatically updated
+ *      (unless this is an attribute node, which updates
+ *      the attributes map).
+ *
+ *      This returns the following errors:
+ *
+ *      --  ERROR_NOT_ENOUGH_MEMORY
+ *
+ *      --  ERROR_DOM_NOT_SUPPORTED: invalid ulNodeType
+ *          specified.
+ *
+ *      --  ERROR_DOM_WRONG_DOCUMENT: cannot find the
+ *          document for this node. This happens if you do
+ *          not have a document node at the root of your tree.
+ */
+
+APIRET xmlCreateDomNode(PDOMNODE pParentNode,        // in: parent node or NULL if root
+                        NODEBASETYPE ulNodeType,     // in: DOMNODE_* type
+                        const char *pcszNodeName,    // in: node name or NULL
+                        ULONG ulNodeNameLength,      // in: node name length
+                                                     // or 0 to run strlen(pcszNodeName)
+                        PDOMNODE *ppNew)             // out: new node
+{
+    PDOMNODE pNewNode = NULL;
+    APIRET  arc = NO_ERROR;
+
+    ULONG   cb = 0;
+
+    switch (ulNodeType)
+    {
+        case DOMNODE_DOCUMENT:
+            cb = sizeof(DOMDOCUMENTNODE);
+        break;
+
+        case DOMNODE_DOCUMENT_TYPE:
+            cb = sizeof(DOMDOCTYPENODE);
+        break;
+
+        default:
+            cb = sizeof(DOMNODE);
+        break;
+    }
+
+    arc = xmlCreateNodeBase(ulNodeType,
+                            cb,
+                            pcszNodeName,
+                            ulNodeNameLength,
+                            (PNODEBASE*)&pNewNode);
+    if (arc == NO_ERROR)
+    {
+        pNewNode->pParentNode = pParentNode;
+
+        if (pParentNode)
+        {
+            // parent specified:
+            // check if this is an attribute
+            if (ulNodeType == DOMNODE_ATTRIBUTE)
+            {
+                // attribute:
+                // add to parent's attributes list
+                if (treeInsertNode(&pParentNode->AttributesMap,
+                                   &pNewNode->NodeBase.Tree,
+                                   CompareNodeBaseNodes,
+                                   FALSE)      // no duplicates
+                        == TREE_DUPLICATE)
+                    arc = ERROR_DOM_DUPLICATE_ATTRIBUTE;
+                                // shouldn't happen, because expat takes care of this
+            }
+            else
+                // append this new node to the parent's
+                // list of child nodes
+                lstAppendItem(&pParentNode->llChildren,
+                              pNewNode);
+
+            if (!arc)
+            {
+                // set document pointer...
+                // if the parent node has a document pointer,
+                // we can copy that
+                if (pParentNode->pDocumentNode)
+                    pNewNode->pDocumentNode = pParentNode->pDocumentNode;
+                else
+                    // parent has no document pointer: then it is probably
+                    // the document itself... check
+                    if (pParentNode->NodeBase.ulNodeType == DOMNODE_DOCUMENT)
+                        pNewNode->pDocumentNode = pParentNode;
+                    else
+                        arc = ERROR_DOM_NO_DOCUMENT;
+            }
+        }
+
+        lstInit(&pNewNode->llChildren, FALSE);
+        treeInit(&pNewNode->AttributesMap);
+    }
+
+    if (!arc)
+        *ppNew = pNewNode;
+    else
+        if (pNewNode)
+            free(pNewNode);
+
+    return (arc);
+}
+
+/* ******************************************************************
+ *
+ *   Specific DOM node constructors
  *
  ********************************************************************/
@@ -871,26 +856,7 @@
 }
 
-/*
- *@@ xmlGetElementsByTagName:
- *      returns a linked list of @DOM_ELEMENT nodes which
- *      match the specified element name. The special name
- *      "*" matches all elements.
- *
- *      The caller must free the list by calling lstFree.
- *      Returns NULL if no such elements could be found.
- *
- *@@added V0.9.9 (2001-02-14) [umoeller]
- */
-
-PLINKLIST xmlGetElementsByTagName(const char *pcszName)
-{
-    APIRET arc = NO_ERROR;
-
-    return (0);
-}
-
 /* ******************************************************************
  *
- *   Content model methods
+ *   DOM level 3 content models
  *
  ********************************************************************/
@@ -1031,99 +997,4 @@
 
 /*
- *@@ xmlFindElementDecl:
- *      returns the _CMELEMENTDECLNODE for the element
- *      with the specified name or NULL if there's none.
- *
- *@@added V0.9.9 (2001-02-16) [umoeller]
- */
-
-PCMELEMENTDECLNODE xmlFindElementDecl(PXMLDOM pDom,
-                                      const XSTRING *pstrElementName)
-{
-    PCMELEMENTDECLNODE pElementDecl = NULL;
-
-    PDOMDOCTYPENODE pDocTypeNode = pDom->pDocTypeNode;
-    if (    (pDocTypeNode)
-         && (pstrElementName)
-         && (pstrElementName->ulLength)
-       )
-    {
-        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);
-}
-
-/*
- *@@ xmlFindAttribDecl:
- *      returns the _CMATTRIBUTEDEDECL for the specified
- *      element and attribute name, or NULL if none exists.
- *
- *@@added V0.9.9 (2001-02-16) [umoeller]
- */
-
-PCMATTRIBUTEDECL xmlFindAttribDecl(PXMLDOM pDom,
-                                   const XSTRING *pstrElementName,
-                                   const XSTRING *pstrAttribName,
-                                   PCMATTRIBUTEDECLBASE *ppAttribDeclBase)
-                                            // in/out: attr decl base cache;
-                                            // the pointer pointed to by this
-                                            // must be NULL on the first call
-{
-    PCMATTRIBUTEDECL pAttribDecl = NULL;
-    if (pstrElementName && pstrAttribName)
-    {
-        if (!*ppAttribDeclBase)
-            // first call for this:
-            *ppAttribDeclBase = xmlFindAttribDeclBase(pDom,
-                                                      pstrElementName);
-        if (*ppAttribDeclBase)
-        {
-            pAttribDecl = (PCMATTRIBUTEDECL)treeFindEQData(
-                                         &((**ppAttribDeclBase).AttribDeclsTree),
-                                         (void*)pstrAttribName->psz,
-                                         CompareNodeBaseData);
-        }
-    }
-
-    return (pAttribDecl);
-}
-
-/*
  *@@ ValidateElementChildren
  *      validates the specified element against the document's @DTD,
@@ -1176,5 +1047,8 @@
                 return;     // that's OK
             else
-                pDom->arcDOM = ERROR_DOM_DOCTYPE_STRUCTURE;
+                xmlSetError(pDom,
+                            ERROR_DOM_VALIDATE_INVALID_ELEMENT,
+                            pNewElement->NodeBase.strNodeName.psz,
+                            TRUE);
         }
         else
@@ -1826,20 +1700,7 @@
 
     // continue parsing only if we had no errors so far
-    /* if (!pDom->arcDOM)
-    {
-        PLISTNODE pStackLN = NULL;
-        PDOMSTACKITEM pSI = PopElementStack(pDom,
-                                            &pStackLN);
-
-        if (pDom->arcDOM)
-            pDom->arcDOM = ERROR_DOM_DOCTYPE_STRUCTURE;
-        else
-        {
-            if (pSI->pDomNode->NodeBase.ulNodeType != DOMNODE_DOCUMENT_TYPE)
-                pDom->arcDOM = ERROR_DOM_DOCTYPE_STRUCTURE;
-
-            lstRemoveNode(&pDom->llElementStack, pStackLN); // auto-free
-        }
-    } */
+    if (!pDom->arcDOM)
+    {
+    }
 }
 
@@ -1885,5 +1746,6 @@
  *
  *      The pcszPublicId parameter is the public id given in the entity
- *      declaration and may be null.
+ *      declaration and may be null (since XML doesn't require public
+ *      identifiers).
  *
  *      The pcszSystemId is the system identifier specified in the
@@ -1926,9 +1788,7 @@
                                         const XML_Char *pcszPublicId)
 {
-    int i = 0;
-
-    FILE *f = fopen("log", "w");
-    fprintf(f, "sysid: %s\n", pcszSystemId);
-    fclose(f);
+    int i = 1;
+
+    // @@todo: allow caller to load external references some way
 
     /* PXMLDOM     pDom = (PXMLDOM)pUserData;
@@ -1970,5 +1830,8 @@
         PDOMDOCTYPENODE pDocType = pDom->pDocTypeNode;
         if (!pDocType)
-            pDom->arcDOM = ERROR_DOM_DOCTYPE_STRUCTURE;
+            xmlSetError(pDom,
+                        ERROR_DOM_ELEMENT_DECL_OUTSIDE_DOCTYPE,
+                        pcszName,
+                        TRUE);
         else
         {
@@ -2071,5 +1934,8 @@
         PDOMDOCTYPENODE pDocType = pDom->pDocTypeNode;
         if (!pDocType)
-            pDom->arcDOM = ERROR_DOM_DOCTYPE_STRUCTURE;
+            xmlSetError(pDom,
+                        ERROR_DOM_ATTLIST_DECL_OUTSIDE_DOCTYPE,
+                        pcszElementName,
+                        TRUE);
         else
         {
@@ -2253,5 +2119,5 @@
 /* ******************************************************************
  *
- *   DOM APIs
+ *   DOM root APIs
  *
  ********************************************************************/
@@ -2271,5 +2137,9 @@
  *
  *      --  DF_PARSEDTD: add the @DTD of the document into the DOM tree
- *          as well and validate the document.
+ *          as well and validate the document, if a DTD was found.
+ *          Otherwise just parse and do not validate.
+ *
+ *      --  DF_FAIL_IF_NO_DTD: fail if no @DTD was found. Useful
+ *          if you want to enforce validation. @@todo
  *
  *      Usage:
@@ -2278,5 +2148,5 @@
  *
  +          PXMLDOM pDom = NULL;
- +          APIRET arc = xmlCreateDom(flags, &pDom);
+ +          APIRET arc = xmlCreateDOM(flags, &pDom);
  +
  *      2) Give chunks of data (or an entire buffer)
@@ -2412,5 +2282,5 @@
  *         error information in the XMLDOM fields.
  *
- *      -- ERROR_DOM_PARSING: the document is not @valid.
+ *      -- ERROR_DOM_VALIDITY: the document is not @valid.
  *         This can only happen if @DTD parsing was enabled
  *         with xmlCreateDOM.
@@ -2510,2 +2380,266 @@
     return (arc);
 }
+
+/* ******************************************************************
+ *
+ *   DOM lookup
+ *
+ ********************************************************************/
+
+/*
+ *@@ xmlFindElementDecl:
+ *      returns the _CMELEMENTDECLNODE for the element
+ *      with the specified name or NULL if there's none.
+ *
+ *@@added V0.9.9 (2001-02-16) [umoeller]
+ */
+
+PCMELEMENTDECLNODE xmlFindElementDecl(PXMLDOM pDom,
+                                      const XSTRING *pstrElementName)
+{
+    PCMELEMENTDECLNODE pElementDecl = NULL;
+
+    PDOMDOCTYPENODE pDocTypeNode = pDom->pDocTypeNode;
+    if (    (pDocTypeNode)
+         && (pstrElementName)
+         && (pstrElementName->ulLength)
+       )
+    {
+        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);
+}
+
+/*
+ *@@ xmlFindAttribDecl:
+ *      returns the _CMATTRIBUTEDEDECL for the specified
+ *      element and attribute name, or NULL if none exists.
+ *
+ *@@added V0.9.9 (2001-02-16) [umoeller]
+ */
+
+PCMATTRIBUTEDECL xmlFindAttribDecl(PXMLDOM pDom,
+                                   const XSTRING *pstrElementName,
+                                   const XSTRING *pstrAttribName,
+                                   PCMATTRIBUTEDECLBASE *ppAttribDeclBase)
+                                            // in/out: attr decl base cache;
+                                            // the pointer pointed to by this
+                                            // must be NULL on the first call
+{
+    PCMATTRIBUTEDECL pAttribDecl = NULL;
+    if (pstrElementName && pstrAttribName)
+    {
+        if (!*ppAttribDeclBase)
+            // first call for this:
+            *ppAttribDeclBase = xmlFindAttribDeclBase(pDom,
+                                                      pstrElementName);
+        if (*ppAttribDeclBase)
+        {
+            pAttribDecl = (PCMATTRIBUTEDECL)treeFindEQData(
+                                         &((**ppAttribDeclBase).AttribDeclsTree),
+                                         (void*)pstrAttribName->psz,
+                                         CompareNodeBaseData);
+        }
+    }
+
+    return (pAttribDecl);
+}
+
+/*
+ *@@ xmlGetRootElement:
+ *      returns the root element node from the specified
+ *      DOM. Useful helper to start enumerating elements.
+ *
+ *@@added V0.9.11 (2001-04-22) [umoeller]
+ */
+
+PDOMNODE xmlGetRootElement(PXMLDOM pDom)
+{
+    PDOMDOCUMENTNODE    pDocumentNode;
+    PLISTNODE           pListNode;
+    if (    (pDom)
+         && (pDocumentNode = pDom->pDocumentNode)
+         && (pListNode = lstQueryFirstNode(&pDocumentNode->DomNode.llChildren))
+       )
+    {
+        return ((PDOMNODE)pListNode->pItemData);
+    }
+
+    return (NULL);
+}
+
+/*
+ *@@ xmlGetFirstChild:
+ *      returns the first child node of pDomNode.
+ *      See _DOMNODE for what a "child" can be for the
+ *      various node types.
+ *
+ *@@added V0.9.9 (2001-02-14) [umoeller]
+ */
+
+PDOMNODE xmlGetFirstChild(PDOMNODE pDomNode)
+{
+    PLISTNODE pListNode = lstQueryFirstNode(&pDomNode->llChildren);
+    if (pListNode)
+        return ((PDOMNODE)pListNode->pItemData);
+
+    return (0);
+}
+
+/*
+ *@@ xmlGetLastChild:
+ *      returns the last child node of pDomNode.
+ *      See _DOMNODE for what a "child" can be for the
+ *      various node types.
+ *
+ *@@added V0.9.9 (2001-02-14) [umoeller]
+ */
+
+PDOMNODE xmlGetLastChild(PDOMNODE pDomNode)
+{
+    PLISTNODE pListNode = lstQueryLastNode(&pDomNode->llChildren);
+    if (pListNode)
+        return ((PDOMNODE)pListNode->pItemData);
+
+    return (0);
+}
+
+/*
+ *@@ xmlGetFirstText:
+ *      returns the first text (character data) node
+ *      of pElement or NULL if there's none.
+ *
+ *@@added V0.9.11 (2001-04-22) [umoeller]
+ */
+
+PDOMNODE xmlGetFirstText(PDOMNODE pElement)
+{
+    PLISTNODE   pNode;
+    PDOMNODE    pDomNodeThis;
+
+    for (pNode = lstQueryFirstNode(&pElement->llChildren);
+         pNode;
+         pNode = pNode->pNext)
+    {
+        if (    (pDomNodeThis = (PDOMNODE)pNode->pItemData)
+             && (pDomNodeThis->NodeBase.ulNodeType == DOMNODE_TEXT)
+           )
+            return (pDomNodeThis);
+    }
+
+    return (NULL);
+}
+
+/*
+ *@@ xmlGetElementsByTagName:
+ *      returns a linked list of _DOMNODE nodes which
+ *      match the specified element name. The special name
+ *      "*" matches all elements.
+ *
+ *      pParent must be the parent element DOMNODE...
+ *      the only allowed exception is
+ *
+ *      The caller must free the list by calling lstFree.
+ *      Returns NULL if no such elements could be found.
+ *
+ *@@added V0.9.9 (2001-02-14) [umoeller]
+ */
+
+PLINKLIST xmlGetElementsByTagName(PDOMNODE pParent,
+                                  const char *pcszName)
+{
+    APIRET arc = NO_ERROR;
+
+    PLINKLIST pll = lstCreate(FALSE);       // no free
+    if (pll)
+    {
+        ULONG   cItems = 0;
+        BOOL    fFindAll = !strcmp(pcszName, "*");
+
+        PLISTNODE   pNode;
+        PDOMNODE    pDomNodeThis;
+
+        for (pNode = lstQueryFirstNode(&pParent->llChildren);
+             pNode;
+             pNode = pNode->pNext)
+        {
+            if (    (pDomNodeThis = (PDOMNODE)pNode->pItemData)
+                 && (pDomNodeThis->NodeBase.ulNodeType == DOMNODE_ELEMENT)
+                 && (   fFindAll
+                     || (!strcmp(pcszName, pDomNodeThis->NodeBase.strNodeName.psz))
+                   )
+               )
+            {
+                // element matches:
+                lstAppendItem(pll, pDomNodeThis);
+                cItems++;
+            }
+        }
+
+        if (cItems)
+            return (pll);
+        else
+            lstFree(pll);
+    }
+
+    return (0);
+}
+
+/*
+ *@@ xmlGetAttribute:
+ *      returns the value of pElement's attribute
+ *      with the given name or NULL.
+ *
+ *      This is a const pointer into the element's
+ *      attribute list.
+ *
+ *@@added V0.9.11 (2001-04-22) [umoeller]
+ */
+
+const XSTRING* xmlGetAttribute(PDOMNODE pElement,
+                               const char *pcszAttribName)
+{
+    PDOMNODE pAttrNode = (PDOMNODE)treeFindEQData(&pElement->AttributesMap,
+                                                  (void*)pcszAttribName,
+                                                  CompareNodeBaseData);
+    if (pAttrNode)
+        return (pAttrNode->pstrNodeValue);
+
+    return (NULL);
+}
Index: trunk/src/helpers/xmldefs.c
===================================================================
--- trunk/src/helpers/xmldefs.c	(revision 62)
+++ trunk/src/helpers/xmldefs.c	(revision 63)
@@ -876,6 +876,10 @@
  +              <!DOCTYPE name SYSTEM "whatever.dtd">
  *
- *      or an internal subset in brackets, which contains the markup
- *      directly:
+ *      The SYSTEM identifier is required with XML, while a public
+ *      identifier is not. (In SGML, neither is required, but at
+ *      least one must be present.)
+ *
+ *      Alternatively,specify an internal subset in brackets, which
+ *      contains the markup directly:
  *
  +              <!DOCTYPE name [
Index: trunk/src/helpers/xstring.c
===================================================================
--- trunk/src/helpers/xstring.c	(revision 62)
+++ trunk/src/helpers/xstring.c	(revision 63)
@@ -690,7 +690,7 @@
 /*
  *@@ xstrrpl:
- *      replaces cReplLen characters in pxstr, starting
- *      at the position ulFirstReplPos, with the string
- *      in pxstrReplaceWith.
+ *      replaces "cReplLen" characters in pxstr, starting
+ *      at the position "ulFirstReplPos", with the first
+ *      "cReplaceWithLen" characters from pcszReplaceWith.
  *
  *      Returns the new length of the string, excluding
@@ -727,4 +727,5 @@
  *@@changed V0.9.9 (2001-02-14) [umoeller]: fixed NULL target crash
  *@@changed V0.9.9 (2001-03-09) [umoeller]: now using xstrReserve
+ *@@changed V0.9.11 (2001-04-22) [umoeller]: replaced replacement XSTRING with PCSZ
  */
 
@@ -732,5 +733,7 @@
               ULONG ulFirstReplOfs,             // in: ofs of first char to replace
               ULONG cReplLen,                   // in: no. of chars to replace
-              const XSTRING *pstrReplaceWith)   // in: string to replace chars with
+              const char *pcszReplaceWith,      // in: string to replace chars with
+              ULONG cReplaceWithLen)            // in: length of replacement string
+                                                // (this MUST be specified; if 0, chars are removed only)
 {
     ULONG   ulrc = 0;
@@ -739,16 +742,12 @@
     if (    (pxstr)         // V0.9.9 (2001-02-14) [umoeller]
          && (ulFirstReplOfs + cReplLen <= pxstr->ulLength)
-         && (pstrReplaceWith)
-         // && (pstrReplaceWith->ulLength)      no, this can be empty
+         && (pcszReplaceWith)
        )
     {
-        ULONG   cReplaceLen = pstrReplaceWith->ulLength;
-                    // can be 0!
-
         // size of new buffer:
-        ULONG   cbNeeded = pxstr->ulLength
-                         + cReplaceLen
-                         - cReplLen
-                         + 1;                  // null terminator
+        ULONG   cbNeeded = pxstr->ulLength      // existing
+                         + cReplaceWithLen      // plus replacement string length
+                         - cReplLen             // minus replaced characters
+                         + 1;                   // plus null terminator
         // offset where pszSearch was found
         PSZ     pFound = pxstr->psz + ulFirstReplOfs;
@@ -791,11 +790,21 @@
                        ulFirstReplOfs);     // up to "found"
 
-            if (cReplaceLen)
+            if (cReplaceWithLen)
             {
                 // we have a replacement:
                 // insert it next
+
+                /* memcpy(pszNew + ulFirstReplOfs,
+                       pstrReplaceWith->psz,
+                       cReplaceWithLen + 1);        // include null terminator
+                */
+                // no, we no longer can be sure that pcszReplaceWith is
+                // null terminated, so terminate explicitly
+                // V0.9.11 (2001-04-22) [umoeller]
+
                 memcpy(pszNew + ulFirstReplOfs,
-                       pstrReplaceWith->psz,
-                       cReplaceLen + 1);        // include null terminator
+                       pcszReplaceWith,
+                       cReplaceWithLen);
+                *(pszNew + ulFirstReplOfs + cReplaceWithLen) = '\0';
             }
 
@@ -810,5 +819,5 @@
             //            ³            ³
             //            pxstr->ulLength = 14
-            memcpy(pszNew + ulFirstReplOfs + cReplaceLen,
+            memcpy(pszNew + ulFirstReplOfs + cReplaceWithLen,
                    pFound + cReplLen,
                    // remaining bytes:
@@ -833,14 +842,14 @@
             // first, we move the end to its new location
             // (memmove handles overlap if needed)
-            memmove(pFound + cReplaceLen,
+            memmove(pFound + cReplaceWithLen,
                     pFound + cReplLen,
                     cTailLength + 1); // including null terminator
 
             // now overwrite "found" in the middle
-            if (cReplaceLen)
+            if (cReplaceWithLen)
             {
                 memcpy(pFound,
-                       pstrReplaceWith->psz,
-                       cReplaceLen);        // no null terminator
+                       pcszReplaceWith,
+                       cReplaceWithLen);        // no null terminator
             }
 
@@ -1032,5 +1041,6 @@
                                ulFirstReplOfs,              // where to start
                                cSearchLen,                  // chars to replace
-                               pstrReplace);
+                               pstrReplace->psz,
+                               pstrReplace->ulLength);      // adjusted V0.9.11 (2001-04-22) [umoeller]
 
                 // return new length
