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

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

Misc fixes.

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