Index: trunk/src/helpers/configsys.c
===================================================================
--- trunk/src/helpers/configsys.c	(revision 70)
+++ trunk/src/helpers/configsys.c	(revision 71)
@@ -159,4 +159,25 @@
  *      for analyzing CONFIG.SYS settings.
  *
+ *      Remarks:
+ *
+ *      --  With pcszKey, you can either specify a CONFIG.SYS
+ *          key WITH an equals sign (e.g. search for "LIBPATH=")
+ *          or the key name only (e.g. "SET SHELL").
+ *
+ *          Only in the latter case, this function makes sure
+ *          that the search key is complete; e.g. it will not
+ *          find "SET SHELLHANDLESINC=" then.
+ *
+ *      --  If the line found starts with leading spaces,
+ *          *ppStartOfLine will still receive the start of
+ *          the line.
+ *          For example, if you're searching for "SET PATH"
+ *          and the PATH line is
+ *
+ +              "   SET PATH=C:\os2"
+ *
+ *          the first space in the line is written into
+ *          *ppStartOfLine.
+ *
  *@@changed V0.9.0 [umoeller]: fixed bug in that this would also return something if only the first chars matched
  *@@changed V0.9.0 [umoeller]: fixed bug which could cause character before pszSearchIn to be examined
@@ -164,4 +185,5 @@
  *@@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
+ *@@changed V0.9.12 (2001-05-22) [umoeller]: added checks for key termination (SET SHELLHANDLESINC)
  */
 
@@ -177,11 +199,13 @@
     ULONG       ulKeyLength = strlen(pcszKey);
 
+    BOOL        fSearchKeyContainsEquals = (strchr(pcszKey, '=') != 0);
+
     do
     {
-        p = strhistr(p, pcszKey);
-
-        if (    (p)
-             && (p >= pcszSearchIn)
-           )
+        // find the key
+        // (on first loop, p is start of buffer;
+        // on subsequent loops, p is somewhere in bufer,
+        // if we've found the key somewhere else)
+        if (p = strhistr(p, pcszKey))
         {
             // make sure the key is at the beginning of a line
@@ -207,32 +231,52 @@
                 // OK, we're at the start of a line:
 
-                // return address of key
-                pReturn = (PSZ)p;
-                // return start of line
-                if (ppStartOfLine)
-                    *ppStartOfLine = (PSZ)pStartOfLine;
-
-                // test for all upper case?
-                if (pfIsAllUpperCase)
+                // check the character AFTER the
+                // key if it's a space or '=';
+                // without this check, we'd find
+                // SET SHELL= if caller is really
+                // looking for SET SHELLHANDLESINC=,
+                // but only do this check if the caller
+                // doesn't have '=' in the search string
+                // already
+                // V0.9.12 (2001-05-22) [umoeller]
+                CHAR c = *(p + ulKeyLength);
+                if (    (fSearchKeyContainsEquals)
+                     || (c == ' ')
+                     || (c == '=')
+                     || (c == '\n')
+                     || (c == '\r')
+                     || (c == '\t')
+                   )
                 {
-                    ULONG   ul = 0;
-                    *pfIsAllUpperCase = TRUE;
-
-                    for (ul = 0;
-                         ul < ulKeyLength;
-                         ul++)
+                    // return address of key
+                    pReturn = (PSZ)p;
+                    // return start of line
+                    if (ppStartOfLine)
+                        *ppStartOfLine = (PSZ)pStartOfLine;
+
+                    // test for all upper case?
+                    if (pfIsAllUpperCase)
                     {
-                        if (islower(*(p + ul)))
+                        ULONG   ul = 0;
+                        *pfIsAllUpperCase = TRUE;
+
+                        for (ul = 0;
+                             ul < ulKeyLength;
+                             ul++)
                         {
-                            *pfIsAllUpperCase = FALSE;
-                            break; // for
+                            if (islower(*(p + ul)))
+                            {
+                                *pfIsAllUpperCase = FALSE;
+                                break; // for
+                            }
                         }
                     }
-                }
-
-                break; // do
+
+                    break; // do
+                } // else search next key
             } // else search next key
 
