home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / xwphescr.zip / XWPH0208.ZIP / src / helpers / xml.c < prev    next >
C/C++ Source or Header  |  2002-08-08  |  118KB  |  3,313 lines

  1.  
  2. /*
  3.  *@@sourcefile xml.c:
  4.  *      XML document handling.
  5.  *
  6.  *      XML support in the XWorkplace Helpers is broken into two
  7.  *      layers:
  8.  *
  9.  *      --  The bottom layer is implemented by the @expat parser,
  10.  *          which I have ported and hacked to the xwphelpers.
  11.  *
  12.  *          See xmlparse.c for an introduction.
  13.  *
  14.  *      --  Because expat requires so many callbacks and is non-validating,
  15.  *          I have added a top layer above the expat library
  16.  *          which is vaguely modelled after the Document Object Model
  17.  *          (DOM) standardized by the W3C. That's this file.
  18.  *
  19.  *          This top layer allows you to do two things VERY EASILY:
  20.  *
  21.  *          1)  Parse an XML document (which uses expat internally)
  22.  *              and build a DOM tree from that. See xmlCreateDOM.
  23.  *
  24.  *          2)  Create a DOM tree in memory and write an XML
  25.  *              document from that. See xmlCreateDocument.
  26.  *
  27.  *      <B>XML</B>
  28.  *
  29.  *      In order to understand XML myself, I have written a couple of
  30.  *      glossary entries for the complex XML terminology. See @XML
  31.  *      for a start.
  32.  *
  33.  *      <B>Document Object Model (DOM)</B>
  34.  *
  35.  *      See @DOM for a general introduction.
  36.  *
  37.  *      DOM really calls for object oriented programming so the various
  38.  *      structs can inherit from each other. Since this implementation
  39.  *      was supposed to be a C-only interface, we cannot implement
  40.  *      inheritance at the language level. Instead, each XML document
  41.  *      is broken up into a tree of node structures only (see DOMNODE),
  42.  *      each of which has a special type. The W3C DOM allows this
  43.  *      (and calls this the "flattened" view, as opposed to the
  44.  *      "inheritance view").
  45.  *
  46.  *      The W3C DOM specification prescribes tons of methods, which I
  47.  *      really had no use for, so I didn't implement them. This implementation
  48.  *      is only a DOM insofar as it uses nodes which represent @documents,
  49.  *      @elements, @attributes, @comments, and @processing_instructions.
  50.  *
  51.  *      Most notably, there are the following differences:
  52.  *
  53.  *      --  External entities don't work yet. As a result, DOCTYPE's
  54.  *          only make sense if the entire DTD is in the same document
  55.  *          (internal subset).
  56.  *
  57.  *      --  Not all node types are implemented. See DOMNODE for
  58.  *          the supported types.
  59.  *
  60.  *      --  Only a subset of the standardized methods is implemented,
  61.  *          and they are called differently to adhere to the xwphelpers
  62.  *          conventions.
  63.  *
  64.  *      --  DOM uses UTF-16 for its DOMString type. @expat gives UTF-8
  65.  *          strings to all the handlers though, so all data in the DOM nodes
  66.  *          is UTF-8 encoded. This still needs to be fixed.
  67.  *
  68.  *      --  DOM defines the DOMException class. This isn't supported in C.
  69.  *          Instead, we use special error codes which add to the standard
  70.  *          OS/2 error codes (APIRET). All our error codes are >= 40000
  71.  *          to avoid conflicts.
  72.  *
  73.  *      It shouldn't be too difficult to write a C++ encapsulation
  74.  *      of this though which fully implements all the DOM methods.
  75.  *
  76.  *      However, we do implement node management as in the standard.
  77.  *      See xmlCreateDomNode and xmlDeleteNode.
  78.  *
  79.  *      The main entry point into this is xmlCreateDOM. See remarks
  80.  *      there for how this will be typically used.
  81.  *
  82.  *      <B>Validation</B>
  83.  *
  84.  *      @expat doesn't check XML documents for whether they are @valid.
  85.  *      In other words, expat is a non-validating XML processor.
  86.  *
  87.  *      By contrast, this pseudo-DOM implementation can validate to
  88.  *      a certain extent.
  89.  *
  90.  *      -- If you pass DF_PARSEDTD to xmlCreateDOM, the DTD will be
  91.  *         parsed and the document will be validated against it.
  92.  *         Validation is working as far as elements and attributes
  93.  *         are checked for proper nesting. However, we cannot fully
  94.  *         check for proper ordering etc. in (children) mode of
  95.  *         @element_declarations. This will only check for whether
  96.  *         elements may appear in another element at all -- not for
  97.  *         the correct order.
  98.  *
  99.  *      -- Otherwise the @DTD entries will not be stored in the DOM
  100.  *         nodes, and no validation occurs. Still, if a DTD exists,
  101.  *         @expat will insert attributes that have a default value
  102.  *         in their @attribute declaraion and have not been specified.
  103.  *
  104.  *@@header "helpers\xml.h"
  105.  *@@added V0.9.6 (2000-10-29) [umoeller]
  106.  */
  107.  
  108. /*
  109.  *      Copyright (C) 2000-2002 Ulrich Möller.
  110.  *      This file is part of the "XWorkplace helpers" source package.
  111.  *      This is free software; you can redistribute it and/or modify
  112.  *      it under the terms of the GNU General Public License as published
  113.  *      by the Free Software Foundation, in version 2 as it comes in the
  114.  *      "COPYING" file of the XWorkplace main distribution.
  115.  *      This program is distributed in the hope that it will be useful,
  116.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  117.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  118.  *      GNU General Public License for more details.
  119.  */
  120.  
  121. #define OS2EMX_PLAIN_CHAR
  122.     // this is needed for "os2emx.h"; if this is defined,
  123.     // emx will define PSZ as _signed_ char, otherwise
  124.     // as unsigned char
  125.  
  126. #define INCL_DOSERRORS
  127. #include <os2.h>
  128.  
  129. #include <stdlib.h>
  130. #include <string.h>
  131. #include <stdio.h>
  132.  
  133. #include "setup.h"                      // code generation and debugging options
  134.  
  135. #include "expat\expat.h"
  136.  
  137. #include "helpers\linklist.h"
  138. #include "helpers\standards.h"
  139. #include "helpers\stringh.h"
  140. #include "helpers\tree.h"
  141. #include "helpers\xstring.h"
  142. #include "helpers\xml.h"
  143.  
  144. #pragma hdrstop
  145.  
  146. /*
  147.  *@@category: Helpers\XML
  148.  *      see xml.c.
  149.  */
  150.  
  151. /*
  152.  *@@category: Helpers\XML\Document Object Model (DOM)
  153.  *      see xml.c.
  154.  */
  155.  
  156. /* ******************************************************************
  157.  *
  158.  *   Error handling
  159.  *
  160.  ********************************************************************/
  161.  
  162. /*
  163.  *@@ xmlDescribeError:
  164.  *      returns a string describing the error corresponding to code.
  165.  *      The code should be one of the enums that can be returned from
  166.  *      XML_GetErrorCode.
  167.  *
  168.  *@@changed V0.9.9 (2001-02-14) [umoeller]: adjusted for new error codes
  169.  *@@changed V0.9.9 (2001-02-16) [umoeller]: moved this here from xmlparse.c
  170.  */
  171.  
  172. const char* xmlDescribeError(int code)
  173. {
  174.     switch (code)
  175.     {
  176.         // start of expat (parser) errors
  177.         case ERROR_EXPAT_NO_MEMORY:
  178.             return ("Out of memory");
  179.  
  180.         case ERROR_EXPAT_SYNTAX:
  181.             return ("Syntax error");
  182.         case ERROR_EXPAT_NO_ELEMENTS:
  183.             return ("No element found");
  184.         case ERROR_EXPAT_INVALID_TOKEN:
  185.             return ("Not well-formed (invalid token)");
  186.         case ERROR_EXPAT_UNCLOSED_TOKEN:
  187.             return ("Unclosed token");
  188.         case ERROR_EXPAT_PARTIAL_CHAR:
  189.             return ("Unclosed token");
  190.         case ERROR_EXPAT_TAG_MISMATCH:
  191.             return ("Mismatched tag");
  192.         case ERROR_EXPAT_DUPLICATE_ATTRIBUTE:
  193.             return ("Duplicate attribute");
  194.         case ERROR_EXPAT_JUNK_AFTER_DOC_ELEMENT:
  195.             return ("Junk after root element");
  196.         case ERROR_EXPAT_PARAM_ENTITY_REF:
  197.             return ("Illegal parameter entity reference");
  198.         case ERROR_EXPAT_UNDEFINED_ENTITY:
  199.             return ("Undefined entity");
  200.         case ERROR_EXPAT_RECURSIVE_ENTITY_REF:
  201.             return ("Recursive entity reference");
  202.         case ERROR_EXPAT_ASYNC_ENTITY:
  203.             return ("Asynchronous entity");
  204.         case ERROR_EXPAT_BAD_CHAR_REF:
  205.             return ("Reference to invalid character number");
  206.         case ERROR_EXPAT_BINARY_ENTITY_REF:
  207.             return ("Reference to binary entity");
  208.         case ERROR_EXPAT_ATTRIBUTE_EXTERNAL_ENTITY_REF:
  209.             return ("Reference to external entity in attribute");
  210.         case ERROR_EXPAT_MISPLACED_XML_PI:
  211.             return ("XML processing instruction not at start of external entity");
  212.         case ERROR_EXPAT_UNKNOWN_ENCODING:
  213.             return ("Unknown encoding");
  214.         case ERROR_EXPAT_INCORRECT_ENCODING:
  215.             return ("Encoding specified in XML declaration is incorrect");
  216.         case ERROR_EXPAT_UNCLOSED_CDATA_SECTION:
  217.             return ("Unclosed CDATA section");
  218.         case ERROR_EXPAT_EXTERNAL_ENTITY_HANDLING:
  219.             return ("Error in processing external entity reference");
  220.         case ERROR_EXPAT_NOT_STANDALONE:
  221.             return ("Document is not standalone");
  222.         case ERROR_EXPAT_UNEXPECTED_STATE:
  223.             return ("Unexpected parser state - please send a bug report");
  224.         // end of expat (parser) errors
  225.  
  226.         // start of validation errors
  227.  
  228.         case ERROR_DOM_UNDECLARED_ELEMENT:
  229.             return ("Element has not been declared");
  230.         case ERROR_DOM_ROOT_ELEMENT_MISNAMED:
  231.             return ("Root element name does not match DOCTYPE name");
  232.         case ERROR_DOM_INVALID_ROOT_ELEMENT:
  233.             return ("Invalid or duplicate root element");
  234.  
  235.         case ERROR_DOM_INVALID_SUBELEMENT:
  236.             return ("Invalid sub-element in parent element");
  237.         case ERROR_DOM_DUPLICATE_ELEMENT_DECL:
  238.             return ("Duplicate element declaration");
  239.         case ERROR_DOM_DUPLICATE_ATTRIBUTE_DECL:
  240.             return ("Duplicate attribute declaration");
  241.         case ERROR_DOM_UNDECLARED_ATTRIBUTE:
  242.             return ("Undeclared attribute in element");
  243.         case ERROR_ELEMENT_CANNOT_HAVE_CONTENT:
  244.             return ("Element cannot have content");
  245.         case ERROR_DOM_INVALID_ATTRIB_VALUE:
  246.             return ("Invalid attribute value");
  247.         case ERROR_DOM_REQUIRED_ATTRIBUTE_MISSING:
  248.             return ("Required attribute is missing");
  249.         case ERROR_DOM_SUBELEMENT_IN_EMPTY_ELEMENT:
  250.             return ("Subelement in empty element");
  251.  
  252.         case ERROR_DOM_PARSING:
  253.             return ("Parsing error");
  254.         case ERROR_DOM_VALIDITY:
  255.             return ("Validity error");
  256.  
  257.         case ERROR_DOM_NODETYPE_NOT_SUPPORTED:
  258.             return ("DOM node type not supported");
  259.         case ERROR_DOM_NO_DOCUMENT:
  260.             return ("No DOM document");
  261.         case ERROR_DOM_NO_ELEMENT:
  262.             return ("No DOM element");
  263.         case ERROR_DOM_DUPLICATE_DOCTYPE:
  264.             return ("Duplicate doctype");
  265.         case ERROR_DOM_DOCTYPE_ROOT_NAMES_MISMATCH:
  266.             return ("Root element doesn't match doctype name");
  267.         case ERROR_DOM_INTEGRITY:
  268.             return ("DOM integrity error");
  269.         case ERROR_DOM_DUPLICATE_ATTRIBUTE:
  270.             return ("Duplicate attribute");
  271.  
  272.         case ERROR_DOM_VALIDATE_INVALID_ELEMENT:
  273.             return ("Validation error: Undeclared element name");
  274.         case ERROR_DOM_ELEMENT_DECL_OUTSIDE_DOCTYPE:
  275.             return ("Element declaration outside doctype");
  276.         case ERROR_DOM_ATTLIST_DECL_OUTSIDE_DOCTYPE:
  277.             return ("Attlist declaration outside doctype");
  278.  
  279.         case ERROR_DOM_INCOMPLETE_ENCODING_MAP:
  280.             return ("Incomplete encoding map specified");
  281.  
  282.         case ERROR_DOM_INVALID_EXTERNAL_HANDLER:
  283.             return ("Invalid 'external' handler specified");
  284.     }
  285.  
  286.     return NULL;
  287. }
  288.  
  289. /*
  290.  *@@ xmlSetError:
  291.  *      sets the DOM's error state and stores error information
  292.  *      and parser position.
  293.  *
  294.  *@@added V0.9.9 (2001-02-16) [umoeller]
  295.  */
  296.  
  297. VOID xmlSetError(PXMLDOM pDom,
  298.                  APIRET arc,
  299.                  const char *pcszFailing,
  300.                  BOOL fValidityError)   // in: if TRUE, this is a validation error;
  301.                                         // if FALSE, this is a parser error
  302. {
  303.     pDom->arcDOM = arc;
  304.     pDom->pcszErrorDescription = xmlDescribeError(pDom->arcDOM);
  305.     pDom->ulErrorLine = XML_GetCurrentLineNumber(pDom->pParser);
  306.     pDom->ulErrorColumn = XML_GetCurrentColumnNumber(pDom->pParser);
  307.  
  308.     if (pcszFailing)
  309.     {
  310.         if (!pDom->pxstrFailingNode)
  311.             pDom->pxstrFailingNode = xstrCreate(0);
  312.  
  313.         xstrcpy(pDom->pxstrFailingNode, pcszFailing, 0);
  314.     }
  315.  
  316.     if (fValidityError)
  317.         pDom->fInvalid = TRUE;
  318. }
  319.  
  320. /* ******************************************************************
  321.  *
  322.  *   Most basic node management
  323.  *
  324.  ********************************************************************/
  325.  
  326. /*
  327.  *@@ CompareXStrings:
  328.  *      tree comparison func for NodeBases.
  329.  *      This works for all trees which contain structures
  330.  *      whose first item is a _NODEBASE because NODEBASE's first
  331.  *      member is a TREE.
  332.  *
  333.  *      Used in two places:
  334.  *
  335.  *      --  to insert _CMELEMENTDECLNODE nodes into
  336.  *          _DOMDOCTYPENODE.ElementDeclsTree;
  337.  *
  338.  *      --  to insert _CMELEMENTPARTICLE nodes into
  339.  *          _CMELEMENTDECLNODE.ElementNamesTree.
  340.  *
  341.  *@@added V0.9.9 (2001-02-16) [umoeller]
  342.  *@@changed V0.9.14 (2001-08-09) [umoeller]: fixed map bug which caused the whole XML stuff to fail
  343.  */
  344.  
  345. static int TREEENTRY CompareXStrings(ULONG ul1,
  346.                                      ULONG ul2)
  347. {
  348.     return (strhcmp(((PXSTRING)ul1)->psz,
  349.                     ((PXSTRING)ul2)->psz));
  350. }
  351.  
  352. /*
  353.  *@@ xmlCreateNodeBase:
  354.  *      creates a new NODEBASE node.
  355.  *
  356.  *      Gets called from xmlCreateDomNode also to create
  357.  *      a DOMNODE, since that in turn has a NODEBASE.
  358.  *
  359.  *@@added V0.9.9 (2001-02-16) [umoeller]
  360.  */
  361.  
  362. APIRET xmlCreateNodeBase(NODEBASETYPE ulNodeType,     // in: node type
  363.                          ULONG cb,                    // in: size of struct
  364.                          const char *pcszNodeName,    // in: node name or NULL
  365.                          ULONG ulNodeNameLength,      // in: node name length
  366.                                                       // or 0 to run strlen(pcszNodeName)
  367.                          PNODEBASE *ppNew)            // out: new node
  368. {
  369.     APIRET      arc = NO_ERROR;
  370.     PNODEBASE   pNewNode = (PNODEBASE)malloc(cb);
  371.  
  372.     if (!pNewNode)
  373.         arc = ERROR_NOT_ENOUGH_MEMORY;
  374.     else
  375.     {
  376.         memset(pNewNode, 0, cb);
  377.         pNewNode->ulNodeType = ulNodeType;
  378.  
  379.         pNewNode->Tree.ulKey = (ULONG)&pNewNode->strNodeName;
  380.  
  381.         xstrInit(&pNewNode->strNodeName, 0);
  382.         if (pcszNodeName)
  383.         {
  384.             xstrcpy(&pNewNode->strNodeName,
  385.                     pcszNodeName,
  386.                     ulNodeNameLength);
  387.         }
  388.  
  389.         *ppNew = pNewNode;
  390.     }
  391.  
  392.     return arc;
  393. }
  394.  
  395. /*
  396.  *@@ xmlDeleteNode:
  397.  *      deletes a NODEBASE and frees memory that was
  398.  *      associated with its members.
  399.  *
  400.  *      After calling this, pNode is no longer valid.
  401.  *
  402.  *      If you invoke this on a DOCUMENT node, the
  403.  *      entire DOM tree will get deleted recursively.
  404.  *
  405.  *@@added V0.9.9 (2001-02-16) [umoeller]
  406.  *@@changed V0.9.14 (2001-08-09) [umoeller]: fixed crash on string delete
  407.  */
  408.  
  409. VOID xmlDeleteNode(PNODEBASE pNode)
  410. {
  411.     if (pNode)
  412.     {
  413.         PLISTNODE   pNodeThis;
  414.         PDOMNODE    pDomNode = NULL;
  415.  
  416.         LINKLIST    llDeleteNodes;          // list that nodes to be deleted
  417.                                             // can be appended to
  418.         PLISTNODE   pDelNode;
  419.         lstInit(&llDeleteNodes, FALSE);
  420.  
  421.         // now handle special types and their allocations
  422.         switch (pNode->ulNodeType)
  423.         {
  424.             case DOMNODE_ELEMENT:
  425.             {
  426.                 PDOMNODE pAttrib;
  427.  
  428.                 pDomNode = (PDOMNODE)pNode;
  429.  
  430.                 // delete all attribute nodes
  431.                 pAttrib = (PDOMNODE)treeFirst(pDomNode->AttributesMap);
  432.                 while (pAttrib)
  433.                 {
  434.                     // call delete recursively
  435.                     PDOMNODE pNext = (PDOMNODE)treeNext((TREE*)pAttrib);
  436.                     xmlDeleteNode((PNODEBASE)pAttrib);
  437.                             // this will remove pAttrib from pNode's attrib
  438.                             // tree and rebalance the tree
  439.                     pAttrib = pNext;
  440.                 }
  441.             break; }
  442.  
  443.             case DOMNODE_ATTRIBUTE:
  444.             case DOMNODE_TEXT:
  445.             case DOMNODE_PROCESSING_INSTRUCTION:
  446.             case DOMNODE_COMMENT:
  447.                 pDomNode = (PDOMNODE)pNode;
  448.             break;
  449.  
  450.             case DOMNODE_DOCUMENT:
  451.                 if (((PDOMDOCUMENTNODE)pNode)->pDocType)
  452.                     xmlDeleteNode((PNODEBASE)((PDOMDOCUMENTNODE)pNode)->pDocType);
  453.                 pDomNode = (PDOMNODE)pNode;
  454.             break;
  455.  
  456.             case DOMNODE_DOCUMENT_TYPE:
  457.             {
  458.                 PDOMDOCTYPENODE pDocType = (PDOMDOCTYPENODE)pNode;
  459.                 PCMELEMENTDECLNODE pElDecl;
  460.                 PCMATTRIBUTEDECLBASE pAttrDeclBase;
  461.  
  462.                 pDomNode = (PDOMNODE)pNode;
  463.  
  464.                 pElDecl = (PCMELEMENTDECLNODE)treeFirst(pDocType->ElementDeclsTree);
  465.                 while (pElDecl)
  466.                 {
  467.                     lstAppendItem(&llDeleteNodes, pElDecl);
  468.                     pElDecl = (PCMELEMENTDECLNODE)treeNext((TREE*)pElDecl);
  469.                 }
  470.  
  471.                 pAttrDeclBase = (PCMATTRIBUTEDECLBASE)treeFirst(pDocType->AttribDeclBasesTree);
  472.                 while (pAttrDeclBase)
  473.                 {
  474.                     lstAppendItem(&llDeleteNodes, pAttrDeclBase);
  475.                     pAttrDeclBase = (PCMATTRIBUTEDECLBASE)treeNext((TREE*)pAttrDeclBase);
  476.                 }
  477.  
  478.                 xstrClear(&pDocType->strPublicID);
  479.                 xstrClear(&pDocType->strSystemID);
  480.             break; }
  481.  
  482.             case ELEMENTPARTICLE_EMPTY:
  483.             case ELEMENTPARTICLE_ANY:
  484.             case ELEMENTPARTICLE_MIXED:
  485.             case ELEMENTPARTICLE_CHOICE:
  486.             case ELEMENTPARTICLE_SEQ:
  487.             case ELEMENTPARTICLE_NAME:
  488.             {
  489.                 PCMELEMENTPARTICLE pp = (PCMELEMENTPARTICLE)pNode;
  490.                 if (pp->pllSubNodes)
  491.                 {
  492.                     pDelNode = lstQueryFirstNode(pp->pllSubNodes);
  493.                     while (pDelNode)
  494.                     {
  495.                         PCMELEMENTPARTICLE
  496.                                 pParticle = (PCMELEMENTPARTICLE)pDelNode->pItemData;
  497.                         xmlDeleteNode((PNODEBASE)pParticle);
  498.                         //  treeDelete(pp->         // @@todo
  499.                         pDelNode = pDelNode->pNext;
  500.                     }
  501.                 }
  502.             break; }
  503.  
  504.             // case ATTRIBUTE_DECLARATION_ENUM:     // this is a plain NODEBASE
  505.  
  506.             case ATTRIBUTE_DECLARATION:
  507.             {
  508.                 // PCMATTRIBUTEDECL pDecl = (PCMATTRIBUTEDECL)pNode;
  509.             break; }
  510.  
  511.             case ATTRIBUTE_DECLARATION_BASE:
  512.             break;
  513.         }
  514.  
  515.         if (pDomNode)
  516.         {
  517.             // recurse into child nodes
  518.             while (pNodeThis = lstQueryFirstNode(&pDomNode->llChildren))
  519.                 // recurse!!
  520.                 xmlDeleteNode((PNODEBASE)(pNodeThis->pItemData));
  521.                             // this updates llChildren
  522.  
  523.             if (pDomNode->pParentNode)
  524.             {
  525.                 // node has a parent:
  526.                 if (pNode->ulNodeType == DOMNODE_ATTRIBUTE)
  527.                     // this is an attribute:
  528.                     // remove from parent's attributes map
  529.                     treeDelete(&pDomNode->pParentNode->AttributesMap,
  530.                                NULL,
  531.                                (TREE*)pNode);
  532.                 else
  533.                     // remove this node from the parent's list
  534.                     // of child nodes before deleting this node
  535.                     lstRemoveItem(&pDomNode->pParentNode->llChildren,
  536.                                   pNode);
  537.  
  538.                 pDomNode->pParentNode = NULL;
  539.             }
  540.  
  541.             xstrFree(&pDomNode->pstrNodeValue);
  542.             lstClear(&pDomNode->llChildren);
  543.         }
  544.  
  545.         pDelNode = lstQueryFirstNode(&llDeleteNodes);
  546.         while (pDelNode)
  547.         {
  548.             PNODEBASE pNodeBase = (PNODEBASE)pDelNode->pItemData;
  549.             xmlDeleteNode(pNodeBase);
  550.             pDelNode = pDelNode->pNext;
  551.         }
  552.  
  553.         lstClear(&llDeleteNodes);
  554.  
  555.         xstrClear(&pNode->strNodeName);
  556.         free(pNode);
  557.     }
  558. }
  559.  
  560. /*
  561.  *@@ xmlCreateDomNode:
  562.  *      creates a new DOMNODE with the specified
  563.  *      type and parent. Other than that, the
  564.  *      node fields are zeroed.
  565.  *
  566.  *      If pParentNode is specified (which is required,
  567.  *      unless you are creating a document node),
  568.  *      its children list is automatically updated
  569.  *      (unless this is an attribute node, which updates
  570.  *      the attributes map).
  571.  *
  572.  *      This returns the following errors:
  573.  *
  574.  *      --  ERROR_NOT_ENOUGH_MEMORY
  575.  *
  576.  *      --  ERROR_DOM_NOT_SUPPORTED: invalid ulNodeType
  577.  *          specified.
  578.  *
  579.  *      --  ERROR_DOM_WRONG_DOCUMENT: cannot find the
  580.  *          document for this node. This happens if you do
  581.  *          not have a document node at the root of your tree.
  582.  */
  583.  
  584. APIRET xmlCreateDomNode(PDOMNODE pParentNode,        // in: parent node or NULL if root
  585.                         NODEBASETYPE ulNodeType,     // in: DOMNODE_* type
  586.                         const char *pcszNodeName,    // in: node name or NULL
  587.                         ULONG ulNodeNameLength,      // in: node name length
  588.                                                      // or 0 to run strlen(pcszNodeName)
  589.                         PDOMNODE *ppNew)             // out: new node
  590. {
  591.     PDOMNODE pNewNode = NULL;
  592.     APIRET  arc = NO_ERROR;
  593.  
  594.     ULONG   cb = 0;
  595.  
  596.     switch (ulNodeType)
  597.     {
  598.         case DOMNODE_DOCUMENT:
  599.             cb = sizeof(DOMDOCUMENTNODE);
  600.         break;
  601.  
  602.         case DOMNODE_DOCUMENT_TYPE:
  603.             cb = sizeof(DOMDOCTYPENODE);
  604.         break;
  605.  
  606.         default:
  607.             cb = sizeof(DOMNODE);
  608.         break;
  609.     }
  610.  
  611.     if (!(arc = xmlCreateNodeBase(ulNodeType,
  612.                                   cb,
  613.                                   pcszNodeName,
  614.                                   ulNodeNameLength,
  615.                                   (PNODEBASE*)&pNewNode)))
  616.     {
  617.         pNewNode->pParentNode = pParentNode;
  618.  
  619.         if (pParentNode)
  620.         {
  621.             // parent specified:
  622.             // check if this is an attribute
  623.             if (ulNodeType == DOMNODE_ATTRIBUTE)
  624.             {
  625.                 // attribute:
  626.                 // add to parent's attributes list
  627.                 if (treeInsert(&pParentNode->AttributesMap,
  628.                                NULL,
  629.                                &pNewNode->NodeBase.Tree,
  630.                                CompareXStrings))
  631.                     arc = ERROR_DOM_DUPLICATE_ATTRIBUTE;
  632.                                 // shouldn't happen, because expat takes care of this
  633.             }
  634.             else
  635.                 // append this new node to the parent's
  636.                 // list of child nodes
  637.                 lstAppendItem(&pParentNode->llChildren,
  638.                               pNewNode);
  639.  
  640.             if (!arc)
  641.             {
  642.                 // set document pointer...
  643.                 // if the parent node has a document pointer,
  644.                 // we can copy that
  645.                 if (pParentNode->pDocumentNode)
  646.                     pNewNode->pDocumentNode = pParentNode->pDocumentNode;
  647.                 else
  648.                     // parent has no document pointer: then it is probably
  649.                     // the document itself... check
  650.                     if (pParentNode->NodeBase.ulNodeType == DOMNODE_DOCUMENT)
  651.                         pNewNode->pDocumentNode = pParentNode;
  652.                     else
  653.                         arc = ERROR_DOM_NO_DOCUMENT;
  654.             }
  655.         }
  656.  
  657.         lstInit(&pNewNode->llChildren, FALSE);
  658.         treeInit(&pNewNode->AttributesMap, NULL);
  659.     }
  660.  
  661.     if (!arc)
  662.         *ppNew = pNewNode;
  663.     else
  664.         if (pNewNode)
  665.             free(pNewNode);
  666.  
  667.     return arc;
  668. }
  669.  
  670. /* ******************************************************************
  671.  *
  672.  *   Specific DOM node constructors
  673.  *
  674.  ********************************************************************/
  675.  
  676. /*
  677.  *@@ xmlCreateElementNode:
  678.  *      creates a new element node with the specified name.
  679.  *
  680.  *@@added V0.9.9 (2001-02-14) [umoeller]
  681.  */
  682.  
  683. APIRET xmlCreateElementNode(PDOMNODE pParent,         // in: parent node (either document or element)
  684.                             const char *pcszElement,  // in: element name (null-terminated)
  685.                             PDOMNODE *ppNew)
  686. {
  687.     PDOMNODE pNew = NULL;
  688.     APIRET arc;
  689.  
  690.     if (!(arc = xmlCreateDomNode(pParent,
  691.                                  DOMNODE_ELEMENT,
  692.                                  pcszElement,
  693.                                  0,
  694.                                  &pNew)))
  695.         *ppNew = pNew;
  696.  
  697.     return arc;
  698. }
  699.  
  700. /*
  701.  *@@ xmlCreateAttributeNode:
  702.  *      creates a new attribute node with the specified data.
  703.  *
  704.  *      NOTE: Attributes have no "parent" node, technically.
  705.  *      They are added to a special, separate list in @DOM_ELEMENT
  706.  *      nodes.
  707.  *
  708.  *      This returns the following errors:
  709.  *
  710.  *      --  Error codes from xmlCreateDomNode.
  711.  *
  712.  *      --  ERROR_DOM_NO_ELEMENT: pElement is invalid or does
  713.  *          not point to an @DOM_ELEMENT node.
  714.  *
  715.  *@@added V0.9.9 (2001-02-14) [umoeller]
  716.  */
  717.  
  718. APIRET xmlCreateAttributeNode(PDOMNODE pElement,        // in: element node
  719.                               const char *pcszName,     // in: attribute name (null-terminated)
  720.                               const char *pcszValue,    // in: attribute value (null-terminated)
  721.                               PDOMNODE *ppNew)
  722. {
  723.     APIRET arc = NO_ERROR;
  724.  
  725.     if (    (!pElement)
  726.          || (pElement->NodeBase.ulNodeType != DOMNODE_ELEMENT)
  727.        )
  728.         arc = ERROR_DOM_NO_ELEMENT;
  729.     else
  730.     {
  731.         PDOMNODE pNew = NULL;
  732.         if (!(arc = xmlCreateDomNode(pElement,          // this takes care of adding to the list
  733.                                      DOMNODE_ATTRIBUTE,
  734.                                      pcszName,
  735.                                      0,
  736.                                      &pNew)))
  737.         {
  738.             pNew->pstrNodeValue = xstrCreate(0);
  739.             xstrcpy(pNew->pstrNodeValue, pcszValue, 0);
  740.  
  741.             *ppNew = pNew;
  742.         }
  743.     }
  744.  
  745.     return arc;
  746. }
  747.  
  748. /*
  749.  *@@ xmlCreateTextNode:
  750.  *      creates a new text node with the specified content.
  751.  *
  752.  *      Note: This differs from the createText method
  753.  *      as specified by DOM, which has no ulLength parameter.
  754.  *      We need this for speed with @expat though.
  755.  *
  756.  *@@added V0.9.9 (2001-02-14) [umoeller]
  757.  */
  758.  
  759. APIRET xmlCreateTextNode(PDOMNODE pParent,         // in: parent element node
  760.                          const char *pcszText,     // in: ptr to start of text
  761.                          ULONG ulLength,           // in: length of *pcszText
  762.                          PDOMNODE *ppNew)
  763. {
  764.     PDOMNODE pNew = NULL;
  765.     APIRET arc;
  766.  
  767.     if (!(arc = xmlCreateDomNode(pParent,
  768.                                  DOMNODE_TEXT,
  769.                                  NULL,
  770.                                  0,
  771.                                  &pNew)))
  772.     {
  773.         PSZ pszNodeValue;
  774.         if (pszNodeValue = (PSZ)malloc(ulLength + 1))
  775.         {
  776.             memcpy(pszNodeValue, pcszText, ulLength);
  777.             pszNodeValue[ulLength] = '\0';
  778.             pNew->pstrNodeValue = xstrCreate(0);
  779.             xstrset(pNew->pstrNodeValue, pszNodeValue);
  780.  
  781.             *ppNew = pNew;
  782.         }
  783.         else
  784.         {
  785.             arc = ERROR_NOT_ENOUGH_MEMORY;
  786.             xmlDeleteNode((PNODEBASE)pNew);
  787.         }
  788.     }
  789.  
  790.     return arc;
  791. }
  792.  
  793. /*
  794.  *@@ xmlCreateCommentNode:
  795.  *      creates a new comment node with the specified
  796.  *      content.
  797.  *
  798.  *@@added V0.9.9 (2001-02-14) [umoeller]
  799.  */
  800.  
  801. APIRET xmlCreateCommentNode(PDOMNODE pParent,         // in: parent element node
  802.                             const char *pcszText,     // in: comment (null-terminated)
  803.                             PDOMNODE *ppNew)
  804. {
  805.     PDOMNODE pNew = NULL;
  806.     APIRET arc;
  807.     if (!(arc = xmlCreateDomNode(pParent,
  808.                                  DOMNODE_COMMENT,
  809.                                  NULL,
  810.                                  0,
  811.                                  &pNew)))
  812.     {
  813.         pNew->pstrNodeValue = xstrCreate(0);
  814.         xstrcpy(pNew->pstrNodeValue, pcszText, 0);
  815.         *ppNew = pNew;
  816.     }
  817.  
  818.     return arc;
  819. }
  820.  
  821. /*
  822.  *@@ xmlCreatePINode:
  823.  *      creates a new processing instruction node with the
  824.  *      specified data.
  825.  *
  826.  *@@added V0.9.9 (2001-02-14) [umoeller]
  827.  */
  828.  
  829. APIRET xmlCreatePINode(PDOMNODE pParent,         // in: parent element node
  830.                        const char *pcszTarget,   // in: PI target (null-terminated)
  831.                        const char *pcszData,     // in: PI data (null-terminated)
  832.                        PDOMNODE *ppNew)
  833. {
  834.     PDOMNODE pNew = NULL;
  835.     APIRET arc;
  836.  
  837.     if (!(arc = xmlCreateDomNode(pParent,
  838.                                  DOMNODE_PROCESSING_INSTRUCTION,
  839.                                  pcszTarget,
  840.                                  0,
  841.                                  &pNew)))
  842.     {
  843.         pNew->pstrNodeValue = xstrCreate(0);
  844.         xstrcpy(pNew->pstrNodeValue, pcszData, 0);
  845.  
  846.         *ppNew = pNew;
  847.     }
  848.  
  849.     return arc;
  850. }
  851.  
  852. /*
  853.  *@@ xmlCreateDocumentTypeNode:
  854.  *      creates a new document type node with the
  855.  *      specified data.
  856.  *
  857.  *@@added V0.9.9 (2001-02-14) [umoeller]
  858.  */
  859.  
  860. APIRET xmlCreateDocumentTypeNode(PDOMDOCUMENTNODE pDocumentNode,            // in: document node
  861.                                  const char *pcszDoctypeName,
  862.                                  const char *pcszSysid,
  863.                                  const char *pcszPubid,
  864.                                  int fHasInternalSubset,
  865.                                  PDOMDOCTYPENODE *ppNew)
  866. {
  867.     APIRET arc = NO_ERROR;
  868.  
  869.     if (pDocumentNode->pDocType)
  870.         // we already have a doctype:
  871.         arc = ERROR_DOM_DUPLICATE_DOCTYPE;
  872.     else
  873.     {
  874.         // create doctype node
  875.         PDOMDOCTYPENODE pNew = NULL;
  876.         if (!(arc = xmlCreateDomNode((PDOMNODE)pDocumentNode,
  877.                                      DOMNODE_DOCUMENT_TYPE,
  878.                                      pcszDoctypeName,
  879.                                      0,
  880.                                      (PDOMNODE*)&pNew)))
  881.         {
  882.             // the node has already been added to the children
  883.             // list of the document node... in addition, set
  884.             // the doctype field in the document
  885.             pDocumentNode->pDocType = pNew;
  886.  
  887.             // initialize the extra fields
  888.             xstrcpy(&pNew->strPublicID, pcszPubid, 0);
  889.             xstrcpy(&pNew->strSystemID, pcszSysid, 0);
  890.             pNew->fHasInternalSubset = fHasInternalSubset;
  891.  
  892.             treeInit(&pNew->ElementDeclsTree, NULL);
  893.             treeInit(&pNew->AttribDeclBasesTree, NULL);
  894.  
  895.             *ppNew = pNew;
  896.         }
  897.     }
  898.     return arc;
  899. }
  900.  
  901. /* ******************************************************************
  902.  *
  903.  *   DOM level 3 content models
  904.  *
  905.  ********************************************************************/
  906.  
  907. /*
  908.  *@@ SetupParticleAndSubs:
  909.  *
  910.  *      This creates sub-particles and recurses to set them up,
  911.  *      if necessary.
  912.  *
  913.  *@@added V0.9.9 (2001-02-16) [umoeller]
  914.  */
  915.  
  916. static APIRET SetupParticleAndSubs(PCMELEMENTPARTICLE pParticle,
  917.                                    PXMLCONTENT pModel,
  918.                                    TREE **ppElementNamesTree) // in: ptr to _CMELEMENTDECLNODE.ElementNamesTree
  919.                                                               // (passed to all recursions)
  920. {
  921.     APIRET arc = NO_ERROR;
  922.  
  923.     // set up member NODEBASE
  924.     switch (pModel->type)
  925.     {
  926.         case XML_CTYPE_EMPTY: // that's easy
  927.             pParticle->NodeBase.ulNodeType = ELEMENTPARTICLE_EMPTY;
  928.         break;
  929.  
  930.         case XML_CTYPE_ANY:   // that's easy
  931.             pParticle->NodeBase.ulNodeType = ELEMENTPARTICLE_ANY;
  932.         break;
  933.  
  934.         case XML_CTYPE_NAME:   // that's easy
  935.             pParticle->NodeBase.ulNodeType = ELEMENTPARTICLE_NAME;
  936.             xstrcpy(&pParticle->NodeBase.strNodeName, pModel->name, 0);
  937.             treeInsert(ppElementNamesTree,
  938.                        NULL,
  939.                        &pParticle->NodeBase.Tree,
  940.                        CompareXStrings);
  941.         break;
  942.  
  943.         case XML_CTYPE_MIXED:
  944.             pParticle->NodeBase.ulNodeType = ELEMENTPARTICLE_MIXED;
  945.         break;
  946.  
  947.         case XML_CTYPE_CHOICE:
  948.             pParticle->NodeBase.ulNodeType = ELEMENTPARTICLE_CHOICE;
  949.         break;
  950.  
  951.         case XML_CTYPE_SEQ:
  952.             pParticle->NodeBase.ulNodeType = ELEMENTPARTICLE_SEQ;
  953.         break;
  954.     }
  955.  
  956.     pParticle->ulRepeater = pModel->quant;
  957.  
  958.     if (pModel->numchildren)
  959.     {
  960.         // these are the three cases where we have subnodes
  961.         // in the XMLCONTENT... go for these and recurse
  962.         ULONG ul;
  963.         pParticle->pllSubNodes = lstCreate(FALSE);
  964.         for (ul = 0;
  965.              ul < pModel->numchildren;
  966.              ul++)
  967.         {
  968.             PXMLCONTENT pSubModel = &pModel->children[ul];
  969.             PCMELEMENTPARTICLE pSubNew = NULL;
  970.             if (!(arc = xmlCreateNodeBase(TYPE_UNKNOWN, //       node type... for now
  971.                                           sizeof(CMELEMENTPARTICLE),
  972.                                           0,
  973.                                           0,
  974.                                           (PNODEBASE*)&pSubNew)))
  975.             {
  976.                 if (!(arc = SetupParticleAndSubs(pSubNew,
  977.                                                  pSubModel,
  978.                                                  ppElementNamesTree)))
  979.                 {
  980.                     // no error: append sub-particle to this particle's
  981.                     // children list
  982.                     lstAppendItem(pParticle->pllSubNodes,
  983.                                   pSubNew);
  984.                     // and store this particle as the parent in the
  985.                     // sub-particle
  986.                     pSubNew->pParentParticle = pParticle;
  987.                 }
  988.             }
  989.  
  990.             if (arc)
  991.                 break;
  992.         }
  993.     }
  994.  
  995.     return arc;
  996. }
  997.  
  998. /*
  999.  *@@ xmlCreateElementDecl:
  1000.  *      creates a new _CMELEMENTDECLNODE for the specified
  1001.  *      _XMLCONTENT content model (which is the @expat structure).
  1002.  *      This recurses, if necessary.
  1003.  *
  1004.  *      NOTE: As opposed to the other "create" functions,
  1005.  *      this does not take a parent node as input. If this
  1006.  *      returns NO_ERROR, it is the caller's responsibility
  1007.  *      to add the produced node to the document's DOCTYPE node.
  1008.  *
  1009.  *@@added V0.9.9 (2001-02-16) [umoeller]
  1010.  */
  1011.  
  1012. APIRET xmlCreateElementDecl(const char *pcszName,
  1013.                             PXMLCONTENT pModel,
  1014.                             PCMELEMENTDECLNODE *ppNew)
  1015. {
  1016.     APIRET arc = NO_ERROR;
  1017.     PCMELEMENTDECLNODE pNew = NULL;
  1018.  
  1019.     if (!(arc = xmlCreateNodeBase(TYPE_UNKNOWN,      // for now
  1020.                                   sizeof(CMELEMENTDECLNODE),
  1021.                                   pcszName,
  1022.                                   0,
  1023.                                   (PNODEBASE*)&pNew)))
  1024.     {
  1025.         treeInit(&pNew->ParticleNamesTree, NULL);
  1026.  
  1027.         // set up the "particle" member and recurse into sub-particles
  1028.         if (!(arc = SetupParticleAndSubs(&pNew->Particle,
  1029.                                          pModel,
  1030.                                          &pNew->ParticleNamesTree)))
  1031.             *ppNew = pNew;
  1032.         else
  1033.             free(pNew);
  1034.     }
  1035.  
  1036.     return arc;
  1037. }
  1038.  
  1039. /*
  1040.  *@@ ValidateElementChildren
  1041.  *      validates the specified element against the document's @DTD,
  1042.  *      more specifically, against the element declaration of the
  1043.  *      new element's parent.
  1044.  *
  1045.  *      This sets arcDOM in XMLDOM on errors.
  1046.  *
  1047.  *      According to the XML spec, an element is valid if there
  1048.  *      is a declaration matching the element declaration where the
  1049.  *      element's name matches the element type, and _one_ of the
  1050.  *      following holds:
  1051.  *
  1052.  *      (1) The declaration matches EMPTY and the element has no @content. (done)
  1053.  *
  1054.  *      (2) The declaration matches (children) (see @element_declaration)
  1055.  *          and the sequence of child elements belongs to the language
  1056.  *          generated by the regular expression in the content model, with
  1057.  *          optional @white_space between the start-tag and the first child
  1058.  *          element, between child elements, or between the last
  1059.  *          child element and the end-tag. Note that a CDATA section
  1060.  *          is never considered "whitespace", even if it contains
  1061.  *          white space only. @@todo
  1062.  *
  1063.  *      (3) The declaration matches (mixed) (see @element_declaration)
  1064.  *          and the content consists of @content and child elements
  1065.  *          whose types match names in the content model. (done)
  1066.  *
  1067.  *      (4) The declaration matches ANY, and the types of any child
  1068.  *          elements have been declared. (done)
  1069.  *
  1070.  *      Preconditions: The element must already have been inserted
  1071.  *      into the parent element's list, or we cannot validate sequences.
  1072.  *
  1073.  *@@added V0.9.9 (2001-02-16) [umoeller]
  1074.  */
  1075.  
  1076. static VOID ValidateElement(PXMLDOM pDom,
  1077.                             PDOMNODE pNewElement,     // in: new element
  1078.                             PCMELEMENTDECLNODE pParentElementDecl)
  1079.                                                       // in: element decl of element's parent
  1080. {
  1081.     if (pDom && pNewElement)
  1082.     {
  1083.         if (!pParentElementDecl)
  1084.         {
  1085.             // this is always missing for the root element, of course,
  1086.             // because the parent is the document
  1087.             if (pNewElement->pParentNode == (PDOMNODE)pDom->pDocumentNode)
  1088.                 return;     // that's OK
  1089.             else
  1090.                 xmlSetError(pDom,
  1091.                             ERROR_DOM_VALIDATE_INVALID_ELEMENT,
  1092.                             pNewElement->NodeBase.strNodeName.psz,
  1093.                             TRUE);
  1094.         }
  1095.         else
  1096.         {
  1097.             ULONG ulDeclType = pParentElementDecl->Particle.NodeBase.ulNodeType;
  1098.  
  1099.             switch (ulDeclType)
  1100.             {
  1101.                 case ELEMENTPARTICLE_EMPTY:
  1102.                     // this is an error for sure
  1103.                     xmlSetError(pDom,
  1104.                                 ERROR_DOM_SUBELEMENT_IN_EMPTY_ELEMENT,
  1105.                                 pNewElement->NodeBase.strNodeName.psz,
  1106.                                 TRUE);
  1107.                 break;
  1108.  
  1109.                 case ELEMENTPARTICLE_ANY:
  1110.                     // that's always OK
  1111.                 break;
  1112.  
  1113.                 case ELEMENTPARTICLE_MIXED:
  1114.                 case ELEMENTPARTICLE_CHOICE:
  1115.                 case ELEMENTPARTICLE_SEQ:
  1116.                 {
  1117.                     PXSTRING pstrNewElementName
  1118.                         = &pNewElement->NodeBase.strNodeName;
  1119.  
  1120.                     // for all these, we first need to check if
  1121.                     // the element is allowed at all
  1122.                     PCMELEMENTPARTICLE pParticle
  1123.                         = (PCMELEMENTPARTICLE)treeFind(
  1124.                                          pParentElementDecl->ParticleNamesTree,
  1125.                                          (ULONG)pstrNewElementName,
  1126.                                          CompareXStrings);
  1127.                     if (!pParticle)
  1128.                         // not found: then this element is not allowed within this
  1129.                         // parent
  1130.                         xmlSetError(pDom,
  1131.                                     ERROR_DOM_INVALID_SUBELEMENT,
  1132.                                     pstrNewElementName->psz,
  1133.                                     TRUE);
  1134.                     else
  1135.                     {
  1136.                         // the element is allowed at all: now check for the
  1137.                         // lists case... @@todo
  1138.                         switch (ulDeclType)
  1139.                         {
  1140.                             case ELEMENTPARTICLE_CHOICE:
  1141.                             break;
  1142.  
  1143.                             case ELEMENTPARTICLE_SEQ:
  1144.                             break;
  1145.                         }
  1146.                     }
  1147.  
  1148.                 break; }
  1149.             }
  1150.         }
  1151.     }
  1152.     else
  1153.         pDom->arcDOM = ERROR_INVALID_PARAMETER;
  1154.  
  1155.     // yes: get the element decl from the tree
  1156.     /* PCMELEMENTDECLNODE pElementDecl = xmlFindElementDecl(pDom,
  1157.                                                          &pElement->NodeBase.strNodeName);
  1158.     if (!pElementDecl)
  1159.     {
  1160.         xmlSetError(pDom,
  1161.                     ERROR_DOM_UNDECLARED_ELEMENT,
  1162.                     pElement->NodeBase.strNodeName.psz,
  1163.                     TRUE);
  1164.     }
  1165.     else
  1166.     {
  1167.         // element has been declared:
  1168.         // check if it may appear in this element's parent...
  1169.         PDOMNODE pParentElement = pElement->pParentNode;
  1170.  
  1171.         if (!pParentElement)
  1172.             pDom->arcDOM = ERROR_DOM_INTEGRITY;
  1173.         else switch (pParentElement->NodeBase.ulNodeType)
  1174.         {
  1175.             case DOMNODE_DOCUMENT:
  1176.             {
  1177.                 // if this is the root element, compare its name
  1178.                 // to the DOCTYPE name
  1179.                 if (pParentElement != (PDOMNODE)pDom->pDocumentNode)
  1180.                     xmlSetError(pDom,
  1181.                                 ERROR_DOM_INVALID_ROOT_ELEMENT,
  1182.                                 pElement->NodeBase.strNodeName.psz,
  1183.                                 TRUE);
  1184.                 else if (strcmp(pDom->pDocumentNode->DomNode.NodeBase.strNodeName.psz,
  1185.                                 pElement->NodeBase.strNodeName.psz))
  1186.                     // no match:
  1187.                     xmlSetError(pDom,
  1188.                                 ERROR_DOM_ROOT_ELEMENT_MISNAMED,
  1189.                                 pElement->NodeBase.strNodeName.psz,
  1190.                                 TRUE);
  1191.             break; }
  1192.  
  1193.             case DOMNODE_ELEMENT:
  1194.             {
  1195.                 // parent of element is another element:
  1196.                 // check the parent in the DTD and find out if
  1197.                 // this element may appear in the parent element
  1198.                 PCMELEMENTDECLNODE pParentElementDecl
  1199.                         = xmlFindElementDecl(pDom,
  1200.                                              &pParentElement->NodeBase.strNodeName);
  1201.                 if (!pParentElementDecl)
  1202.                     pDom->arcDOM = ERROR_DOM_INTEGRITY;
  1203.                 else
  1204.                 {
  1205.                     // now check the element names tree of the parent element decl
  1206.                     // for whether this element is allowed as a sub-element at all
  1207.                     PCMELEMENTPARTICLE pParticle
  1208.                         = treeFindEQData(&pParentElementDecl->ParticleNamesTree,
  1209.                                          (void*)pElement->NodeBase.strNodeName.psz,
  1210.                                          CompareNodeBaseData);
  1211.                     if (!pParticle)
  1212.                         // not found: then this element is not allowed within this
  1213.                         // parent
  1214.                         xmlSetError(pDom,
  1215.                                     ERROR_DOM_INVALID_SUBELEMENT,
  1216.                                     pElement->NodeBase.strNodeName.psz,
  1217.                                     TRUE);
  1218.                 }
  1219.             break; }
  1220.         }
  1221.     }
  1222.     */
  1223. }
  1224.  
  1225. /*
  1226.  *@@ ValidateAttributeType:
  1227.  *      validates the specified attribute's type against the
  1228.  *      document's @DTD.
  1229.  *
  1230.  *      This sets arcDOM in XMLDOM on errors.
  1231.  *
  1232.  *@@added V0.9.9 (2001-02-16) [umoeller]
  1233.  */
  1234.  
  1235. static VOID ValidateAttributeType(PXMLDOM pDom,
  1236.                                   PDOMNODE pAttrib,
  1237.                                   PCMATTRIBUTEDECLBASE *ppAttribDeclBase)
  1238. {
  1239.     PDOMNODE pElement = pAttrib->pParentNode;
  1240.  
  1241.     PCMATTRIBUTEDECL pAttribDecl = xmlFindAttribDecl(pDom,
  1242.                                                      &pElement->NodeBase.strNodeName,
  1243.                                                      &pAttrib->NodeBase.strNodeName,
  1244.                                                      ppAttribDeclBase);
  1245.     if (!pAttribDecl)
  1246.         xmlSetError(pDom,
  1247.                     ERROR_DOM_UNDECLARED_ATTRIBUTE,
  1248.                     pAttrib->NodeBase.strNodeName.psz,
  1249.                     TRUE);
  1250.     else
  1251.     {
  1252.         // check if the attribute value is allowed
  1253.         switch (pAttribDecl->ulAttrType)
  1254.         {
  1255.             case CMAT_CDATA:
  1256.             case CMAT_ID:
  1257.             case CMAT_IDREF:
  1258.             case CMAT_IDREFS:       // ###
  1259.             case CMAT_ENTITY:
  1260.             case CMAT_ENTITIES:
  1261.             case CMAT_NMTOKEN:
  1262.             case CMAT_NMTOKENS:
  1263.             break;
  1264.  
  1265.             case CMAT_ENUM:
  1266.             {
  1267.                 // enumeration: then check if it has one of the
  1268.                 // allowed values
  1269.                 PNODEBASE pValue = (PNODEBASE)treeFind(
  1270.                                                 pAttribDecl->ValuesTree,
  1271.                                                 (ULONG)pAttrib->pstrNodeValue,
  1272.                                                 CompareXStrings);
  1273.                 if (!pValue)
  1274.                     xmlSetError(pDom,
  1275.                                 ERROR_DOM_INVALID_ATTRIB_VALUE,
  1276.                                 pAttrib->NodeBase.strNodeName.psz,
  1277.                                 TRUE);
  1278.             }
  1279.         }
  1280.  
  1281.         if (pAttribDecl->ulConstraint == CMAT_FIXED_VALUE)
  1282.             if (strcmp(pAttrib->pstrNodeValue->psz, pAttribDecl->pstrDefaultValue->psz))
  1283.                 // fixed value doesn't match:
  1284.                 xmlSetError(pDom,
  1285.                             ERROR_DOM_INVALID_ATTRIB_VALUE,
  1286.                             pAttrib->NodeBase.strNodeName.psz,
  1287.                             TRUE);
  1288.     }
  1289. }
  1290.  
  1291. /*
  1292.  *@@ ValidateAllAttributes:
  1293.  *      validates the constraints of all attributes of the specified
  1294.  *      element against the document's @DTD.
  1295.  *
  1296.  *@@added V0.9.9 (2001-02-16) [umoeller]
  1297.  */
  1298.  
  1299. static VOID ValidateAllAttributes(PXMLDOM pDom,
  1300.                                   PCMATTRIBUTEDECLBASE pAttribDeclBase,
  1301.                                   PDOMNODE pNewElement)
  1302. {
  1303.     PCMATTRIBUTEDECL pDeclThis
  1304.         = (PCMATTRIBUTEDECL)treeFirst(pAttribDeclBase->AttribDeclsTree);
  1305.  
  1306.     while ((pDeclThis) && (!pDom->arcDOM))
  1307.     {
  1308.         // if attribute is all optional: then we don't need
  1309.         // to check for whether it's here
  1310.         if (    (pDeclThis->ulConstraint != CMAT_IMPLIED)
  1311.              && (pDeclThis->ulConstraint != CMAT_DEFAULT_VALUE)
  1312.                     // we don't have to check this case because expat
  1313.                     // already adds default attributes for us
  1314.            )
  1315.         {
  1316.             // for all others , we need to find the attribute
  1317.             PXSTRING pstrAttrNameThis = &pDeclThis->NodeBase.strNodeName;
  1318.             PDOMNODE pAttrNode = (PDOMNODE)treeFind(
  1319.                                             pNewElement->AttributesMap,
  1320.                                             (ULONG)pstrAttrNameThis,
  1321.                                             CompareXStrings);
  1322.  
  1323.             // now switch again
  1324.             switch (pDeclThis->ulConstraint)
  1325.             {
  1326.                 case CMAT_REQUIRED:
  1327.                     if (!pAttrNode)
  1328.                         // required, but no attribute with this name exists:
  1329.                         xmlSetError(pDom,
  1330.                                     ERROR_DOM_REQUIRED_ATTRIBUTE_MISSING,
  1331.                                     pstrAttrNameThis->psz,
  1332.                                     TRUE);
  1333.                 break;
  1334.             }
  1335.         }
  1336.  
  1337.         pDeclThis = (PCMATTRIBUTEDECL)treeNext((TREE*)pDeclThis);
  1338.     }
  1339. }
  1340.  
  1341. /* ******************************************************************
  1342.  *
  1343.  *   Expat stack
  1344.  *
  1345.  ********************************************************************/
  1346.  
  1347. /*
  1348.  *@@ DOMSTACKITEM:
  1349.  *
  1350.  *@@added V0.9.9 (2001-02-16) [umoeller]
  1351.  */
  1352.  
  1353. typedef struct _DOMSTACKITEM
  1354. {
  1355.     PDOMNODE                pDomNode;
  1356.     PCMELEMENTDECLNODE      pElementDecl;
  1357.  
  1358. } DOMSTACKITEM, *PDOMSTACKITEM;
  1359.  
  1360. /*
  1361.  *@@ PopElementStack:
  1362.  *
  1363.  *      NOTE:
  1364.  *
  1365.  *@@added V0.9.9 (2001-02-16) [umoeller]
  1366.  */
  1367.  
  1368. static PDOMSTACKITEM PopElementStack(PXMLDOM pDom,
  1369.                                      PLISTNODE *ppListNode)
  1370. {
  1371.     PDOMSTACKITEM   pStackItem = NULL;
  1372.     PLISTNODE       pParentLN = lstPop(&pDom->llElementStack);
  1373.  
  1374.     if (!pParentLN)
  1375.         pDom->arcDOM = ERROR_DOM_NO_ELEMENT;
  1376.     else
  1377.     {
  1378.         // we have at least one node:
  1379.         pStackItem = (PDOMSTACKITEM)pParentLN->pItemData;
  1380.  
  1381.         if (ppListNode)
  1382.             *ppListNode = pParentLN;
  1383.     }
  1384.  
  1385.     return (pStackItem);
  1386. }
  1387.  
  1388. /*
  1389.  *@@ PushElementStack:
  1390.  *
  1391.  *      NOTE: pDomNode will most frequently be an element
  1392.  *      node, but will also be the document for root and
  1393.  *      a DOCTYPE node while parsing the DTD.
  1394.  *
  1395.  *@@added V0.9.9 (2001-02-16) [umoeller]
  1396.  */
  1397.  
  1398. static VOID PushElementStack(PXMLDOM pDom,
  1399.                              PDOMNODE pDomNode)
  1400. {
  1401.     PDOMSTACKITEM pNew = (PDOMSTACKITEM)malloc(sizeof(*pNew));
  1402.     if (!pNew)
  1403.         pDom->arcDOM = ERROR_NOT_ENOUGH_MEMORY;
  1404.     else
  1405.     {
  1406.         memset(pNew, 0, sizeof(*pNew));
  1407.         pNew->pDomNode = pDomNode;
  1408.  
  1409.         // shall we validate?
  1410.         if (    (pDom->pDocTypeNode)
  1411.              && (pDomNode->NodeBase.ulNodeType == DOMNODE_ELEMENT)
  1412.            )
  1413.             pNew->pElementDecl = xmlFindElementDecl(pDom,
  1414.                                                     &pDomNode->NodeBase.strNodeName);
  1415.  
  1416.         lstPush(&pDom->llElementStack,
  1417.                 pNew);
  1418.     }
  1419. }
  1420.  
  1421. /* ******************************************************************
  1422.  *
  1423.  *   Expat handlers
  1424.  *
  1425.  ********************************************************************/
  1426.  
  1427. /*
  1428.  *@@ UnknownEncodingHandler:
  1429.  *      @expat handler called when the xml
  1430.  *      @text_declaration has an @encoding that is not
  1431.  *      one of the four encodings built into expat.
  1432.  *
  1433.  *      See XML_SetUnknownEncodingHandler.
  1434.  *
  1435.  *@@added V0.9.14 (2001-08-09) [umoeller]
  1436.  */
  1437.  
  1438. static int EXPATENTRY UnknownEncodingHandler(void *pUserData,   // in: out PXMLDOM really
  1439.                                              const XML_Char *pcszName,
  1440.                                              XML_Encoding *pEncoding)
  1441. {
  1442.     PXMLDOM     pDom = (PXMLDOM)pUserData;
  1443.  
  1444.     ULONG ulCP;
  1445.     if (    (pDom->pfnGetCPData)            // callback exists?
  1446.          && (!strncmp(pcszName, "cp", 2))
  1447.          && (strlen(pcszName) > 4)              // at least five chars (e.g. "cp850")
  1448.          && (ulCP = atoi(pcszName + 2))
  1449.        )
  1450.     {
  1451.         // this is a PC codepage:
  1452. /* typedef struct _XML_Encoding
  1453. {
  1454.   int           map[256];
  1455.   void          *data;
  1456.   int           (* EXPATENTRY convert)(void *data, const char *s);
  1457.   void          (* EXPATENTRY release)(void *data);
  1458. } XML_Encoding; */
  1459.  
  1460.         // ZERO(pEncoding);
  1461.  
  1462.         pEncoding->convert = NULL;
  1463.         pEncoding->release = NULL;
  1464.  
  1465.         memset(&pEncoding->map, -1, sizeof(pEncoding->map));
  1466.  
  1467.         if (pDom->pfnGetCPData(pDom,
  1468.                                ulCP,
  1469.                                pEncoding->map))
  1470.         {
  1471.             // go check if there's any -1 chars left
  1472.             ULONG ul;
  1473.             for (ul = 0;
  1474.                  ul < 256;
  1475.                  ul++)
  1476.             {
  1477.                 if (pEncoding->map[ul] < 0)
  1478.                     xmlSetError(pDom,
  1479.                                 ERROR_DOM_INCOMPLETE_ENCODING_MAP,
  1480.                                 NULL,
  1481.                                 FALSE);
  1482.             }
  1483.             // return success
  1484.             return (1);
  1485.         }
  1486.     }
  1487.  
  1488.     // error
  1489.     return (0);
  1490. }
  1491.  
  1492. /*
  1493.  *@@ StartElementHandler:
  1494.  *      @expat handler called when a new element is
  1495.  *      found.
  1496.  *
  1497.  *      We create a new record in the container and
  1498.  *      push it onto our stack so we can insert
  1499.  *      children into it. We first start with the
  1500.  *      attributes.
  1501.  */
  1502.  
  1503. static void EXPATENTRY StartElementHandler(void *pUserData,      // in: our PXMLDOM really
  1504.                                            const char *pcszElement,
  1505.                                            const char **papcszAttribs)
  1506. {
  1507.     PXMLDOM     pDom = (PXMLDOM)pUserData;
  1508.  
  1509.     // continue parsing only if we had no errors so far
  1510.     if (!pDom->arcDOM)
  1511.     {
  1512.         ULONG       i;
  1513.  
  1514.         PDOMSTACKITEM pSI = PopElementStack(pDom,
  1515.                                             NULL);     // no free
  1516.         if (!pDom->arcDOM)
  1517.         {
  1518.             PDOMNODE    pParent = pSI->pDomNode,
  1519.                         pNew = NULL;
  1520.  
  1521.             if (!(pDom->arcDOM = xmlCreateElementNode(pParent,
  1522.                                                       pcszElement,
  1523.                                                       &pNew)))
  1524.                 // OK, node is valid:
  1525.                 // push this on the stack so we can add child elements
  1526.                 PushElementStack(pDom,
  1527.                                  pNew);
  1528.  
  1529.             // shall we validate?
  1530.             if (    (!pDom->arcDOM)
  1531.                  && (pDom->pDocTypeNode)
  1532.                )
  1533.                 ValidateElement(pDom,
  1534.                                 pNew,                // new element
  1535.                                 pSI->pElementDecl);  // parent's elem decl
  1536.  
  1537.             if (!pDom->arcDOM)
  1538.             {
  1539.                 PCMATTRIBUTEDECLBASE pAttribDeclBase = NULL;
  1540.  
  1541.                 // shall we validate?
  1542.                 if (pDom->pDocTypeNode)
  1543.                     // yes: get attrib decl base for speed
  1544.                     pAttribDeclBase
  1545.                         = xmlFindAttribDeclBase(pDom,
  1546.                                                 &pNew->NodeBase.strNodeName);
  1547.  
  1548.                 // now for the attribs
  1549.                 for (i = 0;
  1550.                      (papcszAttribs[i]) && (!pDom->arcDOM);
  1551.                      i += 2)
  1552.                 {
  1553.                     PDOMNODE pAttrib;
  1554.                     if (!(pDom->arcDOM = xmlCreateAttributeNode(pNew,                  // element,
  1555.                                                                 papcszAttribs[i],      // attr name
  1556.                                                                 papcszAttribs[i + 1],  // attr value
  1557.                                                                 &pAttrib)))
  1558.                     {
  1559.                         // shall we validate?
  1560.                         if (pDom->pDocTypeNode)
  1561.                             ValidateAttributeType(pDom,
  1562.                                                   pAttrib,
  1563.                                                   &pAttribDeclBase);
  1564.                     }
  1565.                     else
  1566.                     {
  1567.                         xmlSetError(pDom,
  1568.                                     pDom->arcDOM,
  1569.                                     papcszAttribs[i],
  1570.                                     FALSE);      // validation
  1571.                         break;
  1572.                     }
  1573.                 }
  1574.  
  1575.                 // OK, now we got all attributes:
  1576.                 // now look for defaults in the DTD,
  1577.                 // if we shall validate
  1578.                 if (    (pDom->pDocTypeNode)
  1579.                      && (!pDom->arcDOM)
  1580.                      && (pAttribDeclBase)
  1581.                    )
  1582.                     ValidateAllAttributes(pDom,
  1583.                                           pAttribDeclBase,
  1584.                                           pNew);
  1585.             }
  1586.         }
  1587.  
  1588.         pDom->pLastWasTextNode = NULL;
  1589.     }
  1590. }
  1591.  
  1592. /*
  1593.  *@@ EndElementHandler:
  1594.  *      @expat handler for when parsing an element is done.
  1595.  *      We pop the element off of our stack then.
  1596.  */
  1597.  
  1598. static void EXPATENTRY EndElementHandler(void *pUserData,      // in: our PXMLDOM really
  1599.                                          const XML_Char *name)
  1600. {
  1601.     PXMLDOM     pDom = (PXMLDOM)pUserData;
  1602.     // continue parsing only if we had no errors so far
  1603.     if (!pDom->arcDOM)
  1604.     {
  1605.         PLISTNODE pStackLN = NULL;
  1606.         PDOMSTACKITEM pSI = PopElementStack(pDom,
  1607.                                             &pStackLN);
  1608.  
  1609.         if (!pDom->arcDOM)
  1610.         {
  1611.             // shall we validate?
  1612.             /* if (pDom->pDocTypeNode)
  1613.                 // yes:
  1614.                 ValidateElementChildren(pDom,
  1615.                                         pSI->pDomNode); */
  1616.  
  1617.             lstRemoveNode(&pDom->llElementStack, pStackLN); // auto-free
  1618.         }
  1619.         else
  1620.             pDom->arcDOM = ERROR_DOM_INTEGRITY;
  1621.  
  1622.         pDom->pLastWasTextNode = NULL;
  1623.     }
  1624. }
  1625.  
  1626. /*
  1627.  *@@ CharacterDataHandler:
  1628.  *      @expat handler for character data (@content).
  1629.  *
  1630.  *      Note: expat passes chunks of content without zero-terminating
  1631.  *      them. We must concatenate the chunks to a full text node.
  1632.  */
  1633.  
  1634. static void EXPATENTRY CharacterDataHandler(void *pUserData,      // in: our PXMLDOM really
  1635.                                             const XML_Char *s,
  1636.                                             int len)
  1637. {
  1638.     PXMLDOM     pDom = (PXMLDOM)pUserData;
  1639.  
  1640.     // continue parsing only if we had no errors so far
  1641.     if (    (!pDom->arcDOM)
  1642.          && (len)
  1643.        )
  1644.     {
  1645.         // we need a new text node:
  1646.         PDOMSTACKITEM pSI = PopElementStack(pDom,
  1647.                                             NULL);     // no free
  1648.         if (!pDom->arcDOM)
  1649.         {
  1650.             PDOMNODE    pParent = pSI->pDomNode;
  1651.                         // pNew = NULL;
  1652.  
  1653.             BOOL fIsWhitespace = FALSE;
  1654.  
  1655.             // shall we validate?
  1656.             if (pDom->pDocTypeNode)
  1657.             {
  1658.                 // yes: check if the parent element allows
  1659.                 // for content at all (must be "mixed" model)
  1660.  
  1661.                 // get the element decl from the tree
  1662.                 PCMELEMENTDECLNODE pElementDecl;
  1663.                 if (pElementDecl = pSI->pElementDecl)
  1664.                 {
  1665.                     switch (pElementDecl->Particle.NodeBase.ulNodeType)
  1666.                     {
  1667.                         case ELEMENTPARTICLE_ANY:
  1668.                         case ELEMENTPARTICLE_MIXED:
  1669.                             // those two are okay
  1670.                         break;
  1671.  
  1672.                         case ELEMENTPARTICLE_EMPTY:
  1673.                             // that's an error for sure
  1674.                             pDom->arcDOM = ERROR_ELEMENT_CANNOT_HAVE_CONTENT;
  1675.                         break;
  1676.  
  1677.                         default:
  1678.                         {
  1679.                             // ELEMENTPARTICLE_CHOICE:
  1680.                             // ELEMENTPARTICLE_SEQ:
  1681.                             // with these two, we accept whitespace, but nothing
  1682.                             // else... so if we have characters other than
  1683.                             // whitespace, terminate
  1684.                             ULONG ul;
  1685.                             const char *p = s;
  1686.  
  1687.                             if (pDom->flParserFlags & DF_DROP_WHITESPACE)
  1688.                                 fIsWhitespace = TRUE;
  1689.  
  1690.                             for (ul = 0;
  1691.                                  ul < len;
  1692.                                  ul++, p++)
  1693.                                 if (!strchr("\r\n\t ", *p))
  1694.                                 {
  1695.                                     // other character:
  1696.                                     xmlSetError(pDom,
  1697.                                                 ERROR_ELEMENT_CANNOT_HAVE_CONTENT,
  1698.                                                 pParent->NodeBase.strNodeName.psz,
  1699.                                                 TRUE);
  1700.                                     fIsWhitespace = FALSE;
  1701.                                     break;
  1702.                                 }
  1703.                         }
  1704.                     }
  1705.                 }
  1706.  
  1707.             } // end if (pDom->pDocTypeNode)
  1708.  
  1709.             if (!fIsWhitespace)
  1710.                 // this is false if any of the following
  1711.                 // is true:
  1712.                 // --  we are not validating at all
  1713.                 // --  we are validating, but the the element
  1714.                 //     can have mixed content
  1715.                 // --  we are validating and the element does
  1716.                 //     _not_ have mixed content and DF_DROP_WHITESPACE
  1717.                 //     is set, but the string is whitespace only
  1718.                 //     --> drop it then
  1719.  
  1720.             if (pDom->pLastWasTextNode)
  1721.                 // we had a text node, and no elements or other
  1722.                 // stuff in between:
  1723.                 xstrcat(pDom->pLastWasTextNode->pstrNodeValue,
  1724.                         s,
  1725.                         len);
  1726.             else
  1727.                 pDom->arcDOM = xmlCreateTextNode(pParent,
  1728.                                                  s,
  1729.                                                  len,
  1730.                                                  &pDom->pLastWasTextNode);
  1731.         }
  1732.     }
  1733. }
  1734.  
  1735. /*
  1736.  *@@ CommentHandler:
  1737.  *      @expat handler for @comments.
  1738.  *
  1739.  *      Note: This is only set if DF_PARSECOMMENTS is
  1740.  *      flagged with xmlCreateDOM.
  1741.  *
  1742.  *@@added V0.9.9 (2001-02-14) [umoeller]
  1743.  */
  1744.  
  1745. static void EXPATENTRY CommentHandler(void *pUserData,      // in: our PXMLDOM really
  1746.                                       const XML_Char *data)
  1747. {
  1748.     PXMLDOM     pDom = (PXMLDOM)pUserData;
  1749.  
  1750.     // continue parsing only if we had no errors so far
  1751.     if (!pDom->arcDOM)
  1752.     {
  1753.         // we need a new text node:
  1754.         PDOMSTACKITEM pSI = PopElementStack(pDom,
  1755.                                             NULL);     // no free
  1756.         if (!pDom->arcDOM)
  1757.         {
  1758.             PDOMNODE    pParent = pSI->pDomNode,
  1759.                         pNew = NULL;
  1760.  
  1761.             pDom->arcDOM = xmlCreateCommentNode(pParent,
  1762.                                                 data,
  1763.                                                 &pNew);
  1764.         }
  1765.     }
  1766. }
  1767.  
  1768. /*
  1769.  *@@ StartDoctypeDeclHandler:
  1770.  *      @expat handler that is called at the start of a DOCTYPE
  1771.  *      declaration, before any external or internal subset is
  1772.  *      parsed.
  1773.  *
  1774.  *      Both pcszSysid and pcszPubid may be NULL. "fHasInternalSubset"
  1775.  *      will be non-zero if the DOCTYPE declaration has an internal subset.
  1776.  *
  1777.  *@@added V0.9.9 (2001-02-14) [umoeller]
  1778.  */
  1779.  
  1780. static void EXPATENTRY StartDoctypeDeclHandler(void *pUserData,
  1781.                                                const XML_Char *pcszDoctypeName,
  1782.                                                const XML_Char *pcszSysid,
  1783.                                                const XML_Char *pcszPubid,
  1784.                                                int fHasInternalSubset)
  1785. {
  1786.     PXMLDOM     pDom = (PXMLDOM)pUserData;
  1787.  
  1788.     // continue parsing only if we had no errors so far
  1789.     if (!pDom->arcDOM)
  1790.     {
  1791.         // get the document node
  1792.         PDOMDOCUMENTNODE pDocumentNode = pDom->pDocumentNode;
  1793.         if (!pDocumentNode)
  1794.             pDom->arcDOM = ERROR_DOM_NO_DOCUMENT;
  1795.         else
  1796.         {
  1797.             // doctype must be null
  1798.             if (pDom->pDocTypeNode)
  1799.                 pDom->arcDOM = ERROR_DOM_DUPLICATE_DOCTYPE;
  1800.             else
  1801.                 pDom->arcDOM = xmlCreateDocumentTypeNode(pDocumentNode,
  1802.                                                          pcszDoctypeName,
  1803.                                                          pcszSysid,
  1804.                                                          pcszPubid,
  1805.                                                          fHasInternalSubset,
  1806.                                                          &pDom->pDocTypeNode);
  1807.         }
  1808.     }
  1809. }
  1810.  
  1811. /*
  1812.  *@@ EndDoctypeDeclHandler:
  1813.  *      @expat handler that is called at the end of a DOCTYPE
  1814.  *      declaration, after parsing any external subset.
  1815.  *
  1816.  *@@added V0.9.9 (2001-02-14) [umoeller]
  1817.  */
  1818.  
  1819. static void EXPATENTRY EndDoctypeDeclHandler(void *pUserData)      // in: our PXMLDOM really
  1820. {
  1821.     PXMLDOM     pDom = (PXMLDOM)pUserData;
  1822.  
  1823.     // continue parsing only if we had no errors so far
  1824.     if (!pDom->arcDOM)
  1825.     {
  1826.     }
  1827. }
  1828.  
  1829. /*
  1830.  *@@ NotationDeclHandler:
  1831.  *      @expat handler for @notation_declarations.
  1832.  *
  1833.  *      @@todo
  1834.  *
  1835.  *@@added V0.9.9 (2001-02-14) [umoeller]
  1836.  */
  1837.  
  1838. static void EXPATENTRY NotationDeclHandler(void *pUserData,      // in: our PXMLDOM really
  1839.                                            const XML_Char *pcszNotationName,
  1840.                                            const XML_Char *pcszBase,
  1841.                                            const XML_Char *pcszSystemId,
  1842.                                            const XML_Char *pcszPublicId)
  1843. {
  1844.     PXMLDOM     pDom = (PXMLDOM)pUserData;
  1845.  
  1846.     // continue parsing only if we had no errors so far
  1847.     if (!pDom->arcDOM)
  1848.     {
  1849.     }
  1850. }
  1851.  
  1852. /*
  1853.  *@@ ExternalEntityRefHandler:
  1854.  *      @expat handler for references to @external_entities.
  1855.  *
  1856.  *      This handler is also called for processing an external DTD
  1857.  *      subset if parameter entity parsing is in effect.
  1858.  *      (See XML_SetParamEntityParsing.)
  1859.  *
  1860.  *      The pcszContext argument specifies the parsing context in the
  1861.  *      format expected by the context argument to
  1862.  *      XML_ExternalEntityParserCreate; pcszContext is valid only until
  1863.  *      the handler returns, so if the referenced entity is to be
  1864.  *      parsed later, it must be copied.
  1865.  *
  1866.  *      The pcszBase parameter is the base to use for relative system
  1867.  *      identifiers. It is set by XML_SetBase and may be null.
  1868.  *
  1869.  *      The pcszPublicId parameter is the public id given in the entity
  1870.  *      declaration and may be null (since XML doesn't require public
  1871.  *      identifiers).
  1872.  *
  1873.  *      The pcszSystemId is the system identifier specified in the
  1874.  *      entity declaration and is never null. This is an exact copy
  1875.  *      of what was specified in the reference.
  1876.  *
  1877.  *      There are a couple of ways in which this handler differs
  1878.  *      from others. First, this handler returns an integer. A
  1879.  *      non-zero value should be returned for successful handling
  1880.  *      of the external entity reference. Returning a zero indicates
  1881.  *      failure, and causes the calling parser to return an
  1882.  *      ERROR_EXPAT_EXTERNAL_ENTITY_HANDLING error.
  1883.  *
  1884.  *      Second, instead of having pUserData as its first argument,
  1885.  *      it receives the parser that encountered the entity reference.
  1886.  *      This, along with the context parameter, may be used as
  1887.  *      arguments to a call to XML_ExternalEntityParserCreate.
  1888.  *      Using the returned parser, the body of the external entity
  1889.  *      can be recursively parsed.
  1890.  *
  1891.  *      Since this handler may be called recursively, it should not
  1892.  *      be saving information into global or static variables.
  1893.  *
  1894.  *      Your handler isn't actually responsible for parsing the entity,
  1895.  *      but it is responsible for creating a subsidiary parser with
  1896.  *      XML_ExternalEntityParserCreate that will do the job. That returns
  1897.  *      an instance of XML_Parser that has handlers and other data
  1898.  *      structures initialized from the parent parser. You may then use
  1899.  *      XML_Parse or XML_ParseBuffer calls against that parser. Since
  1900.  *      external entities may refer to other external entities, your
  1901.  *      handler should be prepared to be called recursively.
  1902.  *
  1903.  *@@added V0.9.14 (2001-08-09) [umoeller]
  1904.  *@@changed V0.9.20 (2002-07-06) [umoeller]: added automatic doctype support
  1905.  */
  1906.  
  1907. static int EXPATENTRY ExternalEntityRefHandler(void *pUserData,      // in: our PXMLDOM really
  1908.                                                XML_Parser parser,
  1909.                                                const XML_Char *pcszContext,
  1910.                                                const XML_Char *pcszBase,
  1911.                                                const XML_Char *pcszSystemId,
  1912.                                                const XML_Char *pcszPublicId)
  1913. {
  1914.     PXMLDOM     pDom = (PXMLDOM)pUserData;
  1915.  
  1916.     int i = 0;          // return error per default
  1917.  
  1918.     // store the previous parser because
  1919.     // all the callbacks use the parser pointer
  1920.     XML_Parser pOldParser = pDom->pParser;
  1921.     pDom->pParser = NULL;
  1922.  
  1923.     if (    (    (pDom->pfnExternalHandler)
  1924.               || (pDom->cSystemIds)      // V0.9.20 (2002-07-06) [umoeller]
  1925.             )
  1926.             // create sub-parser and replace the one
  1927.             // in the DOM with it
  1928.          && (pDom->pParser = XML_ExternalEntityParserCreate(parser,
  1929.                                                             pcszContext,
  1930.                                                             "US-ASCII"))
  1931.        )
  1932.     {
  1933.         // run through the predefined doctypes given to us
  1934.         // in xmlCreateDOM, if any
  1935.         // V0.9.20 (2002-07-06) [umoeller]
  1936.         BOOL fCallExternal = TRUE;
  1937.         ULONG ul;
  1938.  
  1939.         for (ul = 0;
  1940.              ul < pDom->cSystemIds;
  1941.              ++ul)
  1942.         {
  1943.             const STATICSYSTEMID *pThis = &pDom->paSystemIds[ul];
  1944.             if (!strcmp(pThis->pcszSystemId, pcszSystemId))
  1945.             {
  1946.                 // this one matches:
  1947.                 // then parse the corresponding entry given
  1948.                 // to us
  1949.                 if (XML_Parse(pDom->pParser,
  1950.                               pThis->pcszContent,
  1951.                               strlen(pThis->pcszContent),
  1952.                               TRUE))
  1953.                     i = 1;      // success
  1954.  
  1955.                 fCallExternal = FALSE;
  1956.  
  1957.                 break;
  1958.             }
  1959.         }
  1960.  
  1961.         if (    (fCallExternal)     // not handled above
  1962.              && (pDom->pfnExternalHandler) // user handler set
  1963.            )
  1964.         {
  1965.             APIRET  arc;
  1966.  
  1967.             if (!(arc = pDom->pfnExternalHandler(pDom,
  1968.                                                  pDom->pParser,
  1969.                                                  pcszSystemId,
  1970.                                                  pcszPublicId)))
  1971.                 i = 1;      // success
  1972.             else
  1973.             {
  1974.                 // error:
  1975.                 // now this needs special handling, since we're
  1976.                 // dealing with a sub-handler here...
  1977.  
  1978.                 if (arc == -1)
  1979.                     // parser error: well, then xmlSetError has been
  1980.                     // called from somewhere in the callbacks already,
  1981.                     // and we can safely ignore this
  1982.                     ;
  1983.                 else
  1984.                 {
  1985.                     pDom->arcDOM = arc;
  1986.                     if (pcszSystemId)
  1987.                     {
  1988.                         if (!pDom->pxstrFailingNode)
  1989.                             pDom->pxstrFailingNode = xstrCreate(0);
  1990.                         xstrcpy(pDom->pxstrFailingNode, pcszSystemId, 0);
  1991.                     }
  1992.                     pDom->pcszErrorDescription = xmlDescribeError(arc);
  1993.                     pDom->ulErrorLine = XML_GetCurrentLineNumber(pDom->pParser);
  1994.                     pDom->ulErrorColumn = XML_GetCurrentColumnNumber(pDom->pParser);
  1995.                 }
  1996.             }
  1997.         }
  1998.     }
  1999.     else
  2000.         xmlSetError(pDom,
  2001.                     ERROR_DOM_INVALID_EXTERNAL_HANDLER,
  2002.                     NULL,
  2003.                     FALSE);
  2004.  
  2005.     if (pDom->pParser)
  2006.         XML_ParserFree(pDom->pParser);
  2007.  
  2008.     pDom->pParser = pOldParser;
  2009.  
  2010.     return (i);
  2011. }
  2012.  
  2013. /*
  2014.  *@@ ElementDeclHandler:
  2015.  *      @expat handler for element declarations in a DTD. The
  2016.  *      handler gets called with the name of the element in
  2017.  *      the declaration and a pointer to a structure that contains
  2018.  *      the element model.
  2019.  *
  2020.  *      It is the application's responsibility to free this data
  2021.  *      structure. @@todo
  2022.  *
  2023.  *      The XML spec defines that no element may be declared more
  2024.  *      than once.
  2025.  *
  2026.  *@@added V0.9.9 (2001-02-14) [umoeller]
  2027.  */
  2028.  
  2029. static void EXPATENTRY ElementDeclHandler(void *pUserData,      // in: our PXMLDOM really
  2030.                                           const XML_Char *pcszName,
  2031.                                           XMLCONTENT *pModel)
  2032. {
  2033.     PXMLDOM     pDom = (PXMLDOM)pUserData;
  2034.  
  2035.     // continue parsing only if we had no errors so far
  2036.     if (!pDom->arcDOM)
  2037.     {
  2038.         // OK, we're in a DOCTYPE node:
  2039.         PDOMDOCTYPENODE pDocType = pDom->pDocTypeNode;
  2040.         if (!pDocType)
  2041.             xmlSetError(pDom,
  2042.                         ERROR_DOM_ELEMENT_DECL_OUTSIDE_DOCTYPE,
  2043.                         pcszName,
  2044.                         TRUE);
  2045.         else
  2046.         {
  2047.             // create an element declaration and push it unto the
  2048.             // declarations tree
  2049.             PCMELEMENTDECLNODE pNew = NULL;
  2050.             if (!(pDom->arcDOM = xmlCreateElementDecl(pcszName,
  2051.                                                       pModel,
  2052.                                                       &pNew)))
  2053.                                     // this recurses!!
  2054.                                     // after this, pModel is invalid
  2055.             {
  2056.                 // add this to the doctype's declarations tree
  2057.                 if (treeInsert(&pDocType->ElementDeclsTree,
  2058.                                NULL,
  2059.                                (TREE*)pNew,
  2060.                                CompareXStrings))
  2061.                     // element already declared:
  2062.                     // according to the XML specs, this is a validity
  2063.                     // constraint, so we report a validation error
  2064.                     xmlSetError(pDom,
  2065.                                 ERROR_DOM_DUPLICATE_ELEMENT_DECL,
  2066.                                 pNew->Particle.NodeBase.strNodeName.psz,
  2067.                                 TRUE);
  2068.             }
  2069.         }
  2070.     }
  2071. }
  2072.  
  2073. /*
  2074.  *@@ AddEnum:
  2075.  *
  2076.  *@@added V0.9.9 (2001-02-16) [umoeller]
  2077.  */
  2078.  
  2079. static APIRET AddEnum(PCMATTRIBUTEDECL pDecl,
  2080.                       const char *p,           // in: start of name
  2081.                       const char *pNext)       // in: end of name (not included)
  2082. {
  2083.     // PSZ pszType = strhSubstr(p, pNext);
  2084.     PNODEBASE pNew = NULL;
  2085.     APIRET arc;
  2086.  
  2087.     if (!(arc = xmlCreateNodeBase(ATTRIBUTE_DECLARATION_ENUM,
  2088.                                   sizeof(NODEBASE),
  2089.                                   p,
  2090.                                   (pNext - p),
  2091.                                   &pNew)))
  2092.         treeInsert(&pDecl->ValuesTree,
  2093.                    NULL,
  2094.                    (TREE*)pNew,
  2095.                    CompareXStrings);
  2096.  
  2097.     return arc;
  2098. }
  2099.  
  2100. /*
  2101.  *@@ AttlistDeclHandler:
  2102.  *      @expat handler for attlist declarations in the DTD.
  2103.  *
  2104.  *      This handler is called for each attribute. So a single attlist
  2105.  *      declaration with multiple attributes declared will generate
  2106.  *      multiple calls to this handler.
  2107.  *
  2108.  *      --  pcszElementName is the name of the  element for which the
  2109.  *          attribute is being declared.
  2110.  *
  2111.  *      --  pcszAttribName has the attribute name being declared.
  2112.  *
  2113.  *      --  pcszAttribType is the attribute type.
  2114.  *          It is the string representing the type in the declaration
  2115.  *          with whitespace removed.
  2116.  *
  2117.  *      --  pcszDefault holds the default value. It will be
  2118.  *          NULL in the case of "#IMPLIED" or "#REQUIRED" attributes.
  2119.  *          You can distinguish these two cases by checking the
  2120.  *          fIsRequired parameter, which will be true in the case of
  2121.  *          "#REQUIRED" attributes. Attributes which are "#FIXED"
  2122.  *          will have also have a TRUE fIsRequired, but they will have
  2123.  *          the non-NULL fixed value in the pcszDefault parameter.
  2124.  *
  2125.  *@@added V0.9.9 (2001-02-14) [umoeller]
  2126.  */
  2127.  
  2128. static void EXPATENTRY AttlistDeclHandler(void *pUserData,      // in: our PXMLDOM really
  2129.                                           const XML_Char *pcszElementName,
  2130.                                           const XML_Char *pcszAttribName,
  2131.                                           const XML_Char *pcszAttribType,
  2132.                                           const XML_Char *pcszDefault,
  2133.                                           int fIsRequired)
  2134. {
  2135.     PXMLDOM     pDom = (PXMLDOM)pUserData;
  2136.  
  2137.     // continue parsing only if we had no errors so far
  2138.     if (!pDom->arcDOM)
  2139.     {
  2140.         // OK, we're in a DOCTYPE node:
  2141.         PDOMDOCTYPENODE pDocType = pDom->pDocTypeNode;
  2142.         if (!pDocType)
  2143.             xmlSetError(pDom,
  2144.                         ERROR_DOM_ATTLIST_DECL_OUTSIDE_DOCTYPE,
  2145.                         pcszElementName,
  2146.                         TRUE);
  2147.         else
  2148.         {
  2149.             PCMATTRIBUTEDECLBASE    pThis = NULL,
  2150.                                     pCache = pDom->pAttListDeclCache;
  2151.  
  2152.             // check if this is for the same attlist as the previous
  2153.             // call (we cache the pointer for speed)
  2154.             if (    (pCache)
  2155.                  && (!strhcmp(pCache->NodeBase.strNodeName.psz,
  2156.                               pcszElementName))
  2157.                )
  2158.                 // this attdecl is for the same element:
  2159.                 // use that (we won't have to search the tree)
  2160.                 pThis = pDom->pAttListDeclCache;
  2161.  
  2162.             if (!pThis)
  2163.             {
  2164.                 // cache didn't match: look up attributes tree then...
  2165.                 // note: cheap trick, we need an XSTRING for treeFind
  2166.                 // but don't want malloc, so we use xstrInitSet
  2167.                 XSTRING strElementName;
  2168.                 xstrInitSet(&strElementName, (PSZ)pcszElementName);
  2169.                 if (!(pThis = (PCMATTRIBUTEDECLBASE)treeFind(
  2170.                                     pDocType->AttribDeclBasesTree,
  2171.                                     (ULONG)&strElementName,
  2172.                                     CompareXStrings)))
  2173.                 {
  2174.                     // still not found:
  2175.                     // we need a new node then
  2176.                     if (!(pDom->arcDOM = xmlCreateNodeBase(ATTRIBUTE_DECLARATION_BASE,
  2177.                                                      sizeof(CMATTRIBUTEDECLBASE),
  2178.                                                      strElementName.psz,
  2179.                                                      strElementName.ulLength,
  2180.                                                      (PNODEBASE*)&pThis)))
  2181.                     {
  2182.                         // initialize the subtree
  2183.                         treeInit(&pThis->AttribDeclsTree, NULL);
  2184.  
  2185.                         treeInsert(&pDocType->AttribDeclBasesTree,
  2186.                                    NULL,
  2187.                                    (TREE*)pThis,
  2188.                                    CompareXStrings);
  2189.                     }
  2190.                 }
  2191.  
  2192.                 pDom->pAttListDeclCache = pThis;
  2193.             }
  2194.  
  2195.             if (pThis)
  2196.             {
  2197.                 // pThis now has either an existing or a new CMATTRIBUTEDECLBASE;
  2198.                 // add a new attribute def (CMATTRIBUTEDEDECL) to that
  2199.                 PCMATTRIBUTEDECL  pNew = NULL;
  2200.                 if (!(pDom->arcDOM = xmlCreateNodeBase(ATTRIBUTE_DECLARATION,
  2201.                                                        sizeof(CMATTRIBUTEDECL),
  2202.                                                        pcszAttribName,
  2203.                                                        0,
  2204.                                                        (PNODEBASE*)&pNew)))
  2205.                 {
  2206.                     treeInit(&pNew->ValuesTree, NULL);
  2207.  
  2208.                     // check the type... expat is too lazy to parse this for
  2209.                     // us, so we must check manually. Expat only normalizes
  2210.                     // the "type" string to kick out whitespace, so we get:
  2211.                     // (TYPE1|TYPE2|TYPE3)
  2212.                     if (*pcszAttribType == '(')
  2213.                     {
  2214.                         // enumeration:
  2215.                         const char *p = pcszAttribType + 1,
  2216.                                    *pNext;
  2217.                         while (    (pNext = strchr(p, '|'))
  2218.                                 && (!pDom->arcDOM)
  2219.                               )
  2220.                         {
  2221.                             pDom->arcDOM = AddEnum(pNew, p, pNext);
  2222.                             p = pNext + 1;
  2223.                         }
  2224.  
  2225.                         if (!pDom->arcDOM)
  2226.                         {
  2227.                             pNext = strchr(p, ')');
  2228.                             AddEnum(pNew, p, pNext);
  2229.  
  2230.                             pNew->ulAttrType = CMAT_ENUM;
  2231.                         }
  2232.                     }
  2233.                     else if (!strcmp(pcszAttribType, "CDATA"))
  2234.                         pNew->ulAttrType = CMAT_CDATA;
  2235.                     else if (!strcmp(pcszAttribType, "ID"))
  2236.                         pNew->ulAttrType = CMAT_ID;
  2237.                     else if (!strcmp(pcszAttribType, "IDREF"))
  2238.                         pNew->ulAttrType = CMAT_IDREF;
  2239.                     else if (!strcmp(pcszAttribType, "IDREFS"))
  2240.                         pNew->ulAttrType = CMAT_IDREFS;
  2241.                     else if (!strcmp(pcszAttribType, "ENTITY"))
  2242.                         pNew->ulAttrType = CMAT_ENTITY;
  2243.                     else if (!strcmp(pcszAttribType, "ENTITIES"))
  2244.                         pNew->ulAttrType = CMAT_ENTITIES;
  2245.                     else if (!strcmp(pcszAttribType, "NMTOKEN"))
  2246.                         pNew->ulAttrType = CMAT_NMTOKEN;
  2247.                     else if (!strcmp(pcszAttribType, "NMTOKENS"))
  2248.                         pNew->ulAttrType = CMAT_NMTOKENS;
  2249.  
  2250.                     if (!pDom->arcDOM)
  2251.                     {
  2252.                         if (pcszDefault)
  2253.                         {
  2254.                             // fixed or default:
  2255.                             if (fIsRequired)
  2256.                                 // fixed:
  2257.                                 pNew->ulConstraint = CMAT_FIXED_VALUE;
  2258.                             else
  2259.                                 pNew->ulConstraint = CMAT_DEFAULT_VALUE;
  2260.  
  2261.                             pNew->pstrDefaultValue = xstrCreate(0);
  2262.                             xstrcpy(pNew->pstrDefaultValue, pcszDefault, 0);
  2263.                         }
  2264.                         else
  2265.                             // implied or required:
  2266.                             if (fIsRequired)
  2267.                                 pNew->ulConstraint = CMAT_REQUIRED;
  2268.                             else
  2269.                                 pNew->ulConstraint = CMAT_IMPLIED;
  2270.  
  2271.                         if (treeInsert(&pThis->AttribDeclsTree,
  2272.                                        NULL,
  2273.                                        (TREE*)pNew,
  2274.                                        CompareXStrings))
  2275.                             xmlSetError(pDom,
  2276.                                         ERROR_DOM_DUPLICATE_ATTRIBUTE_DECL,
  2277.                                         pcszAttribName,
  2278.                                         TRUE);
  2279.                     }
  2280.                 }
  2281.             }
  2282.         }
  2283.     }
  2284. }
  2285.  
  2286. /*
  2287.  *@@ EntityDeclHandler:
  2288.  *      @expat handler that will be called for all entity declarations.
  2289.  *
  2290.  *      The fIsParameterEntity argument will be non-zero in the case
  2291.  *      of parameter entities and zero otherwise.
  2292.  *
  2293.  *      For internal entities (<!ENTITY foo "bar">), pcszValue will be
  2294.  *      non-NULL and pcszSystemId, pcszPublicId, and pcszNotationName
  2295.  *      will all be NULL. The value string is not NULL terminated; the
  2296.  *      length is provided in the iValueLength parameter. Do not use
  2297.  *      iValueLength to test for internal entities, since it is legal
  2298.  *      to have zero-length values. Instead check for whether or not
  2299.  *      pcszValue is NULL.
  2300.  *
  2301.  *      The pcszNotationName argument will have a non-NULL value only
  2302.  *      for unparsed entity declarations.
  2303.  *
  2304.  *@@added V0.9.9 (2001-02-14) [umoeller]
  2305.  */
  2306.  
  2307. static void EXPATENTRY EntityDeclHandler(void *pUserData,      // in: our PXMLDOM really
  2308.                                          const XML_Char *pcszEntityName,
  2309.                                          int fIsParameterEntity,
  2310.                                          const XML_Char *pcszValue,
  2311.                                          int iValueLength,
  2312.                                          const XML_Char *pcszBase,
  2313.                                          const XML_Char *pcszSystemId,
  2314.                                          const XML_Char *pcszPublicId,
  2315.                                          const XML_Char *pcszNotationName)
  2316. {
  2317.     PXMLDOM     pDom = (PXMLDOM)pUserData;
  2318.  
  2319.     // continue parsing only if we had no errors so far
  2320.     if (!pDom->arcDOM)
  2321.     {
  2322.     }
  2323. }
  2324.  
  2325. /* ******************************************************************
  2326.  *
  2327.  *   DOM parser APIs
  2328.  *
  2329.  ********************************************************************/
  2330.  
  2331. /*
  2332.  *@@ xmlCreateDOM:
  2333.  *      creates an XMLDOM instance, which can be used for parsing
  2334.  *      an XML document and building a @DOM tree from it at the
  2335.  *      same time.
  2336.  *
  2337.  *      Pass the XMLDOM returned here to xmlParse afterwards.
  2338.  *
  2339.  *      Simplest possible usage:
  2340.  *
  2341.  *      1) Create a DOM instance.
  2342.  *
  2343.  +          PXMLDOM pDom = NULL;
  2344.  +          APIRET arc = xmlCreateDOM(flags, NULL, NULL, NULL, &pDom);
  2345.  +
  2346.  *      2) Give chunks of data (or an entire buffer)
  2347.  *         to the DOM instance for parsing.
  2348.  *
  2349.  +          arc = xmlParse(pDom,
  2350.  +                         pBuf,
  2351.  +                         TRUE); // if last, this will clean up the parser
  2352.  *
  2353.  *      3) Process the data in the DOM tree.
  2354.  *
  2355.  *         Look at the DOMNODE definition to see how you
  2356.  *         can traverse the data. Essentially, everything
  2357.  *         is based on linked lists and string maps.
  2358.  *
  2359.  *         A few helper functions have been added for
  2360.  *         quick lookup. See xmlGetRootElement,
  2361.  *         xmlGetFirstChild, xmlGetLastChild, xmlGetFirstText,
  2362.  *         xmlGetElementsByTagName, xmlGetAttribute.
  2363.  *
  2364.  *      4) When done, call xmlFreeDOM, which will free all memory.
  2365.  *
  2366.  *      The above code has limitations: only a few character
  2367.  *      @encodings are supported, and @external_entities are
  2368.  *      silently ignored.
  2369.  *
  2370.  *      This function supports a number of callbacks and flags
  2371.  *      to allow for maximum flexibility. Note however that
  2372.  *      not all @expat features are supported yet.
  2373.  *
  2374.  *      flParserFlags is any combination of the following:
  2375.  *
  2376.  *      --  DF_PARSECOMMENTS: XML @comments are to be returned in
  2377.  *          the DOM tree. Otherwise they are discarded.
  2378.  *
  2379.  *      --  DF_PARSEDTD: add the @DTD of the document into the DOM tree
  2380.  *          as well and validate the document, if a DTD was found.
  2381.  *          Otherwise just parse and do not validate.
  2382.  *
  2383.  *          DF_PARSEDTD is required for external entities to work
  2384.  *          also.
  2385.  *
  2386.  *      --  DF_FAIL_IF_NO_DTD: fail if no @DTD was found. Useful
  2387.  *          if you want to enforce validation. @@todo
  2388.  *
  2389.  *      --  DF_DROP_WHITESPACE: discard all @whitespace for those
  2390.  *          elements that can only have element content. Whitespace
  2391.  *          will be preserved only for elements that can have
  2392.  *          mixed content. -- If this flag is not set, all whitespace
  2393.  *          is preserved.
  2394.  *
  2395.  *      The following callbacks can be specified (any of these
  2396.  *      can be NULL):
  2397.  *
  2398.  *      --  pfnGetCPData should be specified if you want to
  2399.  *          support character @encodings other than the
  2400.  *          four that built into expat itself (see
  2401.  *          XML_SetUnknownEncodingHandler). This is probably
  2402.  *          a good idea to do under OS/2 since most OS/2
  2403.  *          documents are in a PC-specific codepage such as
  2404.  *          CP 850.
  2405.  *
  2406.  *          This callback must have the following prototype:
  2407.  *
  2408.  +              int APIENTRY FNGETCPDATA(PXMLDOM pDom, ULONG ulCP, int *piMap)
  2409.  *
  2410.  *          The callback will only be called once for each
  2411.  *          document if the "encoding" attribute of the
  2412.  *          XML @text_declaration starts with "cp" (e.g.
  2413.  *          "cp850") and will then receive the following
  2414.  *          parameters:
  2415.  *
  2416.  *          --  "pDom" will be the XMLDOM created by this function.
  2417.  *
  2418.  *          --  ulCP has the IBM code page number, such as "850".
  2419.  *
  2420.  *          --  piMap is an array of 256 integer values which must
  2421.  *              be filled with the callback. Each array item index
  2422.  *              is the codepage value, and the value of each field
  2423.  *              is the corresponding Unicode value, or -1 if the
  2424.  *              character is invalid (shouldn't happen with codepages).
  2425.  *
  2426.  *              For example, the German o-umlaut character is
  2427.  *              0x94 in CP850 and 0x00f6 in Unicode. So set
  2428.  *              the int at index 0x94 to 0x00f6.
  2429.  *
  2430.  *      --  pvCallbackUser is a user parameter which is simply stored
  2431.  *          in the XMLDOM struct which is returned. Since the XMLDOM
  2432.  *          is passed to all the callbacks, you can access that pointer
  2433.  *          from them.
  2434.  *
  2435.  *@@added V0.9.9 (2001-02-14) [umoeller]
  2436.  *@@changed V0.9.14 (2001-08-09) [umoeller]: added DF_DROP_WHITESPACE support
  2437.  */
  2438.  
  2439. APIRET xmlCreateDOM(ULONG flParserFlags,            // in: DF_* parser flags
  2440.                     const STATICSYSTEMID *paSystemIds, // in: array of STATICSYSTEMID's or NULL
  2441.                     ULONG cSystemIds,                // in: array item count
  2442.                     PFNGETCPDATA pfnGetCPData,      // in: codepage callback or NULL
  2443.                     PFNEXTERNALHANDLER pfnExternalHandler, // in: external entity callback or NULL
  2444.                     PVOID pvCallbackUser,           // in: user param for callbacks
  2445.                     PXMLDOM *ppDom)                 // out: XMLDOM struct created
  2446. {
  2447.     APIRET  arc = NO_ERROR;
  2448.  
  2449.     PXMLDOM pDom = (PXMLDOM)malloc(sizeof(*pDom));
  2450.     if (!pDom)
  2451.         arc = ERROR_NOT_ENOUGH_MEMORY;
  2452.     else
  2453.     {
  2454.         PDOMNODE pDocument = NULL;
  2455.  
  2456.         memset(pDom, 0, sizeof(XMLDOM));
  2457.  
  2458.         pDom->flParserFlags = flParserFlags;
  2459.         pDom->pfnGetCPData = pfnGetCPData;
  2460.         pDom->pfnExternalHandler = pfnExternalHandler;
  2461.         pDom->pvCallbackUser = pvCallbackUser;
  2462.  
  2463.         // these added with V0.9.20 (2002-07-06) [umoeller]
  2464.         pDom->paSystemIds = paSystemIds;
  2465.         pDom->cSystemIds = cSystemIds;
  2466.  
  2467.         lstInit(&pDom->llElementStack,
  2468.                 TRUE);                 // auto-free
  2469.  
  2470.         // create the document node
  2471.         if (!(arc = xmlCreateDomNode(NULL, // no parent
  2472.                                      DOMNODE_DOCUMENT,
  2473.                                      NULL,
  2474.                                      0,
  2475.                                      &pDocument)))
  2476.         {
  2477.             // store the document in the DOM
  2478.             pDom->pDocumentNode = (PDOMDOCUMENTNODE)pDocument;
  2479.  
  2480.             // push the document on the stack so the handlers
  2481.             // will append to that
  2482.             PushElementStack(pDom,
  2483.                              pDocument);
  2484.  
  2485.             pDom->pParser = XML_ParserCreate(NULL);
  2486.  
  2487.             if (!pDom->pParser)
  2488.                 arc = ERROR_NOT_ENOUGH_MEMORY;
  2489.             else
  2490.             {
  2491.                 if (pfnGetCPData)
  2492.                     XML_SetUnknownEncodingHandler(pDom->pParser,
  2493.                                                   UnknownEncodingHandler,
  2494.                                                   pDom);        // user data
  2495.  
  2496.                 XML_SetParamEntityParsing(pDom->pParser,
  2497.                                           XML_PARAM_ENTITY_PARSING_ALWAYS);
  2498.  
  2499.                 XML_SetElementHandler(pDom->pParser,
  2500.                                       StartElementHandler,
  2501.                                       EndElementHandler);
  2502.  
  2503.                 XML_SetCharacterDataHandler(pDom->pParser,
  2504.                                             CharacterDataHandler);
  2505.  
  2506.                 // XML_SetProcessingInstructionHandler(XML_Parser parser,
  2507.                 //                          XML_ProcessingInstructionHandler handler);
  2508.  
  2509.  
  2510.                 if (flParserFlags & DF_PARSECOMMENTS)
  2511.                     XML_SetCommentHandler(pDom->pParser,
  2512.                                           CommentHandler);
  2513.  
  2514.                 if (    (pfnExternalHandler)
  2515.                      || (cSystemIds)     // V0.9.20 (2002-07-06) [umoeller]
  2516.                    )
  2517.                     XML_SetExternalEntityRefHandler(pDom->pParser,
  2518.                                                     ExternalEntityRefHandler);
  2519.  
  2520.                 if (flParserFlags & DF_PARSEDTD)
  2521.                 {
  2522.                     XML_SetDoctypeDeclHandler(pDom->pParser,
  2523.                                               StartDoctypeDeclHandler,
  2524.                                               EndDoctypeDeclHandler);
  2525.  
  2526.                     XML_SetNotationDeclHandler(pDom->pParser,
  2527.                                                NotationDeclHandler);
  2528.  
  2529.                     XML_SetElementDeclHandler(pDom->pParser,
  2530.                                               ElementDeclHandler);
  2531.  
  2532.                     XML_SetAttlistDeclHandler(pDom->pParser,
  2533.                                               AttlistDeclHandler);
  2534.  
  2535.                     XML_SetEntityDeclHandler(pDom->pParser,
  2536.                                              EntityDeclHandler);
  2537.  
  2538.                     XML_SetParamEntityParsing(pDom->pParser,
  2539.                                               XML_PARAM_ENTITY_PARSING_ALWAYS);
  2540.                 }
  2541.  
  2542.                 // XML_SetXmlDeclHandler ... do we care for this? I guess not
  2543.  
  2544.                 // pass the XMLDOM as user data to the handlers
  2545.                 XML_SetUserData(pDom->pParser,
  2546.                                 pDom);
  2547.             }
  2548.         }
  2549.     }
  2550.  
  2551.     if (arc == NO_ERROR)
  2552.         *ppDom = pDom;
  2553.     else
  2554.         xmlFreeDOM(pDom);
  2555.  
  2556.     return arc;
  2557. }
  2558.  
  2559. /*
  2560.  *@@ xmlParse:
  2561.  *      parses another chunk of XML data.
  2562.  *
  2563.  *      If (fIsLast == TRUE), the internal @expat parser
  2564.  *      will be freed, but not the DOM itself.
  2565.  *
  2566.  *      You can pass an XML document to this function
  2567.  *      in one flush. Set fIsLast = TRUE on the first
  2568.  *      and only call then.
  2569.  *
  2570.  *      This returns NO_ERROR if the chunk was successfully
  2571.  *      parsed. Otherwise one of the following errors is
  2572.  *      returned:
  2573.  *
  2574.  *      -- ERROR_INVALID_PARAMETER
  2575.  *
  2576.  *      -- ERROR_DOM_PARSING: an @expat parsing error occured.
  2577.  *         This might also be memory problems.
  2578.  *         With this error code, you will find specific
  2579.  *         error information in the XMLDOM fields.
  2580.  *
  2581.  *      -- ERROR_DOM_VALIDITY: the document is not @valid.
  2582.  *         This can only happen if @DTD parsing was enabled
  2583.  *         with xmlCreateDOM.
  2584.  *         With this error code, you will find specific
  2585.  *         error information in the XMLDOM fields.
  2586.  *
  2587.  *@@added V0.9.9 (2001-02-14) [umoeller]
  2588.  */
  2589.  
  2590. APIRET xmlParse(PXMLDOM pDom,               // in: DOM created by xmlCreateDOM
  2591.                 const char *pcszBuf,        // in: chunk of XML document data (or full document)
  2592.                 ULONG cb,                   // in: size of that chunk (required)
  2593.                 BOOL fIsLast)               // in: set to TRUE if this is the last chunk
  2594. {
  2595.     APIRET arc = NO_ERROR;
  2596.  
  2597.     if (!pDom)
  2598.         arc = ERROR_INVALID_PARAMETER;
  2599.     else
  2600.     {
  2601.         // go parse then
  2602.         if (!XML_Parse(pDom->pParser,
  2603.                        pcszBuf,
  2604.                        cb,
  2605.                        fIsLast))
  2606.         {
  2607.             // expat parsing error:
  2608.             xmlSetError(pDom,
  2609.                         XML_GetErrorCode(pDom->pParser),
  2610.                         NULL,
  2611.                         FALSE);
  2612.  
  2613.             if (pDom->pDocumentNode)
  2614.             {
  2615.                 xmlDeleteNode((PNODEBASE)pDom->pDocumentNode);
  2616.                 pDom->pDocumentNode = NULL;
  2617.             }
  2618.  
  2619.             arc = ERROR_DOM_PARSING;
  2620.         }
  2621.         else if (pDom->fInvalid)
  2622.         {
  2623.             // expat was doing OK, but the handlers' validation failed:
  2624.             arc = ERROR_DOM_VALIDITY;
  2625.                         // error info has already been set
  2626.         }
  2627.         else
  2628.             // expat was doing OK, but maybe we have integrity errors
  2629.             // from our DOM callbacks:
  2630.             if (pDom->arcDOM)
  2631.                 arc = pDom->arcDOM;
  2632.  
  2633.         if (arc != NO_ERROR || fIsLast)
  2634.         {
  2635.             // last call or error: clean up
  2636.             XML_ParserFree(pDom->pParser);
  2637.             pDom->pParser = NULL;
  2638.  
  2639.             // clean up the stack (but not the DOM itself)
  2640.             lstClear(&pDom->llElementStack);
  2641.         }
  2642.     }
  2643.  
  2644.     return arc;
  2645. }
  2646.  
  2647. #ifdef __DEBUG__
  2648.  
  2649. /*
  2650.  *@@ Dump:
  2651.  *
  2652.  *@@added V0.9.20 (2002-07-03) [umoeller]
  2653.  */
  2654.  
  2655. static VOID Dump(int iIndent,
  2656.                  PDOMNODE pDomNode)
  2657. {
  2658.     PLISTNODE pChildNode;
  2659.     int i;
  2660.     for (i = 0;
  2661.          i < iIndent;
  2662.          ++i)
  2663.     {
  2664.         printf(" ");
  2665.     }
  2666.  
  2667.     switch (pDomNode->NodeBase.ulNodeType)
  2668.     {
  2669.         #define DUMPTYPE(t) case t: printf(#t); break;
  2670.         DUMPTYPE(DOMNODE_ELEMENT)
  2671.         DUMPTYPE(DOMNODE_ATTRIBUTE)
  2672.         DUMPTYPE(DOMNODE_TEXT)
  2673.         DUMPTYPE(DOMNODE_PROCESSING_INSTRUCTION)
  2674.         DUMPTYPE(DOMNODE_COMMENT)
  2675.         DUMPTYPE(DOMNODE_DOCUMENT)
  2676.         DUMPTYPE(DOMNODE_DOCUMENT_TYPE)
  2677.         DUMPTYPE(ELEMENTPARTICLE_EMPTY)
  2678.         DUMPTYPE(ELEMENTPARTICLE_ANY)
  2679.         DUMPTYPE(ELEMENTPARTICLE_MIXED)
  2680.         DUMPTYPE(ELEMENTPARTICLE_CHOICE)
  2681.         DUMPTYPE(ELEMENTPARTICLE_SEQ)
  2682.         DUMPTYPE(ELEMENTPARTICLE_NAME)
  2683.         DUMPTYPE(ATTRIBUTE_DECLARATION_BASE)
  2684.         DUMPTYPE(ATTRIBUTE_DECLARATION)
  2685.         DUMPTYPE(ATTRIBUTE_DECLARATION_ENUM)
  2686.     }
  2687.  
  2688.     printf(" \"%s\"\n", STRINGORNULL(pDomNode->NodeBase.strNodeName.psz));
  2689.  
  2690.     ++iIndent;
  2691.     for (pChildNode = lstQueryFirstNode(&pDomNode->llChildren);
  2692.          pChildNode;
  2693.          pChildNode = pChildNode->pNext)
  2694.     {
  2695.         Dump(iIndent, (PDOMNODE)pChildNode->pItemData);
  2696.     }
  2697.     --iIndent;
  2698. }
  2699.  
  2700. #endif
  2701.  
  2702. /*
  2703.  *@@ xmlDump:
  2704.  *      debug function which dumps the DOM to stdout.
  2705.  *
  2706.  *@@added V0.9.20 (2002-07-03) [umoeller]
  2707.  */
  2708.  
  2709. VOID xmlDump(PXMLDOM pDom)
  2710. {
  2711. #ifdef __DEBUG__
  2712.     if (!pDom)
  2713.     {
  2714.         printf(__FUNCTION__ ": pDom is NULL\n");
  2715.         return;
  2716.     }
  2717.  
  2718.     printf(__FUNCTION__ ": dumping document node ");
  2719.  
  2720.     Dump(0, (PDOMNODE)pDom->pDocumentNode);
  2721. #endif
  2722. }
  2723.  
  2724. /*
  2725.  *@@ xmlFreeDOM:
  2726.  *      cleans up all resources allocated by
  2727.  *      xmlCreateDOM and xmlParse, including
  2728.  *      the entire DOM tree.
  2729.  *
  2730.  *      If you wish to keep any data, make
  2731.  *      a copy of the respective pointers in pDom
  2732.  *      or subitems and set them to NULL before
  2733.  *      calling this function.
  2734.  *
  2735.  *@@added V0.9.9 (2001-02-14) [umoeller]
  2736.  */
  2737.  
  2738. APIRET xmlFreeDOM(PXMLDOM pDom)
  2739. {
  2740.     APIRET arc = NO_ERROR;
  2741.     if (pDom)
  2742.     {
  2743.         // if the parser is still alive for some reason, close it.
  2744.         if (pDom->pParser)
  2745.         {
  2746.             XML_ParserFree(pDom->pParser);
  2747.             pDom->pParser = NULL;
  2748.         }
  2749.  
  2750.         xmlDeleteNode((PNODEBASE)pDom->pDocumentNode);
  2751.  
  2752.         if (pDom->pxstrSystemID)
  2753.             xstrFree(&pDom->pxstrSystemID);
  2754.         if (pDom->pxstrFailingNode)
  2755.             xstrFree(&pDom->pxstrFailingNode);
  2756.  
  2757.         lstClear(&pDom->llElementStack);
  2758.  
  2759.         free(pDom);
  2760.     }
  2761.  
  2762.     return arc;
  2763. }
  2764.  
  2765. /* ******************************************************************
  2766.  *
  2767.  *   DOM lookup
  2768.  *
  2769.  ********************************************************************/
  2770.  
  2771. /*
  2772.  *@@ xmlFindElementDecl:
  2773.  *      returns the CMELEMENTDECLNODE for the element
  2774.  *      with the specified name or NULL if there's none.
  2775.  *
  2776.  *@@added V0.9.9 (2001-02-16) [umoeller]
  2777.  */
  2778.  
  2779. PCMELEMENTDECLNODE xmlFindElementDecl(PXMLDOM pDom,
  2780.                                       const XSTRING *pcstrElementName)
  2781. {
  2782.     PCMELEMENTDECLNODE pElementDecl = NULL;
  2783.  
  2784.     PDOMDOCTYPENODE pDocTypeNode = pDom->pDocTypeNode;
  2785.     if (    (pDocTypeNode)
  2786.          && (pcstrElementName)
  2787.          && (pcstrElementName->ulLength)
  2788.        )
  2789.     {
  2790.         pElementDecl = (PCMELEMENTDECLNODE)treeFind(
  2791.                                       pDocTypeNode->ElementDeclsTree,
  2792.                                       (ULONG)pcstrElementName,
  2793.                                       CompareXStrings);
  2794.     }
  2795.  
  2796.     return (pElementDecl);
  2797. }
  2798.  
  2799. /*
  2800.  *@@ xmlFindAttribDeclBase:
  2801.  *      returns the CMATTRIBUTEDECLBASE for the specified
  2802.  *      element name, or NULL if none exists.
  2803.  *
  2804.  *      To find a specific attribute declaration from both
  2805.  *      an element and an attribute name, use xmlFindAttribDecl
  2806.  *      instead.
  2807.  *
  2808.  *@@added V0.9.9 (2001-02-16) [umoeller]
  2809.  */
  2810.  
  2811. PCMATTRIBUTEDECLBASE xmlFindAttribDeclBase(PXMLDOM pDom,
  2812.                                            const XSTRING *pstrElementName)
  2813. {
  2814.     PDOMDOCTYPENODE pDocTypeNode = pDom->pDocTypeNode;
  2815.     if (    (pDocTypeNode)
  2816.          && (pstrElementName)
  2817.          && (pstrElementName->ulLength)
  2818.        )
  2819.     {
  2820.         return ((PCMATTRIBUTEDECLBASE)treeFind(
  2821.                                         pDocTypeNode->AttribDeclBasesTree,
  2822.                                         (ULONG)pstrElementName,
  2823.                                         CompareXStrings));
  2824.     }
  2825.  
  2826.     return NULL;
  2827. }
  2828.  
  2829. /*
  2830.  *@@ xmlFindAttribDecl:
  2831.  *      returns the CMATTRIBUTEDEDECL for the specified
  2832.  *      element and attribute name, or NULL if none exists.
  2833.  *
  2834.  *@@added V0.9.9 (2001-02-16) [umoeller]
  2835.  */
  2836.  
  2837. PCMATTRIBUTEDECL xmlFindAttribDecl(PXMLDOM pDom,
  2838.                                    const XSTRING *pstrElementName,
  2839.                                    const XSTRING *pstrAttribName,
  2840.                                    PCMATTRIBUTEDECLBASE *ppAttribDeclBase)
  2841.                                             // in/out: attr decl base cache;
  2842.                                             // the pointer pointed to by this
  2843.                                             // must be NULL on the first call
  2844. {
  2845.     if (pstrElementName && pstrAttribName)
  2846.     {
  2847.         if (!*ppAttribDeclBase)
  2848.             // first call for this:
  2849.             *ppAttribDeclBase = xmlFindAttribDeclBase(pDom,
  2850.                                                       pstrElementName);
  2851.         if (*ppAttribDeclBase)
  2852.         {
  2853.             return ((PCMATTRIBUTEDECL)treeFind(
  2854.                                          ((**ppAttribDeclBase).AttribDeclsTree),
  2855.                                          (ULONG)pstrAttribName,
  2856.                                          CompareXStrings));
  2857.         }
  2858.     }
  2859.  
  2860.     return NULL;
  2861. }
  2862.  
  2863. /*
  2864.  *@@ xmlGetRootElement:
  2865.  *      returns the root element node from the specified
  2866.  *      DOM. Useful helper to start enumerating elements.
  2867.  *
  2868.  *@@added V0.9.11 (2001-04-22) [umoeller]
  2869.  *@@changed V0.9.20 (2002-07-03) [umoeller]: this never worked with DTDs, fixed
  2870.  */
  2871.  
  2872. PDOMNODE xmlGetRootElement(PXMLDOM pDom)
  2873. {
  2874.     PDOMDOCUMENTNODE    pDocumentNode;
  2875.     PLISTNODE           pListNode;
  2876.     if (    (pDom)
  2877.          && (pDocumentNode = pDom->pDocumentNode)
  2878.          && (pListNode = lstQueryFirstNode(&pDocumentNode->DomNode.llChildren))
  2879.        )
  2880.     {
  2881.         // V0.9.20 (2002-07-03) [umoeller]:
  2882.         // we can't just return the first node on the
  2883.         // list, because if we have DTD, this might
  2884.         // be the doctype... so loop until we find
  2885.         // an element, which must be the root element
  2886.         while (pListNode)
  2887.         {
  2888.             PDOMNODE pDomNode = (PDOMNODE)pListNode->pItemData;
  2889.             if (pDomNode->NodeBase.ulNodeType == DOMNODE_ELEMENT)
  2890.                 return (pDomNode);
  2891.  
  2892.             pListNode = pListNode->pNext;
  2893.         }
  2894.     }
  2895.  
  2896.     return NULL;
  2897. }
  2898.  
  2899. /*
  2900.  *@@ xmlGetFirstChild:
  2901.  *      returns the first child node of pDomNode.
  2902.  *      See DOMNODE for what a "child" can be for the
  2903.  *      various node types.
  2904.  *
  2905.  *@@added V0.9.9 (2001-02-14) [umoeller]
  2906.  */
  2907.  
  2908. PDOMNODE xmlGetFirstChild(PDOMNODE pDomNode)
  2909. {
  2910.     PLISTNODE pListNode = lstQueryFirstNode(&pDomNode->llChildren);
  2911.     if (pListNode)
  2912.         return ((PDOMNODE)pListNode->pItemData);
  2913.  
  2914.     return (0);
  2915. }
  2916.  
  2917. /*
  2918.  *@@ xmlGetLastChild:
  2919.  *      returns the last child node of pDomNode.
  2920.  *      See DOMNODE for what a "child" can be for the
  2921.  *      various node types.
  2922.  *
  2923.  *@@added V0.9.9 (2001-02-14) [umoeller]
  2924.  */
  2925.  
  2926. PDOMNODE xmlGetLastChild(PDOMNODE pDomNode)
  2927. {
  2928.     PLISTNODE pListNode = lstQueryLastNode(&pDomNode->llChildren);
  2929.     if (pListNode)
  2930.         return ((PDOMNODE)pListNode->pItemData);
  2931.  
  2932.     return (0);
  2933. }
  2934.  
  2935. /*
  2936.  *@@ xmlGetFirstText:
  2937.  *      returns the first text (character data) node
  2938.  *      of pElement or NULL if there's none.
  2939.  *
  2940.  *@@added V0.9.11 (2001-04-22) [umoeller]
  2941.  */
  2942.  
  2943. PDOMNODE xmlGetFirstText(PDOMNODE pElement)
  2944. {
  2945.     PLISTNODE   pNode;
  2946.     PDOMNODE    pDomNodeThis;
  2947.  
  2948.     for (pNode = lstQueryFirstNode(&pElement->llChildren);
  2949.          pNode;
  2950.          pNode = pNode->pNext)
  2951.     {
  2952.         if (    (pDomNodeThis = (PDOMNODE)pNode->pItemData)
  2953.              && (pDomNodeThis->NodeBase.ulNodeType == DOMNODE_TEXT)
  2954.            )
  2955.             return (pDomNodeThis);
  2956.     }
  2957.  
  2958.     return NULL;
  2959. }
  2960.  
  2961. /*
  2962.  *@@ xmlGetElementsByTagName:
  2963.  *      returns a linked list of DOMNODE nodes which
  2964.  *      match the specified element name. The special name
  2965.  *      "*" matches all elements.
  2966.  *
  2967.  *      pParent must be the parent element DOMNODE...
  2968.  *      the only allowed exception is
  2969.  *
  2970.  *      The caller must free the list by calling lstFree.
  2971.  *      Returns NULL if no such elements could be found.
  2972.  *
  2973.  *@@added V0.9.9 (2001-02-14) [umoeller]
  2974.  */
  2975.  
  2976. PLINKLIST xmlGetElementsByTagName(PDOMNODE pParent,
  2977.                                   const char *pcszName)
  2978. {
  2979.     PLINKLIST pll = lstCreate(FALSE);       // no free
  2980.     if (pll)
  2981.     {
  2982.         ULONG   cItems = 0;
  2983.         BOOL    fFindAll = !strcmp(pcszName, "*");
  2984.  
  2985.         PLISTNODE   pNode;
  2986.         PDOMNODE    pDomNodeThis;
  2987.  
  2988.         for (pNode = lstQueryFirstNode(&pParent->llChildren);
  2989.              pNode;
  2990.              pNode = pNode->pNext)
  2991.         {
  2992.             if (    (pDomNodeThis = (PDOMNODE)pNode->pItemData)
  2993.                  && (pDomNodeThis->NodeBase.ulNodeType == DOMNODE_ELEMENT)
  2994.                  && (    (fFindAll)
  2995.                       || (!strcmp(pcszName, pDomNodeThis->NodeBase.strNodeName.psz))
  2996.                     )
  2997.                )
  2998.             {
  2999.                 // element matches:
  3000.                 lstAppendItem(pll, pDomNodeThis);
  3001.                 cItems++;
  3002.             }
  3003.         }
  3004.  
  3005.         if (cItems)
  3006.             return (pll);
  3007.         else
  3008.             lstFree(&pll);
  3009.     }
  3010.  
  3011.     return (0);
  3012. }
  3013.  
  3014. /*
  3015.  *@@ xmlGetAttribute:
  3016.  *      returns the value of pElement's attribute
  3017.  *      with the given name or NULL.
  3018.  *
  3019.  *      This is a const pointer into the element's
  3020.  *      attribute list.
  3021.  *
  3022.  *@@added V0.9.11 (2001-04-22) [umoeller]
  3023.  */
  3024.  
  3025. const XSTRING* xmlGetAttribute(PDOMNODE pElement,
  3026.                                const char *pcszAttribName)
  3027. {
  3028.     XSTRING str;
  3029.     PDOMNODE pAttrNode;
  3030.     xstrInitSet(&str, (PSZ)pcszAttribName);
  3031.     // note, cheap trick: no malloc here, but we need
  3032.     // an XSTRING for treeFind
  3033.     if (pAttrNode = (PDOMNODE)treeFind(pElement->AttributesMap,
  3034.                                        (ULONG)&str,
  3035.                                        CompareXStrings))
  3036.         return (pAttrNode->pstrNodeValue);
  3037.  
  3038.     return NULL;
  3039. }
  3040.  
  3041. /* ******************************************************************
  3042.  *
  3043.  *   DOM build
  3044.  *
  3045.  ********************************************************************/
  3046.  
  3047. /*
  3048.  *@@ xmlCreateDocument:
  3049.  *      creates a new XML document.
  3050.  *
  3051.  *      This is the first step in creating a DOM
  3052.  *      tree in memory.
  3053.  *
  3054.  *      This function creates a DOCUMENT node
  3055.  *      (which is returned) and a root ELEMENT node
  3056.  *      within the document. For convenience, the
  3057.  *      root ELEMENT node is returned as well.
  3058.  *
  3059.  *      This does not create a DOCTYPE node in
  3060.  *      the document.
  3061.  *
  3062.  *      After this, you can add sub-elements and
  3063.  *      attributes to the root element using
  3064.  *      xmlCreateElementNode and xmlCreateAttributeNode.
  3065.  *
  3066.  *      Use xmlDeleteNode on the DOCUMENT node
  3067.  *      to delete the entire DOM tree.
  3068.  *
  3069.  *@@added V0.9.12 (2001-05-21) [umoeller]
  3070.  */
  3071.  
  3072. APIRET xmlCreateDocument(const char *pcszRootElementName,   // in: root element name
  3073.                          PDOMDOCUMENTNODE *ppDocument,      // out: DOCUMENT node
  3074.                          PDOMNODE *ppRootElement)           // out: root ELEMENT node within DOCUMENT
  3075. {
  3076.     APIRET arc = NO_ERROR;
  3077.     PDOMDOCUMENTNODE pDocument = NULL;
  3078.     PDOMNODE pRootElement = NULL;
  3079.  
  3080.     if ( (!pcszRootElementName) || (!ppDocument) || (!ppRootElement) )
  3081.         arc = ERROR_INVALID_PARAMETER;
  3082.     else
  3083.         // create the document node
  3084.         if (!(arc = xmlCreateDomNode(NULL, // no parent
  3085.                                      DOMNODE_DOCUMENT,
  3086.                                      NULL,
  3087.                                      0,
  3088.                                      (PDOMNODE*)&pDocument)))
  3089.             if (!(arc = xmlCreateDomNode((PDOMNODE)pDocument,     // parent
  3090.                                          DOMNODE_ELEMENT,
  3091.                                          pcszRootElementName,
  3092.                                          0,
  3093.                                          &pRootElement)))
  3094.             {
  3095.                 *ppDocument = pDocument;
  3096.                 *ppRootElement = pRootElement;
  3097.             }
  3098.  
  3099.     return arc;
  3100. }
  3101.  
  3102. /*
  3103.  *@@ WriteNodes:
  3104.  *      internal helper for writing out the nodes.
  3105.  *      This recurses.
  3106.  *
  3107.  *@@added V0.9.12 (2001-05-21) [umoeller]
  3108.  */
  3109.  
  3110. static VOID WriteNodes(PXSTRING pxstr,
  3111.                        PDOMNODE pDomNode)       // in: node whose children are to be written (initially DOCUMENT)
  3112. {
  3113.     PLISTNODE pListNode;
  3114.  
  3115.     BOOL fMixedContent = (xmlGetFirstText(pDomNode) != NULL);
  3116.  
  3117.     for (pListNode = lstQueryFirstNode(&pDomNode->llChildren);
  3118.          (pListNode);
  3119.          pListNode = pListNode->pNext)
  3120.     {
  3121.         PDOMNODE pChildNode = (PDOMNODE)pListNode->pItemData;
  3122.  
  3123.         switch (pChildNode->NodeBase.ulNodeType)
  3124.         {
  3125.             case DOMNODE_ELEMENT:
  3126.             {
  3127.                 PDOMNODE pAttribNode;
  3128.                 // write out opening ELEMENT tag
  3129.                 // add a line break if this does NOT have mixed
  3130.                 // content
  3131.                 if (!fMixedContent)
  3132.                     xstrcatc(pxstr, '\n');
  3133.  
  3134.                 xstrcatc(pxstr, '<');
  3135.                 xstrcats(pxstr, &pChildNode->NodeBase.strNodeName);
  3136.  
  3137.                 // go through attributes
  3138.                 for (pAttribNode = (PDOMNODE)treeFirst(pChildNode->AttributesMap);
  3139.                      (pAttribNode);
  3140.                      pAttribNode = (PDOMNODE)treeNext((TREE*)pAttribNode))
  3141.                 {
  3142.                     xstrcat(pxstr, "\n    ", 0);
  3143.                     xstrcats(pxstr, &pAttribNode->NodeBase.strNodeName);
  3144.                     xstrcat(pxstr, "=\"", 0);
  3145.                     xstrcats(pxstr, pAttribNode->pstrNodeValue);
  3146.                     xstrcatc(pxstr, '\"');
  3147.                 }
  3148.  
  3149.                 // now check... do we have child nodes?
  3150.                 if (lstCountItems(&pChildNode->llChildren))
  3151.                 {
  3152.                     // yes:
  3153.                     xstrcatc(pxstr, '>');
  3154.  
  3155.                     // recurse into this child element
  3156.                     WriteNodes(pxstr, pChildNode);
  3157.  
  3158.                     if (!fMixedContent)
  3159.                         xstrcatc(pxstr, '\n');
  3160.  
  3161.                     // write closing tag
  3162.                     xstrcat(pxstr, "</", 0);
  3163.                     xstrcats(pxstr, &pChildNode->NodeBase.strNodeName);
  3164.                     xstrcatc(pxstr, '>');
  3165.                 }
  3166.                 else
  3167.                 {
  3168.                     // no child nodes:
  3169.                     // mark this tag as "empty"
  3170.                     xstrcat(pxstr, "/>", 0);
  3171.                 }
  3172.             }
  3173.             break;
  3174.  
  3175.             case DOMNODE_TEXT:
  3176.             case DOMNODE_COMMENT:
  3177.                 // that's simple
  3178.                 xstrcats(pxstr, pChildNode->pstrNodeValue);
  3179.             break;
  3180.  
  3181.             case DOMNODE_DOCUMENT_TYPE:
  3182.                 // @@todo
  3183.             break;
  3184.  
  3185.         }
  3186.     }
  3187. }
  3188.  
  3189. /*
  3190.  *@@ xmlWriteDocument:
  3191.  *      creates a complete XML document in the specified
  3192.  *      string buffer from the specified DOMDOCUMENTNODE.
  3193.  *
  3194.  *      This creates a full XML document, starting with
  3195.  *      the <?xml...?> header, the DTD (if present),
  3196.  *      and the elements and attributes.
  3197.  *
  3198.  *      The input XSTRING must be initialized. Its
  3199.  *      contents will be overwritten, if any exists.
  3200.  *
  3201.  *      Sooo... to write a full XML document to disk,
  3202.  *      do the following:
  3203.  *
  3204.  *      1)  Call xmlCreateDocument to have an empty
  3205.  *          document with a root element created.
  3206.  *
  3207.  *      2)  Add elements, subelements, and attributes
  3208.  *          using xmlCreateElementNode and
  3209.  *          xmlCreateAttributeNode.
  3210.  *
  3211.  *      3)  Call xmlWriteDocument to have the XML
  3212.  *          document written into an XSTRING.
  3213.  *
  3214.  *      4)  Write the XSTRING to disk, e.g. using
  3215.  *          fwrite().
  3216.  *
  3217.  *          Note: You can also use doshWriteTextFile,
  3218.  *          but you should then first convert the
  3219.  *          line format using xstrConvertLineFormat.
  3220.  *
  3221.  *      Example:
  3222.  *
  3223.  +          APIRET arc = NO_ERROR;
  3224.  +          PDOMDOCUMENTNODE pDocument = NULL;
  3225.  +          PDOMNODE pRootElement = NULL;
  3226.  +
  3227.  +          // create a DOM
  3228.  +          if (!(arc = xmlCreateDocument("MYROOTNODE",
  3229.  +                                        &pDocument,
  3230.  +                                        &pRootElement)))
  3231.  +          {
  3232.  +              // add subelements to the root element
  3233.  +              PDOMNODE pSubelement;
  3234.  +              if (!(arc = xmlCreateElementNode(pRootElement,
  3235.  +                                               "MYSUBELEMENT",
  3236.  +                                               &pSubelement)))
  3237.  +              {
  3238.  +                  // add an attribute
  3239.  +                  PDOMNODE pAttribute;
  3240.  +                  if (!(arc = xmlCreateAttributeNode(pSubElement,
  3241.  +                                                     "MYATTRIB",
  3242.  +                                                     "VALUE",
  3243.  +                                                     &pAttribute)))
  3244.  +                  {
  3245.  +                      // alright, turn this into a string
  3246.  +                      XSTRING str;
  3247.  +                      xstrInit(&str, 1000);
  3248.  +                      if (!(arc = xmlWriteDocument(pDocument,
  3249.  +                                                   "ISO-8859-1",
  3250.  +                                                   NULL,      // or DOCTYPE
  3251.  +                                                   &str)))
  3252.  +                      {
  3253.  +                          FILE *file = fopen("myfile.xml", "w");
  3254.  +                          fwrite(str.psz,
  3255.  +                                 1,
  3256.  +                                 str.ulLength,
  3257.  +                                 file);
  3258.  +                          fclose(file);
  3259.  +                      }
  3260.  +                  }
  3261.  +              }
  3262.  +
  3263.  +              // this kills the entire tree
  3264.  +              xmlDeleteNode((PNODEBASE)pDocument);
  3265.  +
  3266.  +          }
  3267.  +
  3268.  *
  3269.  *      A note about whitespace handling. Presently, this
  3270.  *      adds line breaks after the opening tag of an
  3271.  *      element if the element has element content only.
  3272.  *      However, if the element has mixed content, this
  3273.  *      line break is NOT automatically added because
  3274.  *      white space may then be significant.
  3275.  *
  3276.  *@@added V0.9.12 (2001-05-21) [umoeller]
  3277.  */
  3278.  
  3279. APIRET xmlWriteDocument(PDOMDOCUMENTNODE pDocument,     // in: document node
  3280.                         const char *pcszEncoding,       // in: encoding string (e.g. "ISO-8859-1")
  3281.                         const char *pcszDoctype,        // in: entire DOCTYPE statement or NULL
  3282.                         PXSTRING pxstr)                 // out: document
  3283. {
  3284.     APIRET arc = NO_ERROR;
  3285.  
  3286.     if ( (!pDocument) || (!pcszEncoding) || (!pxstr) )
  3287.         arc = ERROR_INVALID_PARAMETER;
  3288.     else
  3289.     {
  3290.         // <?xml version="1.0" encoding="ISO-8859-1"?>
  3291.         xstrcpy(pxstr, "<?xml version=\"1.0\" encoding=\"", 0);
  3292.         xstrcat(pxstr, pcszEncoding, 0);
  3293.         xstrcat(pxstr, "\"?>\n", 0);
  3294.  
  3295.         // write entire DOCTYPE statement
  3296.         if (pcszDoctype)
  3297.         {
  3298.             xstrcatc(pxstr, '\n');
  3299.             xstrcat(pxstr, pcszDoctype, 0);
  3300.             xstrcatc(pxstr, '\n');
  3301.         }
  3302.  
  3303.         // write out children
  3304.         WriteNodes(pxstr, (PDOMNODE)pDocument);
  3305.  
  3306.         xstrcatc(pxstr, '\n');
  3307.     }
  3308.  
  3309.     return arc;
  3310. }
  3311.  
  3312.  
  3313.