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

Last change on this file since 92 was 91, checked in by umoeller, 24 years ago

Misc changes

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