-            p++; // search on after this key
+            p += ulKeyLength; // search on after this key
+                    // now skipping ulKeyLength V0.9.12 (2001-05-22) [umoeller]
         }
         else
@@ -475,5 +519,5 @@
  *
  *      If the manipulation succeeded, NO_ERROR is returned
- *      and *ppszChanged receives a new string describing
+ *      and pstrChanged receives a new string describing
  *      what was changed. This can be:
  *
@@ -502,5 +546,6 @@
  *
  *      -- This assumes that the line breaks are represented
- *         by \r\n items ONLY.
+ *         by \n ONLY. If *ppszContents has \r\n line breaks,
+ *         they must be converted to pure \n first.
  */
 
@@ -611,4 +656,5 @@
                                     &pStartOfLineWithKeyFound,
                                     &fIsAllUpperCase);
+                // returns beginning of line
 
             if (!pKeyFound)
Index: trunk/src/helpers/dosh2.c
===================================================================
--- trunk/src/helpers/dosh2.c	(revision 70)
+++ trunk/src/helpers/dosh2.c	(revision 71)
@@ -499,4 +499,5 @@
  *
  *@@added V0.9.4 (2000-07-19) [umoeller]
+ *@@changed V0.9.12 (2001-05-21) [umoeller]: fixed memory leak
  */
 
@@ -544,4 +545,6 @@
                 ppszThis++;
             }
+
+            free(pszSearch);        // was missing V0.9.12 (2001-05-21) [umoeller]
         }
     }
@@ -571,4 +574,5 @@
  *@@added V0.9.4 (2000-07-19) [umoeller]
  *@@changed V0.9.7 (2000-12-17) [umoeller]: added fAddFirst
+ *@@changed V0.9.12 (2001-05-21) [umoeller]: fixed memory leak
  */
 
@@ -625,4 +629,7 @@
                 else
                 {
+                    if (pEnv->papszVars)
+                        free(pEnv->papszVars);      // was missing V0.9.12 (2001-05-21) [umoeller]
+
                     pEnv->papszVars = papszNew;
                     pEnv->cVars++;
Index: trunk/src/helpers/linklist.c
===================================================================
--- trunk/src/helpers/linklist.c	(revision 70)
+++ trunk/src/helpers/linklist.c	(revision 71)
@@ -235,5 +235,5 @@
 }
 
-#ifdef __XWPMEMDEBUG__
+#ifdef __DEBUG_MALLOC_ENABLED__
 
 /*
@@ -263,5 +263,5 @@
 }
 
-#else
+#endif      // __DEBUG_MALLOC_ENABLED__
 
 /*
@@ -286,5 +286,5 @@
  */
 
-PLINKLIST lstCreate(BOOL fItemsFreeable)    // in: invoke free() on the data
+PLINKLIST (lstCreate)(BOOL fItemsFreeable)    // in: invoke free() on the data
                                             // item pointers upon destruction?
 {
@@ -294,6 +294,4 @@
     return (pNewList);
 }
-
-#endif      // __XWPMEMDEBUG__
 
 /*
@@ -543,5 +541,5 @@
 }
 
-#ifdef __XWPMEMDEBUG__
+#ifdef __DEBUG_MALLOC_ENABLED__
 
 /*
@@ -603,5 +601,5 @@
 }
 
-#else
+#endif // __DEBUG_MALLOC_ENABLED__
 
 /*
@@ -615,5 +613,5 @@
  */
 
