source: branches/branch-1-0/src/helpers/xml.c

Last change on this file was 264, checked in by pr, 21 years ago

Fix spelling errors.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 118.6 KB
RevLine 
[10]1
2/*
3 *@@sourcefile xml.c:
[35]4 * XML document handling.
[10]5 *
[35]6 * XML support in the XWorkplace Helpers is broken into two
7 * layers:
[10]8 *
[71]9 * -- The bottom layer is implemented by the @expat parser,
10 * which I have ported and hacked to the xwphelpers.
[10]11 *
[65]12 * See xmlparse.c for an introduction.
13 *
[35]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.
[10]18 *
[71]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 *
[38]27 * <B>XML</B>
[10]28 *
[38]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.
[10]32 *
[38]33 * <B>Document Object Model (DOM)</B>
[10]34 *
[38]35 * See @DOM for a general introduction.
[10]36 *
[38]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
[153]41 * is broken up into a tree of node structures only (see DOMNODE),
[38]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").
[10]45 *
[38]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.
[35]50 *
[38]51 * Most notably, there are the following differences:
[35]52 *
[71]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 *
[153]57 * -- Not all node types are implemented. See DOMNODE for
[38]58 * the supported types.
[35]59 *
[71]60 * -- Only a subset of the standardized methods is implemented,
[38]61 * and they are called differently to adhere to the xwphelpers
62 * conventions.
[35]63 *
[38]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.
[35]67 *
[38]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.
[35]72 *
[10]73 * It shouldn't be too difficult to write a C++ encapsulation
[38]74 * of this though which fully implements all the DOM methods.
[10]75 *
[38]76 * However, we do implement node management as in the standard.
[39]77 * See xmlCreateDomNode and xmlDeleteNode.
[10]78 *
[38]79 * The main entry point into this is xmlCreateDOM. See remarks
[39]80 * there for how this will be typically used.
[10]81 *
[38]82 * <B>Validation</B>
[10]83 *
[38]84 * @expat doesn't check XML documents for whether they are @valid.
85 * In other words, expat is a non-validating XML processor.
[10]86 *
[39]87 * By contrast, this pseudo-DOM implementation can validate to
88 * a certain extent.
[38]89 *
[39]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 *
[12]104 *@@header "helpers\xml.h"
[10]105 *@@added V0.9.6 (2000-10-29) [umoeller]
106 */
107
108/*
[196]109 * Copyright (C) 2000-2002 Ulrich M”ller.
[14]110 * This file is part of the "XWorkplace helpers" source package.
111 * This is free software; you can redistribute it and/or modify
[10]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
[11]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
[10]129#include <stdlib.h>
130#include <string.h>
[39]131#include <stdio.h>
[10]132
133#include "setup.h" // code generation and debugging options
134
[35]135#include "expat\expat.h"
136
[10]137#include "helpers\linklist.h"
[97]138#include "helpers\standards.h"
[10]139#include "helpers\stringh.h"
[38]140#include "helpers\tree.h"
[35]141#include "helpers\xstring.h"
[10]142#include "helpers\xml.h"
143
144#pragma hdrstop
145
146/*
[153]147 *@@category: Helpers\XML
[35]148 * see xml.c.
[10]149 */
150
[35]151/*
[153]152 *@@category: Helpers\XML\Document Object Model (DOM)
[35]153 * see xml.c.
154 */
155
[12]156/* ******************************************************************
157 *
[63]158 * Error handling
[12]159 *
160 ********************************************************************/
161
[10]162/*
[63]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
172const char* xmlDescribeError(int code)
173{
[97]174 switch (code)
[63]175 {
176 // start of expat (parser) errors
[97]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");
[63]224 // end of expat (parser) errors
225
226 // start of validation errors
227
[97]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");
[63]234
[97]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");
[63]251
[97]252 case ERROR_DOM_PARSING:
253 return ("Parsing error");
254 case ERROR_DOM_VALIDITY:
255 return ("Validity error");
[63]256
[97]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");
[63]271
[97]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");
[121]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");
[97]284 }
[63]285
[97]286 return NULL;
[63]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
297VOID 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/*
[83]327 *@@ CompareXStrings:
[39]328 * tree comparison func for NodeBases.
[38]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]
[97]342 *@@changed V0.9.14 (2001-08-09) [umoeller]: fixed map bug which caused the whole XML stuff to fail
[38]343 */
344
[222]345STATIC int TREEENTRY CompareXStrings(ULONG ul1,
[142]346 ULONG ul2)
[38]347{
[83]348 return (strhcmp(((PXSTRING)ul1)->psz,
[97]349 ((PXSTRING)ul2)->psz));
[38]350}
351
352/*
[39]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
362APIRET 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
[83]379 pNewNode->Tree.ulKey = (ULONG)&pNewNode->strNodeName;
380
[39]381 xstrInit(&pNewNode->strNodeName, 0);
382 if (pcszNodeName)
[83]383 {
[39]384 xstrcpy(&pNewNode->strNodeName,
385 pcszNodeName,
[83]386 ulNodeNameLength);
387 }
[39]388
389 *ppNew = pNewNode;
390 }
391
[167]392 return arc;
[39]393}
394
395/*
396 *@@ xmlDeleteNode:
397 * deletes a NODEBASE and frees memory that was
398 * associated with its members.
399 *
[71]400 * After calling this, pNode is no longer valid.
[39]401 *
[71]402 * If you invoke this on a DOCUMENT node, the
403 * entire DOM tree will get deleted recursively.
404 *
[39]405 *@@added V0.9.9 (2001-02-16) [umoeller]
[97]406 *@@changed V0.9.14 (2001-08-09) [umoeller]: fixed crash on string delete
[39]407 */
408
409VOID 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
[71]435 PDOMNODE pNext = (PDOMNODE)treeNext((TREE*)pAttrib);
[39]436 xmlDeleteNode((PNODEBASE)pAttrib);
437 // this will remove pAttrib from pNode's attrib
[71]438 // tree and rebalance the tree
439 pAttrib = pNext;
[39]440 }
441 break; }
442
443 case DOMNODE_ATTRIBUTE:
444 case DOMNODE_TEXT:
445 case DOMNODE_PROCESSING_INSTRUCTION:
446 case DOMNODE_COMMENT:
[71]447 pDomNode = (PDOMNODE)pNode;
448 break;
449
[39]450 case DOMNODE_DOCUMENT:
[71]451 if (((PDOMDOCUMENTNODE)pNode)->pDocType)
452 xmlDeleteNode((PNODEBASE)((PDOMDOCUMENTNODE)pNode)->pDocType);
[39]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);
[71]498 // treeDelete(pp-> // @@todo
[39]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 {
[91]508 // PCMATTRIBUTEDECL pDecl = (PCMATTRIBUTEDECL)pNode;
[39]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,
[113]530 NULL,
[39]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
[74]541 xstrFree(&pDomNode->pstrNodeValue);
[39]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
[97]555 xstrClear(&pNode->strNodeName);
[39]556 free(pNode);
557 }
558}
559
560/*
561 *@@ xmlCreateDomNode:
[10]562 * creates a new DOMNODE with the specified
[35]563 * type and parent. Other than that, the
[38]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.
[10]582 */
583
[39]584APIRET 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
[10]590{
[38]591 PDOMNODE pNewNode = NULL;
592 APIRET arc = NO_ERROR;
593
594 ULONG cb = 0;
595
596 switch (ulNodeType)
[10]597 {
[38]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
[97]611 if (!(arc = xmlCreateNodeBase(ulNodeType,
612 cb,
613 pcszNodeName,
614 ulNodeNameLength,
615 (PNODEBASE*)&pNewNode)))
[38]616 {
[10]617 pNewNode->pParentNode = pParentNode;
[38]618
[10]619 if (pParentNode)
620 {
621 // parent specified:
[38]622 // check if this is an attribute
623 if (ulNodeType == DOMNODE_ATTRIBUTE)
624 {
625 // attribute:
626 // add to parent's attributes list
[83]627 if (treeInsert(&pParentNode->AttributesMap,
[113]628 NULL,
[83]629 &pNewNode->NodeBase.Tree,
630 CompareXStrings))
[38]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 }
[10]655 }
656
[38]657 lstInit(&pNewNode->llChildren, FALSE);
[113]658 treeInit(&pNewNode->AttributesMap, NULL);
[10]659 }
660
[38]661 if (!arc)
662 *ppNew = pNewNode;
663 else
664 if (pNewNode)
665 free(pNewNode);
666
[167]667 return arc;
[10]668}
669
[12]670/* ******************************************************************
671 *
[63]672 * Specific DOM node constructors
[38]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
683APIRET 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;
[117]688 APIRET arc;
[38]689
[117]690 if (!(arc = xmlCreateDomNode(pParent,
691 DOMNODE_ELEMENT,
692 pcszElement,
693 0,
694 &pNew)))
[38]695 *ppNew = pNew;
696
[167]697 return arc;
[38]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 *
[39]710 * -- Error codes from xmlCreateDomNode.
[38]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
718APIRET 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
[117]725 if ( (!pElement)
726 || (pElement->NodeBase.ulNodeType != DOMNODE_ELEMENT)
[38]727 )
728 arc = ERROR_DOM_NO_ELEMENT;
729 else
730 {
731 PDOMNODE pNew = NULL;
[117]732 if (!(arc = xmlCreateDomNode(pElement, // this takes care of adding to the list
733 DOMNODE_ATTRIBUTE,
734 pcszName,
735 0,
736 &pNew)))
[38]737 {
738 pNew->pstrNodeValue = xstrCreate(0);
739 xstrcpy(pNew->pstrNodeValue, pcszValue, 0);
740
741 *ppNew = pNew;
742 }
743 }
744
[167]745 return arc;
[38]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
759APIRET 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;
[117]765 APIRET arc;
766
767 if (!(arc = xmlCreateDomNode(pParent,
768 DOMNODE_TEXT,
769 NULL,
770 0,
771 &pNew)))
[38]772 {
[117]773 PSZ pszNodeValue;
774 if (pszNodeValue = (PSZ)malloc(ulLength + 1))
[38]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 }
[117]783 else
784 {
785 arc = ERROR_NOT_ENOUGH_MEMORY;
786 xmlDeleteNode((PNODEBASE)pNew);
787 }
[38]788 }
789
[167]790 return arc;
[38]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
801APIRET xmlCreateCommentNode(PDOMNODE pParent, // in: parent element node
802 const char *pcszText, // in: comment (null-terminated)
803 PDOMNODE *ppNew)
804{
805 PDOMNODE pNew = NULL;
[117]806 APIRET arc;
807 if (!(arc = xmlCreateDomNode(pParent,
808 DOMNODE_COMMENT,
809 NULL,
810 0,
811 &pNew)))
[38]812 {
813 pNew->pstrNodeValue = xstrCreate(0);
814 xstrcpy(pNew->pstrNodeValue, pcszText, 0);
815 *ppNew = pNew;
816 }
817
[167]818 return arc;
[38]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
829APIRET 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;
[117]835 APIRET arc;
836
837 if (!(arc = xmlCreateDomNode(pParent,
838 DOMNODE_PROCESSING_INSTRUCTION,
839 pcszTarget,
840 0,
841 &pNew)))
[38]842 {
843 pNew->pstrNodeValue = xstrCreate(0);
844 xstrcpy(pNew->pstrNodeValue, pcszData, 0);
845
846 *ppNew = pNew;
847 }
848
[167]849 return arc;
[38]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
860APIRET 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;
[117]876 if (!(arc = xmlCreateDomNode((PDOMNODE)pDocumentNode,
877 DOMNODE_DOCUMENT_TYPE,
878 pcszDoctypeName,
879 0,
880 (PDOMNODE*)&pNew)))
[38]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
[113]892 treeInit(&pNew->ElementDeclsTree, NULL);
893 treeInit(&pNew->AttribDeclBasesTree, NULL);
[38]894
895 *ppNew = pNew;
896 }
897 }
[167]898 return arc;
[38]899}
900
901/* ******************************************************************
902 *
[63]903 * DOM level 3 content models
[38]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
[222]916STATIC APIRET SetupParticleAndSubs(PCMELEMENTPARTICLE pParticle,
[142]917 PXMLCONTENT pModel,
918 TREE **ppElementNamesTree) // in: ptr to _CMELEMENTDECLNODE.ElementNamesTree
919 // (passed to all recursions)
[38]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
[39]927 pParticle->NodeBase.ulNodeType = ELEMENTPARTICLE_EMPTY;
[38]928 break;
929
930 case XML_CTYPE_ANY: // that's easy
[39]931 pParticle->NodeBase.ulNodeType = ELEMENTPARTICLE_ANY;
[38]932 break;
933
934 case XML_CTYPE_NAME: // that's easy
[39]935 pParticle->NodeBase.ulNodeType = ELEMENTPARTICLE_NAME;
[83]936 xstrcpy(&pParticle->NodeBase.strNodeName, pModel->name, 0);
937 treeInsert(ppElementNamesTree,
[113]938 NULL,
[83]939 &pParticle->NodeBase.Tree,
940 CompareXStrings);
[38]941 break;
942
943 case XML_CTYPE_MIXED:
[39]944 pParticle->NodeBase.ulNodeType = ELEMENTPARTICLE_MIXED;
[38]945 break;
946
947 case XML_CTYPE_CHOICE:
[39]948 pParticle->NodeBase.ulNodeType = ELEMENTPARTICLE_CHOICE;
[38]949 break;
950
951 case XML_CTYPE_SEQ:
[39]952 pParticle->NodeBase.ulNodeType = ELEMENTPARTICLE_SEQ;
[38]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];
[39]969 PCMELEMENTPARTICLE pSubNew = NULL;
[117]970 if (!(arc = xmlCreateNodeBase(TYPE_UNKNOWN, // node type... for now
971 sizeof(CMELEMENTPARTICLE),
972 0,
973 0,
974 (PNODEBASE*)&pSubNew)))
[38]975 {
[117]976 if (!(arc = SetupParticleAndSubs(pSubNew,
977 pSubModel,
978 ppElementNamesTree)))
[39]979 {
[38]980 // no error: append sub-particle to this particle's
981 // children list
982 lstAppendItem(pParticle->pllSubNodes,
983 pSubNew);
[39]984 // and store this particle as the parent in the
985 // sub-particle
986 pSubNew->pParentParticle = pParticle;
987 }
[38]988 }
989
990 if (arc)
991 break;
992 }
993 }
994
[167]995 return arc;
[38]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 *
[71]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 *
[38]1009 *@@added V0.9.9 (2001-02-16) [umoeller]
1010 */
1011
1012APIRET xmlCreateElementDecl(const char *pcszName,
1013 PXMLCONTENT pModel,
1014 PCMELEMENTDECLNODE *ppNew)
1015{
1016 APIRET arc = NO_ERROR;
[39]1017 PCMELEMENTDECLNODE pNew = NULL;
[38]1018
[117]1019 if (!(arc = xmlCreateNodeBase(TYPE_UNKNOWN, // for now
1020 sizeof(CMELEMENTDECLNODE),
1021 pcszName,
1022 0,
1023 (PNODEBASE*)&pNew)))
[39]1024 {
[113]1025 treeInit(&pNew->ParticleNamesTree, NULL);
[38]1026
1027 // set up the "particle" member and recurse into sub-particles
[117]1028 if (!(arc = SetupParticleAndSubs(&pNew->Particle,
1029 pModel,
1030 &pNew->ParticleNamesTree)))
[38]1031 *ppNew = pNew;
1032 else
1033 free(pNew);
1034 }
1035
[167]1036 return arc;
[38]1037}
1038
1039/*
[39]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.
[38]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
[39]1050 * following holds:
[38]1051 *
[39]1052 * (1) The declaration matches EMPTY and the element has no @content. (done)
[38]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
[39]1061 * white space only. @@todo
[38]1062 *
1063 * (3) The declaration matches (mixed) (see @element_declaration)
1064 * and the content consists of @content and child elements
[39]1065 * whose types match names in the content model. (done)
[38]1066 *
1067 * (4) The declaration matches ANY, and the types of any child
1068 * elements have been declared. (done)
1069 *
[39]1070 * Preconditions: The element must already have been inserted
1071 * into the parent element's list, or we cannot validate sequences.
1072 *
[38]1073 *@@added V0.9.9 (2001-02-16) [umoeller]
1074 */
1075
[222]1076STATIC VOID ValidateElement(PXMLDOM pDom,
[142]1077 PDOMNODE pNewElement, // in: new element
1078 PCMELEMENTDECLNODE pParentElementDecl)
1079 // in: element decl of element's parent
[38]1080{
[39]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
[63]1090 xmlSetError(pDom,
1091 ERROR_DOM_VALIDATE_INVALID_ELEMENT,
1092 pNewElement->NodeBase.strNodeName.psz,
1093 TRUE);
[39]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 {
[83]1117 PXSTRING pstrNewElementName
1118 = &pNewElement->NodeBase.strNodeName;
[39]1119
1120 // for all these, we first need to check if
1121 // the element is allowed at all
1122 PCMELEMENTPARTICLE pParticle
[83]1123 = (PCMELEMENTPARTICLE)treeFind(
1124 pParentElementDecl->ParticleNamesTree,
1125 (ULONG)pstrNewElementName,
1126 CompareXStrings);
[39]1127 if (!pParticle)
1128 // not found: then this element is not allowed within this
1129 // parent
1130 xmlSetError(pDom,
1131 ERROR_DOM_INVALID_SUBELEMENT,
[83]1132 pstrNewElementName->psz,
[39]1133 TRUE);
1134 else
1135 {
1136 // the element is allowed at all: now check for the
[127]1137 // lists case... @@todo
[39]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
[38]1155 // yes: get the element decl from the tree
[39]1156 /* PCMELEMENTDECLNODE pElementDecl = xmlFindElementDecl(pDom,
[38]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,
[39]1210 CompareNodeBaseData);
[38]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 }
[39]1222 */
[38]1223}
1224
1225/*
[39]1226 *@@ ValidateAttributeType:
1227 * validates the specified attribute's type against the
1228 * document's @DTD.
[38]1229 *
1230 * This sets arcDOM in XMLDOM on errors.
1231 *
1232 *@@added V0.9.9 (2001-02-16) [umoeller]
1233 */
1234
[222]1235STATIC VOID ValidateAttributeType(PXMLDOM pDom,
[142]1236 PDOMNODE pAttrib,
1237 PCMATTRIBUTEDECLBASE *ppAttribDeclBase)
[38]1238{
1239 PDOMNODE pElement = pAttrib->pParentNode;
1240
1241 PCMATTRIBUTEDECL pAttribDecl = xmlFindAttribDecl(pDom,
1242 &pElement->NodeBase.strNodeName,
[39]1243 &pAttrib->NodeBase.strNodeName,
1244 ppAttribDeclBase);
[38]1245 if (!pAttribDecl)
1246 xmlSetError(pDom,
1247 ERROR_DOM_UNDECLARED_ATTRIBUTE,
1248 pAttrib->NodeBase.strNodeName.psz,
1249 TRUE);
[39]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
[83]1269 PNODEBASE pValue = (PNODEBASE)treeFind(
1270 pAttribDecl->ValuesTree,
1271 (ULONG)pAttrib->pstrNodeValue,
1272 CompareXStrings);
[39]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 }
[38]1289}
1290
[39]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
[222]1299STATIC VOID ValidateAllAttributes(PXMLDOM pDom,
[142]1300 PCMATTRIBUTEDECLBASE pAttribDeclBase,
1301 PDOMNODE pNewElement)
[39]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
[83]1317 PXSTRING pstrAttrNameThis = &pDeclThis->NodeBase.strNodeName;
1318 PDOMNODE pAttrNode = (PDOMNODE)treeFind(
1319 pNewElement->AttributesMap,
1320 (ULONG)pstrAttrNameThis,
1321 CompareXStrings);
[39]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,
[83]1331 pstrAttrNameThis->psz,
[39]1332 TRUE);
1333 break;
1334 }
1335 }
1336
1337 pDeclThis = (PCMATTRIBUTEDECL)treeNext((TREE*)pDeclThis);
1338 }
1339}
1340
[38]1341/* ******************************************************************
1342 *
[39]1343 * Expat stack
1344 *
1345 ********************************************************************/
1346
1347/*
1348 *@@ DOMSTACKITEM:
1349 *
1350 *@@added V0.9.9 (2001-02-16) [umoeller]
1351 */
1352
1353typedef 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
[222]1368STATIC PDOMSTACKITEM PopElementStack(PXMLDOM pDom,
[142]1369 PLISTNODE *ppListNode)
[39]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
[222]1398STATIC VOID PushElementStack(PXMLDOM pDom,
[142]1399 PDOMNODE pDomNode)
[39]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 *
[35]1423 * Expat handlers
[12]1424 *
1425 ********************************************************************/
1426
1427/*
[97]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
[222]1438STATIC int EXPATENTRY UnknownEncodingHandler(void *pUserData, // in: out PXMLDOM really
[142]1439 const XML_Char *pcszName,
1440 XML_Encoding *pEncoding)
[97]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/*
[35]1493 *@@ StartElementHandler:
[38]1494 * @expat handler called when a new element is
[35]1495 * found.
[12]1496 *
[35]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.
[12]1501 */
1502
[222]1503STATIC void EXPATENTRY StartElementHandler(void *pUserData, // in: our PXMLDOM really
[142]1504 const char *pcszElement,
1505 const char **papcszAttribs)
[12]1506{
[38]1507 PXMLDOM pDom = (PXMLDOM)pUserData;
[12]1508
[38]1509 // continue parsing only if we had no errors so far
1510 if (!pDom->arcDOM)
1511 {
1512 ULONG i;
[12]1513
[39]1514 PDOMSTACKITEM pSI = PopElementStack(pDom,
1515 NULL); // no free
1516 if (!pDom->arcDOM)
[38]1517 {
[39]1518 PDOMNODE pParent = pSI->pDomNode,
1519 pNew = NULL;
[10]1520
[117]1521 if (!(pDom->arcDOM = xmlCreateElementNode(pParent,
1522 pcszElement,
1523 &pNew)))
[39]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)
[38]1538 {
[39]1539 PCMATTRIBUTEDECLBASE pAttribDeclBase = NULL;
1540
[38]1541 // shall we validate?
1542 if (pDom->pDocTypeNode)
[39]1543 // yes: get attrib decl base for speed
1544 pAttribDeclBase
1545 = xmlFindAttribDeclBase(pDom,
1546 &pNew->NodeBase.strNodeName);
[10]1547
[39]1548 // now for the attribs
1549 for (i = 0;
1550 (papcszAttribs[i]) && (!pDom->arcDOM);
1551 i += 2)
[38]1552 {
[39]1553 PDOMNODE pAttrib;
[117]1554 if (!(pDom->arcDOM = xmlCreateAttributeNode(pNew, // element,
1555 papcszAttribs[i], // attr name
1556 papcszAttribs[i + 1], // attr value
1557 &pAttrib)))
[121]1558 {
[97]1559 // shall we validate?
1560 if (pDom->pDocTypeNode)
1561 ValidateAttributeType(pDom,
1562 pAttrib,
1563 &pAttribDeclBase);
[121]1564 }
[117]1565 else
1566 {
1567 xmlSetError(pDom,
1568 pDom->arcDOM,
1569 papcszAttribs[i],
[121]1570 FALSE); // validation
[117]1571 break;
1572 }
[39]1573 }
[38]1574
[39]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);
[10]1585 }
[35]1586 }
[38]1587
1588 pDom->pLastWasTextNode = NULL;
[35]1589 }
[10]1590}
1591
1592/*
[35]1593 *@@ EndElementHandler:
[38]1594 * @expat handler for when parsing an element is done.
1595 * We pop the element off of our stack then.
[10]1596 */
1597
[222]1598STATIC void EXPATENTRY EndElementHandler(void *pUserData, // in: our PXMLDOM really
[142]1599 const XML_Char *name)
[10]1600{
[38]1601 PXMLDOM pDom = (PXMLDOM)pUserData;
1602 // continue parsing only if we had no errors so far
1603 if (!pDom->arcDOM)
1604 {
[39]1605 PLISTNODE pStackLN = NULL;
1606 PDOMSTACKITEM pSI = PopElementStack(pDom,
1607 &pStackLN);
[10]1608
[39]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
[38]1622 pDom->pLastWasTextNode = NULL;
1623 }
[10]1624}
1625
1626/*
[35]1627 *@@ CharacterDataHandler:
[38]1628 * @expat handler for character data (@content).
[10]1629 *
[38]1630 * Note: expat passes chunks of content without zero-terminating
1631 * them. We must concatenate the chunks to a full text node.
[10]1632 */
1633
[222]1634STATIC void EXPATENTRY CharacterDataHandler(void *pUserData, // in: our PXMLDOM really
[142]1635 const XML_Char *s,
1636 int len)
[10]1637{
[38]1638 PXMLDOM pDom = (PXMLDOM)pUserData;
[10]1639
[38]1640 // continue parsing only if we had no errors so far
[117]1641 if ( (!pDom->arcDOM)
1642 && (len)
1643 )
[38]1644 {
[117]1645 // we need a new text node:
1646 PDOMSTACKITEM pSI = PopElementStack(pDom,
1647 NULL); // no free
1648 if (!pDom->arcDOM)
[38]1649 {
[117]1650 PDOMNODE pParent = pSI->pDomNode;
1651 // pNew = NULL;
1652
1653 BOOL fIsWhitespace = FALSE;
1654
1655 // shall we validate?
1656 if (pDom->pDocTypeNode)
[38]1657 {
[117]1658 // yes: check if the parent element allows
1659 // for content at all (must be "mixed" model)
[38]1660
[117]1661 // get the element decl from the tree
1662 PCMELEMENTDECLNODE pElementDecl;
1663 if (pElementDecl = pSI->pElementDecl)
[39]1664 {
[117]1665 switch (pElementDecl->Particle.NodeBase.ulNodeType)
1666 {
1667 case ELEMENTPARTICLE_ANY:
1668 case ELEMENTPARTICLE_MIXED:
1669 // those two are okay
1670 break;
[39]1671
[117]1672 case ELEMENTPARTICLE_EMPTY:
1673 // that's an error for sure
1674 pDom->arcDOM = ERROR_ELEMENT_CANNOT_HAVE_CONTENT;
1675 break;
[39]1676
[117]1677 default:
[39]1678 {
[117]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;
[39]1686
[117]1687 if (pDom->flParserFlags & DF_DROP_WHITESPACE)
1688 fIsWhitespace = TRUE;
[39]1689
[117]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 }
[39]1703 }
1704 }
[117]1705 }
[39]1706
[117]1707 } // end if (pDom->pDocTypeNode)
[97]1708
[117]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
[97]1719
[117]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);
[38]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
[222]1745STATIC void EXPATENTRY CommentHandler(void *pUserData, // in: our PXMLDOM really
[142]1746 const XML_Char *data)
[38]1747{
1748 PXMLDOM pDom = (PXMLDOM)pUserData;
1749
1750 // continue parsing only if we had no errors so far
1751 if (!pDom->arcDOM)
[35]1752 {
[39]1753 // we need a new text node:
1754 PDOMSTACKITEM pSI = PopElementStack(pDom,
1755 NULL); // no free
1756 if (!pDom->arcDOM)
[10]1757 {
[39]1758 PDOMNODE pParent = pSI->pDomNode,
1759 pNew = NULL;
[38]1760
1761 pDom->arcDOM = xmlCreateCommentNode(pParent,
1762 data,
[39]1763 &pNew);
[35]1764 }
[38]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
[222]1780STATIC void EXPATENTRY StartDoctypeDeclHandler(void *pUserData,
[142]1781 const XML_Char *pcszDoctypeName,
1782 const XML_Char *pcszSysid,
1783 const XML_Char *pcszPubid,
1784 int fHasInternalSubset)
[38]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;
[35]1795 else
1796 {
[39]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);
[38]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
[222]1819STATIC void EXPATENTRY EndDoctypeDeclHandler(void *pUserData) // in: our PXMLDOM really
[38]1820{
1821 PXMLDOM pDom = (PXMLDOM)pUserData;
1822
[39]1823 // continue parsing only if we had no errors so far
[63]1824 if (!pDom->arcDOM)
[38]1825 {
[63]1826 }
[38]1827}
1828
1829/*
1830 *@@ NotationDeclHandler:
1831 * @expat handler for @notation_declarations.
1832 *
[47]1833 * @@todo
[39]1834 *
[38]1835 *@@added V0.9.9 (2001-02-14) [umoeller]
1836 */
1837
[222]1838STATIC void EXPATENTRY NotationDeclHandler(void *pUserData, // in: our PXMLDOM really
[142]1839 const XML_Char *pcszNotationName,
1840 const XML_Char *pcszBase,
1841 const XML_Char *pcszSystemId,
1842 const XML_Char *pcszPublicId)
[38]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
[63]1870 * declaration and may be null (since XML doesn't require public
1871 * identifiers).
[38]1872 *
1873 * The pcszSystemId is the system identifier specified in the
[39]1874 * entity declaration and is never null. This is an exact copy
1875 * of what was specified in the reference.
[38]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 *
[97]1903 *@@added V0.9.14 (2001-08-09) [umoeller]
[187]1904 *@@changed V0.9.20 (2002-07-06) [umoeller]: added automatic doctype support
[38]1905 */
1906
[222]1907STATIC int EXPATENTRY ExternalEntityRefHandler(void *pUserData, // in: our PXMLDOM really
[142]1908 XML_Parser parser,
1909 const XML_Char *pcszContext,
1910 const XML_Char *pcszBase,
1911 const XML_Char *pcszSystemId,
1912 const XML_Char *pcszPublicId)
[38]1913{
[97]1914 PXMLDOM pDom = (PXMLDOM)pUserData;
[38]1915
[97]1916 int i = 0; // return error per default
[39]1917
[97]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
[187]1923 if ( ( (pDom->pfnExternalHandler)
1924 || (pDom->cSystemIds) // V0.9.20 (2002-07-06) [umoeller]
1925 )
[97]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 )
[38]1932 {
[187]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)
[97]1942 {
[187]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
[38]1954
[187]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
[97]1972 else
1973 {
[187]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
[97]1984 {
[187]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);
[97]1995 }
1996 }
1997 }
1998 }
1999 else
2000 xmlSetError(pDom,
[187]2001 ERROR_DOM_INVALID_EXTERNAL_HANDLER,
[97]2002 NULL,
2003 FALSE);
2004
2005 if (pDom->pParser)
2006 XML_ParserFree(pDom->pParser);
2007
2008 pDom->pParser = pOldParser;
2009
[38]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
[47]2021 * structure. @@todo
[38]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
[222]2029STATIC void EXPATENTRY ElementDeclHandler(void *pUserData, // in: our PXMLDOM really
[142]2030 const XML_Char *pcszName,
2031 XMLCONTENT *pModel)
[38]2032{
2033 PXMLDOM pDom = (PXMLDOM)pUserData;
2034
2035 // continue parsing only if we had no errors so far
2036 if (!pDom->arcDOM)
2037 {
[39]2038 // OK, we're in a DOCTYPE node:
2039 PDOMDOCTYPENODE pDocType = pDom->pDocTypeNode;
2040 if (!pDocType)
[63]2041 xmlSetError(pDom,
2042 ERROR_DOM_ELEMENT_DECL_OUTSIDE_DOCTYPE,
2043 pcszName,
2044 TRUE);
[38]2045 else
2046 {
[39]2047 // create an element declaration and push it unto the
2048 // declarations tree
2049 PCMELEMENTDECLNODE pNew = NULL;
[117]2050 if (!(pDom->arcDOM = xmlCreateElementDecl(pcszName,
2051 pModel,
2052 &pNew)))
[39]2053 // this recurses!!
2054 // after this, pModel is invalid
[10]2055 {
[39]2056 // add this to the doctype's declarations tree
[83]2057 if (treeInsert(&pDocType->ElementDeclsTree,
[113]2058 NULL,
[83]2059 (TREE*)pNew,
2060 CompareXStrings))
[39]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);
[35]2068 }
[38]2069 }
2070 }
2071}
[10]2072
[38]2073/*
2074 *@@ AddEnum:
2075 *
2076 *@@added V0.9.9 (2001-02-16) [umoeller]
2077 */
2078
[222]2079STATIC APIRET AddEnum(PCMATTRIBUTEDECL pDecl,
[142]2080 const char *p, // in: start of name
2081 const char *pNext) // in: end of name (not included)
[38]2082{
[39]2083 // PSZ pszType = strhSubstr(p, pNext);
2084 PNODEBASE pNew = NULL;
[117]2085 APIRET arc;
2086
2087 if (!(arc = xmlCreateNodeBase(ATTRIBUTE_DECLARATION_ENUM,
2088 sizeof(NODEBASE),
2089 p,
2090 (pNext - p),
2091 &pNew)))
[83]2092 treeInsert(&pDecl->ValuesTree,
[113]2093 NULL,
[83]2094 (TREE*)pNew,
2095 CompareXStrings);
[38]2096
[167]2097 return arc;
[38]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
[222]2128STATIC void EXPATENTRY AttlistDeclHandler(void *pUserData, // in: our PXMLDOM really
[142]2129 const XML_Char *pcszElementName,
2130 const XML_Char *pcszAttribName,
2131 const XML_Char *pcszAttribType,
2132 const XML_Char *pcszDefault,
2133 int fIsRequired)
[38]2134{
2135 PXMLDOM pDom = (PXMLDOM)pUserData;
2136
2137 // continue parsing only if we had no errors so far
2138 if (!pDom->arcDOM)
2139 {
[39]2140 // OK, we're in a DOCTYPE node:
2141 PDOMDOCTYPENODE pDocType = pDom->pDocTypeNode;
2142 if (!pDocType)
[63]2143 xmlSetError(pDom,
2144 ERROR_DOM_ATTLIST_DECL_OUTSIDE_DOCTYPE,
2145 pcszElementName,
2146 TRUE);
[38]2147 else
2148 {
[39]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)
[38]2163 {
[83]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);
[147]2169 if (!(pThis = (PCMATTRIBUTEDECLBASE)treeFind(
[83]2170 pDocType->AttribDeclBasesTree,
2171 (ULONG)&strElementName,
[147]2172 CompareXStrings)))
[38]2173 {
[39]2174 // still not found:
2175 // we need a new node then
[147]2176 if (!(pDom->arcDOM = xmlCreateNodeBase(ATTRIBUTE_DECLARATION_BASE,
[39]2177 sizeof(CMATTRIBUTEDECLBASE),
[83]2178 strElementName.psz,
2179 strElementName.ulLength,
[147]2180 (PNODEBASE*)&pThis)))
[38]2181 {
[39]2182 // initialize the subtree
[113]2183 treeInit(&pThis->AttribDeclsTree, NULL);
[38]2184
[83]2185 treeInsert(&pDocType->AttribDeclBasesTree,
[113]2186 NULL,
[83]2187 (TREE*)pThis,
2188 CompareXStrings);
[38]2189 }
2190 }
2191
[39]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;
[117]2200 if (!(pDom->arcDOM = xmlCreateNodeBase(ATTRIBUTE_DECLARATION,
2201 sizeof(CMATTRIBUTEDECL),
2202 pcszAttribName,
2203 0,
2204 (PNODEBASE*)&pNew)))
[38]2205 {
[113]2206 treeInit(&pNew->ValuesTree, NULL);
[39]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 == '(')
[38]2213 {
[39]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 }
[38]2224
[39]2225 if (!pDom->arcDOM)
[38]2226 {
2227 pNext = strchr(p, ')');
2228 AddEnum(pNew, p, pNext);
2229
2230 pNew->ulAttrType = CMAT_ENUM;
2231 }
[39]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;
[38]2249
[39]2250 if (!pDom->arcDOM)
2251 {
[38]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
[83]2271 if (treeInsert(&pThis->AttribDeclsTree,
[113]2272 NULL,
[83]2273 (TREE*)pNew,
2274 CompareXStrings))
[38]2275 xmlSetError(pDom,
2276 ERROR_DOM_DUPLICATE_ATTRIBUTE_DECL,
2277 pcszAttribName,
2278 TRUE);
2279 }
2280 }
2281 }
[35]2282 }
2283 }
2284}
[10]2285
[38]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
[222]2307STATIC void EXPATENTRY EntityDeclHandler(void *pUserData, // in: our PXMLDOM really
[142]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)
[38]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
[35]2325/* ******************************************************************
2326 *
[71]2327 * DOM parser APIs
[35]2328 *
2329 ********************************************************************/
[10]2330
[35]2331/*
2332 *@@ xmlCreateDOM:
[38]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.
[35]2336 *
[38]2337 * Pass the XMLDOM returned here to xmlParse afterwards.
2338 *
[97]2339 * Simplest possible usage:
[38]2340 *
[35]2341 * 1) Create a DOM instance.
2342 *
2343 + PXMLDOM pDom = NULL;
[97]2344 + APIRET arc = xmlCreateDOM(flags, NULL, NULL, NULL, &pDom);
[35]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 *
[71]2353 * 3) Process the data in the DOM tree.
[35]2354 *
[71]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 *
[97]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 *
[187]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 *
[97]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.
[117]2413 * "cp850") and will then receive the following
[97]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 *
[187]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.
[97]2434 *
[38]2435 *@@added V0.9.9 (2001-02-14) [umoeller]
[97]2436 *@@changed V0.9.14 (2001-08-09) [umoeller]: added DF_DROP_WHITESPACE support
[252]2437 *@@changed V0.9.20 (2002-07-06) [umoeller]: added static system IDs
[35]2438 */
[10]2439
[97]2440APIRET xmlCreateDOM(ULONG flParserFlags, // in: DF_* parser flags
[187]2441 const STATICSYSTEMID *paSystemIds, // in: array of STATICSYSTEMID's or NULL
2442 ULONG cSystemIds, // in: array item count
[97]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
[35]2447{
2448 APIRET arc = NO_ERROR;
[10]2449
[38]2450 PXMLDOM pDom = (PXMLDOM)malloc(sizeof(*pDom));
[35]2451 if (!pDom)
2452 arc = ERROR_NOT_ENOUGH_MEMORY;
2453 else
2454 {
[38]2455 PDOMNODE pDocument = NULL;
2456
[35]2457 memset(pDom, 0, sizeof(XMLDOM));
[10]2458
[97]2459 pDom->flParserFlags = flParserFlags;
2460 pDom->pfnGetCPData = pfnGetCPData;
2461 pDom->pfnExternalHandler = pfnExternalHandler;
2462 pDom->pvCallbackUser = pvCallbackUser;
2463
[187]2464 // these added with V0.9.20 (2002-07-06) [umoeller]
2465 pDom->paSystemIds = paSystemIds;
2466 pDom->cSystemIds = cSystemIds;
2467
[39]2468 lstInit(&pDom->llElementStack,
2469 TRUE); // auto-free
[10]2470
[35]2471 // create the document node
[106]2472 if (!(arc = xmlCreateDomNode(NULL, // no parent
2473 DOMNODE_DOCUMENT,
2474 NULL,
2475 0,
2476 &pDocument)))
[35]2477 {
[38]2478 // store the document in the DOM
2479 pDom->pDocumentNode = (PDOMDOCUMENTNODE)pDocument;
2480
[35]2481 // push the document on the stack so the handlers
2482 // will append to that
[39]2483 PushElementStack(pDom,
2484 pDocument);
[10]2485
[35]2486 pDom->pParser = XML_ParserCreate(NULL);
[10]2487
[35]2488 if (!pDom->pParser)
2489 arc = ERROR_NOT_ENOUGH_MEMORY;
2490 else
2491 {
[97]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
[35]2500 XML_SetElementHandler(pDom->pParser,
2501 StartElementHandler,
2502 EndElementHandler);
[10]2503
[35]2504 XML_SetCharacterDataHandler(pDom->pParser,
2505 CharacterDataHandler);
2506
[38]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
[187]2515 if ( (pfnExternalHandler)
2516 || (cSystemIds) // V0.9.20 (2002-07-06) [umoeller]
2517 )
[97]2518 XML_SetExternalEntityRefHandler(pDom->pParser,
2519 ExternalEntityRefHandler);
2520
[38]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
[35]2545 // pass the XMLDOM as user data to the handlers
2546 XML_SetUserData(pDom->pParser,
2547 pDom);
[10]2548 }
2549 }
2550 }
2551
[35]2552 if (arc == NO_ERROR)
2553 *ppDom = pDom;
2554 else
2555 xmlFreeDOM(pDom);
2556
[167]2557 return arc;
[10]2558}
2559
2560/*
[35]2561 *@@ xmlParse:
[71]2562 * parses another chunk of XML data.
[10]2563 *
[38]2564 * If (fIsLast == TRUE), the internal @expat parser
[35]2565 * will be freed, but not the DOM itself.
[10]2566 *
[35]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
[38]2572 * parsed. Otherwise one of the following errors is
2573 * returned:
[35]2574 *
[38]2575 * -- ERROR_INVALID_PARAMETER
2576 *
[264]2577 * -- ERROR_DOM_PARSING: an @expat parsing error occurred.
[38]2578 * This might also be memory problems.
2579 * With this error code, you will find specific
2580 * error information in the XMLDOM fields.
2581 *
[63]2582 * -- ERROR_DOM_VALIDITY: the document is not @valid.
[38]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]
[10]2589 */
2590
[106]2591APIRET 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
[10]2595{
[35]2596 APIRET arc = NO_ERROR;
[10]2597
[35]2598 if (!pDom)
2599 arc = ERROR_INVALID_PARAMETER;
2600 else
[10]2601 {
[97]2602 // go parse then
2603 if (!XML_Parse(pDom->pParser,
2604 pcszBuf,
2605 cb,
2606 fIsLast))
[10]2607 {
[38]2608 // expat parsing error:
2609 xmlSetError(pDom,
2610 XML_GetErrorCode(pDom->pParser),
2611 NULL,
2612 FALSE);
[35]2613
2614 if (pDom->pDocumentNode)
[10]2615 {
[39]2616 xmlDeleteNode((PNODEBASE)pDom->pDocumentNode);
[35]2617 pDom->pDocumentNode = NULL;
[10]2618 }
2619
[35]2620 arc = ERROR_DOM_PARSING;
2621 }
[38]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;
[10]2633
[38]2634 if (arc != NO_ERROR || fIsLast)
[35]2635 {
2636 // last call or error: clean up
2637 XML_ParserFree(pDom->pParser);
2638 pDom->pParser = NULL;
[10]2639
[35]2640 // clean up the stack (but not the DOM itself)
[39]2641 lstClear(&pDom->llElementStack);
[10]2642 }
2643 }
2644
[167]2645 return arc;
[10]2646}
2647
[186]2648#ifdef __DEBUG__
2649
[10]2650/*
[186]2651 *@@ Dump:
2652 *
2653 *@@added V0.9.20 (2002-07-03) [umoeller]
2654 */
2655
[222]2656STATIC VOID Dump(int iIndent,
[186]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
2710VOID 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/*
[35]2726 *@@ xmlFreeDOM:
2727 * cleans up all resources allocated by
2728 * xmlCreateDOM and xmlParse, including
2729 * the entire DOM tree.
[10]2730 *
[35]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.
[10]2735 *
[38]2736 *@@added V0.9.9 (2001-02-14) [umoeller]
[10]2737 */
2738
[35]2739APIRET xmlFreeDOM(PXMLDOM pDom)
[10]2740{
[35]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 }
[10]2750
[71]2751 xmlDeleteNode((PNODEBASE)pDom->pDocumentNode);
2752
[97]2753 if (pDom->pxstrSystemID)
2754 xstrFree(&pDom->pxstrSystemID);
2755 if (pDom->pxstrFailingNode)
2756 xstrFree(&pDom->pxstrFailingNode);
2757
2758 lstClear(&pDom->llElementStack);
2759
[35]2760 free(pDom);
2761 }
[10]2762
[167]2763 return arc;
[10]2764}
[63]2765
2766/* ******************************************************************
2767 *
2768 * DOM lookup
2769 *
2770 ********************************************************************/
2771
2772/*
2773 *@@ xmlFindElementDecl:
[153]2774 * returns the CMELEMENTDECLNODE for the element
[63]2775 * with the specified name or NULL if there's none.
2776 *
2777 *@@added V0.9.9 (2001-02-16) [umoeller]
2778 */
2779
2780PCMELEMENTDECLNODE xmlFindElementDecl(PXMLDOM pDom,
[83]2781 const XSTRING *pcstrElementName)
[63]2782{
2783 PCMELEMENTDECLNODE pElementDecl = NULL;
2784
2785 PDOMDOCTYPENODE pDocTypeNode = pDom->pDocTypeNode;
2786 if ( (pDocTypeNode)
[83]2787 && (pcstrElementName)
2788 && (pcstrElementName->ulLength)
[63]2789 )
2790 {
[83]2791 pElementDecl = (PCMELEMENTDECLNODE)treeFind(
2792 pDocTypeNode->ElementDeclsTree,
2793 (ULONG)pcstrElementName,
2794 CompareXStrings);
[63]2795 }
2796
2797 return (pElementDecl);
2798}
2799
2800/*
2801 *@@ xmlFindAttribDeclBase:
[153]2802 * returns the CMATTRIBUTEDECLBASE for the specified
[63]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
2812PCMATTRIBUTEDECLBASE xmlFindAttribDeclBase(PXMLDOM pDom,
2813 const XSTRING *pstrElementName)
2814{
2815 PDOMDOCTYPENODE pDocTypeNode = pDom->pDocTypeNode;
2816 if ( (pDocTypeNode)
2817 && (pstrElementName)
2818 && (pstrElementName->ulLength)
2819 )
2820 {
[83]2821 return ((PCMATTRIBUTEDECLBASE)treeFind(
2822 pDocTypeNode->AttribDeclBasesTree,
2823 (ULONG)pstrElementName,
2824 CompareXStrings));
[63]2825 }
2826
[169]2827 return NULL;
[63]2828}
2829
2830/*
2831 *@@ xmlFindAttribDecl:
[153]2832 * returns the CMATTRIBUTEDEDECL for the specified
[63]2833 * element and attribute name, or NULL if none exists.
2834 *
2835 *@@added V0.9.9 (2001-02-16) [umoeller]
2836 */
2837
2838PCMATTRIBUTEDECL 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 {
[83]2854 return ((PCMATTRIBUTEDECL)treeFind(
2855 ((**ppAttribDeclBase).AttribDeclsTree),
2856 (ULONG)pstrAttribName,
2857 CompareXStrings));
[63]2858 }
2859 }
2860
[169]2861 return NULL;
[63]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]
[186]2870 *@@changed V0.9.20 (2002-07-03) [umoeller]: this never worked with DTDs, fixed
[63]2871 */
2872
2873PDOMNODE 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 {
[186]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 }
[63]2895 }
2896
[169]2897 return NULL;
[63]2898}
2899
2900/*
2901 *@@ xmlGetFirstChild:
2902 * returns the first child node of pDomNode.
[153]2903 * See DOMNODE for what a "child" can be for the
[63]2904 * various node types.
2905 *
2906 *@@added V0.9.9 (2001-02-14) [umoeller]
2907 */
2908
2909PDOMNODE 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.
[153]2921 * See DOMNODE for what a "child" can be for the
[63]2922 * various node types.
2923 *
2924 *@@added V0.9.9 (2001-02-14) [umoeller]
2925 */
2926
2927PDOMNODE 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
2944PDOMNODE 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
[169]2959 return NULL;
[63]2960}
2961
2962/*
2963 *@@ xmlGetElementsByTagName:
[153]2964 * returns a linked list of DOMNODE nodes which
[63]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
2977PLINKLIST 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)
[153]2995 && ( (fFindAll)
2996 || (!strcmp(pcszName, pDomNodeThis->NodeBase.strNodeName.psz))
2997 )
[63]2998 )
2999 {
3000 // element matches:
3001 lstAppendItem(pll, pDomNodeThis);
3002 cItems++;
3003 }
3004 }
3005
3006 if (cItems)
3007 return (pll);
3008 else
[74]3009 lstFree(&pll);
[63]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
3026const XSTRING* xmlGetAttribute(PDOMNODE pElement,
3027 const char *pcszAttribName)
3028{
[83]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
[153]3034 if (pAttrNode = (PDOMNODE)treeFind(pElement->AttributesMap,
3035 (ULONG)&str,
3036 CompareXStrings))
[63]3037 return (pAttrNode->pstrNodeValue);
3038
[169]3039 return NULL;
[63]3040}
[71]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
3073APIRET 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
[167]3100 return arc;
[71]3101}
3102
3103/*
[212]3104 *@@ ESCAPES:
3105 *
[229]3106 *@@added V1.0.0 (2002-08-21) [umoeller]
[212]3107 */
3108
3109typedef struct _ESCAPES
3110{
3111 XSTRING strQuot1, // "
3112 strQuot2, // &quot;
3113 strAmp1, // &
3114 strAmp2, // &amp;
3115 strLT1, // <
3116 strLT2, // &lt;
3117 strGT1, // >
3118 strGT2; // &gt;
3119
3120 XSTRING strTemp; // temp buffer
3121
3122} ESCAPES, *PESCAPES;
3123
3124/*
3125 *@@ DoEscapes:
3126 *
[229]3127 *@@added V1.0.0 (2002-08-21) [umoeller]
[212]3128 */
3129
3130VOID 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/*
[71]3183 *@@ WriteNodes:
3184 * internal helper for writing out the nodes.
[169]3185 * This recurses.
[71]3186 *
3187 *@@added V0.9.12 (2001-05-21) [umoeller]
[229]3188 *@@changed V1.0.0 (2002-08-21) [umoeller]: changed prototype, fixed unescaped characters in attributes and content
[71]3189 */
3190
[222]3191STATIC VOID WriteNodes(PXSTRING pxstr,
[212]3192 PESCAPES pEscapes,
[142]3193 PDOMNODE pDomNode) // in: node whose children are to be written (initially DOCUMENT)
[71]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);
[212]3227
3228 // copy attribute value to temp buffer first
3229 // so we can escape quotes and ampersands
[229]3230 // V1.0.0 (2002-08-21) [umoeller]
[212]3231 xstrcpys(&pEscapes->strTemp, pAttribNode->pstrNodeValue);
3232
3233 DoEscapes(pEscapes,
3234 TRUE); // quotes too
3235
3236 // alright, use that
3237 xstrcats(pxstr, &pEscapes->strTemp);
[71]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
[212]3248 WriteNodes(pxstr, pEscapes, pChildNode);
[71]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
[212]3270 xstrcpys(&pEscapes->strTemp,
3271 pChildNode->pstrNodeValue);
3272
[229]3273 DoEscapes(pEscapes, // V1.0.0 (2002-08-21) [umoeller]
[212]3274 FALSE); // quotes not
3275
3276 xstrcats(pxstr, &pEscapes->strTemp);
[71]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
3377APIRET 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 {
[212]3388 ESCAPES esc;
3389
[71]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
[212]3403 xstrInitCopy(&esc.strQuot1, "\"", 0);
3404 xstrInitCopy(&esc.strQuot2, "&quot;", 0);
3405 xstrInitCopy(&esc.strAmp1, "&", 0);
3406 xstrInitCopy(&esc.strAmp2, "&amp;", 0);
3407 xstrInitCopy(&esc.strLT1, "<", 0);
3408 xstrInitCopy(&esc.strLT2, "&lt;", 0);
3409 xstrInitCopy(&esc.strGT1, ">", 0);
3410 xstrInitCopy(&esc.strGT2, "&gt;", 0);
3411
3412 xstrInit(&esc.strTemp, 0); // temp buffer
3413
[71]3414 // write out children
[212]3415 WriteNodes(pxstr, &esc, (PDOMNODE)pDocument);
[71]3416
[212]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
[71]3428 xstrcatc(pxstr, '\n');
3429 }
3430
[167]3431 return arc;
[71]3432}
3433
3434
Note: See TracBrowser for help on using the repository browser.