source: trunk/src/helpers/xml.c@ 78

Last change on this file since 78 was 74, checked in by umoeller, 24 years ago

Misc updates

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