-PLISTNODE lstAppendItem(PLINKLIST pList,
+PLISTNODE (lstAppendItem)(PLINKLIST pList,
                         void* pNewItemData)     // in: data to store in list node
 {
@@ -656,6 +654,4 @@
     return (pNewNode);
 }
-
-#endif // __XWPMEMDEBUG__
 
 /*
Index: trunk/src/helpers/memdebug.c
===================================================================
--- trunk/src/helpers/memdebug.c	(revision 70)
+++ trunk/src/helpers/memdebug.c	(revision 71)
@@ -456,4 +456,5 @@
  *
  *@@added V0.9.6 (2000-11-12) [umoeller]
+ *@@changed V0.9.12 (2001-05-21) [umoeller]: this reported errors on realloc(0), which is a valid call, fixed
  */
 
@@ -466,4 +467,9 @@
     void *prc = NULL;
     BOOL fFound = FALSE;
+
+    if (!p)
+        // p == NULL: this is valid, use malloc() instead
+        // V0.9.12 (2001-05-21) [umoeller]
+        return (memdMalloc(stSize, pcszSourceFile, ulLine, pcszFunction));
 
     if (memdLock())
Index: trunk/src/helpers/memdebug_win.c
===================================================================
--- trunk/src/helpers/memdebug_win.c	(revision 70)
+++ trunk/src/helpers/memdebug_win.c	(revision 71)
@@ -479,5 +479,5 @@
         case WM_CREATE:
         {
-            TRY_LOUD(excpt1, NULL)
+            TRY_LOUD(excpt1)
             {
                 // PCREATESTRUCT pcs = (PCREATESTRUCT)mp2;
Index: trunk/src/helpers/prfh.c
===================================================================
--- trunk/src/helpers/prfh.c	(revision 70)
+++ trunk/src/helpers/prfh.c	(revision 71)
@@ -122,5 +122,5 @@
 }
 
-#ifdef __XWPMEMDEBUG__ // setup.h, helpers\memdebug.c
+#ifdef __DEBUG_MALLOC_ENABLED__ // setup.h, helpers\memdebug.c
 
 /*
Index: trunk/src/helpers/winh.c
===================================================================
--- trunk/src/helpers/winh.c	(revision 70)
+++ trunk/src/helpers/winh.c	(revision 71)
@@ -1492,4 +1492,37 @@
 
 /*
+ *@@ winhLboxFindItemFromHandle:
+ *      finds the list box item with the specified
+ *      handle.
+ *
+ *      Of course this only makes sense if each item
+ *      has a unique handle indeed.
+ *
+ *      Returns the index of the item found or -1.
+ *
+ *@@added V0.9.12 (2001-05-18) [umoeller]
+ */
+
+ULONG winhLboxFindItemFromHandle(HWND hwndListBox,
+                                 ULONG ulHandle)
+{
+    LONG cItems = WinQueryLboxCount(hwndListBox);
+    if (cItems)
+    {
+        ULONG ul;
+        for (ul = 0;
+             ul < cItems;
+             ul++)
+        {
+            if (ulHandle == winhQueryLboxItemHandle(hwndListBox,
+                                                    ul))
+                return (ul);
+        }
+    }
+
+    return (-1);
+}
+
+/*
  *@@category: Helpers\PM helpers\Scroll bar helpers
  */
@@ -3566,4 +3599,5 @@
  *
  *@@added V0.9.3 (2000-04-29) [umoeller]
+ *@@changed V0.9.12 (2001-05-21) [umoeller]: this failed if INI data had root dir, fixed
  */
 
@@ -3580,4 +3614,6 @@
 {
     FILEDLG fd;
+    FILESTATUS3 fs3;
+
     memset(&fd, 0, sizeof(FILEDLG));
     fd.cbSize = sizeof(FILEDLG);
@@ -3589,23 +3625,29 @@
         fd.fl |= FDS_OPEN_DIALOG;
 
-    // default: copy pszFile
-    strcpy(fd.szFullFile, pszFile);
-
-    if ( (hini) && (flFlags & WINH_FOD_INILOADDIR) )
-    {
-        // overwrite with initial directory for FOD from OS2.INI
-        if (PrfQueryProfileString(hini,
-                                  (PSZ)pcszApplication,
-                                  (PSZ)pcszKey,
-                                  "",      // default string V0.9.9 (2001-02-10) [umoeller]
-                                  fd.szFullFile,
-                                  sizeof(fd.szFullFile)-10)
-                    >= 2)
-        {
-            // found: append "\*"
-            strcat(fd.szFullFile, "\\");
-            strcat(fd.szFullFile, pszFile);
-        }
-    }
+    if (    (hini)
+         && (flFlags & WINH_FOD_INILOADDIR)
+         && (PrfQueryProfileString(hini,
+                                   (PSZ)pcszApplication,
+                                   (PSZ)pcszKey,
+                                   "",      // default string V0.9.9 (2001-02-10) [umoeller]
+                                   fd.szFullFile,
+                                   sizeof(fd.szFullFile)-10)
+                     > 2)
+         // added these checks V0.9.12 (2001-05-21) [umoeller]
+         && (!DosQueryPathInfo(fd.szFullFile,
+                               FIL_STANDARD,
+                               &fs3,
+                               sizeof(fs3)))
+         && (fs3.attrFile & FILE_DIRECTORY)
+       )
+    {
+        // found: append "\*"
+        strcat(fd.szFullFile, "\\");
+        strcat(fd.szFullFile, pszFile);
+    }
+    else
+        // default: copy pszFile
+        strcpy(fd.szFullFile, pszFile);
+        // fixed V0.9.12 (2001-05-21) [umoeller]
 
     if (    WinFileDlg(HWND_DESKTOP,    // parent
Index: trunk/src/helpers/xml.c
===================================================================
--- trunk/src/helpers/xml.c	(revision 70)
+++ trunk/src/helpers/xml.c	(revision 71)
@@ -7,6 +7,6 @@
  *      layers:
  *
- *      --  The bottom layer is implemented by @expat, which I have
- *          ported and hacked to the xwphelpers.
+ *      --  The bottom layer is implemented by the @expat parser,
+ *          which I have ported and hacked to the xwphelpers.
  *
  *          See xmlparse.c for an introduction.
@@ -16,4 +16,12 @@
  *          which is vaguely modelled after the Document Object Model
  *          (DOM) standardized by the W3C. That's this file.
+ *
+ *          This top layer allows you to do two things VERY EASILY:
+ *
+ *          1)  Parse an XML document (which uses expat internally)
+ *              and build a DOM tree from that. See xmlCreateDOM.
+ *
+ *          2)  Create a DOM tree in memory and write an XML
+ *              document from that. See xmlCreateDocument.
  *
  *      <B>XML</B>
@@ -43,8 +51,12 @@
  *      Most notably, there are the following differences:
  *
+ *      --  External entities don't work yet. As a result, DOCTYPE's
+ *          only make sense if the entire DTD is in the same document
+ *          (internal subset).
+ *
  *      --  Not all node types are implemented. See _DOMNODE for
  *          the supported types.
  *
- *      --  Only a small subset of the standardized methods is implemented,
+ *      --  Only a subset of the standardized methods is implemented,
  *          and they are called differently to adhere to the xwphelpers
  *          conventions.
@@ -349,8 +361,8 @@
  *      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.
+ *      After calling this, pNode is no longer valid.
+ *
+ *      If you invoke this on a DOCUMENT node, the
+ *      entire DOM tree will get deleted recursively.
  *
  *@@added V0.9.9 (2001-02-16) [umoeller]
@@ -383,9 +395,9 @@
                 {
                     // call delete recursively
+                    PDOMNODE pNext = (PDOMNODE)treeNext((TREE*)pAttrib);
                     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);
+                            // tree and rebalance the tree
+                    pAttrib = pNext;
                 }
             break; }
@@ -395,5 +407,10 @@
             case DOMNODE_PROCESSING_INSTRUCTION:
             case DOMNODE_COMMENT:
+                pDomNode = (PDOMNODE)pNode;
+            break;
+
             case DOMNODE_DOCUMENT:
+                if (((PDOMDOCUMENTNODE)pNode)->pDocType)
+                    xmlDeleteNode((PNODEBASE)((PDOMDOCUMENTNODE)pNode)->pDocType);
                 pDomNode = (PDOMNODE)pNode;
             break;
@@ -441,5 +458,5 @@
                                 pParticle = (PCMELEMENTPARTICLE)pDelNode->pItemData;
                         xmlDeleteNode((PNODEBASE)pParticle);
-                        //  treeDelete(pp->         ###
+                        //  treeDelete(pp->         // @@todo
                         pDelNode = pDelNode->pNext;
                     }
