| 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 Mller. | 
|---|
| 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 | *@@changed V0.9.20 (2002-07-06) [umoeller]: added static system IDs | 
|---|
| 2438 | */ | 
|---|
| 2439 |  | 
|---|
| 2440 | APIRET xmlCreateDOM(ULONG flParserFlags,            // in: DF_* parser flags | 
|---|
| 2441 | const STATICSYSTEMID *paSystemIds, // in: array of STATICSYSTEMID's or NULL | 
|---|
| 2442 | ULONG cSystemIds,                // in: array item count | 
|---|
| 2443 | PFNGETCPDATA pfnGetCPData,      // in: codepage callback or NULL | 
|---|
| 2444 | PFNEXTERNALHANDLER pfnExternalHandler, // in: external entity callback or NULL | 
|---|
| 2445 | PVOID pvCallbackUser,           // in: user param for callbacks | 
|---|
| 2446 | PXMLDOM *ppDom)                 // out: XMLDOM struct created | 
|---|
| 2447 | { | 
|---|
| 2448 | APIRET  arc = NO_ERROR; | 
|---|
| 2449 |  | 
|---|
| 2450 | PXMLDOM pDom = (PXMLDOM)malloc(sizeof(*pDom)); | 
|---|
| 2451 | if (!pDom) | 
|---|
| 2452 | arc = ERROR_NOT_ENOUGH_MEMORY; | 
|---|
| 2453 | else | 
|---|
| 2454 | { | 
|---|
| 2455 | PDOMNODE pDocument = NULL; | 
|---|
| 2456 |  | 
|---|
| 2457 | memset(pDom, 0, sizeof(XMLDOM)); | 
|---|
| 2458 |  | 
|---|
| 2459 | pDom->flParserFlags = flParserFlags; | 
|---|
| 2460 | pDom->pfnGetCPData = pfnGetCPData; | 
|---|
| 2461 | pDom->pfnExternalHandler = pfnExternalHandler; | 
|---|
| 2462 | pDom->pvCallbackUser = pvCallbackUser; | 
|---|
| 2463 |  | 
|---|
| 2464 | // these added with V0.9.20 (2002-07-06) [umoeller] | 
|---|
| 2465 | pDom->paSystemIds = paSystemIds; | 
|---|
| 2466 | pDom->cSystemIds = cSystemIds; | 
|---|
| 2467 |  | 
|---|
| 2468 | lstInit(&pDom->llElementStack, | 
|---|
| 2469 | TRUE);                 // auto-free | 
|---|
| 2470 |  | 
|---|
| 2471 | // create the document node | 
|---|
| 2472 | if (!(arc = xmlCreateDomNode(NULL, // no parent | 
|---|
| 2473 | DOMNODE_DOCUMENT, | 
|---|
| 2474 | NULL, | 
|---|
| 2475 | 0, | 
|---|
| 2476 | &pDocument))) | 
|---|
| 2477 | { | 
|---|
| 2478 | // store the document in the DOM | 
|---|
| 2479 | pDom->pDocumentNode = (PDOMDOCUMENTNODE)pDocument; | 
|---|
| 2480 |  | 
|---|
| 2481 | // push the document on the stack so the handlers | 
|---|
| 2482 | // will append to that | 
|---|
| 2483 | PushElementStack(pDom, | 
|---|
| 2484 | pDocument); | 
|---|
| 2485 |  | 
|---|
| 2486 | pDom->pParser = XML_ParserCreate(NULL); | 
|---|
| 2487 |  | 
|---|
| 2488 | if (!pDom->pParser) | 
|---|
| 2489 | arc = ERROR_NOT_ENOUGH_MEMORY; | 
|---|
| 2490 | else | 
|---|
| 2491 | { | 
|---|
| 2492 | if (pfnGetCPData) | 
|---|
| 2493 | XML_SetUnknownEncodingHandler(pDom->pParser, | 
|---|
| 2494 | UnknownEncodingHandler, | 
|---|
| 2495 | pDom);        // user data | 
|---|
| 2496 |  | 
|---|
| 2497 | XML_SetParamEntityParsing(pDom->pParser, | 
|---|
| 2498 | XML_PARAM_ENTITY_PARSING_ALWAYS); | 
|---|
| 2499 |  | 
|---|
| 2500 | XML_SetElementHandler(pDom->pParser, | 
|---|
| 2501 | StartElementHandler, | 
|---|
| 2502 | EndElementHandler); | 
|---|
| 2503 |  | 
|---|
| 2504 | XML_SetCharacterDataHandler(pDom->pParser, | 
|---|
| 2505 | CharacterDataHandler); | 
|---|
| 2506 |  | 
|---|
| 2507 | // XML_SetProcessingInstructionHandler(XML_Parser parser, | 
|---|
| 2508 | //                          XML_ProcessingInstructionHandler handler); | 
|---|
| 2509 |  | 
|---|
| 2510 |  | 
|---|
| 2511 | if (flParserFlags & DF_PARSECOMMENTS) | 
|---|
| 2512 | XML_SetCommentHandler(pDom->pParser, | 
|---|
| 2513 | CommentHandler); | 
|---|
| 2514 |  | 
|---|
| 2515 | if (    (pfnExternalHandler) | 
|---|
| 2516 | || (cSystemIds)     // V0.9.20 (2002-07-06) [umoeller] | 
|---|
| 2517 | ) | 
|---|
| 2518 | XML_SetExternalEntityRefHandler(pDom->pParser, | 
|---|
| 2519 | ExternalEntityRefHandler); | 
|---|
| 2520 |  | 
|---|
| 2521 | if (flParserFlags & DF_PARSEDTD) | 
|---|
| 2522 | { | 
|---|
| 2523 | XML_SetDoctypeDeclHandler(pDom->pParser, | 
|---|
| 2524 | StartDoctypeDeclHandler, | 
|---|
| 2525 | EndDoctypeDeclHandler); | 
|---|
| 2526 |  | 
|---|
| 2527 | XML_SetNotationDeclHandler(pDom->pParser, | 
|---|
| 2528 | NotationDeclHandler); | 
|---|
| 2529 |  | 
|---|
| 2530 | XML_SetElementDeclHandler(pDom->pParser, | 
|---|
| 2531 | ElementDeclHandler); | 
|---|
| 2532 |  | 
|---|
| 2533 | XML_SetAttlistDeclHandler(pDom->pParser, | 
|---|
| 2534 | AttlistDeclHandler); | 
|---|
| 2535 |  | 
|---|
| 2536 | XML_SetEntityDeclHandler(pDom->pParser, | 
|---|
| 2537 | EntityDeclHandler); | 
|---|
| 2538 |  | 
|---|
| 2539 | XML_SetParamEntityParsing(pDom->pParser, | 
|---|
| 2540 | XML_PARAM_ENTITY_PARSING_ALWAYS); | 
|---|
| 2541 | } | 
|---|
| 2542 |  | 
|---|
| 2543 | // XML_SetXmlDeclHandler ... do we care for this? I guess not | 
|---|
| 2544 |  | 
|---|
| 2545 | // pass the XMLDOM as user data to the handlers | 
|---|
| 2546 | XML_SetUserData(pDom->pParser, | 
|---|
| 2547 | pDom); | 
|---|
| 2548 | } | 
|---|
| 2549 | } | 
|---|
| 2550 | } | 
|---|
| 2551 |  | 
|---|
| 2552 | if (arc == NO_ERROR) | 
|---|
| 2553 | *ppDom = pDom; | 
|---|
| 2554 | else | 
|---|
| 2555 | xmlFreeDOM(pDom); | 
|---|
| 2556 |  | 
|---|
| 2557 | return arc; | 
|---|
| 2558 | } | 
|---|
| 2559 |  | 
|---|
| 2560 | /* | 
|---|
| 2561 | *@@ xmlParse: | 
|---|
| 2562 | *      parses another chunk of XML data. | 
|---|
| 2563 | * | 
|---|
| 2564 | *      If (fIsLast == TRUE), the internal @expat parser | 
|---|
| 2565 | *      will be freed, but not the DOM itself. | 
|---|
| 2566 | * | 
|---|
| 2567 | *      You can pass an XML document to this function | 
|---|
| 2568 | *      in one flush. Set fIsLast = TRUE on the first | 
|---|
| 2569 | *      and only call then. | 
|---|
| 2570 | * | 
|---|
| 2571 | *      This returns NO_ERROR if the chunk was successfully | 
|---|
| 2572 | *      parsed. Otherwise one of the following errors is | 
|---|
| 2573 | *      returned: | 
|---|
| 2574 | * | 
|---|
| 2575 | *      -- ERROR_INVALID_PARAMETER | 
|---|
| 2576 | * | 
|---|
| 2577 | *      -- ERROR_DOM_PARSING: an @expat parsing error occurred. | 
|---|
| 2578 | *         This might also be memory problems. | 
|---|
| 2579 | *         With this error code, you will find specific | 
|---|
| 2580 | *         error information in the XMLDOM fields. | 
|---|
| 2581 | * | 
|---|
| 2582 | *      -- ERROR_DOM_VALIDITY: the document is not @valid. | 
|---|
| 2583 | *         This can only happen if @DTD parsing was enabled | 
|---|
| 2584 | *         with xmlCreateDOM. | 
|---|
| 2585 | *         With this error code, you will find specific | 
|---|
| 2586 | *         error information in the XMLDOM fields. | 
|---|
| 2587 | * | 
|---|
| 2588 | *@@added V0.9.9 (2001-02-14) [umoeller] | 
|---|
| 2589 | */ | 
|---|
| 2590 |  | 
|---|
| 2591 | APIRET xmlParse(PXMLDOM pDom,               // in: DOM created by xmlCreateDOM | 
|---|
| 2592 | const char *pcszBuf,        // in: chunk of XML document data (or full document) | 
|---|
| 2593 | ULONG cb,                   // in: size of that chunk (required) | 
|---|
| 2594 | BOOL fIsLast)               // in: set to TRUE if this is the last chunk | 
|---|
| 2595 | { | 
|---|
| 2596 | APIRET arc = NO_ERROR; | 
|---|
| 2597 |  | 
|---|
| 2598 | if (!pDom) | 
|---|
| 2599 | arc = ERROR_INVALID_PARAMETER; | 
|---|
| 2600 | else | 
|---|
| 2601 | { | 
|---|
| 2602 | // go parse then | 
|---|
| 2603 | if (!XML_Parse(pDom->pParser, | 
|---|
| 2604 | pcszBuf, | 
|---|
| 2605 | cb, | 
|---|
| 2606 | fIsLast)) | 
|---|
| 2607 | { | 
|---|
| 2608 | // expat parsing error: | 
|---|
| 2609 | xmlSetError(pDom, | 
|---|
| 2610 | XML_GetErrorCode(pDom->pParser), | 
|---|
| 2611 | NULL, | 
|---|
| 2612 | FALSE); | 
|---|
| 2613 |  | 
|---|
| 2614 | if (pDom->pDocumentNode) | 
|---|
| 2615 | { | 
|---|
| 2616 | xmlDeleteNode((PNODEBASE)pDom->pDocumentNode); | 
|---|
| 2617 | pDom->pDocumentNode = NULL; | 
|---|
| 2618 | } | 
|---|
| 2619 |  | 
|---|
| 2620 | arc = ERROR_DOM_PARSING; | 
|---|
| 2621 | } | 
|---|
| 2622 | else if (pDom->fInvalid) | 
|---|
| 2623 | { | 
|---|
| 2624 | // expat was doing OK, but the handlers' validation failed: | 
|---|
| 2625 | arc = ERROR_DOM_VALIDITY; | 
|---|
| 2626 | // error info has already been set | 
|---|
| 2627 | } | 
|---|
| 2628 | else | 
|---|
| 2629 | // expat was doing OK, but maybe we have integrity errors | 
|---|
| 2630 | // from our DOM callbacks: | 
|---|
| 2631 | if (pDom->arcDOM) | 
|---|
| 2632 | arc = pDom->arcDOM; | 
|---|
| 2633 |  | 
|---|
| 2634 | if (arc != NO_ERROR || fIsLast) | 
|---|
| 2635 | { | 
|---|
| 2636 | // last call or error: clean up | 
|---|
| 2637 | XML_ParserFree(pDom->pParser); | 
|---|
| 2638 | pDom->pParser = NULL; | 
|---|
| 2639 |  | 
|---|
| 2640 | // clean up the stack (but not the DOM itself) | 
|---|
| 2641 | lstClear(&pDom->llElementStack); | 
|---|
| 2642 | } | 
|---|
| 2643 | } | 
|---|
| 2644 |  | 
|---|
| 2645 | return arc; | 
|---|
| 2646 | } | 
|---|
| 2647 |  | 
|---|
| 2648 | #ifdef __DEBUG__ | 
|---|
| 2649 |  | 
|---|
| 2650 | /* | 
|---|
| 2651 | *@@ Dump: | 
|---|
| 2652 | * | 
|---|
| 2653 | *@@added V0.9.20 (2002-07-03) [umoeller] | 
|---|
| 2654 | */ | 
|---|
| 2655 |  | 
|---|
| 2656 | STATIC VOID Dump(int iIndent, | 
|---|
| 2657 | PDOMNODE pDomNode) | 
|---|
| 2658 | { | 
|---|
| 2659 | PLISTNODE pChildNode; | 
|---|
| 2660 | int i; | 
|---|
| 2661 | for (i = 0; | 
|---|
| 2662 | i < iIndent; | 
|---|
| 2663 | ++i) | 
|---|
| 2664 | { | 
|---|
| 2665 | printf(" "); | 
|---|
| 2666 | } | 
|---|
| 2667 |  | 
|---|
| 2668 | switch (pDomNode->NodeBase.ulNodeType) | 
|---|
| 2669 | { | 
|---|
| 2670 | #define DUMPTYPE(t) case t: printf(#t); break; | 
|---|
| 2671 | DUMPTYPE(DOMNODE_ELEMENT) | 
|---|
| 2672 | DUMPTYPE(DOMNODE_ATTRIBUTE) | 
|---|
| 2673 | DUMPTYPE(DOMNODE_TEXT) | 
|---|
| 2674 | DUMPTYPE(DOMNODE_PROCESSING_INSTRUCTION) | 
|---|
| 2675 | DUMPTYPE(DOMNODE_COMMENT) | 
|---|
| 2676 | DUMPTYPE(DOMNODE_DOCUMENT) | 
|---|
| 2677 | DUMPTYPE(DOMNODE_DOCUMENT_TYPE) | 
|---|
| 2678 | DUMPTYPE(ELEMENTPARTICLE_EMPTY) | 
|---|
| 2679 | DUMPTYPE(ELEMENTPARTICLE_ANY) | 
|---|
| 2680 | DUMPTYPE(ELEMENTPARTICLE_MIXED) | 
|---|
| 2681 | DUMPTYPE(ELEMENTPARTICLE_CHOICE) | 
|---|
| 2682 | DUMPTYPE(ELEMENTPARTICLE_SEQ) | 
|---|
| 2683 | DUMPTYPE(ELEMENTPARTICLE_NAME) | 
|---|
| 2684 | DUMPTYPE(ATTRIBUTE_DECLARATION_BASE) | 
|---|
| 2685 | DUMPTYPE(ATTRIBUTE_DECLARATION) | 
|---|
| 2686 | DUMPTYPE(ATTRIBUTE_DECLARATION_ENUM) | 
|---|
| 2687 | } | 
|---|
| 2688 |  | 
|---|
| 2689 | printf(" \"%s\"\n", STRINGORNULL(pDomNode->NodeBase.strNodeName.psz)); | 
|---|
| 2690 |  | 
|---|
| 2691 | ++iIndent; | 
|---|
| 2692 | for (pChildNode = lstQueryFirstNode(&pDomNode->llChildren); | 
|---|
| 2693 | pChildNode; | 
|---|
| 2694 | pChildNode = pChildNode->pNext) | 
|---|
| 2695 | { | 
|---|
| 2696 | Dump(iIndent, (PDOMNODE)pChildNode->pItemData); | 
|---|
| 2697 | } | 
|---|
| 2698 | --iIndent; | 
|---|
| 2699 | } | 
|---|
| 2700 |  | 
|---|
| 2701 | #endif | 
|---|
| 2702 |  | 
|---|
| 2703 | /* | 
|---|
| 2704 | *@@ xmlDump: | 
|---|
| 2705 | *      debug function which dumps the DOM to stdout. | 
|---|
| 2706 | * | 
|---|
| 2707 | *@@added V0.9.20 (2002-07-03) [umoeller] | 
|---|
| 2708 | */ | 
|---|
| 2709 |  | 
|---|
| 2710 | VOID xmlDump(PXMLDOM pDom) | 
|---|
| 2711 | { | 
|---|
| 2712 | #ifdef __DEBUG__ | 
|---|
| 2713 | if (!pDom) | 
|---|
| 2714 | { | 
|---|
| 2715 | printf(__FUNCTION__ ": pDom is NULL\n"); | 
|---|
| 2716 | return; | 
|---|
| 2717 | } | 
|---|
| 2718 |  | 
|---|
| 2719 | printf(__FUNCTION__ ": dumping document node "); | 
|---|
| 2720 |  | 
|---|
| 2721 | Dump(0, (PDOMNODE)pDom->pDocumentNode); | 
|---|
| 2722 | #endif | 
|---|
| 2723 | } | 
|---|
| 2724 |  | 
|---|
| 2725 | /* | 
|---|
| 2726 | *@@ xmlFreeDOM: | 
|---|
| 2727 | *      cleans up all resources allocated by | 
|---|
| 2728 | *      xmlCreateDOM and xmlParse, including | 
|---|
| 2729 | *      the entire DOM tree. | 
|---|
| 2730 | * | 
|---|
| 2731 | *      If you wish to keep any data, make | 
|---|
| 2732 | *      a copy of the respective pointers in pDom | 
|---|
| 2733 | *      or subitems and set them to NULL before | 
|---|
| 2734 | *      calling this function. | 
|---|
| 2735 | * | 
|---|
| 2736 | *@@added V0.9.9 (2001-02-14) [umoeller] | 
|---|
| 2737 | */ | 
|---|
| 2738 |  | 
|---|
| 2739 | APIRET xmlFreeDOM(PXMLDOM pDom) | 
|---|
| 2740 | { | 
|---|
| 2741 | APIRET arc = NO_ERROR; | 
|---|
| 2742 | if (pDom) | 
|---|
| 2743 | { | 
|---|
| 2744 | // if the parser is still alive for some reason, close it. | 
|---|
| 2745 | if (pDom->pParser) | 
|---|
| 2746 | { | 
|---|
| 2747 | XML_ParserFree(pDom->pParser); | 
|---|
| 2748 | pDom->pParser = NULL; | 
|---|
| 2749 | } | 
|---|
| 2750 |  | 
|---|
| 2751 | xmlDeleteNode((PNODEBASE)pDom->pDocumentNode); | 
|---|
| 2752 |  | 
|---|
| 2753 | if (pDom->pxstrSystemID) | 
|---|
| 2754 | xstrFree(&pDom->pxstrSystemID); | 
|---|
| 2755 | if (pDom->pxstrFailingNode) | 
|---|
| 2756 | xstrFree(&pDom->pxstrFailingNode); | 
|---|
| 2757 |  | 
|---|
| 2758 | lstClear(&pDom->llElementStack); | 
|---|
| 2759 |  | 
|---|
| 2760 | free(pDom); | 
|---|
| 2761 | } | 
|---|
| 2762 |  | 
|---|
| 2763 | return arc; | 
|---|
| 2764 | } | 
|---|
| 2765 |  | 
|---|
| 2766 | /* ****************************************************************** | 
|---|
| 2767 | * | 
|---|
| 2768 | *   DOM lookup | 
|---|
| 2769 | * | 
|---|
| 2770 | ********************************************************************/ | 
|---|
| 2771 |  | 
|---|
| 2772 | /* | 
|---|
| 2773 | *@@ xmlFindElementDecl: | 
|---|
| 2774 | *      returns the CMELEMENTDECLNODE for the element | 
|---|
| 2775 | *      with the specified name or NULL if there's none. | 
|---|
| 2776 | * | 
|---|
| 2777 | *@@added V0.9.9 (2001-02-16) [umoeller] | 
|---|
| 2778 | */ | 
|---|
| 2779 |  | 
|---|
| 2780 | PCMELEMENTDECLNODE xmlFindElementDecl(PXMLDOM pDom, | 
|---|
| 2781 | const XSTRING *pcstrElementName) | 
|---|
| 2782 | { | 
|---|
| 2783 | PCMELEMENTDECLNODE pElementDecl = NULL; | 
|---|
| 2784 |  | 
|---|
| 2785 | PDOMDOCTYPENODE pDocTypeNode = pDom->pDocTypeNode; | 
|---|
| 2786 | if (    (pDocTypeNode) | 
|---|
| 2787 | && (pcstrElementName) | 
|---|
| 2788 | && (pcstrElementName->ulLength) | 
|---|
| 2789 | ) | 
|---|
| 2790 | { | 
|---|
| 2791 | pElementDecl = (PCMELEMENTDECLNODE)treeFind( | 
|---|
| 2792 | pDocTypeNode->ElementDeclsTree, | 
|---|
| 2793 | (ULONG)pcstrElementName, | 
|---|
| 2794 | CompareXStrings); | 
|---|
| 2795 | } | 
|---|
| 2796 |  | 
|---|
| 2797 | return (pElementDecl); | 
|---|
| 2798 | } | 
|---|
| 2799 |  | 
|---|
| 2800 | /* | 
|---|
| 2801 | *@@ xmlFindAttribDeclBase: | 
|---|
| 2802 | *      returns the CMATTRIBUTEDECLBASE for the specified | 
|---|
| 2803 | *      element name, or NULL if none exists. | 
|---|
| 2804 | * | 
|---|
| 2805 | *      To find a specific attribute declaration from both | 
|---|
| 2806 | *      an element and an attribute name, use xmlFindAttribDecl | 
|---|
| 2807 | *      instead. | 
|---|
| 2808 | * | 
|---|
| 2809 | *@@added V0.9.9 (2001-02-16) [umoeller] | 
|---|
| 2810 | */ | 
|---|
| 2811 |  | 
|---|
| 2812 | PCMATTRIBUTEDECLBASE xmlFindAttribDeclBase(PXMLDOM pDom, | 
|---|
| 2813 | const XSTRING *pstrElementName) | 
|---|
| 2814 | { | 
|---|
| 2815 | PDOMDOCTYPENODE pDocTypeNode = pDom->pDocTypeNode; | 
|---|
| 2816 | if (    (pDocTypeNode) | 
|---|
| 2817 | && (pstrElementName) | 
|---|
| 2818 | && (pstrElementName->ulLength) | 
|---|
| 2819 | ) | 
|---|
| 2820 | { | 
|---|
| 2821 | return ((PCMATTRIBUTEDECLBASE)treeFind( | 
|---|
| 2822 | pDocTypeNode->AttribDeclBasesTree, | 
|---|
| 2823 | (ULONG)pstrElementName, | 
|---|
| 2824 | CompareXStrings)); | 
|---|
| 2825 | } | 
|---|
| 2826 |  | 
|---|
| 2827 | return NULL; | 
|---|
| 2828 | } | 
|---|
| 2829 |  | 
|---|
| 2830 | /* | 
|---|
| 2831 | *@@ xmlFindAttribDecl: | 
|---|
| 2832 | *      returns the CMATTRIBUTEDEDECL for the specified | 
|---|
| 2833 | *      element and attribute name, or NULL if none exists. | 
|---|
| 2834 | * | 
|---|
| 2835 | *@@added V0.9.9 (2001-02-16) [umoeller] | 
|---|
| 2836 | */ | 
|---|
| 2837 |  | 
|---|
| 2838 | PCMATTRIBUTEDECL xmlFindAttribDecl(PXMLDOM pDom, | 
|---|
| 2839 | const XSTRING *pstrElementName, | 
|---|
| 2840 | const XSTRING *pstrAttribName, | 
|---|
| 2841 | PCMATTRIBUTEDECLBASE *ppAttribDeclBase) | 
|---|
| 2842 | // in/out: attr decl base cache; | 
|---|
| 2843 | // the pointer pointed to by this | 
|---|
| 2844 | // must be NULL on the first call | 
|---|
| 2845 | { | 
|---|
| 2846 | if (pstrElementName && pstrAttribName) | 
|---|
| 2847 | { | 
|---|
| 2848 | if (!*ppAttribDeclBase) | 
|---|
| 2849 | // first call for this: | 
|---|
| 2850 | *ppAttribDeclBase = xmlFindAttribDeclBase(pDom, | 
|---|
| 2851 | pstrElementName); | 
|---|
| 2852 | if (*ppAttribDeclBase) | 
|---|
| 2853 | { | 
|---|
| 2854 | return ((PCMATTRIBUTEDECL)treeFind( | 
|---|
| 2855 | ((**ppAttribDeclBase).AttribDeclsTree), | 
|---|
| 2856 | (ULONG)pstrAttribName, | 
|---|
| 2857 | CompareXStrings)); | 
|---|
| 2858 | } | 
|---|
| 2859 | } | 
|---|
| 2860 |  | 
|---|
| 2861 | return NULL; | 
|---|
| 2862 | } | 
|---|
| 2863 |  | 
|---|
| 2864 | /* | 
|---|
| 2865 | *@@ xmlGetRootElement: | 
|---|
| 2866 | *      returns the root element node from the specified | 
|---|
| 2867 | *      DOM. Useful helper to start enumerating elements. | 
|---|
| 2868 | * | 
|---|
| 2869 | *@@added V0.9.11 (2001-04-22) [umoeller] | 
|---|
| 2870 | *@@changed V0.9.20 (2002-07-03) [umoeller]: this never worked with DTDs, fixed | 
|---|
| 2871 | */ | 
|---|
| 2872 |  | 
|---|
| 2873 | PDOMNODE xmlGetRootElement(PXMLDOM pDom) | 
|---|
| 2874 | { | 
|---|
| 2875 | PDOMDOCUMENTNODE    pDocumentNode; | 
|---|
| 2876 | PLISTNODE           pListNode; | 
|---|
| 2877 | if (    (pDom) | 
|---|
| 2878 | && (pDocumentNode = pDom->pDocumentNode) | 
|---|
| 2879 | && (pListNode = lstQueryFirstNode(&pDocumentNode->DomNode.llChildren)) | 
|---|
| 2880 | ) | 
|---|
| 2881 | { | 
|---|
| 2882 | // V0.9.20 (2002-07-03) [umoeller]: | 
|---|
| 2883 | // we can't just return the first node on the | 
|---|
| 2884 | // list, because if we have DTD, this might | 
|---|
| 2885 | // be the doctype... so loop until we find | 
|---|
| 2886 | // an element, which must be the root element | 
|---|
| 2887 | while (pListNode) | 
|---|
| 2888 | { | 
|---|
| 2889 | PDOMNODE pDomNode = (PDOMNODE)pListNode->pItemData; | 
|---|
| 2890 | if (pDomNode->NodeBase.ulNodeType == DOMNODE_ELEMENT) | 
|---|
| 2891 | return (pDomNode); | 
|---|
| 2892 |  | 
|---|
| 2893 | pListNode = pListNode->pNext; | 
|---|
| 2894 | } | 
|---|
| 2895 | } | 
|---|
| 2896 |  | 
|---|
| 2897 | return NULL; | 
|---|
| 2898 | } | 
|---|
| 2899 |  | 
|---|
| 2900 | /* | 
|---|
| 2901 | *@@ xmlGetFirstChild: | 
|---|
| 2902 | *      returns the first child node of pDomNode. | 
|---|
| 2903 | *      See DOMNODE for what a "child" can be for the | 
|---|
| 2904 | *      various node types. | 
|---|
| 2905 | * | 
|---|
| 2906 | *@@added V0.9.9 (2001-02-14) [umoeller] | 
|---|
| 2907 | */ | 
|---|
| 2908 |  | 
|---|
| 2909 | PDOMNODE xmlGetFirstChild(PDOMNODE pDomNode) | 
|---|
| 2910 | { | 
|---|
| 2911 | PLISTNODE pListNode = lstQueryFirstNode(&pDomNode->llChildren); | 
|---|
| 2912 | if (pListNode) | 
|---|
| 2913 | return ((PDOMNODE)pListNode->pItemData); | 
|---|
| 2914 |  | 
|---|
| 2915 | return (0); | 
|---|
| 2916 | } | 
|---|
| 2917 |  | 
|---|
| 2918 | /* | 
|---|
| 2919 | *@@ xmlGetLastChild: | 
|---|
| 2920 | *      returns the last child node of pDomNode. | 
|---|
| 2921 | *      See DOMNODE for what a "child" can be for the | 
|---|
| 2922 | *      various node types. | 
|---|
| 2923 | * | 
|---|
| 2924 | *@@added V0.9.9 (2001-02-14) [umoeller] | 
|---|
| 2925 | */ | 
|---|
| 2926 |  | 
|---|
| 2927 | PDOMNODE xmlGetLastChild(PDOMNODE pDomNode) | 
|---|
| 2928 | { | 
|---|
| 2929 | PLISTNODE pListNode = lstQueryLastNode(&pDomNode->llChildren); | 
|---|
| 2930 | if (pListNode) | 
|---|
| 2931 | return ((PDOMNODE)pListNode->pItemData); | 
|---|
| 2932 |  | 
|---|
| 2933 | return (0); | 
|---|
| 2934 | } | 
|---|
| 2935 |  | 
|---|
| 2936 | /* | 
|---|
| 2937 | *@@ xmlGetFirstText: | 
|---|
| 2938 | *      returns the first text (character data) node | 
|---|
| 2939 | *      of pElement or NULL if there's none. | 
|---|
| 2940 | * | 
|---|
| 2941 | *@@added V0.9.11 (2001-04-22) [umoeller] | 
|---|
| 2942 | */ | 
|---|
| 2943 |  | 
|---|
| 2944 | PDOMNODE xmlGetFirstText(PDOMNODE pElement) | 
|---|
| 2945 | { | 
|---|
| 2946 | PLISTNODE   pNode; | 
|---|
| 2947 | PDOMNODE    pDomNodeThis; | 
|---|
| 2948 |  | 
|---|
| 2949 | for (pNode = lstQueryFirstNode(&pElement->llChildren); | 
|---|
| 2950 | pNode; | 
|---|
| 2951 | pNode = pNode->pNext) | 
|---|
| 2952 | { | 
|---|
| 2953 | if (    (pDomNodeThis = (PDOMNODE)pNode->pItemData) | 
|---|
| 2954 | && (pDomNodeThis->NodeBase.ulNodeType == DOMNODE_TEXT) | 
|---|
| 2955 | ) | 
|---|
| 2956 | return (pDomNodeThis); | 
|---|
| 2957 | } | 
|---|
| 2958 |  | 
|---|
| 2959 | return NULL; | 
|---|
| 2960 | } | 
|---|
| 2961 |  | 
|---|
| 2962 | /* | 
|---|
| 2963 | *@@ xmlGetElementsByTagName: | 
|---|
| 2964 | *      returns a linked list of DOMNODE nodes which | 
|---|
| 2965 | *      match the specified element name. The special name | 
|---|
| 2966 | *      "*" matches all elements. | 
|---|
| 2967 | * | 
|---|
| 2968 | *      pParent must be the parent element DOMNODE... | 
|---|
| 2969 | *      the only allowed exception is | 
|---|
| 2970 | * | 
|---|
| 2971 | *      The caller must free the list by calling lstFree. | 
|---|
| 2972 | *      Returns NULL if no such elements could be found. | 
|---|
| 2973 | * | 
|---|
| 2974 | *@@added V0.9.9 (2001-02-14) [umoeller] | 
|---|
| 2975 | */ | 
|---|
| 2976 |  | 
|---|
| 2977 | PLINKLIST xmlGetElementsByTagName(PDOMNODE pParent, | 
|---|
| 2978 | const char *pcszName) | 
|---|
| 2979 | { | 
|---|
| 2980 | PLINKLIST pll = lstCreate(FALSE);       // no free | 
|---|
| 2981 | if (pll) | 
|---|
| 2982 | { | 
|---|
| 2983 | ULONG   cItems = 0; | 
|---|
| 2984 | BOOL    fFindAll = !strcmp(pcszName, "*"); | 
|---|
| 2985 |  | 
|---|
| 2986 | PLISTNODE   pNode; | 
|---|
| 2987 | PDOMNODE    pDomNodeThis; | 
|---|
| 2988 |  | 
|---|
| 2989 | for (pNode = lstQueryFirstNode(&pParent->llChildren); | 
|---|
| 2990 | pNode; | 
|---|
| 2991 | pNode = pNode->pNext) | 
|---|
| 2992 | { | 
|---|
| 2993 | if (    (pDomNodeThis = (PDOMNODE)pNode->pItemData) | 
|---|
| 2994 | && (pDomNodeThis->NodeBase.ulNodeType == DOMNODE_ELEMENT) | 
|---|
| 2995 | && (    (fFindAll) | 
|---|
| 2996 | || (!strcmp(pcszName, pDomNodeThis->NodeBase.strNodeName.psz)) | 
|---|
| 2997 | ) | 
|---|
| 2998 | ) | 
|---|
| 2999 | { | 
|---|
| 3000 | // element matches: | 
|---|
| 3001 | lstAppendItem(pll, pDomNodeThis); | 
|---|
| 3002 | cItems++; | 
|---|
| 3003 | } | 
|---|
| 3004 | } | 
|---|
| 3005 |  | 
|---|
| 3006 | if (cItems) | 
|---|
| 3007 | return (pll); | 
|---|
| 3008 | else | 
|---|
| 3009 | lstFree(&pll); | 
|---|
| 3010 | } | 
|---|
| 3011 |  | 
|---|
| 3012 | return (0); | 
|---|
| 3013 | } | 
|---|
| 3014 |  | 
|---|
| 3015 | /* | 
|---|
| 3016 | *@@ xmlGetAttribute: | 
|---|
| 3017 | *      returns the value of pElement's attribute | 
|---|
| 3018 | *      with the given name or NULL. | 
|---|
| 3019 | * | 
|---|
| 3020 | *      This is a const pointer into the element's | 
|---|
| 3021 | *      attribute list. | 
|---|
| 3022 | * | 
|---|
| 3023 | *@@added V0.9.11 (2001-04-22) [umoeller] | 
|---|
| 3024 | */ | 
|---|
| 3025 |  | 
|---|
| 3026 | const XSTRING* xmlGetAttribute(PDOMNODE pElement, | 
|---|
| 3027 | const char *pcszAttribName) | 
|---|
| 3028 | { | 
|---|
| 3029 | XSTRING str; | 
|---|
| 3030 | PDOMNODE pAttrNode; | 
|---|
| 3031 | xstrInitSet(&str, (PSZ)pcszAttribName); | 
|---|
| 3032 | // note, cheap trick: no malloc here, but we need | 
|---|
| 3033 | // an XSTRING for treeFind | 
|---|
| 3034 | if (pAttrNode = (PDOMNODE)treeFind(pElement->AttributesMap, | 
|---|
| 3035 | (ULONG)&str, | 
|---|
| 3036 | CompareXStrings)) | 
|---|
| 3037 | return (pAttrNode->pstrNodeValue); | 
|---|
| 3038 |  | 
|---|
| 3039 | return NULL; | 
|---|
| 3040 | } | 
|---|
| 3041 |  | 
|---|
| 3042 | /* ****************************************************************** | 
|---|
| 3043 | * | 
|---|
| 3044 | *   DOM build | 
|---|
| 3045 | * | 
|---|
| 3046 | ********************************************************************/ | 
|---|
| 3047 |  | 
|---|
| 3048 | /* | 
|---|
| 3049 | *@@ xmlCreateDocument: | 
|---|
| 3050 | *      creates a new XML document. | 
|---|
| 3051 | * | 
|---|
| 3052 | *      This is the first step in creating a DOM | 
|---|
| 3053 | *      tree in memory. | 
|---|
| 3054 | * | 
|---|
| 3055 | *      This function creates a DOCUMENT node | 
|---|
| 3056 | *      (which is returned) and a root ELEMENT node | 
|---|
| 3057 | *      within the document. For convenience, the | 
|---|
| 3058 | *      root ELEMENT node is returned as well. | 
|---|
| 3059 | * | 
|---|
| 3060 | *      This does not create a DOCTYPE node in | 
|---|
| 3061 | *      the document. | 
|---|
| 3062 | * | 
|---|
| 3063 | *      After this, you can add sub-elements and | 
|---|
| 3064 | *      attributes to the root element using | 
|---|
| 3065 | *      xmlCreateElementNode and xmlCreateAttributeNode. | 
|---|
| 3066 | * | 
|---|
| 3067 | *      Use xmlDeleteNode on the DOCUMENT node | 
|---|
| 3068 | *      to delete the entire DOM tree. | 
|---|
| 3069 | * | 
|---|
| 3070 | *@@added V0.9.12 (2001-05-21) [umoeller] | 
|---|
| 3071 | */ | 
|---|
| 3072 |  | 
|---|
| 3073 | APIRET xmlCreateDocument(const char *pcszRootElementName,   // in: root element name | 
|---|
| 3074 | PDOMDOCUMENTNODE *ppDocument,      // out: DOCUMENT node | 
|---|
| 3075 | PDOMNODE *ppRootElement)           // out: root ELEMENT node within DOCUMENT | 
|---|
| 3076 | { | 
|---|
| 3077 | APIRET arc = NO_ERROR; | 
|---|
| 3078 | PDOMDOCUMENTNODE pDocument = NULL; | 
|---|
| 3079 | PDOMNODE pRootElement = NULL; | 
|---|
| 3080 |  | 
|---|
| 3081 | if ( (!pcszRootElementName) || (!ppDocument) || (!ppRootElement) ) | 
|---|
| 3082 | arc = ERROR_INVALID_PARAMETER; | 
|---|
| 3083 | else | 
|---|
| 3084 | // create the document node | 
|---|
| 3085 | if (!(arc = xmlCreateDomNode(NULL, // no parent | 
|---|
| 3086 | DOMNODE_DOCUMENT, | 
|---|
| 3087 | NULL, | 
|---|
| 3088 | 0, | 
|---|
| 3089 | (PDOMNODE*)&pDocument))) | 
|---|
| 3090 | if (!(arc = xmlCreateDomNode((PDOMNODE)pDocument,     // parent | 
|---|
| 3091 | DOMNODE_ELEMENT, | 
|---|
| 3092 | pcszRootElementName, | 
|---|
| 3093 | 0, | 
|---|
| 3094 | &pRootElement))) | 
|---|
| 3095 | { | 
|---|
| 3096 | *ppDocument = pDocument; | 
|---|
| 3097 | *ppRootElement = pRootElement; | 
|---|
| 3098 | } | 
|---|
| 3099 |  | 
|---|
| 3100 | return arc; | 
|---|
| 3101 | } | 
|---|
| 3102 |  | 
|---|
| 3103 | /* | 
|---|
| 3104 | *@@ ESCAPES: | 
|---|
| 3105 | * | 
|---|
| 3106 | *@@added V1.0.0 (2002-08-21) [umoeller] | 
|---|
| 3107 | */ | 
|---|
| 3108 |  | 
|---|
| 3109 | typedef struct _ESCAPES | 
|---|
| 3110 | { | 
|---|
| 3111 | XSTRING strQuot1,       // " | 
|---|
| 3112 | strQuot2,       // " | 
|---|
| 3113 | strAmp1,        // & | 
|---|
| 3114 | strAmp2,        // & | 
|---|
| 3115 | strLT1,         // < | 
|---|
| 3116 | strLT2,         // < | 
|---|
| 3117 | strGT1,         // > | 
|---|
| 3118 | strGT2;         // > | 
|---|
| 3119 |  | 
|---|
| 3120 | XSTRING strTemp;        // temp buffer | 
|---|
| 3121 |  | 
|---|
| 3122 | } ESCAPES, *PESCAPES; | 
|---|
| 3123 |  | 
|---|
| 3124 | /* | 
|---|
| 3125 | *@@ DoEscapes: | 
|---|
| 3126 | * | 
|---|
| 3127 | *@@added V1.0.0 (2002-08-21) [umoeller] | 
|---|
| 3128 | */ | 
|---|
| 3129 |  | 
|---|
| 3130 | VOID DoEscapes(PESCAPES pEscapes, | 
|---|
| 3131 | BOOL fQuotesToo) | 
|---|
| 3132 | { | 
|---|
| 3133 | ULONG   ulOfs; | 
|---|
| 3134 | size_t  ShiftTable[256]; | 
|---|
| 3135 | BOOL    fRepeat; | 
|---|
| 3136 |  | 
|---|
| 3137 | if (fQuotesToo) | 
|---|
| 3138 | { | 
|---|
| 3139 | ulOfs = 0; | 
|---|
| 3140 | fRepeat = FALSE; | 
|---|
| 3141 | while (xstrFindReplace(&pEscapes->strTemp, | 
|---|
| 3142 | &ulOfs, | 
|---|
| 3143 | &pEscapes->strQuot1, | 
|---|
| 3144 | &pEscapes->strQuot2, | 
|---|
| 3145 | ShiftTable, | 
|---|
| 3146 | &fRepeat)) | 
|---|
| 3147 | ; | 
|---|
| 3148 | } | 
|---|
| 3149 |  | 
|---|
| 3150 | ulOfs = 0; | 
|---|
| 3151 | fRepeat = FALSE; | 
|---|
| 3152 | while (xstrFindReplace(&pEscapes->strTemp, | 
|---|
| 3153 | &ulOfs, | 
|---|
| 3154 | &pEscapes->strLT1, | 
|---|
| 3155 | &pEscapes->strLT2, | 
|---|
| 3156 | ShiftTable, | 
|---|
| 3157 | &fRepeat)) | 
|---|
| 3158 | ; | 
|---|
| 3159 |  | 
|---|
| 3160 | ulOfs = 0; | 
|---|
| 3161 | fRepeat = FALSE; | 
|---|
| 3162 | while (xstrFindReplace(&pEscapes->strTemp, | 
|---|
| 3163 | &ulOfs, | 
|---|
| 3164 | &pEscapes->strGT1, | 
|---|
| 3165 | &pEscapes->strGT2, | 
|---|
| 3166 | ShiftTable, | 
|---|
| 3167 | &fRepeat)) | 
|---|
| 3168 | ; | 
|---|
| 3169 |  | 
|---|
| 3170 | // replace ampersands last | 
|---|
| 3171 | ulOfs = 0; | 
|---|
| 3172 | fRepeat = FALSE; | 
|---|
| 3173 | while (xstrFindReplace(&pEscapes->strTemp, | 
|---|
| 3174 | &ulOfs, | 
|---|
| 3175 | &pEscapes->strAmp1, | 
|---|
| 3176 | &pEscapes->strAmp2, | 
|---|
| 3177 | ShiftTable, | 
|---|
| 3178 | &fRepeat)) | 
|---|
| 3179 | ; | 
|---|
| 3180 | } | 
|---|
| 3181 |  | 
|---|
| 3182 | /* | 
|---|
| 3183 | *@@ WriteNodes: | 
|---|
| 3184 | *      internal helper for writing out the nodes. | 
|---|
| 3185 | *      This recurses. | 
|---|
| 3186 | * | 
|---|
| 3187 | *@@added V0.9.12 (2001-05-21) [umoeller] | 
|---|
| 3188 | *@@changed V1.0.0 (2002-08-21) [umoeller]: changed prototype, fixed unescaped characters in attributes and content | 
|---|
| 3189 | */ | 
|---|
| 3190 |  | 
|---|
| 3191 | STATIC VOID WriteNodes(PXSTRING pxstr, | 
|---|
| 3192 | PESCAPES pEscapes, | 
|---|
| 3193 | PDOMNODE pDomNode)       // in: node whose children are to be written (initially DOCUMENT) | 
|---|
| 3194 | { | 
|---|
| 3195 | PLISTNODE pListNode; | 
|---|
| 3196 |  | 
|---|
| 3197 | BOOL fMixedContent = (xmlGetFirstText(pDomNode) != NULL); | 
|---|
| 3198 |  | 
|---|
| 3199 | for (pListNode = lstQueryFirstNode(&pDomNode->llChildren); | 
|---|
| 3200 | (pListNode); | 
|---|
| 3201 | pListNode = pListNode->pNext) | 
|---|
| 3202 | { | 
|---|
| 3203 | PDOMNODE pChildNode = (PDOMNODE)pListNode->pItemData; | 
|---|
| 3204 |  | 
|---|
| 3205 | switch (pChildNode->NodeBase.ulNodeType) | 
|---|
| 3206 | { | 
|---|
| 3207 | case DOMNODE_ELEMENT: | 
|---|
| 3208 | { | 
|---|
| 3209 | PDOMNODE pAttribNode; | 
|---|
| 3210 | // write out opening ELEMENT tag | 
|---|
| 3211 | // add a line break if this does NOT have mixed | 
|---|
| 3212 | // content | 
|---|
| 3213 | if (!fMixedContent) | 
|---|
| 3214 | xstrcatc(pxstr, '\n'); | 
|---|
| 3215 |  | 
|---|
| 3216 | xstrcatc(pxstr, '<'); | 
|---|
| 3217 | xstrcats(pxstr, &pChildNode->NodeBase.strNodeName); | 
|---|
| 3218 |  | 
|---|
| 3219 | // go through attributes | 
|---|
| 3220 | for (pAttribNode = (PDOMNODE)treeFirst(pChildNode->AttributesMap); | 
|---|
| 3221 | (pAttribNode); | 
|---|
| 3222 | pAttribNode = (PDOMNODE)treeNext((TREE*)pAttribNode)) | 
|---|
| 3223 | { | 
|---|
| 3224 | xstrcat(pxstr, "\n    ", 0); | 
|---|
| 3225 | xstrcats(pxstr, &pAttribNode->NodeBase.strNodeName); | 
|---|
| 3226 | xstrcat(pxstr, "=\"", 0); | 
|---|
| 3227 |  | 
|---|
| 3228 | // copy attribute value to temp buffer first | 
|---|
| 3229 | // so we can escape quotes and ampersands | 
|---|
| 3230 | // V1.0.0 (2002-08-21) [umoeller] | 
|---|
| 3231 | xstrcpys(&pEscapes->strTemp, pAttribNode->pstrNodeValue); | 
|---|
| 3232 |  | 
|---|
| 3233 | DoEscapes(pEscapes, | 
|---|
| 3234 | TRUE);        // quotes too | 
|---|
| 3235 |  | 
|---|
| 3236 | // alright, use that | 
|---|
| 3237 | xstrcats(pxstr, &pEscapes->strTemp); | 
|---|
| 3238 | xstrcatc(pxstr, '\"'); | 
|---|
| 3239 | } | 
|---|
| 3240 |  | 
|---|
| 3241 | // now check... do we have child nodes? | 
|---|
| 3242 | if (lstCountItems(&pChildNode->llChildren)) | 
|---|
| 3243 | { | 
|---|
| 3244 | // yes: | 
|---|
| 3245 | xstrcatc(pxstr, '>'); | 
|---|
| 3246 |  | 
|---|
| 3247 | // recurse into this child element | 
|---|
| 3248 | WriteNodes(pxstr, pEscapes, pChildNode); | 
|---|
| 3249 |  | 
|---|
| 3250 | if (!fMixedContent) | 
|---|
| 3251 | xstrcatc(pxstr, '\n'); | 
|---|
| 3252 |  | 
|---|
| 3253 | // write closing tag | 
|---|
| 3254 | xstrcat(pxstr, "</", 0); | 
|---|
| 3255 | xstrcats(pxstr, &pChildNode->NodeBase.strNodeName); | 
|---|
| 3256 | xstrcatc(pxstr, '>'); | 
|---|
| 3257 | } | 
|---|
| 3258 | else | 
|---|
| 3259 | { | 
|---|
| 3260 | // no child nodes: | 
|---|
| 3261 | // mark this tag as "empty" | 
|---|
| 3262 | xstrcat(pxstr, "/>", 0); | 
|---|
| 3263 | } | 
|---|
| 3264 | } | 
|---|
| 3265 | break; | 
|---|
| 3266 |  | 
|---|
| 3267 | case DOMNODE_TEXT: | 
|---|
| 3268 | case DOMNODE_COMMENT: | 
|---|
| 3269 | // that's simple | 
|---|
| 3270 | xstrcpys(&pEscapes->strTemp, | 
|---|
| 3271 | pChildNode->pstrNodeValue); | 
|---|
| 3272 |  | 
|---|
| 3273 | DoEscapes(pEscapes,         // V1.0.0 (2002-08-21) [umoeller] | 
|---|
| 3274 | FALSE);           // quotes not | 
|---|
| 3275 |  | 
|---|
| 3276 | xstrcats(pxstr, &pEscapes->strTemp); | 
|---|
| 3277 | break; | 
|---|
| 3278 |  | 
|---|
| 3279 | case DOMNODE_DOCUMENT_TYPE: | 
|---|
| 3280 | // @@todo | 
|---|
| 3281 | break; | 
|---|
| 3282 |  | 
|---|
| 3283 | } | 
|---|
| 3284 | } | 
|---|
| 3285 | } | 
|---|
| 3286 |  | 
|---|
| 3287 | /* | 
|---|
| 3288 | *@@ xmlWriteDocument: | 
|---|
| 3289 | *      creates a complete XML document in the specified | 
|---|
| 3290 | *      string buffer from the specified DOMDOCUMENTNODE. | 
|---|
| 3291 | * | 
|---|
| 3292 | *      This creates a full XML document, starting with | 
|---|
| 3293 | *      the <?xml...?> header, the DTD (if present), | 
|---|
| 3294 | *      and the elements and attributes. | 
|---|
| 3295 | * | 
|---|
| 3296 | *      The input XSTRING must be initialized. Its | 
|---|
| 3297 | *      contents will be overwritten, if any exists. | 
|---|
| 3298 | * | 
|---|
| 3299 | *      Sooo... to write a full XML document to disk, | 
|---|
| 3300 | *      do the following: | 
|---|
| 3301 | * | 
|---|
| 3302 | *      1)  Call xmlCreateDocument to have an empty | 
|---|
| 3303 | *          document with a root element created. | 
|---|
| 3304 | * | 
|---|
| 3305 | *      2)  Add elements, subelements, and attributes | 
|---|
| 3306 | *          using xmlCreateElementNode and | 
|---|
| 3307 | *          xmlCreateAttributeNode. | 
|---|
| 3308 | * | 
|---|
| 3309 | *      3)  Call xmlWriteDocument to have the XML | 
|---|
| 3310 | *          document written into an XSTRING. | 
|---|
| 3311 | * | 
|---|
| 3312 | *      4)  Write the XSTRING to disk, e.g. using | 
|---|
| 3313 | *          fwrite(). | 
|---|
| 3314 | * | 
|---|
| 3315 | *          Note: You can also use doshWriteTextFile, | 
|---|
| 3316 | *          but you should then first convert the | 
|---|
| 3317 | *          line format using xstrConvertLineFormat. | 
|---|
| 3318 | * | 
|---|
| 3319 | *      Example: | 
|---|
| 3320 | * | 
|---|
| 3321 | +          APIRET arc = NO_ERROR; | 
|---|
| 3322 | +          PDOMDOCUMENTNODE pDocument = NULL; | 
|---|
| 3323 | +          PDOMNODE pRootElement = NULL; | 
|---|
| 3324 | + | 
|---|
| 3325 | +          // create a DOM | 
|---|
| 3326 | +          if (!(arc = xmlCreateDocument("MYROOTNODE", | 
|---|
| 3327 | +                                        &pDocument, | 
|---|
| 3328 | +                                        &pRootElement))) | 
|---|
| 3329 | +          { | 
|---|
| 3330 | +              // add subelements to the root element | 
|---|
| 3331 | +              PDOMNODE pSubelement; | 
|---|
| 3332 | +              if (!(arc = xmlCreateElementNode(pRootElement, | 
|---|
| 3333 | +                                               "MYSUBELEMENT", | 
|---|
| 3334 | +                                               &pSubelement))) | 
|---|
| 3335 | +              { | 
|---|
| 3336 | +                  // add an attribute | 
|---|
| 3337 | +                  PDOMNODE pAttribute; | 
|---|
| 3338 | +                  if (!(arc = xmlCreateAttributeNode(pSubElement, | 
|---|
| 3339 | +                                                     "MYATTRIB", | 
|---|
| 3340 | +                                                     "VALUE", | 
|---|
| 3341 | +                                                     &pAttribute))) | 
|---|
| 3342 | +                  { | 
|---|
| 3343 | +                      // alright, turn this into a string | 
|---|
| 3344 | +                      XSTRING str; | 
|---|
| 3345 | +                      xstrInit(&str, 1000); | 
|---|
| 3346 | +                      if (!(arc = xmlWriteDocument(pDocument, | 
|---|
| 3347 | +                                                   "ISO-8859-1", | 
|---|
| 3348 | +                                                   NULL,      // or DOCTYPE | 
|---|
| 3349 | +                                                   &str))) | 
|---|
| 3350 | +                      { | 
|---|
| 3351 | +                          FILE *file = fopen("myfile.xml", "w"); | 
|---|
| 3352 | +                          fwrite(str.psz, | 
|---|
| 3353 | +                                 1, | 
|---|
| 3354 | +                                 str.ulLength, | 
|---|
| 3355 | +                                 file); | 
|---|
| 3356 | +                          fclose(file); | 
|---|
| 3357 | +                      } | 
|---|
| 3358 | +                  } | 
|---|
| 3359 | +              } | 
|---|
| 3360 | + | 
|---|
| 3361 | +              // this kills the entire tree | 
|---|
| 3362 | +              xmlDeleteNode((PNODEBASE)pDocument); | 
|---|
| 3363 | + | 
|---|
| 3364 | +          } | 
|---|
| 3365 | + | 
|---|
| 3366 | * | 
|---|
| 3367 | *      A note about whitespace handling. Presently, this | 
|---|
| 3368 | *      adds line breaks after the opening tag of an | 
|---|
| 3369 | *      element if the element has element content only. | 
|---|
| 3370 | *      However, if the element has mixed content, this | 
|---|
| 3371 | *      line break is NOT automatically added because | 
|---|
| 3372 | *      white space may then be significant. | 
|---|
| 3373 | * | 
|---|
| 3374 | *@@added V0.9.12 (2001-05-21) [umoeller] | 
|---|
| 3375 | */ | 
|---|
| 3376 |  | 
|---|
| 3377 | APIRET xmlWriteDocument(PDOMDOCUMENTNODE pDocument,     // in: document node | 
|---|
| 3378 | const char *pcszEncoding,       // in: encoding string (e.g. "ISO-8859-1") | 
|---|
| 3379 | const char *pcszDoctype,        // in: entire DOCTYPE statement or NULL | 
|---|
| 3380 | PXSTRING pxstr)                 // out: document | 
|---|
| 3381 | { | 
|---|
| 3382 | APIRET arc = NO_ERROR; | 
|---|
| 3383 |  | 
|---|
| 3384 | if ( (!pDocument) || (!pcszEncoding) || (!pxstr) ) | 
|---|
| 3385 | arc = ERROR_INVALID_PARAMETER; | 
|---|
| 3386 | else | 
|---|
| 3387 | { | 
|---|
| 3388 | ESCAPES esc; | 
|---|
| 3389 |  | 
|---|
| 3390 | // <?xml version="1.0" encoding="ISO-8859-1"?> | 
|---|
| 3391 | xstrcpy(pxstr, "<?xml version=\"1.0\" encoding=\"", 0); | 
|---|
| 3392 | xstrcat(pxstr, pcszEncoding, 0); | 
|---|
| 3393 | xstrcat(pxstr, "\"?>\n", 0); | 
|---|
| 3394 |  | 
|---|
| 3395 | // write entire DOCTYPE statement | 
|---|
| 3396 | if (pcszDoctype) | 
|---|
| 3397 | { | 
|---|
| 3398 | xstrcatc(pxstr, '\n'); | 
|---|
| 3399 | xstrcat(pxstr, pcszDoctype, 0); | 
|---|
| 3400 | xstrcatc(pxstr, '\n'); | 
|---|
| 3401 | } | 
|---|
| 3402 |  | 
|---|
| 3403 | xstrInitCopy(&esc.strQuot1, "\"", 0); | 
|---|
| 3404 | xstrInitCopy(&esc.strQuot2, """, 0); | 
|---|
| 3405 | xstrInitCopy(&esc.strAmp1, "&", 0); | 
|---|
| 3406 | xstrInitCopy(&esc.strAmp2, "&", 0); | 
|---|
| 3407 | xstrInitCopy(&esc.strLT1, "<", 0); | 
|---|
| 3408 | xstrInitCopy(&esc.strLT2, "<", 0); | 
|---|
| 3409 | xstrInitCopy(&esc.strGT1, ">", 0); | 
|---|
| 3410 | xstrInitCopy(&esc.strGT2, ">", 0); | 
|---|
| 3411 |  | 
|---|
| 3412 | xstrInit(&esc.strTemp, 0);       // temp buffer | 
|---|
| 3413 |  | 
|---|
| 3414 | // write out children | 
|---|
| 3415 | WriteNodes(pxstr, &esc, (PDOMNODE)pDocument); | 
|---|
| 3416 |  | 
|---|
| 3417 | xstrClear(&esc.strQuot1); | 
|---|
| 3418 | xstrClear(&esc.strQuot2); | 
|---|
| 3419 | xstrClear(&esc.strAmp1); | 
|---|
| 3420 | xstrClear(&esc.strAmp2); | 
|---|
| 3421 | xstrClear(&esc.strLT1); | 
|---|
| 3422 | xstrClear(&esc.strLT2); | 
|---|
| 3423 | xstrClear(&esc.strGT1); | 
|---|
| 3424 | xstrClear(&esc.strGT2); | 
|---|
| 3425 |  | 
|---|
| 3426 | xstrClear(&esc.strTemp);       // temp buffer | 
|---|
| 3427 |  | 
|---|
| 3428 | xstrcatc(pxstr, '\n'); | 
|---|
| 3429 | } | 
|---|
| 3430 |  | 
|---|
| 3431 | return arc; | 
|---|
| 3432 | } | 
|---|
| 3433 |  | 
|---|
| 3434 |  | 
|---|