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

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

Tons of changes.

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