@@ -962,4 +979,9 @@
  *      _XMLCONTENT content model (which is the @expat structure).
  *      This recurses, if necessary.
+ *
+ *      NOTE: As opposed to the other "create" functions,
+ *      this does not take a parent node as input. If this
+ *      returns NO_ERROR, it is the caller's responsibility
+ *      to add the produced node to the document's DOCTYPE node.
  *
  *@@added V0.9.9 (2001-02-16) [umoeller]
@@ -2120,5 +2142,5 @@
 /* ******************************************************************
  *
- *   DOM root APIs
+ *   DOM parser APIs
  *
  ********************************************************************/
@@ -2158,6 +2180,16 @@
  +                         TRUE); // if last, this will clean up the parser
  *
- *      3) Process the data in the DOM tree. When done,
- *         call xmlFreeDOM, which will free all memory.
+ *      3) Process the data in the DOM tree.
+ *
+ *         Look at the DOMNODE definition to see how you
+ *         can traverse the data. Essentially, everything
+ *         is based on linked lists and string maps.
+ *
+ *         A few helper functions have been added for
+ *         quick lookup. See xmlGetRootElement,
+ *         xmlGetFirstChild, xmlGetLastChild, xmlGetFirstText,
+ *         xmlGetElementsByTagName, xmlGetAttribute.
+ *
+ *      4) When done, call xmlFreeDOM, which will free all memory.
  *
  *@@added V0.9.9 (2001-02-14) [umoeller]
