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

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