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

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

Misc helpers updates.

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