@@ -2183,8 +2215,8 @@
         // create the document node
         arc = xmlCreateDomNode(NULL, // no parent
-                            DOMNODE_DOCUMENT,
-                            NULL,
-                            0,
-                            &pDocument);
+                               DOMNODE_DOCUMENT,
+                               NULL,
+                               0,
+                               &pDocument);
 
         if (arc == NO_ERROR)
@@ -2263,5 +2295,5 @@
 /*
  *@@ xmlParse:
- *      parses another piece of XML data.
+ *      parses another chunk of XML data.
  *
  *      If (fIsLast == TRUE), the internal @expat parser
@@ -2375,4 +2407,6 @@
             pDom->pParser = NULL;
         }
+
+        xmlDeleteNode((PNODEBASE)pDom->pDocumentNode);
 
         free(pDom);
@@ -2645,2 +2679,274 @@
     return (NULL);
 }
+
+/* ******************************************************************
+ *
+ *   DOM build
+ *
+ ********************************************************************/
+
+/*
+ *@@ xmlCreateDocument:
+ *      creates a new XML document.
+ *
+ *      This is the first step in creating a DOM
+ *      tree in memory.
+ *
+ *      This function creates a DOCUMENT node
+ *      (which is returned) and a root ELEMENT node
+ *      within the document. For convenience, the
+ *      root ELEMENT node is returned as well.
+ *
+ *      This does not create a DOCTYPE node in
+ *      the document.
+ *
+ *      After this, you can add sub-elements and
+ *      attributes to the root element using
+ *      xmlCreateElementNode and xmlCreateAttributeNode.
+ *
+ *      Use xmlDeleteNode on the DOCUMENT node
+ *      to delete the entire DOM tree.
+ *
+ *@@added V0.9.12 (2001-05-21) [umoeller]
+ */
+
+APIRET xmlCreateDocument(const char *pcszRootElementName,   // in: root element name
+                         PDOMDOCUMENTNODE *ppDocument,      // out: DOCUMENT node
+                         PDOMNODE *ppRootElement)           // out: root ELEMENT node within DOCUMENT
+{
+    APIRET arc = NO_ERROR;
+    PDOMDOCUMENTNODE pDocument = NULL;
+    PDOMNODE pRootElement = NULL;
+
+    if ( (!pcszRootElementName) || (!ppDocument) || (!ppRootElement) )
+        arc = ERROR_INVALID_PARAMETER;
+    else
+        // create the document node
+        if (!(arc = xmlCreateDomNode(NULL, // no parent
+                                     DOMNODE_DOCUMENT,
+                                     NULL,
+                                     0,
+                                     (PDOMNODE*)&pDocument)))
+            if (!(arc = xmlCreateDomNode((PDOMNODE)pDocument,     // parent
+                                         DOMNODE_ELEMENT,
+                                         pcszRootElementName,
+                                         0,
+                                         &pRootElement)))
+            {
+                *ppDocument = pDocument;
+                *ppRootElement = pRootElement;
+            }
+
+    return (arc);
+}
+
+/*
+ *@@ WriteNodes:
+ *      internal helper for writing out the nodes.
+ *
+ *@@added V0.9.12 (2001-05-21) [umoeller]
+ */
+
+VOID WriteNodes(PXSTRING pxstr,
+                PDOMNODE pDomNode)       // in: node whose children are to be written (initially DOCUMENT)
+{
+    PLISTNODE pListNode;
+
+    BOOL fMixedContent = (xmlGetFirstText(pDomNode) != NULL);
+
+    for (pListNode = lstQueryFirstNode(&pDomNode->llChildren);
+         (pListNode);
+         pListNode = pListNode->pNext)
+    {
+        PDOMNODE pChildNode = (PDOMNODE)pListNode->pItemData;
+
+        switch (pChildNode->NodeBase.ulNodeType)
+        {
+            case DOMNODE_ELEMENT:
+            {
+                PDOMNODE pAttribNode;
+                // write out opening ELEMENT tag
+                // add a line break if this does NOT have mixed
+                // content
+                if (!fMixedContent)
+                    xstrcatc(pxstr, '\n');
+
+                xstrcatc(pxstr, '<');
+                xstrcats(pxstr, &pChildNode->NodeBase.strNodeName);
+
+                // go through attributes
+                for (pAttribNode = (PDOMNODE)treeFirst(pChildNode->AttributesMap);
+                     (pAttribNode);
+                     pAttribNode = (PDOMNODE)treeNext((TREE*)pAttribNode))
+                {
+                    xstrcat(pxstr, "\n    ", 0);
+                    xstrcats(pxstr, &pAttribNode->NodeBase.strNodeName);
+                    xstrcat(pxstr, "=\"", 0);
+                    xstrcats(pxstr, pAttribNode->pstrNodeValue);
+                    xstrcatc(pxstr, '\"');
+                }
+
+                // now check... do we have child nodes?
+                if (lstCountItems(&pChildNode->llChildren))
+                {
+                    // yes:
+                    xstrcatc(pxstr, '>');
+
+                    // recurse into this child element
+                    WriteNodes(pxstr, pChildNode);
+
+                    if (!fMixedContent)
+                        xstrcatc(pxstr, '\n');
+
+                    // write closing tag
+                    xstrcat(pxstr, "</", 0);
+                    xstrcats(pxstr, &pChildNode->NodeBase.strNodeName);
+                    xstrcatc(pxstr, '>');
+                }
+                else
+                {
+                    // no child nodes:
+                    // mark this tag as "empty"
+                    xstrcat(pxstr, "/>", 0);
+                }
+            }
+            break;
+
+            case DOMNODE_TEXT:
+            case DOMNODE_COMMENT:
+                // that's simple
+                xstrcats(pxstr, pChildNode->pstrNodeValue);
+            break;
+
+            case DOMNODE_DOCUMENT_TYPE:
+                // @@todo
+            break;
+
+        }
+    }
+}
+
+/*
+ *@@ xmlWriteDocument:
+ *      creates a complete XML document in the specified
+ *      string buffer from the specified DOMDOCUMENTNODE.
+ *
+ *      This creates a full XML document, starting with
+ *      the <?xml...?> header, the DTD (if present),
+ *      and the elements and attributes.
+ *
+ *      The input XSTRING must be initialized. Its
+ *      contents will be overwritten, if any exists.
+ *
+ *      Sooo... to write a full XML document to disk,
+ *      do the following:
+ *
+ *      1)  Call xmlCreateDocument to have an empty
+ *          document with a root element created.
+ *
+ *      2)  Add elements, subelements, and attributes
+ *          using xmlCreateElementNode and
+ *          xmlCreateAttributeNode.
+ *
+ *      3)  Call xmlWriteDocument to have the XML
+ *          document written into an XSTRING.
+ *
+ *      4)  Write the XSTRING to disk, e.g. using
+ *          fwrite().
+ *
+ *          Note: You can also use doshWriteTextFile,
+ *          but you should then first convert the
+ *          line format using xstrConvertLineFormat.
+ *
+ *      Example:
+ *
+ +          APIRET arc = NO_ERROR;
+ +          PDOMDOCUMENTNODE pDocument = NULL;
+ +          PDOMNODE pRootElement = NULL;
+ +
+ +          // create a DOM
+ +          if (!(arc = xmlCreateDocument("MYROOTNODE",
+ +                                        &pDocument,
+ +                                        &pRootElement)))
+ +          {
+ +              // add subelements to the root element
+ +              PDOMNODE pSubelement;
+ +              if (!(arc = xmlCreateElementNode(pRootElement,
+ +                                               "MYSUBELEMENT",
+ +                                               &pSubelement)))
+ +              {
+ +                  // add an attribute
+ +                  PDOMNODE pAttribute;
+ +                  if (!(arc = xmlCreateAttributeNode(pSubElement,
+ +                                                     "MYATTRIB",
+ +                                                     "VALUE",
+ +                                                     &pAttribute)))
+ +                  {
+ +                      // alright, turn this into a string
+ +                      XSTRING str;
+ +                      xstrInit(&str, 1000);
+ +                      if (!(arc = xmlWriteDocument(pDocument,
+ +                                                   "ISO-8859-1",
+ +                                                   NULL,      // or DOCTYPE
+ +                                                   &str)))
+ +                      {
+ +                          FILE *file = fopen("myfile.xml", "w");
+ +                          fwrite(str.psz,
+ +                                 1,
+ +                                 str.ulLength,
+ +                                 file);
+ +                          fclose(file);
+ +                      }
+ +                  }
+ +              }
+ +
+ +              // this kills the entire tree
+ +              xmlDeleteNode((PNODEBASE)pDocument);
+ +
+ +          }
+ +
+ *
+ *      A note about whitespace handling. Presently, this
+ *      adds line breaks after the opening tag of an
+ *      element if the element has element content only.
+ *      However, if the element has mixed content, this
+ *      line break is NOT automatically added because
+ *      white space may then be significant.
+ *
+ *@@added V0.9.12 (2001-05-21) [umoeller]
+ */
+
+APIRET xmlWriteDocument(PDOMDOCUMENTNODE pDocument,     // in: document node
+                        const char *pcszEncoding,       // in: encoding string (e.g. "ISO-8859-1")
+                        const char *pcszDoctype,        // in: entire DOCTYPE statement or NULL
+                        PXSTRING pxstr)                 // out: document
+{
+    APIRET arc = NO_ERROR;
+
+    if ( (!pDocument) || (!pcszEncoding) || (!pxstr) )
+        arc = ERROR_INVALID_PARAMETER;
+    else
+    {
+        // <?xml version="1.0" encoding="ISO-8859-1"?>
+        xstrcpy(pxstr, "<?xml version=\"1.0\" encoding=\"", 0);
+        xstrcat(pxstr, pcszEncoding, 0);
+        xstrcat(pxstr, "\"?>\n", 0);
+
+        // write entire DOCTYPE statement
+        if (pcszDoctype)
+        {
+            xstrcatc(pxstr, '\n');
+            xstrcat(pxstr, pcszDoctype, 0);
+            xstrcatc(pxstr, '\n');
+        }
+
+        // write out children
+        WriteNodes(pxstr, (PDOMNODE)pDocument);
+
+        xstrcatc(pxstr, '\n');
+    }
+
+    return (arc);
+}
+
+
Index: trunk/src/helpers/xmlparse.c
===================================================================
--- trunk/src/helpers/xmlparse.c	(revision 70)
+++ trunk/src/helpers/xmlparse.c	(revision 71)
@@ -873,6 +873,7 @@
         XML_Memory_Handling_Suite *mtemp;
 
