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

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

misc changes

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