-        parser = malloc(sizeof(Parser));
+        parser = (malloc)(sizeof(Parser));
         mtemp = &(((Parser*)parser)->m_mem);
+
         mtemp->malloc_fcn = malloc;
         mtemp->realloc_fcn = realloc;
Index: trunk/src/helpers/xstring.c
===================================================================
--- trunk/src/helpers/xstring.c	(revision 70)
+++ trunk/src/helpers/xstring.c	(revision 71)
@@ -285,4 +285,5 @@
  *@@added V0.9.7 (2001-01-07) [umoeller]
  *@@changed V0.9.9 (2001-03-09) [umoeller]: now using ulDelta
+ *@@changed V0.9.12 (2001-05-21) [umoeller]: now reporting error on realloc fail
  */
 
@@ -318,8 +319,11 @@
         // this gives the C runtime a chance to expand the
         // existing block
-        pxstr->psz = (PSZ)realloc(pxstr->psz, cbAllocate);
+        if (pxstr->psz = (PSZ)realloc(pxstr->psz, cbAllocate))
                     // if pxstr->psz is NULL, realloc behaves like malloc
-        pxstr->cbAllocated = cbAllocate;
+            pxstr->cbAllocated = cbAllocate;
                 // ulLength is unchanged
+        else
+            // error: V0.9.12 (2001-05-21) [umoeller]
+            pxstr->cbAllocated = 0;
     }
     // else: we have enough memory
@@ -448,4 +452,5 @@
  *@@changed V0.9.9 (2001-02-16) [umoeller]: now supporting non-zero-terminated pcszSource
  *@@changed V0.9.9 (2001-03-09) [umoeller]: now using xstrReserve
+ *@@changed V0.9.12 (2001-05-21) [umoeller]: added xstrReserve error checking
  */
 
@@ -470,15 +475,18 @@
     {
         // we do have a source string:
-        xstrReserve(pxstr,
-                    // required memory:
-                    ulSourceLength + 1);
-
-        memcpy(pxstr->psz,
-               pcszSource,
-               ulSourceLength);
-        *(pxstr->psz + ulSourceLength) = '\0';
-                // V0.9.9 (2001-02-16) [umoeller]
-                // we must do this or otherwise we require pcszSource
-                // to be zero-terminated... not a good idea
+        if (xstrReserve(pxstr,
+                        // required memory:
+                        ulSourceLength + 1))
+        {
+            memcpy(pxstr->psz,
+                   pcszSource,
+                   ulSourceLength);
+            *(pxstr->psz + ulSourceLength) = '\0';
+                    // V0.9.9 (2001-02-16) [umoeller]
+                    // we must do this or otherwise we require pcszSource
+                    // to be zero-terminated... not a good idea
+        }
+        else
+            pxstr->ulLength = 0;        // error V0.9.12 (2001-05-21) [umoeller]
     }
     else
