1 /*---------------------------------------------------------------------------
2 | Copyright (C) 1999 Jochen C. Loewer (loewerj@hotmail.com)
3 +----------------------------------------------------------------------------
4 |
5 | $Id$
6 |
7 |
8 | A DOM interface upon the expat XML parser for the C language
9 | according to the W3C recommendation REC-DOM-Level-1-19981001
10 |
11 |
12 | The contents of this file are subject to the Mozilla Public License
13 | Version 1.1 (the "License"); you may not use this file except in
14 | compliance with the License. You may obtain a copy of the License at
15 | http://www.mozilla.org/MPL/
16 |
17 | Software distributed under the License is distributed on an "AS IS"
18 | basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
19 | License for the specific language governing rights and limitations
20 | under the License.
21 |
22 | The Original Code is tDOM.
23 |
24 | The Initial Developer of the Original Code is Jochen Loewer
25 | Portions created by Jochen Loewer are Copyright (C) 1998, 1999
26 | Jochen Loewer. All Rights Reserved.
27 |
28 | Contributor(s):
29 | Sept99 Carsten Zerbst Added comment and processing instructions
30 | nodes.
31 |
32 | June00 Zoran Vasiljevic Made thread-safe.
33 |
34 | 01 Rolf Ade baseURI stuff, ID support, external
35 | entities, tdom command
36 |
37 |
38 | written by Jochen Loewer
39 | April 5, 1999
40 |
41 \--------------------------------------------------------------------------*/
42
43
44
45 /*---------------------------------------------------------------------------
46 | Includes
47 |
48 \--------------------------------------------------------------------------*/
49 #include <tcl.h>
50 #include <dom.h>
51 #include <domxpath.h>
52 #include <tclexpat.h>
53
54
55 /* #define DEBUG */
56 /*----------------------------------------------------------------------------
57 | Debug Macros
58 |
59 \---------------------------------------------------------------------------*/
60 #ifdef DEBUG
61 # define DBG(x) x
62 #else
63 # define DBG(x)
64 #endif
65
66 #define MutationEvent()
67 #define MutationEvent2(type,node)
68 #define MutationEvent3(type,node,relatioNode)
69
70 #define MCHK(a) if ((a)==NULL) { \
71 fprintf(stderr, \
72 "Memory alloc error line: %d",__LINE__); \
73 exit(1); \
74 }
75
76 #define INITIAL_BASEURISTACK_SIZE 4;
77
78 /*---------------------------------------------------------------------------
79 | Globals
80 | In threading environment, some are located in domDocument structure
81 | and some are handled differently (domUniqueNodeNr, domUniqueDocNr)
82 |
83 \--------------------------------------------------------------------------*/
84
85 #ifndef TCL_THREADS
86 unsigned long domUniqueNodeNr = 0;
87 unsigned long domUniqueDocNr = 0;
88 Tcl_HashTable tdom_tagNames;
89 Tcl_HashTable tdom_attrNames;
90 #endif
91
92 static int domModuleIsInitialized = 0;
93 TDomThreaded(static Tcl_Mutex initMutex;)
94
95 static char *domException2StringTable [] = {
96
97 "OK - no exception",
98 "INDEX_SIZE_ERR",
99 "DOMSTRING_SIZE_ERR",
100 "HIERARCHY_REQUEST_ERR",
101 "WRONG_DOCUMENT_ERR",
102 "INVALID_CHARACTER_ERR",
103 "NO_DATA_ALLOWED_ERR",
104 "NO_MODIFICATION_ALLOWED_ERR",
105 "NOT_FOUND_ERR",
106 "NOT_SUPPORTED_ERR",
107 "INUSE_ATTRIBUTE_ERR"
108 };
109
110 static char tdom_usage[] =
111 "Usage tdom <expat parser obj> <subCommand>, where subCommand can be:\n"
112 " enable \n"
113 " getdoc \n"
114 " setStoreLineColumn \n"
115 ;
116
117 /*---------------------------------------------------------------------------
118 | type domBaseURIstackElem
119 |
120 \--------------------------------------------------------------------------*/
121 typedef struct _domActiveBaseURI {
122
123 int depth;
124 const char *baseURI;
125
126 } domActiveBaseURI;
127
128 /*---------------------------------------------------------------------------
129 | type domReadInfo
130 |
131 \--------------------------------------------------------------------------*/
132 typedef struct _domReadInfo {
133
134 XML_Parser parser;
135 domDocument *document;
136 domNode *currentNode;
137 int depth;
138 int ignoreWhiteSpaces;
139 int cdataSection;
140 Tcl_DString *cdata;
141 int storeLineColumn;
142 int ignorexmlns;
143 int feedbackAfter;
144 Tcl_Obj *feedbackCmd;
145 XML_Index nextFeedbackPosition;
146 Tcl_Interp *interp;
147 int activeNSsize;
148 int activeNSpos;
149 domActiveNS *activeNS;
150 int baseURIstackSize;
151 int baseURIstackPos;
152 domActiveBaseURI *baseURIstack;
153 int insideDTD;
154 int status;
155
156 } domReadInfo;
157
158 /*----------------------------------------------------------------------------
159 | Prototypes
160 |
161 \---------------------------------------------------------------------------*/
162 static void DispatchPCDATA (domReadInfo *info);
163
164
165 #ifndef TCL_THREADS
166
167 /*---------------------------------------------------------------------------
168 | domModuleFinalize
169 |
170 \--------------------------------------------------------------------------*/
171 static void
domModuleFinalize(ClientData unused)172 domModuleFinalize(ClientData unused)
173 {
174 Tcl_HashEntry *entryPtr;
175 Tcl_HashSearch search;
176
177 entryPtr = Tcl_FirstHashEntry(&tdom_tagNames, &search);
178 while (entryPtr) {
179 Tcl_DeleteHashEntry(entryPtr);
180 entryPtr = Tcl_NextHashEntry(&search);
181 }
182 Tcl_DeleteHashTable(&tdom_tagNames);
183
184 entryPtr = Tcl_FirstHashEntry(&tdom_attrNames, &search);
185 while (entryPtr) {
186 Tcl_DeleteHashEntry(entryPtr);
187 entryPtr = Tcl_NextHashEntry(&search);
188 }
189 Tcl_DeleteHashTable(&tdom_attrNames);
190
191 return;
192 }
193 #endif /* TCL_THREADS */
194
195 /*---------------------------------------------------------------------------
196 | domModuleInitialize
197 |
198 \--------------------------------------------------------------------------*/
199 void
domModuleInitialize()200 domModuleInitialize (
201 )
202 {
203 if (domModuleIsInitialized == 0) {
204 TDomThreaded(Tcl_MutexLock(&initMutex);)
205 if (domModuleIsInitialized == 0) {
206 domAllocInit();
207 TDomNotThreaded (
208 Tcl_InitHashTable(&tdom_tagNames, TCL_STRING_KEYS);
209 Tcl_InitHashTable(&tdom_attrNames, TCL_STRING_KEYS);
210 Tcl_CreateExitHandler(domModuleFinalize, NULL);
211 )
212 TDomThreaded(
213 Tcl_CreateExitHandler(domLocksFinalize, NULL);
214 )
215 domModuleIsInitialized = 1;
216 }
217 TDomThreaded(Tcl_MutexUnlock(&initMutex);)
218 }
219 }
220
221 /*---------------------------------------------------------------------------
222 | coercion routines for calling from C++
223 |
224 \--------------------------------------------------------------------------*/
coerceToAttrNode(domNode * n)225 domAttrNode * coerceToAttrNode( domNode *n ) {
226 return (domAttrNode *)n;
227 }
228
coerceToTextNode(domNode * n)229 domTextNode * coerceToTextNode( domNode *n ) {
230 return (domTextNode *)n;
231 }
232
coerceToProcessingInstructionNode(domNode * n)233 domProcessingInstructionNode * coerceToProcessingInstructionNode( domNode *n ) {
234 return (domProcessingInstructionNode *)n;
235 }
236
237 /*---------------------------------------------------------------------------
238 | domIsNAME
239 |
240 \--------------------------------------------------------------------------*/
241 int
domIsNAME(const char * name)242 domIsNAME (
243 const char *name
244 )
245 {
246 const char *p;
247
248 p = name;
249 if (!isNameStart(p)) return 0;
250 p += UTF8_CHAR_LEN(*p);
251 while (*p) {
252 if (isNameChar(p))
253 p += UTF8_CHAR_LEN(*p);
254 else return 0;
255 }
256 return 1;
257 }
258
259 /*---------------------------------------------------------------------------
260 | domIsPINAME
261 |
262 \--------------------------------------------------------------------------*/
263 int
domIsPINAME(const char * name)264 domIsPINAME (
265 const char *name
266 )
267 {
268 if (strlen (name) == 3
269 && ((name[0] == 'x') || (name[0] == 'X'))
270 && ((name[1] == 'm') || (name[1] == 'M'))
271 && ((name[2] == 'l') || (name[2] == 'L')) ) {
272 return 0;
273 }
274 return domIsNAME (name);
275 }
276
277 /*---------------------------------------------------------------------------
278 | domIsQNAME
279 |
280 \--------------------------------------------------------------------------*/
281 int
domIsQNAME(const char * name)282 domIsQNAME (
283 const char *name
284 )
285 {
286 const char *p;
287
288 p = name;
289 if (!isNCNameStart(p)) return 0;
290 p += UTF8_CHAR_LEN(*p);
291 while (*p) {
292 if (isNCNameChar(p))
293 p += UTF8_CHAR_LEN(*p);
294 else {
295 if (*p == ':') {
296 p += 1;
297 if (!isNCNameStart(p)) return 0;
298 p += UTF8_CHAR_LEN(*p);
299 break;
300 }
301 else return 0;
302 }
303 }
304 while (*p) {
305 if (isNCNameChar(p))
306 p += UTF8_CHAR_LEN(*p);
307 else return 0;
308 }
309 return 1;
310 }
311
312 /*---------------------------------------------------------------------------
313 | domIsNCNAME
314 |
315 \--------------------------------------------------------------------------*/
316 int
domIsNCNAME(const char * name)317 domIsNCNAME (
318 const char *name
319 )
320 {
321 const char *p;
322
323 p = name;
324 if (!isNCNameStart(p)) return 0;
325 p += UTF8_CHAR_LEN(*p);
326 while (*p) {
327 if (isNCNameChar(p))
328 p += UTF8_CHAR_LEN(*p);
329 else return 0;
330 }
331 return 1;
332 }
333
334 /*---------------------------------------------------------------------------
335 | domIsChar
336 |
337 \--------------------------------------------------------------------------*/
338 int
domIsChar(const char * str)339 domIsChar (
340 const char *str
341 )
342 {
343 const char *p;
344 int clen;
345
346 p = str;
347 while (*p) {
348 clen = UTF8_CHAR_LEN(*p);
349 if (clen > 4) return 0;
350 if (UTF8_XMLCHAR((unsigned const char *)p,clen))
351 p += clen;
352 else return 0;
353 }
354 return 1;
355 }
356
357 /*---------------------------------------------------------------------------
358 | domIsBMPChar
359 |
360 \--------------------------------------------------------------------------*/
361 int
domIsBMPChar(const char * str)362 domIsBMPChar (
363 const char *str
364 )
365 {
366 const char *p;
367 int clen;
368
369 p = str;
370 while (*p) {
371 clen = UTF8_CHAR_LEN(*p);
372 if (clen > 3 || clen == 0) return 0;
373 p += clen;
374 }
375 return 1;
376 }
377
378 /*---------------------------------------------------------------------------
379 | domIsComment
380 |
381 \--------------------------------------------------------------------------*/
382 int
domIsComment(const char * str)383 domIsComment (
384 const char *str
385 )
386 {
387 const char *p;
388 int len, i = 0;
389
390 p = str;
391 len = strlen (str);
392 while (i < len) {
393 if (*p == '-') {
394 if (i == len - 1) return 0;
395 p++; i++;
396 if (*p == '-') return 0;
397 }
398 p++; i++;
399 }
400 return domIsChar (str);
401 }
402
403 /*---------------------------------------------------------------------------
404 | domIsCDATA
405 |
406 \--------------------------------------------------------------------------*/
407 int
domIsCDATA(const char * str)408 domIsCDATA (
409 const char *str
410 )
411 {
412 const char *p;
413 int len, i = 0;
414
415 p = str;
416 len = strlen (str);
417 while (i < len - 2) {
418 if ( *p == ']'
419 && p[1] == ']'
420 && p[2] == '>') return 0;
421 p++; i++;
422 }
423 return domIsChar (str);
424 }
425
426 /*---------------------------------------------------------------------------
427 | domIsPIValue
428 |
429 \--------------------------------------------------------------------------*/
430 int
domIsPIValue(const char * str)431 domIsPIValue (
432 const char *str
433 )
434 {
435 const char *p;
436 int len, i = 0;
437
438 p = str;
439 len = strlen (str);
440 while (i < len - 1) {
441 if (*p == '?' && p[1] == '>') return 0;
442 p++; i++;
443 }
444 return domIsChar (str);
445 }
446
447 /*---------------------------------------------------------------------------
448 | domLookupNamespace
449 |
450 \--------------------------------------------------------------------------*/
451 domNS *
domLookupNamespace(domDocument * doc,const char * prefix,const char * namespaceURI)452 domLookupNamespace (
453 domDocument *doc,
454 const char *prefix,
455 const char *namespaceURI
456 )
457 {
458 domNS *ns;
459 int i;
460
461 if (prefix==NULL) return NULL;
462 for (i = 0; i <= doc->nsptr; i++) {
463 ns = doc->namespaces[i];
464 if ( (ns->prefix != NULL)
465 && (strcmp(prefix,ns->prefix)==0)
466 && (strcmp(namespaceURI, ns->uri)==0)
467 ) {
468 return ns;
469 }
470 }
471 return NULL;
472 }
473
474
475 /*
476 *----------------------------------------------------------------------
477 *
478 * domPrecedes --
479 *
480 * This helper procedure returns if node precedes other with regard
481 * to their position in the document and according to the document
482 * order. The two nodes could be out of the two documents. Both
483 * nodes must not be out of the fragments list.
484 *
485 * Results:
486 * 1 if node precedes other in document order, 0 otherwise.
487 *
488 * Side effects:
489 * None.
490 *
491 *----------------------------------------------------------------------
492 */
493
494 int
domPrecedes(domNode * node,domNode * other)495 domPrecedes (
496 domNode *node,
497 domNode *other
498 )
499 {
500 domNode *nodeAncestor, *otherAncestor;
501 domAttrNode *attrN, *attrO;
502
503 if (node == other) {
504 return 0;
505 }
506
507 if (node->nodeType == ATTRIBUTE_NODE) {
508 attrN = (domAttrNode*)node;
509 if (other->nodeType == ATTRIBUTE_NODE) {
510 attrO = (domAttrNode*)other;
511 if (attrN->parentNode == attrO->parentNode) {
512 attrN = attrN->nextSibling;
513 while (attrN) {
514 if (attrN == attrO) {
515 return 1;
516 }
517 attrN = attrN->nextSibling;
518 }
519 return 0;
520 } else {
521 node = attrN->parentNode;
522 other = attrO->parentNode;
523 }
524 } else {
525 if (attrN->parentNode == other) {
526 return 0;
527 } else {
528 node = attrN->parentNode;
529 }
530 }
531 }
532 if (other->nodeType == ATTRIBUTE_NODE) {
533 attrO = (domAttrNode*)other;
534 if (node == attrO->parentNode) {
535 return 1;
536 } else {
537 other = attrO->parentNode;
538 }
539 }
540
541 if (node->ownerDocument != other->ownerDocument) {
542 /* For mt tdom, this does not, what it should:
543 whatever relative order two nodes out of different
544 documents ever have (that is not determined by the rec) it
545 must return always the same order (that is required by the
546 rec). */
547 return (node->ownerDocument->documentNumber <
548 other->ownerDocument->documentNumber);
549 }
550
551 #ifndef TCL_THREADS
552 if (node->ownerDocument->nodeFlags & NEEDS_RENUMBERING) {
553 domRenumberTree (node->ownerDocument->rootNode);
554 node->ownerDocument->nodeFlags &= ~NEEDS_RENUMBERING;
555 }
556 return (node->nodeNumber < other->nodeNumber);
557 # else
558 if (node->ownerDocument->nodeFlags & NEEDS_RENUMBERING
559 && node->ownerDocument->refCount <= 1) {
560 domRenumberTree (node->ownerDocument->rootNode);
561 node->ownerDocument->nodeFlags &= ~NEEDS_RENUMBERING;
562 }
563 if (!(node->ownerDocument->nodeFlags & NEEDS_RENUMBERING)) {
564 return (node->nodeNumber < other->nodeNumber);
565 }
566 #endif
567
568 otherAncestor = other;
569 while (otherAncestor->parentNode) {
570 otherAncestor = otherAncestor->parentNode;
571 if (otherAncestor == node) {
572 return 1;
573 }
574 }
575
576 nodeAncestor = node;
577 while (nodeAncestor->parentNode) {
578 otherAncestor = other;
579 while (otherAncestor->parentNode) {
580 if (nodeAncestor->parentNode == otherAncestor->parentNode) {
581 nodeAncestor = nodeAncestor->nextSibling;
582 while (nodeAncestor) {
583 if (nodeAncestor == otherAncestor) {
584 return 1;
585 }
586 nodeAncestor = nodeAncestor->nextSibling;
587 }
588 return 0;
589 }
590 otherAncestor = otherAncestor->parentNode;
591 }
592 nodeAncestor = nodeAncestor->parentNode;
593 if (nodeAncestor == other) {
594 return 0;
595 }
596 }
597 nodeAncestor = nodeAncestor->nextSibling;
598 while (nodeAncestor) {
599 if (nodeAncestor == otherAncestor) {
600 return 1;
601 }
602 nodeAncestor = nodeAncestor->nextSibling;
603 }
604 if (node == node->ownerDocument->rootNode) {
605 return 1;
606 }
607 return 0;
608 }
609
610 /*---------------------------------------------------------------------------
611 | domRenumberTree
612 |
613 \--------------------------------------------------------------------------*/
614 void
domRenumberTree(domNode * node)615 domRenumberTree (
616 domNode *node
617 )
618 {
619 while (node) {
620 node->nodeNumber = NODE_NO(node->ownerDocument);
621 if (node->nodeType == ELEMENT_NODE) {
622 domRenumberTree (node->firstChild);
623 }
624 node = node->nextSibling;
625 }
626 }
627
628 /*---------------------------------------------------------------------------
629 | domLookupPrefixWithMappings
630 |
631 \--------------------------------------------------------------------------*/
632 const char *
domLookupPrefixWithMappings(domNode * node,const char * prefix,char ** prefixMappings)633 domLookupPrefixWithMappings (
634 domNode *node,
635 const char *prefix,
636 char **prefixMappings
637 )
638 {
639 int i;
640 domNS *ns;
641
642 if (prefixMappings) {
643 i = 0;
644 while (prefixMappings[i]) {
645 if (strcmp (prefix, prefixMappings[i]) == 0) {
646 return prefixMappings[i+1];
647 }
648 i += 2;
649 }
650 }
651 ns = domLookupPrefix (node, prefix);
652 if (ns) return ns->uri;
653 else return NULL;
654 }
655
656 /*---------------------------------------------------------------------------
657 | domLookupPrefix
658 |
659 \--------------------------------------------------------------------------*/
660 domNS *
domLookupPrefix(domNode * node,const char * prefix)661 domLookupPrefix (
662 domNode *node,
663 const char *prefix
664 )
665 {
666 domAttrNode *NSattr;
667 domNode *orgNode = node;
668 int found;
669
670 found = 0;
671 while (node) {
672 if (node->firstAttr && !(node->firstAttr->nodeFlags & IS_NS_NODE)) {
673 node = node->parentNode;
674 continue;
675 }
676 NSattr = node->firstAttr;
677 while (NSattr && (NSattr->nodeFlags & IS_NS_NODE)) {
678 if (prefix[0] == '\0') {
679 if (NSattr->nodeName[5] == '\0') {
680 found = 1;
681 break;
682 }
683 } else {
684 if (NSattr->nodeName[5] != '\0'
685 && strcmp (&NSattr->nodeName[6], prefix)==0) {
686 found = 1;
687 break;
688 }
689 }
690 NSattr = NSattr->nextSibling;
691 }
692 if (found) {
693 return domGetNamespaceByIndex (node->ownerDocument,
694 NSattr->namespace);
695 }
696 node = node->parentNode;
697 }
698 if (prefix && (strcmp (prefix, "xml")==0)) {
699 NSattr = orgNode->ownerDocument->rootNode->firstAttr;
700 return domGetNamespaceByIndex (orgNode->ownerDocument,
701 NSattr->namespace);
702 }
703 return NULL;
704 }
705
706 /*---------------------------------------------------------------------------
707 | domIsNamespaceInScope
708 |
709 \--------------------------------------------------------------------------*/
710 int
domIsNamespaceInScope(domActiveNS * NSstack,int NSstackPos,const char * prefix,const char * namespaceURI)711 domIsNamespaceInScope (
712 domActiveNS *NSstack,
713 int NSstackPos,
714 const char *prefix,
715 const char *namespaceURI
716 )
717 {
718 int i;
719
720 for (i = NSstackPos; i >= 0; i--) {
721 if (NSstack[i].namespace->prefix[0] &&
722 (strcmp(NSstack[i].namespace->prefix, prefix)==0)) {
723 if (strcmp(NSstack[i].namespace->uri, namespaceURI)==0) {
724 /* OK, exactly the same namespace declaration is in scope */
725 return 1;
726 } else {
727 /* This prefix is currently assigned to another uri,
728 we need a new NS declaration, to override this one */
729 return 0;
730 }
731 }
732 }
733 return 0;
734 }
735
736 /*---------------------------------------------------------------------------
737 | domLookupURI
738 |
739 \--------------------------------------------------------------------------*/
740 domNS *
domLookupURI(domNode * node,char * uri)741 domLookupURI (
742 domNode *node,
743 char *uri
744 )
745 {
746 domAttrNode *NSattr;
747 int found, alreadyHaveDefault;
748
749 found = 0;
750 alreadyHaveDefault = 0;
751 while (node) {
752 if (node->firstAttr && !(node->firstAttr->nodeFlags & IS_NS_NODE)) {
753 node = node->parentNode;
754 continue;
755 }
756 NSattr = node->firstAttr;
757 while (NSattr && (NSattr->nodeFlags & IS_NS_NODE)) {
758 if (NSattr->nodeName[5] == '\0') {
759 if (!alreadyHaveDefault) {
760 if (strcmp (NSattr->nodeValue, uri)==0) {
761 found = 1;
762 break;
763 } else {
764 alreadyHaveDefault = 1;
765 }
766 }
767 } else {
768 if (strcmp (NSattr->nodeValue, uri)==0) {
769 found = 1;
770 break;
771 }
772 }
773 NSattr = NSattr->nextSibling;
774 }
775 if (found) {
776 return domGetNamespaceByIndex (node->ownerDocument,
777 NSattr->namespace);
778 }
779 node = node->parentNode;
780 }
781 return NULL;
782 }
783
784
785 /*---------------------------------------------------------------------------
786 | domGetNamespaceByIndex
787 |
788 \--------------------------------------------------------------------------*/
789 domNS *
domGetNamespaceByIndex(domDocument * doc,int nsIndex)790 domGetNamespaceByIndex (
791 domDocument *doc,
792 int nsIndex
793 )
794 {
795 if (!nsIndex) return NULL;
796 return doc->namespaces[nsIndex-1];
797 }
798
799
800 /*---------------------------------------------------------------------------
801 | domNewNamespace
802 |
803 \--------------------------------------------------------------------------*/
domNewNamespace(domDocument * doc,const char * prefix,const char * namespaceURI)804 domNS* domNewNamespace (
805 domDocument *doc,
806 const char *prefix,
807 const char *namespaceURI
808 )
809 {
810 domNS *ns = NULL;
811
812 DBG(fprintf(stderr, "domNewNamespace '%s' --> '%s' \n", prefix, namespaceURI);)
813
814 ns = domLookupNamespace (doc, prefix, namespaceURI);
815 if (ns != NULL) return ns;
816 doc->nsptr++;
817 #ifdef TDOM_LESS_NS
818 if (doc->nsptr > 254) {
819 DBG(fprintf (stderr, "maximum number of namespaces exceeded!!!\n");)
820 domPanic("domNewNamespace: maximum number of namespaces exceeded!");
821 }
822 #endif
823 if (doc->nsptr >= doc->nslen) {
824 doc->namespaces = (domNS**) REALLOC ((char*) doc->namespaces,
825 sizeof (domNS*) * 2 * doc->nslen);
826 doc->nslen *= 2;
827 }
828 doc->namespaces[doc->nsptr] = (domNS*)MALLOC (sizeof (domNS));
829 ns = doc->namespaces[doc->nsptr];
830
831
832 if (prefix == NULL) {
833 ns->prefix = tdomstrdup("");
834 } else {
835 ns->prefix = tdomstrdup(prefix);
836 }
837 if (namespaceURI == NULL) {
838 ns->uri = tdomstrdup("");
839 } else {
840 ns->uri = tdomstrdup(namespaceURI);
841 }
842 ns->index = doc->nsptr + 1;
843
844 return ns;
845 }
846
847
848 /*---------------------------------------------------------------------------
849 | domSplitQName - extract namespace prefix (if any)
850 |
851 \--------------------------------------------------------------------------*/
852 int
domSplitQName(const char * name,char * prefix,const char ** localName)853 domSplitQName (
854 const char *name,
855 char *prefix,
856 const char **localName
857 )
858 {
859 const char *s;
860 char *p, *prefixEnd;
861
862 s = name;
863 p = prefix;
864 prefixEnd = &prefix[MAX_PREFIX_LEN-1];
865 while (*s && (*s != ':')) {
866 if (p < prefixEnd) *p++ = *s;
867 s++;
868 }
869 if (*s != ':') {
870 *prefix = '\0';
871 *localName = name;
872 return 0;
873 }
874 *p++ = '\0';
875 *localName = ++s;
876 DBG(fprintf(stderr, "domSplitName %s -> '%s' '%s'\n",
877 name, prefix, *localName);
878 )
879 return 1;
880 }
881
882
883 /*---------------------------------------------------------------------------
884 | domNamespaceURI
885 |
886 \--------------------------------------------------------------------------*/
887 const char *
domNamespaceURI(domNode * node)888 domNamespaceURI (
889 domNode *node
890 )
891 {
892 domAttrNode *attr;
893 domNS *ns;
894
895 if (node->nodeType == ATTRIBUTE_NODE) {
896 attr = (domAttrNode*)node;
897 if (!attr->namespace) return NULL;
898 if (attr->nodeFlags & IS_NS_NODE) return NULL;
899 ns = attr->parentNode->ownerDocument->namespaces[attr->namespace-1];
900 } else
901 if (node->nodeType == ELEMENT_NODE) {
902 if (!node->namespace) return NULL;
903 ns = node->ownerDocument->namespaces[node->namespace-1];
904 } else {
905 return NULL;
906 }
907 return ns->uri;
908 }
909
910
911 /*---------------------------------------------------------------------------
912 | domNamespacePrefix
913 |
914 \--------------------------------------------------------------------------*/
915 const char *
domNamespacePrefix(domNode * node)916 domNamespacePrefix (
917 domNode *node
918 )
919 {
920 domAttrNode *attr;
921 domNS *ns;
922
923 if (node->nodeType == ATTRIBUTE_NODE) {
924 attr = (domAttrNode*)node;
925 if (!attr->namespace) return NULL;
926 ns = attr->parentNode->ownerDocument->namespaces[attr->namespace-1];
927 } else
928 if (node->nodeType == ELEMENT_NODE) {
929 if (!node->namespace) return NULL;
930 ns = node->ownerDocument->namespaces[node->namespace-1];
931 } else {
932 return NULL;
933 }
934 if (ns) return ns->prefix;
935 return NULL;
936 }
937
938
939 /*---------------------------------------------------------------------------
940 | domGetLocalName
941 |
942 \--------------------------------------------------------------------------*/
943 const char *
domGetLocalName(const char * nodeName)944 domGetLocalName (
945 const char *nodeName
946 )
947 {
948 char prefix[MAX_PREFIX_LEN];
949 const char *localName;
950
951 domSplitQName (nodeName, prefix, &localName);
952 return localName;
953 }
954
955 /*
956 *----------------------------------------------------------------------
957 *
958 * domGetAttributeNodeNS --
959 *
960 * Search a given node for an attribute with namespace "uri" and
961 * localname "localname".
962 *
963 * Results:
964 * Returns a pointer to the attribute, if there is one with the
965 * given namespace and localname. Otherwise returns NULL.
966 *
967 * Side effects:
968 * None.
969 *
970 *----------------------------------------------------------------------
971 */
972
973 domAttrNode *
domGetAttributeNodeNS(domNode * node,const char * uri,const char * localname)974 domGetAttributeNodeNS (
975 domNode *node, /* The attributes of this node are searched for a
976 matching attribute; the node must exist */
977 const char *uri, /* The namespace of the demanded attribute */
978 const char *localname /* The localname of the demanded attribute */
979 )
980 {
981 domAttrNode *attr;
982 domNS *ns;
983 int noNS;
984 char prefix[MAX_PREFIX_LEN];
985 const char *attrLocalName;
986
987 if (uri[0] == '\0') noNS = 1;
988 else noNS = 0;
989
990 attr = node->firstAttr;
991 while (attr) {
992 if (noNS) {
993 if (!attr->namespace
994 && strcmp (attr->nodeName, localname) == 0) {
995 return attr;
996
997 }
998 } else {
999 if (attr->namespace) {
1000 domSplitQName (attr->nodeName, prefix, &attrLocalName);
1001 if (strcmp (localname, attrLocalName) == 0) {
1002 ns = domGetNamespaceByIndex (node->ownerDocument,
1003 attr->namespace);
1004 if (strcmp (ns->uri, uri) == 0) {
1005 return attr;
1006 }
1007 }
1008 }
1009 }
1010 attr = attr->nextSibling;
1011 }
1012 return NULL;
1013 }
1014
1015 /*
1016 *----------------------------------------------------------------------
1017 *
1018 * domPreviousSibling --
1019 *
1020 * Returns the previous node to the given node or NULL, if there
1021 * is no previous node. This function is needed in situations,
1022 * where the given node may also be an domAttrNode. Namespace
1023 * declaring attributes are treated as any other
1024 * attributes. Since the domAttrNode struct doesn't has an
1025 * element for the previous attribute, we need a function for the
1026 * relatively rare cases, the 'previous attribute' is
1027 * needed. Remember, that the XML rec say, that there is no
1028 * specific order of the attributes of a node.
1029 *
1030 * Results:
1031 * A pointer to the previous node of the given one
1032 * or NULL, if there isn't a previous node.
1033 *
1034 * Side effects:
1035 * None.
1036 *
1037 *----------------------------------------------------------------------
1038 */
1039
1040 domNode *
domPreviousSibling(domNode * node)1041 domPreviousSibling (
1042 domNode *node /* The reference attribute */
1043 )
1044 {
1045 domAttrNode *attr, *attr1;
1046
1047 if (node->nodeType != ATTRIBUTE_NODE) {
1048 return node->previousSibling;
1049 }
1050
1051 attr = (domAttrNode*) node;
1052 if (attr->parentNode->firstAttr == attr) {
1053 return NULL;
1054 }
1055 attr1 = attr->parentNode->firstAttr;
1056 while (attr1) {
1057 if (attr1->nextSibling == attr) {
1058 return (domNode*)attr1;
1059 }
1060 attr1 = attr1->nextSibling;
1061 }
1062 /* Not reached */
1063 return NULL;
1064 }
1065
1066 #ifndef TDOM_NO_EXPAT
1067
1068
1069 /*---------------------------------------------------------------------------
1070 | startElement
1071 |
1072 \--------------------------------------------------------------------------*/
1073 static void
startElement(void * userData,const char * name,const char ** atts)1074 startElement(
1075 void *userData,
1076 const char *name,
1077 const char **atts
1078 )
1079 {
1080 domReadInfo *info = userData;
1081 domNode *node, *parentNode;
1082 domLineColumn *lc;
1083 domAttrNode *attrnode, *lastAttr;
1084 const char **atPtr, **idAttPtr;
1085 Tcl_HashEntry *h;
1086 int hnew, len, pos, idatt, newNS, result;
1087 const char *xmlns, *localname;
1088 char tagPrefix[MAX_PREFIX_LEN];
1089 char prefix[MAX_PREFIX_LEN];
1090 domNS *ns;
1091 char feedbackCmd[24];
1092
1093 if (info->feedbackAfter) {
1094
1095 if (info->nextFeedbackPosition
1096 <= XML_GetCurrentByteIndex (info->parser)
1097 ) {
1098 if (info->feedbackCmd) {
1099 result = Tcl_GlobalEvalObj(info->interp, info->feedbackCmd);
1100 } else {
1101 sprintf(feedbackCmd, "%s", "::dom::domParseFeedback");
1102 result = Tcl_Eval(info->interp, feedbackCmd);
1103 }
1104 if (result != TCL_OK) {
1105 DBG(fprintf(stderr, "%s\n",
1106 Tcl_GetStringResult (info->interp)););
1107 info->status = result;
1108 XML_StopParser(info->parser, 1);
1109 return;
1110 }
1111 info->nextFeedbackPosition =
1112 XML_GetCurrentByteIndex (info->parser) + info->feedbackAfter;
1113 Tcl_ResetResult (info->interp);
1114 }
1115 }
1116
1117 DispatchPCDATA (info);
1118
1119 h = Tcl_CreateHashEntry(&HASHTAB(info->document,tdom_tagNames), name,
1120 &hnew);
1121 if (info->storeLineColumn) {
1122 node = (domNode*) domAlloc(sizeof(domNode)
1123 + sizeof(domLineColumn));
1124 } else {
1125 node = (domNode*) domAlloc(sizeof(domNode));
1126 }
1127 memset(node, 0, sizeof(domNode));
1128 node->nodeType = ELEMENT_NODE;
1129 node->nodeFlags = 0;
1130 node->namespace = 0;
1131 node->nodeName = (char *)&(h->key);
1132 node->nodeNumber = NODE_NO(info->document);
1133 node->ownerDocument = info->document;
1134
1135 if (info->baseURIstack[info->baseURIstackPos].baseURI
1136 != XML_GetBase (info->parser)) {
1137 h = Tcl_CreateHashEntry (info->document->baseURIs,
1138 (char*) node,
1139 &hnew);
1140 Tcl_SetHashValue (h, tdomstrdup (XML_GetBase (info->parser)));
1141 node->nodeFlags |= HAS_BASEURI;
1142 info->baseURIstackPos++;
1143 if (info->baseURIstackPos >= info->baseURIstackSize) {
1144 info->baseURIstack = (domActiveBaseURI*) REALLOC(
1145 (char*)info->baseURIstack,
1146 sizeof(domActiveBaseURI) * 2 * info->baseURIstackSize);
1147 info->baseURIstackSize = 2 * info->baseURIstackSize;
1148 }
1149 info->baseURIstack[info->baseURIstackPos].baseURI
1150 = XML_GetBase (info->parser);
1151 info->baseURIstack[info->baseURIstackPos].depth
1152 = info->depth;
1153 }
1154
1155 if (info->depth == 0) {
1156 if (info->document->rootNode->lastChild) {
1157 info->document->rootNode->lastChild->nextSibling = node;
1158 node->previousSibling = info->document->rootNode->lastChild;
1159 } else {
1160 info->document->rootNode->firstChild = node;
1161 }
1162 info->document->rootNode->lastChild = node;
1163 } else {
1164 parentNode = info->currentNode;
1165 node->parentNode = parentNode;
1166 if (parentNode->firstChild) {
1167 parentNode->lastChild->nextSibling = node;
1168 node->previousSibling = parentNode->lastChild;
1169 parentNode->lastChild = node;
1170 } else {
1171 parentNode->firstChild = parentNode->lastChild = node;
1172 }
1173 }
1174 info->currentNode = node;
1175 if (info->storeLineColumn) {
1176 lc = (domLineColumn*) ( ((char*)node) + sizeof(domNode));
1177 node->nodeFlags |= HAS_LINE_COLUMN;
1178 lc->line = XML_GetCurrentLineNumber(info->parser);
1179 lc->column = XML_GetCurrentColumnNumber(info->parser);
1180 }
1181
1182
1183 lastAttr = NULL;
1184 /*--------------------------------------------------------------
1185 | process namespace declarations
1186 |
1187 \-------------------------------------------------------------*/
1188 if (!info->ignorexmlns) {
1189 for (atPtr = atts; atPtr[0] && atPtr[1]; atPtr += 2) {
1190
1191 if (strncmp(atPtr[0], "xmlns", 5) == 0) {
1192 xmlns = atPtr[0];
1193 newNS = 1;
1194 if (xmlns[5] == ':') {
1195 if (atPtr[1][0] == '\0') {
1196 Tcl_SetResult (info->interp, "Missing URI in Namespace "
1197 "declaration", NULL);
1198 XML_StopParser(info->parser, 0);
1199 return;
1200 }
1201 if (domIsNamespaceInScope (info->activeNS, info->activeNSpos,
1202 &(xmlns[6]), atPtr[1])) {
1203 ns = domLookupPrefix (info->currentNode, &(xmlns[6]));
1204 newNS = 0;
1205 }
1206 else {
1207 ns = domNewNamespace(info->document, &xmlns[6], atPtr[1]);
1208 }
1209 } else {
1210 ns = domNewNamespace(info->document, "", atPtr[1]);
1211 }
1212 if (newNS) {
1213 /* push active namespace */
1214 info->activeNSpos++;
1215 if (info->activeNSpos >= info->activeNSsize) {
1216 info->activeNS = (domActiveNS*) REALLOC(
1217 (char*)info->activeNS,
1218 sizeof(domActiveNS) * 2 * info->activeNSsize);
1219 info->activeNSsize = 2 * info->activeNSsize;
1220 }
1221 info->activeNS[info->activeNSpos].depth = info->depth;
1222 info->activeNS[info->activeNSpos].namespace = ns;
1223 }
1224
1225 h = Tcl_CreateHashEntry(&HASHTAB(info->document, tdom_attrNames),
1226 atPtr[0], &hnew);
1227 attrnode = (domAttrNode*) domAlloc(sizeof(domAttrNode));
1228 memset(attrnode, 0, sizeof(domAttrNode));
1229 attrnode->nodeType = ATTRIBUTE_NODE;
1230 attrnode->nodeFlags = IS_NS_NODE;
1231 attrnode->namespace = ns->index;
1232 attrnode->nodeName = (char *)&(h->key);
1233 attrnode->parentNode = node;
1234 len = strlen(atPtr[1]);
1235 attrnode->valueLength = len;
1236 attrnode->nodeValue = (char*)MALLOC(len+1);
1237 strcpy(attrnode->nodeValue, atPtr[1]);
1238 if (node->firstAttr) {
1239 lastAttr->nextSibling = attrnode;
1240 } else {
1241 node->firstAttr = attrnode;
1242 }
1243 lastAttr = attrnode;
1244 }
1245
1246 }
1247
1248 /*----------------------------------------------------------
1249 | look for namespace of element
1250 \---------------------------------------------------------*/
1251 domSplitQName (name, tagPrefix, &localname);
1252 for (pos = info->activeNSpos; pos >= 0; pos--) {
1253 if ( ((tagPrefix[0] == '\0')
1254 && (info->activeNS[pos].namespace->prefix[0] == '\0'))
1255 || ((tagPrefix[0] != '\0')
1256 && (info->activeNS[pos].namespace->prefix[0] != '\0')
1257 && (strcmp(tagPrefix,
1258 info->activeNS[pos].namespace->prefix) == 0))
1259 ) {
1260 if (info->activeNS[pos].namespace->prefix[0] == '\0'
1261 && info->activeNS[pos].namespace->uri[0] == '\0'
1262 && tagPrefix[0] == '\0') {
1263 /* xml-names rec. 5.2: "The default namespace can be
1264 set to the empty string. This has the same effect,
1265 within the scope of the declaration, of there being
1266 no default namespace." */
1267 goto elemNSfound;
1268 }
1269 node->namespace = info->activeNS[pos].namespace->index;
1270 DBG(fprintf(stderr, "tag='%s' uri='%s' \n",
1271 node->nodeName,
1272 info->activeNS[pos].namespace->uri);
1273 )
1274 goto elemNSfound;
1275 }
1276 }
1277 if (tagPrefix[0] != '\0') {
1278 if (strcmp (tagPrefix, "xml")==0) {
1279 node->namespace = info->document->rootNode->firstAttr->namespace;
1280 } else {
1281 /* Since where here, this means, the element has a
1282 up to now not declared namespace prefix. */
1283 Tcl_SetResult (info->interp, "Namespace prefix is not "
1284 "defined", NULL);
1285 XML_StopParser(info->parser, 0);
1286 return;
1287 }
1288 }
1289 }
1290 elemNSfound:
1291
1292 /*--------------------------------------------------------------
1293 | add the attribute nodes
1294 |
1295 \-------------------------------------------------------------*/
1296 if ((idatt = XML_GetIdAttributeIndex (info->parser)) != -1) {
1297 if (!info->document->ids) {
1298 info->document->ids = MALLOC (sizeof (Tcl_HashTable));
1299 Tcl_InitHashTable (info->document->ids, TCL_STRING_KEYS);
1300 }
1301 h = Tcl_CreateHashEntry (info->document->ids,
1302 atts[idatt+1],
1303 &hnew);
1304 /* if hnew isn't 1 this is a validation error. Hm, no clear way
1305 to report this. And more, XSLT and XPath can process not
1306 valid XML, the spec mentioned this even within the context
1307 of id(). If some elements share the same ID, the first in
1308 document order should be used. Doing it this way, this is
1309 guaranteed for unchanged DOM trees. There are problems, if
1310 the DOM tree is changed, befor using id() */
1311 if (hnew) {
1312 Tcl_SetHashValue (h, node);
1313 }
1314 idAttPtr = atts + idatt;
1315 } else {
1316 idAttPtr = NULL;
1317 }
1318 /* lastAttr already set right, either to NULL above, or to the last
1319 NS attribute */
1320 for (atPtr = atts; atPtr[0] && atPtr[1]; atPtr += 2) {
1321 if (!info->ignorexmlns) {
1322 if (strncmp(atPtr[0], "xmlns", 5) == 0) {
1323 continue;
1324 }
1325 }
1326 h = Tcl_CreateHashEntry(&HASHTAB(info->document, tdom_attrNames),
1327 atPtr[0], &hnew);
1328 attrnode = (domAttrNode*) domAlloc(sizeof(domAttrNode));
1329 memset(attrnode, 0, sizeof(domAttrNode));
1330 attrnode->nodeType = ATTRIBUTE_NODE;
1331 if (atPtr == idAttPtr) {
1332 attrnode->nodeFlags |= IS_ID_ATTRIBUTE;
1333 } else {
1334 attrnode->nodeFlags = 0;
1335 }
1336 attrnode->namespace = 0;
1337 attrnode->nodeName = (char *)&(h->key);
1338 attrnode->parentNode = node;
1339 len = strlen(atPtr[1]);
1340 attrnode->valueLength = len;
1341 attrnode->nodeValue = (char*)MALLOC(len+1);
1342 strcpy(attrnode->nodeValue, (char *)atPtr[1]);
1343
1344 if (node->firstAttr) {
1345 lastAttr->nextSibling = attrnode;
1346 } else {
1347 node->firstAttr = attrnode;
1348 }
1349 lastAttr = attrnode;
1350
1351 if (!info->ignorexmlns) {
1352 /*----------------------------------------------------------
1353 | look for attribute namespace
1354 \---------------------------------------------------------*/
1355 domSplitQName (attrnode->nodeName, prefix, &localname);
1356 if (prefix[0] != '\0') {
1357 for (pos = info->activeNSpos; pos >= 0; pos--) {
1358 if ( ((prefix[0] == '\0')
1359 && (info->activeNS[pos].namespace->prefix[0] == '\0'))
1360 || ((prefix[0] != '\0')
1361 && (info->activeNS[pos].namespace->prefix[0] != '\0')
1362 && (strcmp(prefix, info->activeNS[pos].namespace->prefix) == 0))
1363 ) {
1364 attrnode->namespace = info->activeNS[pos].namespace->index;
1365 DBG(fprintf(stderr, "attr='%s' uri='%s' \n",
1366 attrnode->nodeName,
1367 info->activeNS[pos].namespace->uri);
1368 )
1369 goto attrNSfound;
1370 }
1371 }
1372 if (strcmp (prefix, "xml")==0) {
1373 attrnode->namespace =
1374 info->document->rootNode->firstAttr->namespace;
1375 } else {
1376 /* Since where here, this means, the attribute has a
1377 up to now not declared namespace prefix. We probably
1378 should return this as an error, shouldn't we?*/
1379 }
1380 attrNSfound:
1381 ;
1382 }
1383 }
1384 }
1385
1386 info->depth++;
1387 }
1388
1389 /*---------------------------------------------------------------------------
1390 | endElement
1391 |
1392 \--------------------------------------------------------------------------*/
1393 static void
endElement(void * userData,const char * name)1394 endElement (
1395 void *userData,
1396 const char *name
1397 )
1398 {
1399 domReadInfo *info = userData;
1400
1401 DispatchPCDATA (info);
1402
1403 info->depth--;
1404 if (!info->ignorexmlns) {
1405 /* pop active namespaces */
1406 while ( (info->activeNSpos >= 0) &&
1407 (info->activeNS[info->activeNSpos].depth == info->depth) )
1408 {
1409 info->activeNSpos--;
1410 }
1411 }
1412
1413 if (info->depth != -1) {
1414 info->currentNode = info->currentNode->parentNode;
1415 } else {
1416 info->currentNode = NULL;
1417 }
1418
1419 if (info->depth) {
1420 if (info->baseURIstack[info->baseURIstackPos].depth == info->depth) {
1421 info->baseURIstackPos--;
1422 }
1423 }
1424 }
1425
1426 /*---------------------------------------------------------------------------
1427 | characterDataHandler
1428 |
1429 \--------------------------------------------------------------------------*/
1430 static void
characterDataHandler(void * userData,const char * s,int len)1431 characterDataHandler (
1432 void *userData,
1433 const char *s,
1434 int len
1435 )
1436 {
1437 domReadInfo *info = userData;
1438
1439 Tcl_DStringAppend (info->cdata, s, len);
1440 return;
1441
1442 }
1443
1444 /*---------------------------------------------------------------------------
1445 | startCDATA
1446 |
1447 \--------------------------------------------------------------------------*/
1448 static void
startCDATA(void * userData)1449 startCDATA (
1450 void *userData
1451 )
1452 {
1453 domReadInfo *info = userData;
1454
1455 DispatchPCDATA (info);
1456 info->cdataSection = 1;
1457 }
1458
1459 /*---------------------------------------------------------------------------
1460 | endCDATA
1461 |
1462 \--------------------------------------------------------------------------*/
1463 static void
endCDATA(void * userData)1464 endCDATA (
1465 void *userData
1466 )
1467 {
1468 domReadInfo *info = userData;
1469
1470 DispatchPCDATA (info);
1471 info->cdataSection = 0;
1472 }
1473
1474 /*---------------------------------------------------------------------------
1475 | DispatchPCDATA
1476 |
1477 \--------------------------------------------------------------------------*/
1478 static void
DispatchPCDATA(domReadInfo * info)1479 DispatchPCDATA (
1480 domReadInfo *info
1481 )
1482 {
1483 domTextNode *node;
1484 domNode *parentNode;
1485 domLineColumn *lc;
1486 Tcl_HashEntry *h;
1487 char *s;
1488 int len, hnew;
1489
1490 len = Tcl_DStringLength (info->cdata);
1491 if (!len && !info->cdataSection) return;
1492 s = Tcl_DStringValue (info->cdata);
1493
1494 parentNode = info->currentNode;
1495 if (!parentNode) return;
1496
1497 if ( parentNode->lastChild
1498 && parentNode->lastChild->nodeType == TEXT_NODE
1499 && !info->cdataSection) {
1500
1501 /* normalize text node, i.e. there are no adjacent text nodes */
1502 node = (domTextNode*)parentNode->lastChild;
1503 node->nodeValue = REALLOC(node->nodeValue, node->valueLength + len);
1504 memmove(node->nodeValue + node->valueLength, s, len);
1505 node->valueLength += len;
1506
1507 } else {
1508
1509 if (info->ignoreWhiteSpaces) {
1510 char *pc;
1511 int i, only_whites;
1512
1513 only_whites = 1;
1514 for (i=0, pc = s; i < len; i++, pc++) {
1515 if ( (*pc != ' ') &&
1516 (*pc != '\t') &&
1517 (*pc != '\n') &&
1518 (*pc != '\r') ) {
1519 only_whites = 0;
1520 break;
1521 }
1522 }
1523 if (only_whites) {
1524 Tcl_DStringSetLength (info->cdata, 0);
1525 return;
1526 }
1527 }
1528
1529 if (info->storeLineColumn) {
1530 node = (domTextNode*) domAlloc(sizeof(domTextNode)
1531 + sizeof(domLineColumn));
1532 } else {
1533 node = (domTextNode*) domAlloc(sizeof(domTextNode));
1534 }
1535 memset(node, 0, sizeof(domTextNode));
1536 if (info->cdataSection)
1537 node->nodeType = CDATA_SECTION_NODE;
1538 else
1539 node->nodeType = TEXT_NODE;
1540 node->nodeFlags = 0;
1541 node->nodeNumber = NODE_NO(info->document);
1542 node->valueLength = len;
1543 node->nodeValue = (char*)MALLOC(len);
1544 memmove(node->nodeValue, s, len);
1545
1546 node->ownerDocument = info->document;
1547 node->parentNode = parentNode;
1548 if (parentNode->nodeType == ELEMENT_NODE) {
1549 if (parentNode->firstChild) {
1550 parentNode->lastChild->nextSibling = (domNode*)node;
1551 node->previousSibling = parentNode->lastChild;
1552 } else {
1553 parentNode->firstChild = (domNode*)node;
1554 }
1555 parentNode->lastChild = (domNode*)node;
1556 }
1557
1558 if (info->baseURIstack[info->baseURIstackPos].baseURI
1559 != XML_GetBase (info->parser)) {
1560 h = Tcl_CreateHashEntry (info->document->baseURIs,
1561 (char*) node,
1562 &hnew);
1563 Tcl_SetHashValue (h, tdomstrdup (XML_GetBase (info->parser)));
1564 node->nodeFlags |= HAS_BASEURI;
1565 }
1566
1567 if (info->storeLineColumn) {
1568 lc = (domLineColumn*) ( ((char*)node) + sizeof(domTextNode) );
1569 node->nodeFlags |= HAS_LINE_COLUMN;
1570 lc->line = XML_GetCurrentLineNumber(info->parser);
1571 lc->column = XML_GetCurrentColumnNumber(info->parser);
1572 }
1573 }
1574 Tcl_DStringSetLength (info->cdata, 0);
1575 }
1576
1577
1578 /*---------------------------------------------------------------------------
1579 | commentHandler
1580 |
1581 \--------------------------------------------------------------------------*/
1582 static void
commentHandler(void * userData,const char * s)1583 commentHandler (
1584 void *userData,
1585 const char *s
1586 )
1587 {
1588 domReadInfo *info = userData;
1589 domTextNode *node;
1590 domNode *parentNode;
1591 domLineColumn *lc;
1592 int len, hnew;
1593 Tcl_HashEntry *h;
1594
1595 if (info->insideDTD) {
1596 DBG(fprintf (stderr, "commentHandler: insideDTD, skipping\n");)
1597 return;
1598 }
1599
1600 DispatchPCDATA (info);
1601
1602 len = strlen(s);
1603 parentNode = info->currentNode;
1604
1605 if (info->storeLineColumn) {
1606 node = (domTextNode*) domAlloc(sizeof(domTextNode)
1607 + sizeof(domLineColumn));
1608 } else {
1609 node = (domTextNode*) domAlloc(sizeof(domTextNode));
1610 }
1611 memset(node, 0, sizeof(domTextNode));
1612 node->nodeType = COMMENT_NODE;
1613 node->nodeFlags = 0;
1614 node->nodeNumber = NODE_NO(info->document);
1615 node->valueLength = len;
1616 node->nodeValue = (char*)MALLOC(len);
1617 memmove(node->nodeValue, s, len);
1618
1619 node->ownerDocument = info->document;
1620 node->parentNode = parentNode;
1621 if (parentNode == NULL) {
1622 if (info->document->rootNode->lastChild) {
1623 info->document->rootNode->lastChild->nextSibling = (domNode*)node;
1624 node->previousSibling = info->document->rootNode->lastChild;
1625 } else {
1626 info->document->rootNode->firstChild = (domNode*)node;
1627 }
1628 info->document->rootNode->lastChild = (domNode*)node;
1629 } else if(parentNode->nodeType == ELEMENT_NODE) {
1630 if (parentNode->firstChild) {
1631 parentNode->lastChild->nextSibling = (domNode*)node;
1632 node->previousSibling = parentNode->lastChild;
1633 parentNode->lastChild = (domNode*)node;
1634 } else {
1635 parentNode->firstChild = parentNode->lastChild = (domNode*)node;
1636 }
1637 }
1638
1639 if (info->baseURIstack[info->baseURIstackPos].baseURI
1640 != XML_GetBase (info->parser)) {
1641 h = Tcl_CreateHashEntry (info->document->baseURIs,
1642 (char*) node,
1643 &hnew);
1644 Tcl_SetHashValue (h, tdomstrdup (XML_GetBase (info->parser)));
1645 node->nodeFlags |= HAS_BASEURI;
1646 }
1647
1648 if (info->storeLineColumn) {
1649 lc = (domLineColumn*) ( ((char*)node) + sizeof(domTextNode) );
1650 node->nodeFlags |= HAS_LINE_COLUMN;
1651 lc->line = XML_GetCurrentLineNumber(info->parser);
1652 lc->column = XML_GetCurrentColumnNumber(info->parser);
1653 }
1654 }
1655
1656
1657 /*---------------------------------------------------------------------------
1658 | processingInstructionHandler
1659 |
1660 \--------------------------------------------------------------------------*/
1661 static void
processingInstructionHandler(void * userData,const char * target,const char * data)1662 processingInstructionHandler(
1663 void *userData,
1664 const char *target,
1665 const char *data
1666 )
1667 {
1668 domProcessingInstructionNode *node;
1669 domReadInfo *info = userData;
1670 domNode *parentNode;
1671 domLineColumn *lc;
1672 int len,hnew;
1673 Tcl_HashEntry *h;
1674
1675 if (info->insideDTD) {
1676 DBG(fprintf (stderr,
1677 "processingInstructionHandler: insideDTD, skipping\n");)
1678 return;
1679 }
1680
1681 DispatchPCDATA (info);
1682
1683 parentNode = info->currentNode;
1684
1685 if (info->storeLineColumn) {
1686 node = (domProcessingInstructionNode*)
1687 domAlloc(sizeof(domProcessingInstructionNode)
1688 + sizeof(domLineColumn));
1689 } else {
1690 node = (domProcessingInstructionNode*)
1691 domAlloc(sizeof(domProcessingInstructionNode));
1692 }
1693 memset(node, 0, sizeof(domProcessingInstructionNode));
1694 node->nodeType = PROCESSING_INSTRUCTION_NODE;
1695 node->nodeFlags = 0;
1696 node->namespace = 0;
1697 node->nodeNumber = NODE_NO(info->document);
1698
1699 if (info->baseURIstack[info->baseURIstackPos].baseURI
1700 != XML_GetBase (info->parser)) {
1701 h = Tcl_CreateHashEntry (info->document->baseURIs,
1702 (char*) node,
1703 &hnew);
1704 Tcl_SetHashValue (h, tdomstrdup (XML_GetBase (info->parser)));
1705 node->nodeFlags |= HAS_BASEURI;
1706 }
1707
1708 len = strlen(target);
1709 node->targetLength = len;
1710 node->targetValue = (char*)MALLOC(len);
1711 memmove(node->targetValue, target, len);
1712
1713 len = strlen(data);
1714 node->dataLength = len;
1715 node->dataValue = (char*)MALLOC(len);
1716 memmove(node->dataValue, data, len);
1717
1718 node->ownerDocument = info->document;
1719 node->parentNode = parentNode;
1720 if (parentNode == NULL) {
1721 if (info->document->rootNode->lastChild) {
1722 info->document->rootNode->lastChild->nextSibling = (domNode*)node;
1723 node->previousSibling = info->document->rootNode->lastChild;
1724 } else {
1725 info->document->rootNode->firstChild = (domNode*)node;
1726 }
1727 info->document->rootNode->lastChild = (domNode*)node;
1728 } else if(parentNode->nodeType == ELEMENT_NODE) {
1729 if (parentNode->firstChild) {
1730 parentNode->lastChild->nextSibling = (domNode*)node;
1731 node->previousSibling = parentNode->lastChild;
1732 parentNode->lastChild = (domNode*)node;
1733 } else {
1734 parentNode->firstChild = parentNode->lastChild = (domNode*)node;
1735 }
1736 }
1737 if (info->storeLineColumn) {
1738 lc = (domLineColumn*)(((char*)node)+sizeof(domProcessingInstructionNode));
1739 node->nodeFlags |= HAS_LINE_COLUMN;
1740 lc->line = XML_GetCurrentLineNumber(info->parser);
1741 lc->column = XML_GetCurrentColumnNumber(info->parser);
1742 }
1743 }
1744
1745 /*---------------------------------------------------------------------------
1746 | entityDeclHandler
1747 |
1748 \--------------------------------------------------------------------------*/
1749 static void
entityDeclHandler(void * userData,const char * entityName,int is_parameter_entity,const char * value,int value_length,const char * base,const char * systemId,const char * publicId,const char * notationName)1750 entityDeclHandler (
1751 void *userData,
1752 const char *entityName,
1753 int is_parameter_entity,
1754 const char *value,
1755 int value_length,
1756 const char *base,
1757 const char *systemId,
1758 const char *publicId,
1759 const char *notationName
1760 )
1761 {
1762 domReadInfo *info = (domReadInfo *) userData;
1763 Tcl_HashEntry *entryPtr;
1764 int hnew;
1765
1766 if (notationName) {
1767 if (!info->document->unparsedEntities) {
1768 info->document->unparsedEntities = MALLOC (sizeof (Tcl_HashTable));
1769 Tcl_InitHashTable (info->document->unparsedEntities,
1770 TCL_STRING_KEYS);
1771 }
1772 entryPtr = Tcl_CreateHashEntry (info->document->unparsedEntities,
1773 entityName, &hnew);
1774 if (hnew) {
1775 Tcl_SetHashValue (entryPtr, tdomstrdup (systemId));
1776 }
1777 }
1778 }
1779
1780 /*---------------------------------------------------------------------------
1781 | externalEntityRefHandler
1782 |
1783 \--------------------------------------------------------------------------*/
1784 static int
externalEntityRefHandler(XML_Parser parser,const char * openEntityNames,const char * base,const char * systemId,const char * publicId)1785 externalEntityRefHandler (
1786 XML_Parser parser,
1787 const char *openEntityNames,
1788 const char *base,
1789 const char *systemId,
1790 const char *publicId
1791 )
1792 {
1793 domReadInfo *info = (domReadInfo *) XML_GetUserData (parser);
1794
1795 Tcl_Obj *cmdPtr, *resultObj, *resultTypeObj, *extbaseObj, *xmlstringObj;
1796 Tcl_Obj *channelIdObj;
1797 int result, mode, done, byteIndex, i;
1798 int keepresult = 0;
1799 size_t len;
1800 int tclLen;
1801 XML_Parser extparser, oldparser = NULL;
1802 char buf[4096], *resultType, *extbase, *xmlstring, *channelId, s[50];
1803 Tcl_Channel chan = (Tcl_Channel) NULL;
1804 enum XML_Status status;
1805 XML_Index storedNextFeedbackPosition;
1806 const char *interpResult;
1807
1808 if (info->document->extResolver == NULL) {
1809 Tcl_AppendResult (info->interp, "Can't read external entity \"",
1810 systemId, "\": No -externalentitycommand given",
1811 NULL);
1812 return 0;
1813 }
1814
1815 DispatchPCDATA (info);
1816
1817 /*
1818 * Take a copy of the callback script so that arguments may be appended.
1819 */
1820 cmdPtr = Tcl_NewStringObj(info->document->extResolver, -1);
1821 Tcl_IncrRefCount(cmdPtr);
1822
1823 if (base) {
1824 Tcl_ListObjAppendElement(info->interp, cmdPtr,
1825 Tcl_NewStringObj(base, strlen(base)));
1826 } else {
1827 Tcl_ListObjAppendElement(info->interp, cmdPtr,
1828 Tcl_NewObj());
1829 }
1830
1831 /* For a document with doctype declaration, the systemId is always
1832 != NULL. But if the document doesn't have a doctype declaration
1833 and the user uses -useForeignDTD 1, the externalEntityRefHandler
1834 will be called with a systemId (and publicId and openEntityNames)
1835 == NULL. */
1836 if (systemId) {
1837 Tcl_ListObjAppendElement(info->interp, cmdPtr,
1838 Tcl_NewStringObj(systemId, strlen(systemId)));
1839 } else {
1840 Tcl_ListObjAppendElement(info->interp, cmdPtr,
1841 Tcl_NewObj());
1842 }
1843
1844 if (publicId) {
1845 Tcl_ListObjAppendElement(info->interp, cmdPtr,
1846 Tcl_NewStringObj(publicId, strlen(publicId)));
1847 } else {
1848 Tcl_ListObjAppendElement(info->interp, cmdPtr,
1849 Tcl_NewObj());
1850 }
1851
1852
1853 result = Tcl_EvalObjEx (info->interp, cmdPtr,
1854 TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL);
1855
1856 Tcl_DecrRefCount(cmdPtr);
1857
1858 if (result != TCL_OK) {
1859 info->status = result;
1860 return 0;
1861 }
1862
1863 extparser = XML_ExternalEntityParserCreate (parser, openEntityNames, 0);
1864
1865 resultObj = Tcl_GetObjResult (info->interp);
1866 Tcl_IncrRefCount (resultObj);
1867
1868 result = Tcl_ListObjLength (info->interp, resultObj, &tclLen);
1869 if ((result != TCL_OK) || (tclLen != 3)) {
1870 goto wrongScriptResult;
1871 }
1872 result = Tcl_ListObjIndex (info->interp, resultObj, 0, &resultTypeObj);
1873 if (result != TCL_OK) {
1874 goto wrongScriptResult;
1875 }
1876 resultType = Tcl_GetString(resultTypeObj);
1877
1878 if (strcmp (resultType, "string") == 0) {
1879 result = Tcl_ListObjIndex (info->interp, resultObj, 2, &xmlstringObj);
1880 xmlstring = Tcl_GetString(xmlstringObj);
1881 len = strlen (xmlstring);
1882 chan = NULL;
1883 } else if (strcmp (resultType, "channel") == 0) {
1884 xmlstring = NULL;
1885 len = 0;
1886 result = Tcl_ListObjIndex (info->interp, resultObj, 2, &channelIdObj);
1887 channelId = Tcl_GetString(channelIdObj);
1888 chan = Tcl_GetChannel (info->interp, channelId, &mode);
1889 if (chan == (Tcl_Channel) NULL) {
1890 goto wrongScriptResult;
1891 }
1892 if ((mode & TCL_READABLE) == 0) {
1893 return 0;
1894 }
1895 } else if (strcmp (resultType, "filename") == 0) {
1896 /* result type "filename" not yet implemented */
1897 return 0;
1898 } else {
1899 goto wrongScriptResult;
1900 }
1901
1902 result = Tcl_ListObjIndex (info->interp, resultObj, 1, &extbaseObj);
1903 if (result != TCL_OK) {
1904 goto wrongScriptResult;
1905 }
1906 extbase = Tcl_GetString(extbaseObj);
1907
1908 /* TODO: what to do, if this document was already parsed before ? */
1909
1910 if (!extparser) {
1911 Tcl_DecrRefCount (resultObj);
1912 Tcl_SetResult (info->interp,
1913 "unable to create expat external entity parser",
1914 NULL);
1915 return 0;
1916 }
1917
1918 oldparser = info->parser;
1919 info->parser = extparser;
1920 XML_SetBase (extparser, extbase);
1921 storedNextFeedbackPosition = info->nextFeedbackPosition;
1922 info->nextFeedbackPosition = info->feedbackAfter;
1923
1924 Tcl_ResetResult (info->interp);
1925 result = 1;
1926 if (chan == NULL) {
1927 status = XML_Parse(extparser, xmlstring, strlen (xmlstring), 1);
1928 switch (status) {
1929 case XML_STATUS_ERROR:
1930 interpResult = Tcl_GetStringResult(info->interp);
1931 sprintf(s, "%ld", XML_GetCurrentLineNumber(extparser));
1932 if (interpResult[0] == '\0') {
1933 Tcl_ResetResult (info->interp);
1934 Tcl_AppendResult(info->interp, "error \"",
1935 XML_ErrorString(XML_GetErrorCode(extparser)),
1936 "\" in entity \"", systemId,
1937 "\" at line ", s, " character ", NULL);
1938 sprintf(s, "%ld", XML_GetCurrentColumnNumber(extparser));
1939 Tcl_AppendResult(info->interp, s, NULL);
1940 byteIndex = XML_GetCurrentByteIndex(extparser);
1941 if (byteIndex != -1) {
1942 Tcl_AppendResult(info->interp, "\n\"", NULL);
1943 s[1] = '\0';
1944 for (i=-20; i < 40; i++) {
1945 if ((byteIndex+i)>=0) {
1946 if (xmlstring[byteIndex+i]) {
1947 s[0] = xmlstring[byteIndex+i];
1948 Tcl_AppendResult(info->interp, s, NULL);
1949 if (i==0) {
1950 Tcl_AppendResult(info->interp,
1951 " <--Error-- ", NULL);
1952 }
1953 } else {
1954 break;
1955 }
1956 }
1957 }
1958 Tcl_AppendResult(info->interp, "\"",NULL);
1959 }
1960 } else {
1961 Tcl_AppendResult(info->interp, ", referenced in entity \"",
1962 systemId,
1963 "\" at line ", s, " character ", NULL);
1964 sprintf(s, "%ld", XML_GetCurrentColumnNumber(extparser));
1965 Tcl_AppendResult(info->interp, s, NULL);
1966 }
1967 keepresult = 1;
1968 result = 0;
1969 break;
1970 case XML_STATUS_SUSPENDED:
1971 XML_StopParser (oldparser, 1);
1972 keepresult = 1;
1973 break;
1974 default:
1975 break;
1976 }
1977 } else {
1978 do {
1979 len = Tcl_Read (chan, buf, sizeof(buf));
1980 done = len < sizeof(buf);
1981 status = XML_Parse (extparser, buf, len, done);
1982 switch (status) {
1983 case XML_STATUS_ERROR:
1984 interpResult = Tcl_GetStringResult(info->interp);
1985 sprintf(s, "%ld", XML_GetCurrentLineNumber(extparser));
1986 if (interpResult[0] == '\0') {
1987 Tcl_ResetResult (info->interp);
1988 Tcl_AppendResult(info->interp, "error \"",
1989 XML_ErrorString(XML_GetErrorCode(extparser)),
1990 "\" in entity \"", systemId,
1991 "\" at line ", s, " character ", NULL);
1992 sprintf(s, "%ld", XML_GetCurrentColumnNumber(extparser));
1993 Tcl_AppendResult(info->interp, s, NULL);
1994 } else {
1995 Tcl_AppendResult(info->interp, ", referenced in entity \"",
1996 systemId,
1997 "\" at line ", s, " character ", NULL);
1998 sprintf(s, "%ld", XML_GetCurrentColumnNumber(extparser));
1999 Tcl_AppendResult(info->interp, s, NULL);
2000 }
2001 result = 0;
2002 keepresult = 1;
2003 done = 1;
2004 break;
2005 case XML_STATUS_SUSPENDED:
2006 XML_StopParser (oldparser, 1);
2007 keepresult = 1;
2008 done = 1;
2009 break;
2010 default:
2011 break;
2012 }
2013 } while (!done);
2014 }
2015
2016 if (result) {
2017 DispatchPCDATA (info);
2018 }
2019 if (!keepresult) {
2020 Tcl_ResetResult (info->interp);
2021 }
2022 XML_ParserFree (extparser);
2023 info->parser = oldparser;
2024 info->nextFeedbackPosition = storedNextFeedbackPosition;
2025 Tcl_DecrRefCount (resultObj);
2026 return result;
2027
2028 wrongScriptResult:
2029 Tcl_DecrRefCount (resultObj);
2030 Tcl_ResetResult (info->interp);
2031 XML_ParserFree (extparser);
2032 if (oldparser) {
2033 info->parser = oldparser;
2034 }
2035 info->status = TCL_ERROR;
2036 Tcl_AppendResult (info->interp, "The -externalentitycommand script "
2037 "has to return a Tcl list with 3 elements.\n"
2038 "Syntax: {string|channel|filename <baseurl> <data>}\n",
2039 NULL);
2040 return 0;
2041 }
2042
2043 /*---------------------------------------------------------------------------
2044 | startDoctypeDeclHandler
2045 |
2046 \--------------------------------------------------------------------------*/
2047 static void
startDoctypeDeclHandler(void * userData,const char * doctypeName,const char * sysid,const char * pubid,int has_internal_subset)2048 startDoctypeDeclHandler (
2049 void *userData,
2050 const char *doctypeName,
2051 const char *sysid,
2052 const char *pubid,
2053 int has_internal_subset
2054 )
2055 {
2056 domReadInfo *info = (domReadInfo *) userData;
2057
2058 if (pubid) {
2059 info->document->doctype = (domDocInfo*)MALLOC (sizeof (domDocInfo));
2060 memset (info->document->doctype, 0, sizeof (domDocInfo));
2061 info->document->doctype->systemId = tdomstrdup (sysid);
2062 info->document->doctype->publicId = tdomstrdup (pubid);
2063 } else if (sysid) {
2064 info->document->doctype = (domDocInfo*)MALLOC (sizeof (domDocInfo));
2065 memset (info->document->doctype, 0, sizeof (domDocInfo));
2066 info->document->doctype->systemId = tdomstrdup (sysid);
2067 }
2068 info->insideDTD = 1;
2069 }
2070
2071 /*---------------------------------------------------------------------------
2072 | endDoctypeDeclHandler
2073 |
2074 \--------------------------------------------------------------------------*/
2075 static void
endDoctypeDeclHandler(void * userData)2076 endDoctypeDeclHandler (
2077 void *userData
2078 )
2079 {
2080 domReadInfo *info = (domReadInfo *) userData;
2081
2082 info->insideDTD = 0;
2083 }
2084
2085 /*---------------------------------------------------------------------------
2086 | domReadDocument
2087 |
2088 \--------------------------------------------------------------------------*/
2089 domDocument *
domReadDocument(XML_Parser parser,char * xml,int length,int ignoreWhiteSpaces,int keepCDATA,int storeLineColumn,int ignorexmlns,int feedbackAfter,Tcl_Obj * feedbackCmd,Tcl_Channel channel,const char * baseurl,Tcl_Obj * extResolver,int useForeignDTD,int paramEntityParsing,Tcl_Interp * interp,int * resultcode)2090 domReadDocument (
2091 XML_Parser parser,
2092 char *xml,
2093 int length,
2094 int ignoreWhiteSpaces,
2095 int keepCDATA,
2096 int storeLineColumn,
2097 int ignorexmlns,
2098 int feedbackAfter,
2099 Tcl_Obj *feedbackCmd,
2100 Tcl_Channel channel,
2101 const char *baseurl,
2102 Tcl_Obj *extResolver,
2103 int useForeignDTD,
2104 int paramEntityParsing,
2105 Tcl_Interp *interp,
2106 int *resultcode
2107 )
2108 {
2109 int done, tclLen;
2110 enum XML_Status status;
2111 size_t len;
2112 domReadInfo info;
2113 char buf[8192];
2114 Tcl_Obj *bufObj;
2115 Tcl_DString dStr;
2116 int useBinary;
2117 char *str;
2118 domDocument *doc = domCreateDoc(baseurl, storeLineColumn);
2119
2120 if (extResolver) {
2121 doc->extResolver = tdomstrdup (Tcl_GetString (extResolver));
2122 }
2123 if (ignorexmlns) {
2124 doc->nodeFlags |= IGNORE_XMLNS;
2125 }
2126
2127 info.parser = parser;
2128 info.document = doc;
2129 info.currentNode = NULL;
2130 info.depth = 0;
2131 info.ignoreWhiteSpaces = ignoreWhiteSpaces;
2132 info.cdata = (Tcl_DString*) MALLOC (sizeof (Tcl_DString));
2133 Tcl_DStringInit (info.cdata);
2134 info.cdataSection = 0;
2135 info.storeLineColumn = storeLineColumn;
2136 info.ignorexmlns = ignorexmlns;
2137 info.feedbackAfter = feedbackAfter;
2138 info.feedbackCmd = feedbackCmd;
2139 info.nextFeedbackPosition = feedbackAfter;
2140 info.interp = interp;
2141 info.activeNSpos = -1;
2142 info.activeNSsize = 8;
2143 info.activeNS = (domActiveNS*) MALLOC (sizeof(domActiveNS)
2144 * info.activeNSsize);
2145 info.baseURIstackPos = 0;
2146 info.baseURIstackSize = INITIAL_BASEURISTACK_SIZE;
2147 info.baseURIstack = (domActiveBaseURI*)
2148 MALLOC (sizeof(domActiveBaseURI) * info.baseURIstackSize);
2149 info.insideDTD = 0;
2150 info.status = 0;
2151
2152 XML_SetUserData(parser, &info);
2153 XML_SetBase (parser, baseurl);
2154 /* We must use XML_GetBase(), because XML_SetBase copies the baseURI,
2155 and we want to compare the pointers */
2156 info.baseURIstack[0].baseURI = XML_GetBase (parser);
2157 info.baseURIstack[0].depth = 0;
2158 XML_UseForeignDTD (parser, (unsigned char) useForeignDTD);
2159 XML_SetElementHandler(parser, startElement, endElement);
2160 XML_SetCharacterDataHandler(parser, characterDataHandler);
2161 XML_SetCommentHandler(parser, commentHandler);
2162 XML_SetProcessingInstructionHandler(parser, processingInstructionHandler);
2163 XML_SetEntityDeclHandler (parser, entityDeclHandler);
2164 if (extResolver) {
2165 XML_SetExternalEntityRefHandler (parser, externalEntityRefHandler);
2166 }
2167 XML_SetParamEntityParsing (parser,
2168 (enum XML_ParamEntityParsing) paramEntityParsing);
2169 XML_SetDoctypeDeclHandler (parser, startDoctypeDeclHandler,
2170 endDoctypeDeclHandler);
2171 if (keepCDATA) {
2172 XML_SetCdataSectionHandler(parser, startCDATA, endCDATA);
2173 }
2174
2175
2176 if (channel == NULL) {
2177 status = XML_Parse(parser, xml, length, 1);
2178 switch (status) {
2179 case XML_STATUS_SUSPENDED:
2180 DBG(fprintf(stderr, "XML_STATUS_SUSPENDED\n");)
2181 if (info.status == TCL_BREAK) {
2182 Tcl_ResetResult(interp);
2183 }
2184 /* fall throu */
2185 case XML_STATUS_ERROR:
2186 DBG(fprintf(stderr, "XML_STATUS_ERROR\n");)
2187 FREE ( info.activeNS );
2188 FREE ( info.baseURIstack );
2189 Tcl_DStringFree (info.cdata);
2190 FREE ( info.cdata);
2191 domFreeDocument (doc, NULL, NULL);
2192 *resultcode = info.status;
2193 return NULL;
2194 case XML_STATUS_OK:
2195 break;
2196 }
2197 } else {
2198 Tcl_DStringInit (&dStr);
2199 if (Tcl_GetChannelOption (interp, channel, "-encoding", &dStr) != TCL_OK) {
2200 FREE ( (char*) info.activeNS );
2201 FREE ( info.baseURIstack );
2202 Tcl_DStringFree (info.cdata);
2203 FREE ( info.cdata);
2204 domFreeDocument (doc, NULL, NULL);
2205 *resultcode = info.status;
2206 return NULL;
2207 }
2208 if (strcmp (Tcl_DStringValue (&dStr), "utf-8")==0 ) useBinary = 1;
2209 else useBinary = 0;
2210 Tcl_DStringFree (&dStr);
2211 if (useBinary) {
2212 do {
2213 len = Tcl_Read (channel, buf, sizeof(buf));
2214 done = len < sizeof(buf);
2215 status = XML_Parse (parser, buf, len, done);
2216 switch (status) {
2217 case XML_STATUS_SUSPENDED:
2218 DBG(fprintf(stderr, "XML_STATUS_SUSPENDED\n"););
2219 if (info.status == TCL_BREAK) {
2220 Tcl_ResetResult(interp);
2221 }
2222 /* fall throu */
2223 case XML_STATUS_ERROR:
2224 DBG(fprintf(stderr, "XML_STATUS_ERROR\n");)
2225 FREE ( info.activeNS );
2226 FREE ( info.baseURIstack );
2227 Tcl_DStringFree (info.cdata);
2228 FREE ( info.cdata);
2229 domFreeDocument (doc, NULL, NULL);
2230 *resultcode = info.status;
2231 return NULL;
2232 case XML_STATUS_OK:
2233 break;
2234 }
2235 } while (!done);
2236 } else {
2237 bufObj = Tcl_NewObj();
2238 Tcl_SetObjLength (bufObj, 6144);
2239 do {
2240 len = Tcl_ReadChars (channel, bufObj, 1024, 0);
2241 done = (len < 1024);
2242 str = Tcl_GetStringFromObj(bufObj, &tclLen);
2243 status = XML_Parse (parser, str, tclLen, done);
2244 switch (status) {
2245 case XML_STATUS_SUSPENDED:
2246 DBG(fprintf(stderr, "XML_STATUS_SUSPENDED\n"););
2247 if (info.status == TCL_BREAK) {
2248 Tcl_ResetResult(interp);
2249 }
2250 /* fall throu */
2251 case XML_STATUS_ERROR:
2252 DBG(fprintf(stderr, "XML_STATUS_ERROR\n");)
2253 FREE ( info.activeNS );
2254 FREE ( info.baseURIstack );
2255 Tcl_DStringFree (info.cdata);
2256 FREE ( info.cdata);
2257 domFreeDocument (doc, NULL, NULL);
2258 Tcl_DecrRefCount (bufObj);
2259 *resultcode = info.status;
2260 return NULL;
2261 case XML_STATUS_OK:
2262 break;
2263 }
2264 } while (!done);
2265 Tcl_DecrRefCount (bufObj);
2266 }
2267 }
2268 FREE ( info.activeNS );
2269 FREE ( info.baseURIstack );
2270 Tcl_DStringFree (info.cdata);
2271 FREE ( info.cdata);
2272
2273 domSetDocumentElement (doc);
2274
2275 return doc;
2276 }
2277
2278
2279 #endif /* ifndef TDOM_NO_EXPAT */
2280
2281
2282
2283 /*---------------------------------------------------------------------------
2284 | domException2String
2285 |
2286 \--------------------------------------------------------------------------*/
2287 const char *
domException2String(domException exception)2288 domException2String (
2289 domException exception
2290 )
2291 {
2292 return domException2StringTable[exception];
2293 }
2294
2295
2296 /*---------------------------------------------------------------------------
2297 | domGetLineColumn
2298 |
2299 \--------------------------------------------------------------------------*/
2300 int
domGetLineColumn(domNode * node,int * line,int * column)2301 domGetLineColumn (
2302 domNode *node,
2303 int *line,
2304 int *column
2305 )
2306 {
2307 char *v;
2308 domLineColumn *lc;
2309
2310 *line = -1;
2311 *column = -1;
2312
2313 if (node->nodeFlags & HAS_LINE_COLUMN) {
2314 v = (char*)node;
2315 switch (node->nodeType) {
2316 case ELEMENT_NODE:
2317 v = v + sizeof(domNode);
2318 break;
2319
2320 case TEXT_NODE:
2321 case CDATA_SECTION_NODE:
2322 case COMMENT_NODE:
2323 v = v + sizeof(domTextNode);
2324 break;
2325
2326 case PROCESSING_INSTRUCTION_NODE:
2327 v = v + sizeof(domProcessingInstructionNode);
2328 break;
2329
2330 default:
2331 return -1;
2332 }
2333 lc = (domLineColumn *)v;
2334 *line = lc->line;
2335 *column = lc->column;
2336 return 0;
2337 } else {
2338 return -1;
2339 }
2340 }
2341
2342 domAttrNode *
domCreateXMLNamespaceNode(domNode * parent)2343 domCreateXMLNamespaceNode (
2344 domNode *parent
2345 )
2346 {
2347 Tcl_HashEntry *h;
2348 int hnew;
2349 domAttrNode *attr;
2350 domNS *ns;
2351
2352 attr = (domAttrNode *) domAlloc (sizeof (domAttrNode));
2353 memset (attr, 0, sizeof (domAttrNode));
2354 h = Tcl_CreateHashEntry(&HASHTAB(parent->ownerDocument,tdom_attrNames),
2355 "xmlns:xml", &hnew);
2356 ns = domNewNamespace (parent->ownerDocument, "xml", XML_NAMESPACE);
2357 attr->nodeType = ATTRIBUTE_NODE;
2358 attr->nodeFlags = IS_NS_NODE;
2359 attr->namespace = ns->index;
2360 attr->nodeName = (char *)&(h->key);
2361 attr->parentNode = parent;
2362 attr->valueLength = strlen (XML_NAMESPACE);
2363 attr->nodeValue = tdomstrdup (XML_NAMESPACE);
2364 return attr;
2365 }
2366
2367
2368 /*
2369 *----------------------------------------------------------------------
2370 *
2371 * domCreateDoc --
2372 *
2373 * This procedure allocates a new domDocument, initialize it and
2374 * creates its rootNode (with initialization).
2375 *
2376 * Results:
2377 * The domDocument node:
2378 *
2379 * Side effects:
2380 * Allocates memory for the returned domDocument and its
2381 * rootNode.
2382 *
2383 *----------------------------------------------------------------------
2384 */
2385
2386 domDocument *
domCreateDoc(const char * baseURI,int storeLineColumn)2387 domCreateDoc (
2388 const char * baseURI,
2389 int storeLineColumn
2390 )
2391 {
2392 Tcl_HashEntry *h;
2393 int hnew;
2394 domNode *rootNode;
2395 domDocument *doc;
2396 domLineColumn *lc;
2397
2398 doc = (domDocument *) MALLOC (sizeof (domDocument));
2399 memset(doc, 0, sizeof(domDocument));
2400 doc->nodeType = DOCUMENT_NODE;
2401 doc->documentNumber = DOC_NO(doc);
2402 doc->nsptr = -1;
2403 doc->nslen = 4;
2404 doc->namespaces = (domNS**) MALLOC (sizeof (domNS*) * doc->nslen);
2405
2406 /* We malloc and initialize the baseURIs hash table here to avoid
2407 cluttering of the code all over the place with checks. */
2408 doc->baseURIs = MALLOC (sizeof (Tcl_HashTable));
2409 Tcl_InitHashTable (doc->baseURIs, TCL_ONE_WORD_KEYS);
2410
2411 TDomThreaded(
2412 domLocksAttach(doc);
2413 Tcl_InitHashTable(&doc->tdom_tagNames, TCL_STRING_KEYS);
2414 Tcl_InitHashTable(&doc->tdom_attrNames, TCL_STRING_KEYS);
2415 )
2416
2417 if (storeLineColumn) {
2418 rootNode = (domNode*) domAlloc(sizeof(domNode)+sizeof(domLineColumn));
2419 } else {
2420 rootNode = (domNode*) domAlloc(sizeof(domNode));
2421 }
2422 memset(rootNode, 0, sizeof(domNode));
2423 rootNode->nodeType = ELEMENT_NODE;
2424 if (baseURI) {
2425 h = Tcl_CreateHashEntry (doc->baseURIs, (char*)rootNode, &hnew);
2426 Tcl_SetHashValue (h, tdomstrdup (baseURI));
2427 rootNode->nodeFlags |= HAS_BASEURI;
2428 } else {
2429 rootNode->nodeFlags = 0;
2430 }
2431 rootNode->namespace = 0;
2432 h = Tcl_CreateHashEntry(&HASHTAB(doc,tdom_tagNames), "", &hnew);
2433 rootNode->nodeName = (char *)&(h->key);
2434 rootNode->nodeNumber = NODE_NO(doc);
2435 rootNode->ownerDocument = doc;
2436 rootNode->parentNode = NULL;
2437 rootNode->firstChild = rootNode->lastChild = NULL;
2438 rootNode->firstAttr = domCreateXMLNamespaceNode (rootNode);
2439 if (storeLineColumn) {
2440 lc = (domLineColumn*) ( ((char*)rootNode) + sizeof(domNode));
2441 rootNode->nodeFlags |= HAS_LINE_COLUMN;
2442 lc->line = 0;
2443 lc->column = 0;
2444 }
2445 doc->rootNode = rootNode;
2446
2447 return doc;
2448 }
2449
2450 /*---------------------------------------------------------------------------
2451 | domCreateDocument
2452 |
2453 \--------------------------------------------------------------------------*/
2454 domDocument *
domCreateDocument(const char * uri,char * documentElementTagName)2455 domCreateDocument (
2456 const char *uri,
2457 char *documentElementTagName
2458 )
2459 {
2460 Tcl_HashEntry *h;
2461 int hnew;
2462 domNode *node;
2463 domDocument *doc;
2464 char prefix[MAX_PREFIX_LEN];
2465 const char *localName;
2466 domNS *ns = NULL;
2467
2468 if (uri) {
2469 domSplitQName (documentElementTagName, prefix, &localName);
2470 DBG(fprintf(stderr,
2471 "rootName: -->%s<--, prefix: -->%s<--, localName: -->%s<--\n",
2472 documentElementTagName, prefix, localName););
2473 }
2474 doc = domCreateDoc (NULL, 0);
2475
2476 h = Tcl_CreateHashEntry(&HASHTAB(doc, tdom_tagNames),
2477 documentElementTagName, &hnew);
2478 node = (domNode*) domAlloc(sizeof(domNode));
2479 memset(node, 0, sizeof(domNode));
2480 node->nodeType = ELEMENT_NODE;
2481 node->nodeFlags = 0;
2482 node->nodeNumber = NODE_NO(doc);
2483 node->ownerDocument = doc;
2484 node->nodeName = (char *)&(h->key);
2485 doc->documentElement = node;
2486 if (uri) {
2487 ns = domNewNamespace (doc, prefix, uri);
2488 node->namespace = ns->index;
2489 domAddNSToNode (node, ns);
2490 }
2491 doc->rootNode->firstChild = doc->rootNode->lastChild = doc->documentElement;
2492
2493 return doc;
2494 }
2495
2496
2497 /*---------------------------------------------------------------------------
2498 | domSetDocumentElement
2499 |
2500 \--------------------------------------------------------------------------*/
2501 void
domSetDocumentElement(domDocument * doc)2502 domSetDocumentElement (
2503 domDocument *doc
2504 )
2505 {
2506 domNode *node;
2507
2508 doc->documentElement = NULL;
2509 node = doc->rootNode->firstChild;
2510 while (node) {
2511 if (node->nodeType == ELEMENT_NODE) {
2512 doc->documentElement = node;
2513 break;
2514 }
2515 node = node->nextSibling;
2516 }
2517 if (!doc->documentElement) {
2518 doc->documentElement = doc->rootNode->firstChild;
2519 }
2520 }
2521
2522 /*---------------------------------------------------------------------------
2523 | domFreeNode
2524 |
2525 \--------------------------------------------------------------------------*/
2526 void
domFreeNode(domNode * node,domFreeCallback freeCB,void * clientData,int dontfree)2527 domFreeNode (
2528 domNode * node,
2529 domFreeCallback freeCB,
2530 void * clientData,
2531 int dontfree
2532 )
2533 {
2534 int shared = 0;
2535 domNode *child, *ctemp;
2536 domAttrNode *atemp, *attr, *aprev;
2537 Tcl_HashEntry *entryPtr;
2538
2539 if (node == NULL) {
2540 DBG(fprintf (stderr, "null ptr in domFreeNode (dom.c) !\n");)
2541 return;
2542 }
2543 TDomThreaded (
2544 shared = node->ownerDocument && node->ownerDocument->refCount > 1;
2545 )
2546
2547 /*----------------------------------------------------------------
2548 | dontfree instruct us to walk the node tree and apply the
2549 | user-supplied callback, *w/o* actually deleting nodes.
2550 | This is normally done when a thread detaches from the
2551 | shared DOM tree and wants to garbage-collect all nodecmds
2552 | in it's interpreter which attached to the tree nodes.
2553 \---------------------------------------------------------------*/
2554
2555 if (dontfree) {
2556 shared = 1;
2557 } else {
2558 node->nodeFlags |= IS_DELETED;
2559 }
2560
2561 if (node->nodeType == ATTRIBUTE_NODE && !shared) {
2562 attr = ((domAttrNode*)node)->parentNode->firstAttr;
2563 aprev = NULL;
2564 while (attr && (attr != (domAttrNode*)node)) {
2565 aprev = attr;
2566 attr = attr->nextSibling;
2567 }
2568 if (attr) {
2569 if (aprev) {
2570 aprev->nextSibling = attr->nextSibling;
2571 } else {
2572 ((domAttrNode*)node)->parentNode->firstAttr = attr->nextSibling;
2573 }
2574 FREE (attr->nodeValue);
2575 domFree ((void*)attr);
2576 }
2577 } else if (node->nodeType == ELEMENT_NODE) {
2578 child = node->lastChild;
2579 while (child) {
2580 ctemp = child->previousSibling;
2581 if (freeCB) {
2582 freeCB(child, clientData);
2583 }
2584 domFreeNode (child, freeCB, clientData, dontfree);
2585 child = ctemp;
2586 }
2587 if (shared) {
2588 return;
2589 }
2590 attr = node->firstAttr;
2591 while (attr) {
2592 atemp = attr;
2593 attr = attr->nextSibling;
2594 FREE (atemp->nodeValue);
2595 domFree ((void*)atemp);
2596 }
2597 if (node->nodeFlags & HAS_BASEURI) {
2598 entryPtr = Tcl_FindHashEntry (node->ownerDocument->baseURIs,
2599 (char*)node);
2600 if (entryPtr) {
2601 FREE ((char *) Tcl_GetHashValue (entryPtr));
2602 Tcl_DeleteHashEntry (entryPtr);
2603 }
2604 }
2605 domFree ((void*)node);
2606
2607 } else if (node->nodeType == PROCESSING_INSTRUCTION_NODE && !shared) {
2608 FREE (((domProcessingInstructionNode*)node)->dataValue);
2609 FREE (((domProcessingInstructionNode*)node)->targetValue);
2610 domFree ((void*)node);
2611
2612 } else if (!shared) {
2613 FREE (((domTextNode*)node)->nodeValue);
2614 domFree ((void*)node);
2615 }
2616 }
2617
2618
2619 /*---------------------------------------------------------------------------
2620 | domDeleteNode - unlinks node from tree and free all child nodes
2621 | and itself
2622 |
2623 \--------------------------------------------------------------------------*/
2624 domException
domDeleteNode(domNode * node,domFreeCallback freeCB,void * clientData)2625 domDeleteNode (
2626 domNode * node,
2627 domFreeCallback freeCB,
2628 void * clientData
2629 )
2630 {
2631 TDomThreaded(int shared = 0;)
2632 domDocument *doc;
2633
2634 if (node->nodeType == ATTRIBUTE_NODE) {
2635 domPanic("domDeleteNode on ATTRIBUTE_NODE not supported!");
2636 }
2637 TDomThreaded (
2638 shared = node->ownerDocument->refCount > 1;
2639 )
2640 doc = node->ownerDocument;
2641
2642 /*----------------------------------------------------------------
2643 | unlink node from child or fragment list
2644 \---------------------------------------------------------------*/
2645 if (node->previousSibling) {
2646 node->previousSibling->nextSibling = node->nextSibling;
2647 } else {
2648 if (node->parentNode) {
2649 node->parentNode->firstChild = node->nextSibling;
2650 } else {
2651 /* Node may be a top level node */
2652 if (doc->rootNode->firstChild == node) {
2653 doc->rootNode->firstChild = node->nextSibling;
2654 }
2655 }
2656 }
2657 if (node->nextSibling) {
2658 node->nextSibling->previousSibling = node->previousSibling;
2659 } else {
2660 if (node->parentNode) {
2661 node->parentNode->lastChild = node->previousSibling;
2662 } else {
2663 /* Node may be a top level node */
2664 if (doc->rootNode->lastChild == node) {
2665 doc->rootNode->lastChild = node->previousSibling;
2666 }
2667 }
2668 }
2669 if (doc->fragments == node) {
2670 doc->fragments = node->nextSibling;
2671 }
2672 if (!node->parentNode) {
2673 domSetDocumentElement (doc);
2674 }
2675
2676 /*----------------------------------------------------------------
2677 | for shared docs, append node to the delete nodes list
2678 | otherwise delete the node physically
2679 \---------------------------------------------------------------*/
2680 if (freeCB) {
2681 freeCB(node, clientData);
2682 }
2683 TDomThreaded (
2684 if (shared) {
2685 if (doc->deletedNodes) {
2686 node->nextSibling = doc->deletedNodes;
2687 } else {
2688 node->nextSibling = NULL;
2689 }
2690 doc->deletedNodes = node;
2691 node->nodeFlags |= IS_DELETED;
2692 }
2693 )
2694 MutationEvent3(DOMNodeRemoved, childToRemove, node);
2695 MutationEvent2(DOMSubtreeModified, node);
2696 domFreeNode(node, freeCB, clientData, 0);
2697
2698 return OK;
2699 }
2700
2701
2702 /*---------------------------------------------------------------------------
2703 | domFreeDocument
2704 |
2705 \--------------------------------------------------------------------------*/
2706 void
domFreeDocument(domDocument * doc,domFreeCallback freeCB,void * clientData)2707 domFreeDocument (
2708 domDocument * doc,
2709 domFreeCallback freeCB,
2710 void * clientData
2711 )
2712 {
2713 domNode *node, *next;
2714 domNS *ns;
2715 int i, dontfree = 0;
2716 Tcl_HashEntry *entryPtr;
2717 Tcl_HashSearch search;
2718
2719 if (doc->nodeFlags & DONT_FREE) {
2720 doc->nodeFlags &= ~DONT_FREE;
2721 dontfree = 1;
2722 }
2723 /*-----------------------------------------------------------
2724 | delete main trees, including top level PIs, etc.
2725 \-----------------------------------------------------------*/
2726 node = doc->rootNode;
2727 if (node) {
2728 if (freeCB) {
2729 freeCB(node, clientData);
2730 }
2731 domFreeNode (node, freeCB, clientData, dontfree);
2732 }
2733
2734 /*-----------------------------------------------------------
2735 | delete fragment trees
2736 \-----------------------------------------------------------*/
2737 node = doc->fragments;
2738 while (node) {
2739 next = node->nextSibling;
2740 if (freeCB) {
2741 freeCB(node, clientData);
2742 }
2743 domFreeNode (node, freeCB, clientData, dontfree);
2744 node = next;
2745 }
2746
2747 if (dontfree) return;
2748
2749 /*-----------------------------------------------------------
2750 | delete namespaces
2751 \-----------------------------------------------------------*/
2752 for (i = 0; i <= doc->nsptr; i++) {
2753 ns = doc->namespaces[i];
2754 FREE(ns->uri);
2755 FREE(ns->prefix);
2756 FREE ((char*) ns);
2757 }
2758 FREE ((char *)doc->namespaces);
2759
2760 /*-----------------------------------------------------------
2761 | delete global selectNodes prefix namespace mappings
2762 \-----------------------------------------------------------*/
2763 if (doc->prefixNSMappings) {
2764 i = 0;
2765 while (doc->prefixNSMappings[i]) {
2766 FREE (doc->prefixNSMappings[i]);
2767 i++;
2768 }
2769 FREE (doc->prefixNSMappings);
2770 }
2771
2772 /*-----------------------------------------------------------
2773 | delete doctype info
2774 \-----------------------------------------------------------*/
2775 if (doc->doctype) {
2776 #define DOCINFO_FREE(item) if (doc->doctype->item) FREE(doc->doctype->item)
2777 DOCINFO_FREE(systemId);
2778 DOCINFO_FREE(publicId);
2779 DOCINFO_FREE(internalSubset);
2780 DOCINFO_FREE(encoding);
2781 DOCINFO_FREE(mediaType);
2782 DOCINFO_FREE(method);
2783 if (doc->doctype->cdataSectionElements) {
2784 Tcl_DeleteHashTable (doc->doctype->cdataSectionElements);
2785 FREE (doc->doctype->cdataSectionElements);
2786 }
2787
2788 FREE((char*) doc->doctype);
2789 }
2790
2791 /*-----------------------------------------------------------
2792 | delete ID hash table
2793 \-----------------------------------------------------------*/
2794 if (doc->ids) {
2795 Tcl_DeleteHashTable (doc->ids);
2796 FREE (doc->ids);
2797 }
2798
2799 /*-----------------------------------------------------------
2800 | delete unparsed entities hash table
2801 \-----------------------------------------------------------*/
2802 if (doc->unparsedEntities) {
2803 entryPtr = Tcl_FirstHashEntry (doc->unparsedEntities, &search);
2804 while (entryPtr) {
2805 FREE (Tcl_GetHashValue (entryPtr));
2806 entryPtr = Tcl_NextHashEntry (&search);
2807 }
2808 Tcl_DeleteHashTable (doc->unparsedEntities);
2809 FREE (doc->unparsedEntities);
2810 }
2811
2812 /*-----------------------------------------------------------
2813 | delete base URIs hash table
2814 \-----------------------------------------------------------*/
2815 entryPtr = Tcl_FirstHashEntry (doc->baseURIs, &search);
2816 while (entryPtr) {
2817 FREE (Tcl_GetHashValue (entryPtr));
2818 entryPtr = Tcl_NextHashEntry (&search);
2819 }
2820 Tcl_DeleteHashTable (doc->baseURIs);
2821 FREE (doc->baseURIs);
2822
2823 /*-----------------------------------------------------------
2824 | delete XPath cache hash table
2825 \-----------------------------------------------------------*/
2826 if (doc->xpathCache) {
2827 entryPtr = Tcl_FirstHashEntry (doc->xpathCache, &search);
2828 while (entryPtr) {
2829 xpathFreeAst((ast)Tcl_GetHashValue (entryPtr));
2830 entryPtr = Tcl_NextHashEntry (&search);
2831 }
2832 Tcl_DeleteHashTable (doc->xpathCache);
2833 FREE (doc->xpathCache);
2834 }
2835
2836 if (doc->extResolver) {
2837 FREE (doc->extResolver);
2838 }
2839
2840 /*-----------------------------------------------------------
2841 | delete tag/attribute hash tables (for threaded builds only)
2842 \-----------------------------------------------------------*/
2843 TDomThreaded (
2844 {
2845 Tcl_HashEntry *entryPtr;
2846 Tcl_HashSearch search;
2847 entryPtr = Tcl_FirstHashEntry(&doc->tdom_tagNames, &search);
2848 while (entryPtr) {
2849 Tcl_DeleteHashEntry(entryPtr);
2850 entryPtr = Tcl_NextHashEntry(&search);
2851 }
2852 Tcl_DeleteHashTable(&doc->tdom_tagNames);
2853 entryPtr = Tcl_FirstHashEntry(&doc->tdom_attrNames, &search);
2854 while (entryPtr) {
2855 Tcl_DeleteHashEntry(entryPtr);
2856 entryPtr = Tcl_NextHashEntry(&search);
2857 }
2858 Tcl_DeleteHashTable(&doc->tdom_attrNames);
2859 domLocksDetach(doc);
2860 node = doc->deletedNodes;
2861 while (node) {
2862 next = node->nextSibling;
2863 domFreeNode (node, freeCB, clientData, 0);
2864 node = next;
2865 }
2866 }
2867 )
2868
2869 FREE ((char*)doc);
2870 }
2871
2872 /*---------------------------------------------------------------------------
2873 | domSetAttribute
2874 |
2875 \--------------------------------------------------------------------------*/
2876 domAttrNode *
domSetAttribute(domNode * node,const char * attributeName,const char * attributeValue)2877 domSetAttribute (
2878 domNode *node,
2879 const char *attributeName,
2880 const char *attributeValue
2881 )
2882 {
2883 domAttrNode *attr, *lastAttr;
2884 Tcl_HashEntry *h;
2885 int hnew;
2886
2887 if (!node || node->nodeType != ELEMENT_NODE) {
2888 return NULL;
2889 }
2890
2891 /*----------------------------------------------------
2892 | try to find an existing attribute
2893 \---------------------------------------------------*/
2894 attr = node->firstAttr;
2895 while (attr && strcmp(attr->nodeName, attributeName)) {
2896 attr = attr->nextSibling;
2897 }
2898 if (attr) {
2899 if (attr->nodeFlags & IS_ID_ATTRIBUTE) {
2900 h = Tcl_FindHashEntry (node->ownerDocument->ids, attr->nodeValue);
2901 if (h) {
2902 Tcl_DeleteHashEntry (h);
2903 h = Tcl_CreateHashEntry (node->ownerDocument->ids,
2904 attributeValue, &hnew);
2905 /* XXX what to do, if hnew = 0 ??? */
2906 Tcl_SetHashValue (h, node);
2907 }
2908 }
2909 FREE (attr->nodeValue);
2910 attr->valueLength = strlen(attributeValue);
2911 attr->nodeValue = (char*)MALLOC(attr->valueLength+1);
2912 strcpy(attr->nodeValue, attributeValue);
2913 } else {
2914 /*-----------------------------------------------
2915 | add a complete new attribute node
2916 \----------------------------------------------*/
2917 attr = (domAttrNode*) domAlloc(sizeof(domAttrNode));
2918 memset(attr, 0, sizeof(domAttrNode));
2919 h = Tcl_CreateHashEntry(&HASHTAB(node->ownerDocument,tdom_attrNames),
2920 attributeName, &hnew);
2921 attr->nodeType = ATTRIBUTE_NODE;
2922 attr->nodeFlags = 0;
2923 attr->namespace = 0;
2924 attr->nodeName = (char *)&(h->key);
2925 attr->parentNode = node;
2926 attr->valueLength = strlen(attributeValue);
2927 attr->nodeValue = (char*)MALLOC(attr->valueLength+1);
2928 strcpy(attr->nodeValue, attributeValue);
2929
2930 if (node->firstAttr) {
2931 lastAttr = node->firstAttr;
2932 /* move to the end of the attribute list */
2933 while (lastAttr->nextSibling) lastAttr = lastAttr->nextSibling;
2934 lastAttr->nextSibling = attr;
2935 } else {
2936 node->firstAttr = attr;
2937 }
2938 }
2939 MutationEvent();
2940 return attr;
2941 }
2942
2943 /*---------------------------------------------------------------------------
2944 | domSetAttributeNS
2945 |
2946 \--------------------------------------------------------------------------*/
2947 domAttrNode *
domSetAttributeNS(domNode * node,const char * attributeName,const char * attributeValue,const char * uri,int createNSIfNeeded)2948 domSetAttributeNS (
2949 domNode *node,
2950 const char *attributeName,
2951 const char *attributeValue,
2952 const char *uri,
2953 int createNSIfNeeded
2954 )
2955 {
2956 domAttrNode *attr, *lastAttr;
2957 Tcl_HashEntry *h;
2958 int hnew, hasUri = 1, isNSAttr = 0, isDftNS = 0;
2959 domNS *ns;
2960 char prefix[MAX_PREFIX_LEN];
2961 const char *localName, *newLocalName;
2962 Tcl_DString dStr;
2963
2964 DBG(fprintf (stderr, "domSetAttributeNS: attributeName %s, attributeValue %s, uri %s\n", attributeName, attributeValue, uri);)
2965 if (!node || node->nodeType != ELEMENT_NODE) {
2966 return NULL;
2967 }
2968
2969 domSplitQName (attributeName, prefix, &localName);
2970 if (!uri || uri[0]=='\0') hasUri = 0;
2971 if (hasUri && (prefix[0] == '\0')) return NULL;
2972 if ((prefix[0] == '\0' && strcmp (localName, "xmlns")==0)
2973 || (strcmp (prefix, "xmlns")==0)) {
2974 isNSAttr = 1;
2975 createNSIfNeeded = 0;
2976 if (prefix[0] == '\0') {
2977 isDftNS = 1;
2978 ns = domLookupPrefix (node, "");
2979 } else {
2980 ns = domLookupPrefix (node, prefix);
2981 }
2982 if (ns && (strcmp (ns->uri, attributeValue)==0)) return NULL;
2983 if (!hasUri) {
2984 uri = attributeValue;
2985 isNSAttr = 1;
2986 hasUri = 1;
2987 if (strcmp (localName, "xmlns")==0) isDftNS = 1;
2988 } else {
2989 return NULL;
2990 }
2991 }
2992 if (!hasUri) {
2993 if (prefix[0] != '\0' && strcmp (prefix, "xml")==0) {
2994 uri = "http://www.w3.org/XML/1998/namespace";
2995 hasUri = 1;
2996 }
2997 }
2998 if (!hasUri && prefix[0] != '\0') return NULL;
2999
3000 /*----------------------------------------------------
3001 | try to find an existing attribute
3002 \---------------------------------------------------*/
3003 attr = node->firstAttr;
3004 while (attr) {
3005 if (hasUri) {
3006 if (attr->nodeFlags & IS_NS_NODE) {
3007 if (isNSAttr) {
3008 if (strcmp (attributeName, attr->nodeName)==0) {
3009 break;
3010 }
3011 }
3012 } else {
3013 if (attr->namespace && !isNSAttr) {
3014 ns = domGetNamespaceByIndex (node->ownerDocument,
3015 attr->namespace);
3016 if (strcmp (uri, ns->uri)==0) {
3017 newLocalName = localName;
3018 domSplitQName (attr->nodeName, prefix, &localName);
3019 if (strcmp (newLocalName, localName)==0) break;
3020 }
3021 }
3022 }
3023 } else {
3024 if (!attr->namespace) {
3025 if (strcmp (attr->nodeName, localName)==0) break;
3026 }
3027 }
3028 attr = attr->nextSibling;
3029 }
3030 if (attr) {
3031 DBG(fprintf (stderr, "domSetAttributeNS: resetting existing attribute %s ; old value: %s\n", attr->nodeName, attr->nodeValue);)
3032 if (attr->nodeFlags & IS_ID_ATTRIBUTE) {
3033 h = Tcl_FindHashEntry (node->ownerDocument->ids, attr->nodeValue);
3034 if (h) {
3035 Tcl_DeleteHashEntry (h);
3036 h = Tcl_CreateHashEntry (node->ownerDocument->ids,
3037 attributeValue, &hnew);
3038 Tcl_SetHashValue (h, node);
3039 }
3040 }
3041 FREE (attr->nodeValue);
3042 attr->valueLength = strlen(attributeValue);
3043 attr->nodeValue = (char*)MALLOC(attr->valueLength+1);
3044 strcpy(attr->nodeValue, attributeValue);
3045 } else {
3046 /*--------------------------------------------------------
3047 | add a complete new attribute node
3048 \-------------------------------------------------------*/
3049 attr = (domAttrNode*) domAlloc(sizeof(domAttrNode));
3050 memset(attr, 0, sizeof(domAttrNode));
3051 h = Tcl_CreateHashEntry(&HASHTAB(node->ownerDocument,tdom_attrNames),
3052 attributeName, &hnew);
3053 attr->nodeType = ATTRIBUTE_NODE;
3054 if (hasUri) {
3055 if (isNSAttr) {
3056 if (isDftNS) {
3057 ns = domLookupNamespace (node->ownerDocument, "", uri);
3058 } else {
3059 ns = domLookupNamespace (node->ownerDocument, localName, uri);
3060 }
3061 } else {
3062 ns = domLookupPrefix (node, prefix);
3063 if (ns && (strcmp (ns->uri, uri)!=0)) ns = NULL;
3064 }
3065 if (!ns) {
3066 if (isNSAttr) {
3067 if (isDftNS) {
3068 ns = domNewNamespace (node->ownerDocument, "", uri);
3069 } else {
3070 ns = domNewNamespace (node->ownerDocument, localName, uri);
3071 }
3072 } else {
3073 ns = domNewNamespace (node->ownerDocument, prefix, uri);
3074 if (createNSIfNeeded) {
3075 if (prefix[0] == '\0') {
3076 domSetAttributeNS (node, "xmlns", uri, NULL, 0);
3077 } else {
3078 Tcl_DStringInit (&dStr);
3079 Tcl_DStringAppend (&dStr, "xmlns:", 6);
3080 Tcl_DStringAppend (&dStr, prefix, -1);
3081 domSetAttributeNS (node, Tcl_DStringValue (&dStr),
3082 uri, NULL, 0);
3083 }
3084 }
3085 }
3086 }
3087 attr->namespace = ns->index;
3088 if (isNSAttr) {
3089 attr->nodeFlags = IS_NS_NODE;
3090 }
3091 }
3092 attr->nodeName = (char *)&(h->key);
3093 attr->parentNode = node;
3094 attr->valueLength = strlen(attributeValue);
3095 attr->nodeValue = (char*)MALLOC(attr->valueLength+1);
3096 strcpy(attr->nodeValue, attributeValue);
3097
3098 if (isNSAttr) {
3099 if (node->firstAttr && (node->firstAttr->nodeFlags & IS_NS_NODE)) {
3100 lastAttr = node->firstAttr;
3101 while (lastAttr->nextSibling
3102 && (lastAttr->nextSibling->nodeFlags & IS_NS_NODE)) {
3103 lastAttr = lastAttr->nextSibling;
3104 }
3105 attr->nextSibling = lastAttr->nextSibling;
3106 lastAttr->nextSibling = attr;
3107 } else {
3108 attr->nextSibling = node->firstAttr;
3109 node->firstAttr = attr;
3110 }
3111 } else {
3112 if (node->firstAttr) {
3113 lastAttr = node->firstAttr;
3114 /* move to the end of the attribute list */
3115 while (lastAttr->nextSibling) lastAttr = lastAttr->nextSibling;
3116 lastAttr->nextSibling = attr;
3117 } else {
3118 node->firstAttr = attr;
3119 }
3120 }
3121 }
3122 MutationEvent();
3123 return attr;
3124 }
3125
3126
3127 /*---------------------------------------------------------------------------
3128 | domRemoveAttribute
3129 |
3130 \--------------------------------------------------------------------------*/
3131 int
domRemoveAttribute(domNode * node,const char * attributeName)3132 domRemoveAttribute (
3133 domNode *node,
3134 const char *attributeName
3135 )
3136 {
3137 domAttrNode *attr, *previous = NULL;
3138 Tcl_HashEntry *h;
3139
3140 if (!node || node->nodeType != ELEMENT_NODE) {
3141 return -1;
3142 }
3143
3144 /*----------------------------------------------------
3145 | try to find the attribute
3146 \---------------------------------------------------*/
3147 attr = node->firstAttr;
3148 while (attr && strcmp(attr->nodeName, attributeName)) {
3149 previous = attr;
3150 attr = attr->nextSibling;
3151 }
3152 if (attr) {
3153 if (previous) {
3154 previous->nextSibling = attr->nextSibling;
3155 } else {
3156 attr->parentNode->firstAttr = attr->nextSibling;
3157 }
3158
3159 if (attr->nodeFlags & IS_ID_ATTRIBUTE) {
3160 h = Tcl_FindHashEntry (node->ownerDocument->ids, attr->nodeValue);
3161 if (h) Tcl_DeleteHashEntry (h);
3162 }
3163 FREE (attr->nodeValue);
3164 MutationEvent();
3165
3166 domFree ((void*)attr);
3167 return 0;
3168 }
3169 return -1;
3170 }
3171
3172
3173 /*---------------------------------------------------------------------------
3174 | domRemoveAttributeNS
3175 |
3176 \--------------------------------------------------------------------------*/
3177 int
domRemoveAttributeNS(domNode * node,const char * uri,const char * localName)3178 domRemoveAttributeNS (
3179 domNode *node,
3180 const char *uri,
3181 const char *localName
3182 )
3183 {
3184 domAttrNode *attr, *previous = NULL;
3185 domNS *ns = NULL;
3186 char prefix[MAX_PREFIX_LEN];
3187 const char *str;
3188 Tcl_HashEntry *h;
3189
3190 if (!node || node->nodeType != ELEMENT_NODE) {
3191 return -1;
3192 }
3193
3194 attr = node->firstAttr;
3195 while (attr) {
3196 domSplitQName (attr->nodeName, prefix, &str);
3197 if (strcmp(localName,str)==0) {
3198 ns = domGetNamespaceByIndex(node->ownerDocument, attr->namespace);
3199 if (ns && strcmp(ns->uri, uri)==0) {
3200 if (previous) {
3201 previous->nextSibling = attr->nextSibling;
3202 } else {
3203 attr->parentNode->firstAttr = attr->nextSibling;
3204 }
3205
3206 if (attr->nodeFlags & IS_ID_ATTRIBUTE) {
3207 h = Tcl_FindHashEntry (node->ownerDocument->ids,
3208 attr->nodeValue);
3209 if (h) Tcl_DeleteHashEntry (h);
3210 }
3211 FREE (attr->nodeValue);
3212 MutationEvent();
3213 domFree ((void*)attr);
3214 return 0;
3215 }
3216 }
3217 previous = attr;
3218 attr = attr->nextSibling;
3219 }
3220 return -1;
3221 }
3222
3223
3224 /*---------------------------------------------------------------------------
3225 | __dbgAttr
3226 |
3227 \--------------------------------------------------------------------------*/
DBG(static void __dbgAttr (domAttrNode * node){ DBG(fprintf(stderr, " %s=%s", node->nodeName, node->nodeValue);) if (node->nextSibling) __dbgAttr(node->nextSibling); } )3228 DBG(
3229 static void __dbgAttr (domAttrNode *node) {
3230
3231 DBG(fprintf(stderr, " %s=%s", node->nodeName, node->nodeValue);)
3232 if (node->nextSibling) __dbgAttr(node->nextSibling);
3233 }
3234 )
3235
3236
3237 /*---------------------------------------------------------------------------
3238 | domSetDocument
3239 |
3240 \--------------------------------------------------------------------------*/
3241 void
3242 domSetDocument (
3243 domNode *node,
3244 domDocument *doc
3245 )
3246 {
3247 domNode *child;
3248 domNS *ns, *origNS;
3249 domDocument *origDoc;
3250 domAttrNode *attr;
3251 Tcl_HashEntry *h;
3252 TDomThreaded (
3253 int hnew;
3254 )
3255
3256 if (node->nodeFlags & HAS_BASEURI) {
3257 h = Tcl_FindHashEntry (node->ownerDocument->baseURIs, (char*)node);
3258 if (h) {
3259 FREE ((char *) Tcl_GetHashValue (h));
3260 Tcl_DeleteHashEntry (h);
3261 }
3262 node->nodeFlags &= ~HAS_BASEURI;
3263 }
3264 if (node->nodeType == ELEMENT_NODE) {
3265 origDoc = node->ownerDocument;
3266 node->ownerDocument = doc;
3267 for (attr = node->firstAttr; attr != NULL; attr = attr->nextSibling) {
3268 if (attr->nodeFlags & IS_NS_NODE) {
3269 origNS = origDoc->namespaces[attr->namespace-1];
3270 ns = domNewNamespace (doc, origNS->prefix, origNS->uri);
3271 attr->namespace = ns->index;
3272 } else if (attr->namespace) {
3273 ns = domAddNSToNode (node,
3274 origDoc->namespaces[attr->namespace-1]);
3275 if (ns) attr->namespace = ns->index;
3276 }
3277 }
3278 if (node->namespace) {
3279 ns = domAddNSToNode (node, origDoc->namespaces[node->namespace-1]);
3280 if (ns) node->namespace = ns->index;
3281 } else {
3282 ns = domAddNSToNode (node, NULL);
3283 if (ns) {
3284 node->namespace = ns->index;
3285 }
3286 }
3287 DBG(fprintf(stderr, "domSetDocument node%s ", node->nodeName);
3288 __dbgAttr(node->firstAttr);
3289 fprintf(stderr, "\n");
3290 )
3291
3292 TDomThreaded (
3293 if (origDoc != doc) {
3294 /* Make hash table entries as necessary for
3295 * tdom_tagNames and tdom_attrNames. */
3296 h = Tcl_CreateHashEntry(&doc->tdom_tagNames, node->nodeName,
3297 &hnew);
3298 node->nodeName = (domString) &(h->key);
3299 for (attr = node->firstAttr;
3300 attr != NULL;
3301 attr = attr->nextSibling) {
3302 h = Tcl_CreateHashEntry(&doc->tdom_attrNames,
3303 attr->nodeName, &hnew);
3304 attr->nodeName = (domString) &(h->key);
3305 }
3306 }
3307 )
3308 child = node->firstChild;
3309 while (child != NULL) {
3310 domSetDocument (child, doc);
3311 child = child->nextSibling;
3312 }
3313 } else {
3314 node->ownerDocument = doc;
3315 }
3316
3317 DBG(fprintf(stderr, "end domSetDocument node %s\n", node->nodeName);)
3318 }
3319
3320
3321 /*---------------------------------------------------------------------------
3322 | domSetNodeValue
3323 |
3324 \--------------------------------------------------------------------------*/
3325 domException
domSetNodeValue(domNode * node,const char * nodeValue,int valueLen)3326 domSetNodeValue (
3327 domNode *node,
3328 const char *nodeValue,
3329 int valueLen
3330 )
3331 {
3332 domTextNode *textnode;
3333
3334 if ((node->nodeType != TEXT_NODE) &&
3335 (node->nodeType != CDATA_SECTION_NODE) &&
3336 (node->nodeType != COMMENT_NODE)
3337 ) {
3338 return NO_MODIFICATION_ALLOWED_ERR;
3339 }
3340
3341 textnode = (domTextNode*) node;
3342 FREE(textnode->nodeValue);
3343 textnode->nodeValue = MALLOC (valueLen);
3344 textnode->valueLength = valueLen;
3345 memmove(textnode->nodeValue, nodeValue, valueLen);
3346 MutationEvent();
3347 return OK;
3348 }
3349
3350
3351 /*
3352 *----------------------------------------------------------------------
3353 *
3354 * domRemoveChild --
3355 *
3356 * This procedure implements the dom method removeChild. Removes
3357 * child from the list of children of node.
3358 *
3359 * Results:
3360 * Returns a domException:
3361 *
3362 * NOT_FOUND_ERR: Raised if the node child is not a child of node.
3363 *
3364 * OK: otherwise
3365 *
3366 * Side effects:
3367 * Alters the involved document.
3368 *
3369 *----------------------------------------------------------------------
3370 */
3371
3372 domException
domRemoveChild(domNode * node,domNode * child)3373 domRemoveChild (
3374 domNode *node,
3375 domNode *child
3376 )
3377 {
3378 domNode *n;
3379
3380 /* check, if node is in deed the parent of child */
3381 if (child->parentNode != node) {
3382 /* If node is the root node of a document and child
3383 is in deed a child of this node, then
3384 child->parentNode will be NULL. In this case, we
3385 loop throu the childs of node, to see, if the child
3386 is valid. */
3387 if (node->ownerDocument->rootNode == node) {
3388 n = node->firstChild;
3389 while (n) {
3390 if (n == child) {
3391 /* child is in deed a child of node */
3392 break;
3393 }
3394 n = n->nextSibling;
3395 }
3396 if (!n) {
3397 return NOT_FOUND_ERR;
3398 }
3399 } else {
3400 return NOT_FOUND_ERR;
3401 }
3402 }
3403
3404 if (child->previousSibling) {
3405 child->previousSibling->nextSibling = child->nextSibling;
3406 } else {
3407 node->firstChild = child->nextSibling;
3408 }
3409 if (child->nextSibling) {
3410 child->nextSibling->previousSibling = child->previousSibling;
3411 } else {
3412 node->lastChild = child->previousSibling;
3413 }
3414
3415 /* link child into the fragments list */
3416 if (child->ownerDocument->fragments) {
3417 child->nextSibling = child->ownerDocument->fragments;
3418 child->ownerDocument->fragments->previousSibling = child;
3419 child->ownerDocument->fragments = child;
3420 } else {
3421 child->ownerDocument->fragments = child;
3422 child->nextSibling = NULL;
3423 }
3424 child->parentNode = NULL;
3425 child->previousSibling = NULL;
3426 MutationEvent3(DOMNodeRemoved, child, node);
3427 MutationEvent2(DOMSubtreeModified, node);
3428 return OK;
3429 }
3430
3431
3432 /*
3433 *----------------------------------------------------------------------
3434 *
3435 * domAppendChild --
3436 *
3437 * This procedure implements the dom method appendChild. Adds the
3438 * node newChild to the end of the list of children of this
3439 * node. If the newChild is already in the tree, it is first
3440 * removed.
3441 *
3442 * Results:
3443 * Returns a domException:
3444 *
3445 * HIERARCHY_REQUEST_ERR: Raised if node is of a type that does
3446 * not allow children of the type of the childToAppend node, or
3447 * if the node to append is one of node's ancestors or the
3448 * rootNode of node's document.
3449 *
3450 * NOT_SUPPORTED_ERR: Raised if the childToInsert is the rootNode
3451 * of another document or if node is a rootNode.
3452 *
3453 * OK: otherwise
3454 *
3455 * Side effects:
3456 * Alters the involved document(s).
3457 *
3458 *----------------------------------------------------------------------
3459 */
3460
3461 domException
domAppendChild(domNode * node,domNode * childToAppend)3462 domAppendChild (
3463 domNode *node,
3464 domNode *childToAppend
3465 )
3466 {
3467 domNode *n;
3468
3469 if (node->nodeType != ELEMENT_NODE) {
3470 return HIERARCHY_REQUEST_ERR;
3471 }
3472
3473 /* check, whether childToAppend is node or one of node's ancestors */
3474 n = node;
3475 while (n) {
3476 if (n == childToAppend) {
3477 return HIERARCHY_REQUEST_ERR;
3478 }
3479 n = n->parentNode;
3480 }
3481
3482 if (childToAppend == childToAppend->ownerDocument->rootNode) {
3483 if (childToAppend == node->ownerDocument->rootNode) {
3484 return HIERARCHY_REQUEST_ERR;
3485 } else {
3486 return NOT_SUPPORTED_ERR;
3487 }
3488 }
3489
3490 /* unlink childToAppend */
3491 if (childToAppend->previousSibling) {
3492 childToAppend->previousSibling->nextSibling =
3493 childToAppend->nextSibling;
3494 } else {
3495 if (childToAppend->parentNode) {
3496 childToAppend->parentNode->firstChild = childToAppend->nextSibling;
3497 } else {
3498 /* childToAppend is either out of the fragment list or
3499 a child of the rootNode of its document */
3500 if (childToAppend->ownerDocument->fragments == childToAppend) {
3501 childToAppend->ownerDocument->fragments =
3502 childToAppend->nextSibling;
3503 } else {
3504 childToAppend->ownerDocument->rootNode->firstChild =
3505 childToAppend->nextSibling;
3506 }
3507 }
3508 }
3509 if (childToAppend->nextSibling) {
3510 childToAppend->nextSibling->previousSibling =
3511 childToAppend->previousSibling;
3512 } else {
3513 if (childToAppend->parentNode) {
3514 childToAppend->parentNode->lastChild =
3515 childToAppend->previousSibling;
3516 } else {
3517 if (childToAppend->ownerDocument->rootNode->lastChild
3518 == childToAppend) {
3519 childToAppend->ownerDocument->rootNode->lastChild =
3520 childToAppend->previousSibling;
3521 }
3522 }
3523 }
3524
3525 if (node->lastChild) {
3526 node->lastChild->nextSibling = childToAppend;
3527 childToAppend->previousSibling = node->lastChild;
3528 } else {
3529 node->firstChild = childToAppend;
3530 childToAppend->previousSibling = NULL;
3531 }
3532 node->lastChild = childToAppend;
3533 childToAppend->nextSibling = NULL;
3534 if (!childToAppend->parentNode &&
3535 (childToAppend->ownerDocument->documentElement == childToAppend)) {
3536 childToAppend->ownerDocument->documentElement =
3537 childToAppend->ownerDocument->rootNode->firstChild;
3538 }
3539 if (node == node->ownerDocument->rootNode) {
3540 childToAppend->parentNode = NULL;
3541 } else {
3542 childToAppend->parentNode = node;
3543 }
3544
3545 if ((node->ownerDocument != childToAppend->ownerDocument)
3546 || node->ownerDocument->nsptr
3547 || childToAppend->ownerDocument->baseURIs->numEntries) {
3548 domSetDocument (childToAppend, node->ownerDocument);
3549 }
3550 node->ownerDocument->nodeFlags |= NEEDS_RENUMBERING;
3551 MutationEvent();
3552 return OK;
3553 }
3554
3555
3556 /*
3557 *----------------------------------------------------------------------
3558 *
3559 * domInsertBefore --
3560 *
3561 * This procedure implements the dom method insertBefore.
3562 * It inserts the node childToInsert before the existing child
3563 * node referenceChild. If referenceChild is null, insert
3564 * childToInsert at the end of the list of children of node. The
3565 * arguments node and childToInsert must be non NULL. The
3566 * childToInsert is unlinked from its previous place (fragment
3567 * list or tree).
3568 *
3569 * Results:
3570 * Returns a domException:
3571 *
3572 * HIERARCHY_REQUEST_ERR: Raised if node is of a type that does
3573 * not allow children of the type of the childToInsert node, or
3574 * if the node to insert is node or one of node's ancestors or the
3575 * rootNode of node's document.
3576 *
3577 * NOT_FOUND_ERR: Raised if refChild is not a child of this node.
3578 *
3579 * NOT_SUPPORTED_ERR: Raised if the childToInsert is the rootNode
3580 * of another document or if node is a rootNode.
3581 *
3582 * OK: otherwise
3583 *
3584 * Side effects:
3585 * Alters the involved document(s).
3586 *
3587 *----------------------------------------------------------------------
3588 */
3589
3590 domException
domInsertBefore(domNode * node,domNode * childToInsert,domNode * referenceChild)3591 domInsertBefore (
3592 domNode *node,
3593 domNode *childToInsert,
3594 domNode *referenceChild
3595 )
3596 {
3597 domNode *n;
3598
3599
3600 if (node->nodeType != ELEMENT_NODE) {
3601 return HIERARCHY_REQUEST_ERR;
3602 }
3603
3604 /* check, if node is in deed the parent of referenceChild */
3605 if (referenceChild) {
3606 if (referenceChild->parentNode != node) {
3607 /* If node is the root node of a document and referenceChild
3608 is in deed a child of this node, then
3609 referenceChild->parentNode will be NULL. In this case, we
3610 loop throu the childs of node, to see, if the referenceChild
3611 is valid. */
3612 if (node->ownerDocument->rootNode == node) {
3613 n = node->firstChild;
3614 while (n) {
3615 if (n == referenceChild) {
3616 /* referenceChild is in deed a child of node */
3617 break;
3618 }
3619 n = n->nextSibling;
3620 }
3621 if (!n) {
3622 return NOT_FOUND_ERR;
3623 }
3624 } else {
3625 return NOT_FOUND_ERR;
3626 }
3627 }
3628 }
3629
3630 if (childToInsert == referenceChild) {
3631 return OK;
3632 }
3633
3634 /* check, whether childToInsert is one of node's ancestors */
3635 n = node;
3636 while (n) {
3637 if (n == childToInsert) {
3638 return HIERARCHY_REQUEST_ERR;
3639 }
3640 n = n->parentNode;
3641 }
3642
3643 if (childToInsert == childToInsert->ownerDocument->rootNode) {
3644 if (childToInsert == node->ownerDocument->rootNode) {
3645 return HIERARCHY_REQUEST_ERR;
3646 } else {
3647 /* For now, we simply don't allow the rootNode of
3648 another element as childToInsert. The way to go may
3649 be simply to treat the rootNode as DocumentFragment
3650 and to insert all childs of that rootNode before the
3651 referenceChild. This would result in a document
3652 without documentElement, which then should be
3653 handled right by other methods. This is planed, but
3654 not carefully considered, yet. */
3655 return NOT_SUPPORTED_ERR;
3656 }
3657 }
3658
3659
3660 /* unlink childToInsert */
3661 if (childToInsert->previousSibling) {
3662 childToInsert->previousSibling->nextSibling =
3663 childToInsert->nextSibling;
3664 } else {
3665 if (childToInsert->parentNode) {
3666 childToInsert->parentNode->firstChild = childToInsert->nextSibling;
3667 } else {
3668 /* childToInsert is either out of the fragment list or
3669 a child of the rootNode of its document */
3670 if (childToInsert->ownerDocument->fragments == childToInsert) {
3671 childToInsert->ownerDocument->fragments =
3672 childToInsert->nextSibling;
3673 } else {
3674 childToInsert->ownerDocument->rootNode->firstChild =
3675 childToInsert->nextSibling;
3676 }
3677 }
3678 }
3679 if (childToInsert->nextSibling) {
3680 childToInsert->nextSibling->previousSibling =
3681 childToInsert->previousSibling;
3682 } else {
3683 if (childToInsert->parentNode) {
3684 childToInsert->parentNode->lastChild =
3685 childToInsert->previousSibling;
3686 } else {
3687 if (childToInsert->ownerDocument->rootNode->lastChild
3688 == childToInsert) {
3689 childToInsert->ownerDocument->rootNode->lastChild =
3690 childToInsert->previousSibling;
3691 }
3692 }
3693 }
3694
3695 childToInsert->nextSibling = referenceChild;
3696 if (referenceChild) {
3697 if (referenceChild->previousSibling) {
3698 childToInsert->previousSibling = referenceChild->previousSibling;
3699 referenceChild->previousSibling->nextSibling = childToInsert;
3700 } else {
3701 node->firstChild = childToInsert;
3702 childToInsert->previousSibling = NULL;
3703 }
3704 referenceChild->previousSibling = childToInsert;
3705 } else {
3706 if (node->lastChild) {
3707 node->lastChild->nextSibling = childToInsert;
3708 childToInsert->previousSibling = node->lastChild;
3709 } else {
3710 node->firstChild = childToInsert;
3711 childToInsert->previousSibling = NULL;
3712 }
3713 node->lastChild = childToInsert;
3714 }
3715 if (!childToInsert->parentNode &&
3716 (childToInsert->ownerDocument->documentElement == childToInsert)) {
3717 childToInsert->ownerDocument->documentElement =
3718 childToInsert->ownerDocument->rootNode->firstChild;
3719 }
3720 if (node == node->ownerDocument->rootNode) {
3721 childToInsert->parentNode = NULL;
3722 } else {
3723 childToInsert->parentNode = node;
3724 }
3725 if (node->ownerDocument != childToInsert->ownerDocument
3726 || node->ownerDocument->nsptr
3727 || childToInsert->ownerDocument->baseURIs->numEntries) {
3728 domSetDocument (childToInsert, node->ownerDocument);
3729 }
3730 node->ownerDocument->nodeFlags |= NEEDS_RENUMBERING;
3731 MutationEvent3(DOMNodeInsert, childToInsert, node);
3732 MutationEvent2(DOMSubtreeModified, node);
3733 return OK;
3734 }
3735
3736
3737
3738 /*
3739 *----------------------------------------------------------------------
3740 *
3741 * domReplaceChild --
3742 *
3743 * This procedure implements the dom method replaceChild.
3744 * Replaces the child node oldChild with newChild in the list of
3745 * children of node 'node'.
3746 *
3747 * Results:
3748 * Returns a domException:
3749 *
3750 * HIERARCHY_REQUEST_ERR: Raised if node is of a type that does
3751 * not allow children of the type of the newChild node, or
3752 * if newChild is node or one of node's ancestors or the
3753 * rootNode of node's document.
3754 *
3755 * NOT_FOUND_ERR: Raised if oldChild is not a child of node.
3756 *
3757 * NOT_SUPPORTED_ERR: Raised if the newChild is the rootNode
3758 * of another document.
3759 *
3760 * OK: otherwise
3761 *
3762 * Side effects:
3763 * Alters the involved document(s).
3764 *
3765 *----------------------------------------------------------------------
3766 */
3767
3768 domException
domReplaceChild(domNode * node,domNode * newChild,domNode * oldChild)3769 domReplaceChild (
3770 domNode *node,
3771 domNode *newChild,
3772 domNode *oldChild
3773 )
3774 {
3775 domNode *n;
3776
3777
3778 if (node->nodeType != ELEMENT_NODE) {
3779 return HIERARCHY_REQUEST_ERR;
3780 }
3781
3782 /* check, if node is in deed the parent of oldChild */
3783 if (oldChild->parentNode != node) {
3784 /* If node is the root node of a document and oldChild
3785 is in deed a child of this node, then
3786 oldChild->parentNode will be NULL. In this case, we
3787 loop throu the childs of node, to see, if the oldChild
3788 is valid. */
3789 if (node->ownerDocument->rootNode == node) {
3790 n = node->firstChild;
3791 while (n) {
3792 if (n == oldChild) {
3793 /* oldChild is in deed a child of node */
3794 break;
3795 }
3796 n = n->nextSibling;
3797 }
3798 if (!n) {
3799 return NOT_FOUND_ERR;
3800 }
3801 } else {
3802 return NOT_FOUND_ERR;
3803 }
3804 }
3805
3806 if (oldChild == newChild) {
3807 return OK;
3808 }
3809
3810 /* check, whether newChild is node or one of node's ancestors */
3811 n = node;
3812 while (n) {
3813 if (n == newChild) {
3814 return HIERARCHY_REQUEST_ERR;
3815 }
3816 n = n->parentNode;
3817 }
3818
3819 if (newChild == newChild->ownerDocument->rootNode) {
3820 if (newChild == node->ownerDocument->rootNode) {
3821 return HIERARCHY_REQUEST_ERR;
3822 } else {
3823 return NOT_SUPPORTED_ERR;
3824 }
3825 }
3826
3827 /* unlink newChild */
3828 if (newChild->previousSibling) {
3829 newChild->previousSibling->nextSibling = newChild->nextSibling;
3830 } else {
3831 if (newChild->parentNode) {
3832 newChild->parentNode->firstChild = newChild->nextSibling;
3833 } else {
3834 /* newChild is either out of the fragment list or
3835 a child of the rootNode of its document */
3836 if (newChild->ownerDocument->fragments == newChild) {
3837 newChild->ownerDocument->fragments = newChild->nextSibling;
3838 } else {
3839 newChild->ownerDocument->rootNode->firstChild =
3840 newChild->nextSibling;
3841 }
3842 }
3843 }
3844 if (newChild->nextSibling) {
3845 newChild->nextSibling->previousSibling = newChild->previousSibling;
3846 } else {
3847 if (newChild->parentNode) {
3848 newChild->parentNode->lastChild = newChild->previousSibling;
3849 } else {
3850 if (newChild->ownerDocument->rootNode->lastChild == newChild) {
3851 newChild->ownerDocument->rootNode->lastChild =
3852 newChild->previousSibling;
3853 }
3854 }
3855 }
3856
3857 newChild->nextSibling = oldChild->nextSibling;
3858 newChild->previousSibling = oldChild->previousSibling;
3859 if (!newChild->parentNode &&
3860 (newChild->ownerDocument->documentElement == newChild)) {
3861 newChild->ownerDocument->documentElement =
3862 newChild->ownerDocument->rootNode->firstChild;
3863 }
3864 if (node == node->ownerDocument->rootNode) {
3865 newChild->parentNode = NULL;
3866 } else {
3867 newChild->parentNode = node;
3868 }
3869 if (oldChild->previousSibling) {
3870 oldChild->previousSibling->nextSibling = newChild;
3871 } else {
3872 node->firstChild = newChild;
3873 }
3874 if (oldChild->nextSibling) {
3875 oldChild->nextSibling->previousSibling = newChild;
3876 } else {
3877 node->lastChild = newChild;
3878 }
3879
3880 if (node->ownerDocument != newChild->ownerDocument
3881 || node->ownerDocument->nsptr
3882 || newChild->ownerDocument->baseURIs->numEntries) {
3883 domSetDocument (newChild, node->ownerDocument);
3884 }
3885
3886 /* add old child into his fragment list */
3887 if (oldChild->ownerDocument->fragments) {
3888 oldChild->nextSibling = oldChild->ownerDocument->fragments;
3889 oldChild->ownerDocument->fragments->previousSibling = oldChild;
3890 oldChild->ownerDocument->fragments = oldChild;
3891 } else {
3892 oldChild->ownerDocument->fragments = oldChild;
3893 oldChild->nextSibling = oldChild->previousSibling = NULL;
3894 }
3895 oldChild->parentNode = NULL;
3896 node->ownerDocument->nodeFlags |= NEEDS_RENUMBERING;
3897 MutationEvent();
3898 return OK;
3899 }
3900
3901
3902 /*---------------------------------------------------------------------------
3903 | domNewTextNode
3904 |
3905 \--------------------------------------------------------------------------*/
3906 domTextNode *
domNewTextNode(domDocument * doc,const char * value,int length,domNodeType nodeType)3907 domNewTextNode(
3908 domDocument *doc,
3909 const char *value,
3910 int length,
3911 domNodeType nodeType
3912 )
3913 {
3914 domTextNode *node;
3915
3916 node = (domTextNode*) domAlloc(sizeof(domTextNode));
3917 memset(node, 0, sizeof(domTextNode));
3918 node->nodeType = nodeType;
3919 node->nodeFlags = 0;
3920 node->nodeNumber = NODE_NO(doc);
3921 node->ownerDocument = doc;
3922 node->valueLength = length;
3923 node->nodeValue = (char*)MALLOC(length);
3924 memmove(node->nodeValue, value, length);
3925
3926 if (doc->fragments) {
3927 node->nextSibling = doc->fragments;
3928 doc->fragments->previousSibling = (domNode*)node;
3929 doc->fragments = (domNode*)node;
3930 } else {
3931 doc->fragments = (domNode*)node;
3932
3933 }
3934 return node;
3935 }
3936
3937
3938
3939 void
domEscapeCData(char * value,int length,Tcl_DString * escapedData)3940 domEscapeCData (
3941 char *value,
3942 int length,
3943 Tcl_DString *escapedData
3944 )
3945 {
3946 int i, start = 0;
3947 char *pc;
3948
3949 Tcl_DStringInit (escapedData);
3950 pc = value;
3951 for (i = 0; i < length; i++) {
3952 if (*pc == '&') {
3953 Tcl_DStringAppend (escapedData, &value[start], i - start);
3954 Tcl_DStringAppend (escapedData, "&", 5);
3955 start = i+1;
3956 } else
3957 if (*pc == '<') {
3958 Tcl_DStringAppend (escapedData, &value[start], i - start);
3959 Tcl_DStringAppend (escapedData, "<", 4);
3960 start = i+1;
3961 } else
3962 if (*pc == '>') {
3963 Tcl_DStringAppend (escapedData, &value[start], i - start);
3964 Tcl_DStringAppend (escapedData, ">", 4);
3965 start = i+1;
3966 }
3967 pc++;
3968 }
3969 if (start) {
3970 Tcl_DStringAppend (escapedData, &value[start], length - start);
3971 }
3972 }
3973
3974
3975 /*---------------------------------------------------------------------------
3976 | domAppendNewTextNode
3977 |
3978 \--------------------------------------------------------------------------*/
3979 domTextNode *
domAppendNewTextNode(domNode * parent,char * value,int length,domNodeType nodeType,int disableOutputEscaping)3980 domAppendNewTextNode(
3981 domNode *parent,
3982 char *value,
3983 int length,
3984 domNodeType nodeType,
3985 int disableOutputEscaping
3986 )
3987 {
3988 domTextNode *node;
3989
3990 if (!length) {
3991 return NULL;
3992 }
3993
3994 if (parent->lastChild
3995 && parent->lastChild->nodeType == TEXT_NODE
3996 && nodeType == TEXT_NODE
3997 ) {
3998 /*------------------------------------------------------------------
3999 | append to already existing text node
4000 \-----------------------------------------------------------------*/
4001 domAppendData ((domTextNode *) (parent->lastChild), value, length,
4002 disableOutputEscaping);
4003 MutationEvent();
4004 return (domTextNode*)parent->lastChild;
4005 }
4006 node = (domTextNode*) domAlloc(sizeof(domTextNode));
4007 memset(node, 0, sizeof(domTextNode));
4008 node->nodeType = nodeType;
4009 node->nodeFlags = 0;
4010 if (disableOutputEscaping) {
4011 node->nodeFlags |= DISABLE_OUTPUT_ESCAPING;
4012 }
4013 node->nodeNumber = NODE_NO(parent->ownerDocument);
4014 node->ownerDocument = parent->ownerDocument;
4015 node->valueLength = length;
4016 node->nodeValue = (char*)MALLOC(length);
4017 memmove(node->nodeValue, value, length);
4018
4019 if (parent->lastChild) {
4020 parent->lastChild->nextSibling = (domNode*)node;
4021 node->previousSibling = parent->lastChild;
4022 } else {
4023 parent->firstChild = (domNode*)node;
4024 node->previousSibling = NULL;
4025 }
4026 parent->lastChild = (domNode*)node;
4027 node->nextSibling = NULL;
4028 if (parent != parent->ownerDocument->rootNode) {
4029 node->parentNode = parent;
4030 }
4031
4032 MutationEvent();
4033 return node;
4034 }
4035
4036
4037 /*---------------------------------------------------------------------------
4038 | domAppendNewElementNode
4039 |
4040 \--------------------------------------------------------------------------*/
4041 domNode *
domAppendNewElementNode(domNode * parent,const char * tagName,const char * uri)4042 domAppendNewElementNode(
4043 domNode *parent,
4044 const char *tagName,
4045 const char *uri
4046 )
4047 {
4048 Tcl_HashEntry *h;
4049 domNode *node;
4050 domNS *ns;
4051 domAttrNode *NSattr;
4052 int hnew;
4053 char prefix[MAX_PREFIX_LEN];
4054 const char *localname;
4055 Tcl_DString dStr;
4056
4057 if (parent == NULL) {
4058 DBG(fprintf(stderr, "dom.c: Error parent == NULL!\n");)
4059 return NULL;
4060 }
4061
4062 h = Tcl_CreateHashEntry(&HASHTAB(parent->ownerDocument,tdom_tagNames),
4063 tagName, &hnew);
4064 node = (domNode*) domAlloc(sizeof(domNode));
4065 memset(node, 0, sizeof(domNode));
4066 node->nodeType = ELEMENT_NODE;
4067 node->nodeNumber = NODE_NO(parent->ownerDocument);
4068 node->ownerDocument = parent->ownerDocument;
4069 node->nodeName = (char *)&(h->key);
4070
4071 if (parent->lastChild) {
4072 parent->lastChild->nextSibling = node;
4073 node->previousSibling = parent->lastChild;
4074 } else {
4075 parent->firstChild = node;
4076 node->previousSibling = NULL;
4077 }
4078 parent->lastChild = node;
4079 node->nextSibling = NULL;
4080 if (parent != parent->ownerDocument->rootNode) {
4081 node->parentNode = parent;
4082 }
4083
4084 /*--------------------------------------------------------
4085 | re-use existing namespace or create a new one
4086 \-------------------------------------------------------*/
4087 if (uri) {
4088 domSplitQName (tagName, prefix, &localname);
4089 DBG(fprintf(stderr, "tag '%s' has prefix='%s' \n", tagName, prefix);)
4090 ns = domLookupPrefix (node, prefix);
4091 if (!ns || (strcmp (uri, ns->uri)!=0)) {
4092 ns = domNewNamespace(node->ownerDocument, prefix, uri);
4093 if (prefix[0] == '\0') {
4094 domSetAttributeNS (node, "xmlns", uri, NULL, 1);
4095 } else {
4096 Tcl_DStringInit (&dStr);
4097 Tcl_DStringAppend (&dStr, "xmlns:", 6);
4098 Tcl_DStringAppend (&dStr, prefix, -1);
4099 domSetAttributeNS (node, Tcl_DStringValue (&dStr), uri, NULL,
4100 1);
4101 }
4102 }
4103 node->namespace = ns->index;
4104 } else {
4105 ns = domLookupPrefix (node, "");
4106 if (ns) {
4107 if (strcmp (ns->uri, "")!=0) {
4108 NSattr = domSetAttributeNS (node, "xmlns", "", NULL, 1);
4109 if (NSattr) {
4110 node->namespace = NSattr->namespace;
4111 }
4112 } else {
4113 node->namespace = ns->index;
4114 }
4115 }
4116 }
4117 MutationEvent();
4118 return node;
4119 }
4120
4121
4122 /*
4123 *----------------------------------------------------------------------
4124 *
4125 * domAppendData --
4126 *
4127 * This procedure implements the dom method appendData. It is
4128 * also used by domNormalize and domAppendNewTextNode.
4129 *
4130 * Results:
4131 * A domException; currently always OK.
4132 *
4133 * Side effects:
4134 * Appends the data to node.
4135 *
4136 *----------------------------------------------------------------------
4137 */
4138
4139 domException
domAppendData(domTextNode * node,char * value,int length,int disableOutputEscaping)4140 domAppendData (
4141 domTextNode *node, /* The node, to append value to. Must be
4142 a TEXT_NODE, COMMENT_NODE or
4143 CDATA_SECTION_NODE*/
4144 char *value, /* The data to append */
4145 int length, /* The length of value in byte */
4146 int disableOutputEscaping /* If true, disable output
4147 escaping on the node */
4148 )
4149 {
4150 Tcl_DString escData;
4151
4152 if (node->nodeFlags & DISABLE_OUTPUT_ESCAPING) {
4153 if (disableOutputEscaping) {
4154 node->nodeValue = REALLOC (node->nodeValue,
4155 node->valueLength + length);
4156 memmove (node->nodeValue + node->valueLength, value, length);
4157 node->valueLength += length;
4158 } else {
4159 domEscapeCData (value, length, &escData);
4160 if (Tcl_DStringLength (&escData)) {
4161 node->nodeValue = REALLOC (node->nodeValue,
4162 node->valueLength +
4163 Tcl_DStringLength (&escData));
4164 memmove (node->nodeValue + node->valueLength,
4165 Tcl_DStringValue (&escData),
4166 Tcl_DStringLength (&escData));
4167 node->valueLength += Tcl_DStringLength (&escData);
4168 } else {
4169 node->nodeValue = REALLOC (node->nodeValue,
4170 node->valueLength+length);
4171 memmove (node->nodeValue + node->valueLength,
4172 value, length);
4173 node->valueLength += length;
4174 }
4175 Tcl_DStringFree (&escData);
4176 }
4177 } else {
4178 if (disableOutputEscaping) {
4179 node->nodeFlags |= DISABLE_OUTPUT_ESCAPING;
4180 domEscapeCData (node->nodeValue, node->valueLength,
4181 &escData);
4182 if (Tcl_DStringLength (&escData)) {
4183 FREE (node->nodeValue);
4184 node->nodeValue =
4185 MALLOC (Tcl_DStringLength (&escData) + length);
4186 memmove (node->nodeValue, Tcl_DStringValue (&escData),
4187 Tcl_DStringLength (&escData));
4188 node->valueLength = Tcl_DStringLength (&escData);
4189 } else {
4190 node->nodeValue = REALLOC (node->nodeValue,
4191 node->valueLength+length);
4192 }
4193 Tcl_DStringFree (&escData);
4194 } else {
4195 node->nodeValue = REALLOC (node->nodeValue,
4196 node->valueLength + length);
4197 }
4198 memmove (node->nodeValue + node->valueLength, value, length);
4199 node->valueLength += length;
4200 }
4201
4202 return OK;
4203 }
4204
4205
4206 /*
4207 *----------------------------------------------------------------------
4208 *
4209 * domNormalize --
4210 *
4211 * This procedure implements the dom method normalize. Puts all
4212 * Text nodes in the full depth of the sub-tree underneath node,
4213 * including attribute nodes, into a "normal" form where only
4214 * structure (e.g., elements, comments, processing instructions,
4215 * CDATA sections, and entity references) separates Text nodes,
4216 * i.e., there are neither adjacent Text nodes nor empty Text
4217 * nodes. If the flag forXPath is true, then CDATASection nodes
4218 * are treated as if they are text nodes (and merged with
4219 * circumjacent text nodes). Node must be an ELEMENT_NODE.
4220 *
4221 * Results:
4222 * None.
4223 *
4224 * Side effects:
4225 * May alter the tree.
4226 *
4227 *----------------------------------------------------------------------
4228 */
4229
4230 void
domNormalize(domNode * node,int forXPath,domFreeCallback freeCB,void * clientData)4231 domNormalize (
4232 domNode *node, /* root of the sub-tree to normalize */
4233 int forXPath, /* if true, treat CDATA_SECTION_NODEs as if
4234 they where TEXT_NODEs */
4235 domFreeCallback freeCB, /* Function to call, if a node must be freed */
4236 void *clientData /* ClientData, to provide to the freeCB */
4237
4238 )
4239 {
4240 domNode *child, *nextChild;
4241 int merge = 0;
4242
4243 if (node->nodeType != ELEMENT_NODE) return;
4244
4245 child = node->firstChild;
4246 while (child) {
4247 merge = 0;
4248 switch (child->nodeType) {
4249 case ELEMENT_NODE:
4250 domNormalize (child, forXPath, freeCB, clientData);
4251 break;
4252 case TEXT_NODE:
4253 if (child->previousSibling
4254 && child->previousSibling->nodeType == TEXT_NODE) {
4255 merge = 1;
4256 } else {
4257 if (((domTextNode *)child)->valueLength == 0) {
4258 nextChild = child->nextSibling;
4259 domDeleteNode (child, freeCB, clientData);
4260 child = nextChild;
4261 continue;
4262 }
4263 }
4264 break;
4265 case CDATA_SECTION_NODE:
4266 if (forXPath) {
4267 if (child->previousSibling
4268 && child->previousSibling->nodeType == TEXT_NODE) {
4269 merge = 1;
4270 } else {
4271 if (((domTextNode *)child)->valueLength == 0) {
4272 nextChild = child->nextSibling;
4273 domDeleteNode (child, freeCB, clientData);
4274 child = nextChild;
4275 continue;
4276 }
4277 child->nodeType = TEXT_NODE;
4278 }
4279 }
4280 break;
4281 default:
4282 break;
4283 }
4284 if (merge) {
4285 domAppendData ( (domTextNode *)(child->previousSibling),
4286 ((domTextNode *)child)->nodeValue,
4287 ((domTextNode *)child)->valueLength,
4288 (child->nodeFlags & DISABLE_OUTPUT_ESCAPING) );
4289 nextChild = child->nextSibling;
4290 domDeleteNode (child, freeCB, clientData);
4291 child = nextChild;
4292 } else {
4293 child = child->nextSibling;
4294 }
4295 }
4296 }
4297
4298 /*---------------------------------------------------------------------------
4299 | domAddNSToNode
4300 |
4301 \--------------------------------------------------------------------------*/
4302 domNS *
domAddNSToNode(domNode * node,domNS * nsToAdd)4303 domAddNSToNode (
4304 domNode *node,
4305 domNS *nsToAdd
4306 )
4307 {
4308 domAttrNode *attr, *lastNSAttr;
4309 domNS *ns, noNS;
4310 Tcl_HashEntry *h;
4311 int hnew;
4312 Tcl_DString dStr;
4313
4314 if (!nsToAdd) {
4315 noNS.uri = "";
4316 noNS.prefix = "";
4317 noNS.index = 0;
4318 nsToAdd = &noNS;
4319 }
4320 DBG(fprintf (stderr, "domAddNSToNode to node '%s': prefix: %s, uri: %s\n", node->nodeName, nsToAdd->prefix, nsToAdd->uri);)
4321
4322 ns = domLookupPrefix (node, nsToAdd->prefix);
4323 if (ns) {
4324 if (strcmp (ns->uri, nsToAdd->uri)==0) {
4325 /* namespace already in scope, we're done. */
4326 return ns;
4327 }
4328 } else {
4329 /* If the NS to set was no NS and there isn't a default NS
4330 we're done */
4331 if (nsToAdd->prefix[0] == '\0' && nsToAdd->uri[0] == '\0') return NULL;
4332 }
4333 ns = domNewNamespace (node->ownerDocument, nsToAdd->prefix, nsToAdd->uri);
4334 Tcl_DStringInit (&dStr);
4335 if (nsToAdd->prefix[0] == '\0') {
4336 Tcl_DStringAppend (&dStr, "xmlns", 5);
4337 } else {
4338 Tcl_DStringAppend (&dStr, "xmlns:", 6);
4339 Tcl_DStringAppend (&dStr, nsToAdd->prefix, -1);
4340 }
4341 /* Add new namespace attribute */
4342 attr = (domAttrNode*) domAlloc(sizeof(domAttrNode));
4343 memset(attr, 0, sizeof(domAttrNode));
4344 h = Tcl_CreateHashEntry(&HASHTAB(node->ownerDocument,tdom_attrNames),
4345 Tcl_DStringValue(&dStr), &hnew);
4346 attr->nodeType = ATTRIBUTE_NODE;
4347 attr->nodeFlags = IS_NS_NODE;
4348 attr->namespace = ns->index;
4349 attr->nodeName = (char *)&(h->key);
4350 attr->parentNode = node;
4351 attr->valueLength = strlen(nsToAdd->uri);
4352 attr->nodeValue = (char*)MALLOC(attr->valueLength+1);
4353 strcpy(attr->nodeValue, nsToAdd->uri);
4354
4355 lastNSAttr = NULL;
4356 if (node->firstAttr && (node->firstAttr->nodeFlags & IS_NS_NODE)) {
4357 lastNSAttr = node->firstAttr;
4358 while (lastNSAttr->nextSibling
4359 && (lastNSAttr->nextSibling->nodeFlags & IS_NS_NODE)) {
4360 lastNSAttr = lastNSAttr->nextSibling;
4361 }
4362 }
4363 if (lastNSAttr) {
4364 attr->nextSibling = lastNSAttr->nextSibling;
4365 lastNSAttr->nextSibling = attr;
4366 } else {
4367 attr->nextSibling = node->firstAttr;
4368 node->firstAttr = attr;
4369 }
4370 Tcl_DStringFree (&dStr);
4371 return ns;
4372 }
4373
4374 /*---------------------------------------------------------------------------
4375 | domAppendLiteralNode
4376 |
4377 \--------------------------------------------------------------------------*/
4378 domNode *
domAppendLiteralNode(domNode * parent,domNode * literalNode)4379 domAppendLiteralNode(
4380 domNode *parent,
4381 domNode *literalNode
4382 )
4383 {
4384 Tcl_HashEntry *h;
4385 domNode *node;
4386 int hnew;
4387
4388 if (parent == NULL) {
4389 DBG(fprintf(stderr, "dom.c: Error parent == NULL!\n");)
4390 return NULL;
4391 }
4392
4393 h = Tcl_CreateHashEntry(&HASHTAB(parent->ownerDocument, tdom_tagNames),
4394 literalNode->nodeName, &hnew);
4395 node = (domNode*) domAlloc(sizeof(domNode));
4396 memset(node, 0, sizeof(domNode));
4397 node->nodeType = ELEMENT_NODE;
4398 node->nodeNumber = NODE_NO(parent->ownerDocument);
4399 node->ownerDocument = parent->ownerDocument;
4400 node->nodeName = (char *)&(h->key);
4401
4402 if (parent->lastChild) {
4403 parent->lastChild->nextSibling = node;
4404 node->previousSibling = parent->lastChild;
4405 } else {
4406 parent->firstChild = node;
4407 node->previousSibling = NULL;
4408 }
4409 parent->lastChild = node;
4410 node->nextSibling = NULL;
4411 if (parent != parent->ownerDocument->rootNode) {
4412 node->parentNode = parent;
4413 }
4414
4415 MutationEvent();
4416 return node;
4417 }
4418
4419
4420 /*---------------------------------------------------------------------------
4421 | domNewProcessingInstructionNode
4422 |
4423 \--------------------------------------------------------------------------*/
4424 domProcessingInstructionNode *
domNewProcessingInstructionNode(domDocument * doc,const char * targetValue,int targetLength,const char * dataValue,int dataLength)4425 domNewProcessingInstructionNode(
4426 domDocument *doc,
4427 const char *targetValue,
4428 int targetLength,
4429 const char *dataValue,
4430 int dataLength
4431 )
4432 {
4433 domProcessingInstructionNode *node;
4434
4435 node = (domProcessingInstructionNode*) domAlloc(sizeof(domProcessingInstructionNode));
4436 memset(node, 0, sizeof(domProcessingInstructionNode));
4437 node->nodeType = PROCESSING_INSTRUCTION_NODE;
4438 node->nodeFlags = 0;
4439 node->namespace = 0;
4440 node->nodeNumber = NODE_NO(doc);
4441 node->ownerDocument = doc;
4442 node->targetLength = targetLength;
4443 node->targetValue = (char*)MALLOC(targetLength);
4444 memmove(node->targetValue, targetValue, targetLength);
4445
4446 node->dataLength = dataLength;
4447 node->dataValue = (char*)MALLOC(dataLength);
4448 memmove(node->dataValue, dataValue, dataLength);
4449
4450 if (doc->fragments) {
4451 node->nextSibling = doc->fragments;
4452 doc->fragments->previousSibling = (domNode*)node;
4453 doc->fragments = (domNode*)node;
4454 } else {
4455 doc->fragments = (domNode*)node;
4456
4457 }
4458 MutationEvent();
4459 return node;
4460 }
4461
4462
4463 /*---------------------------------------------------------------------------
4464 | domNewElementNode
4465 |
4466 \--------------------------------------------------------------------------*/
4467 domNode *
domNewElementNode(domDocument * doc,const char * tagName)4468 domNewElementNode(
4469 domDocument *doc,
4470 const char *tagName
4471 )
4472 {
4473 domNode *node;
4474 Tcl_HashEntry *h;
4475 int hnew;
4476
4477 h = Tcl_CreateHashEntry(&HASHTAB(doc, tdom_tagNames), tagName, &hnew);
4478 node = (domNode*) domAlloc(sizeof(domNode));
4479 memset(node, 0, sizeof(domNode));
4480 node->nodeType = ELEMENT_NODE;
4481 node->nodeFlags = 0;
4482 node->namespace = 0;
4483 node->nodeNumber = NODE_NO(doc);
4484 node->ownerDocument = doc;
4485 node->nodeName = (char *)&(h->key);
4486
4487 if (doc->fragments) {
4488 node->nextSibling = doc->fragments;
4489 doc->fragments->previousSibling = node;
4490 doc->fragments = node;
4491 } else {
4492 doc->fragments = node;
4493
4494 }
4495 return node;
4496 }
4497
4498
4499 /*---------------------------------------------------------------------------
4500 | domNewElementNodeNS
4501 |
4502 \--------------------------------------------------------------------------*/
4503 domNode *
domNewElementNodeNS(domDocument * doc,const char * tagName,const char * uri)4504 domNewElementNodeNS (
4505 domDocument *doc,
4506 const char *tagName,
4507 const char *uri
4508 )
4509 {
4510 domNode *node;
4511 Tcl_HashEntry *h;
4512 int hnew;
4513 char prefix[MAX_PREFIX_LEN];
4514 const char *localname;
4515 domNS *ns;
4516
4517 domSplitQName (tagName, prefix, &localname);
4518 if (prefix[0] == '\0' && uri[0] == '\0') {
4519 return NULL;
4520 }
4521
4522 h = Tcl_CreateHashEntry(&HASHTAB(doc, tdom_tagNames), tagName, &hnew);
4523 node = (domNode*) domAlloc(sizeof(domNode));
4524 memset(node, 0, sizeof(domNode));
4525 node->nodeType = ELEMENT_NODE;
4526 node->nodeFlags = 0;
4527 node->namespace = 0;
4528 node->nodeNumber = NODE_NO(doc);
4529 node->ownerDocument = doc;
4530 node->nodeName = (char *)&(h->key);
4531
4532 ns = domNewNamespace(doc, prefix, uri);
4533 node->namespace = ns->index;
4534
4535 if (doc->fragments) {
4536 node->nextSibling = doc->fragments;
4537 doc->fragments->previousSibling = node;
4538 doc->fragments = node;
4539 } else {
4540 doc->fragments = node;
4541
4542 }
4543 return node;
4544 }
4545
4546 /*---------------------------------------------------------------------------
4547 | domCloneNode
4548 |
4549 \--------------------------------------------------------------------------*/
4550 domNode *
domCloneNode(domNode * node,int deep)4551 domCloneNode (
4552 domNode *node,
4553 int deep
4554 )
4555 {
4556 domAttrNode *attr, *nattr;
4557 domNode *n, *child, *newChild;
4558
4559 /*------------------------------------------------------------------
4560 | create new node
4561 \-----------------------------------------------------------------*/
4562 if (node->nodeType == PROCESSING_INSTRUCTION_NODE) {
4563 domProcessingInstructionNode *pinode = (domProcessingInstructionNode*)node;
4564 return (domNode*) domNewProcessingInstructionNode(
4565 pinode->ownerDocument,
4566 pinode->targetValue,
4567 pinode->targetLength,
4568 pinode->dataValue,
4569 pinode->dataLength);
4570 }
4571 if (node->nodeType != ELEMENT_NODE) {
4572 domTextNode *t1node, *tnode = (domTextNode*)node;
4573 if (tnode->info) {
4574 t1node = domNewTextNode(tnode->ownerDocument,
4575 tnode->nodeValue, tnode->valueLength,
4576 tnode->nodeType);
4577 t1node->info = tnode->info;
4578 return (domNode*) t1node;
4579 } else {
4580 return (domNode*) domNewTextNode(tnode->ownerDocument,
4581 tnode->nodeValue, tnode->valueLength,
4582 tnode->nodeType);
4583 }
4584 }
4585
4586 n = domNewElementNode(node->ownerDocument, node->nodeName);
4587 n->namespace = node->namespace;
4588 n->info = node->info;
4589
4590 /*------------------------------------------------------------------
4591 | copy attributes (if any)
4592 \-----------------------------------------------------------------*/
4593 attr = node->firstAttr;
4594 while (attr != NULL) {
4595 nattr = domSetAttribute (n, attr->nodeName, attr->nodeValue );
4596 nattr->namespace = attr->namespace;
4597 if (attr->nodeFlags & IS_NS_NODE) {
4598 nattr->nodeFlags |= IS_NS_NODE;
4599 }
4600 attr = attr->nextSibling;
4601 }
4602
4603 if (deep) {
4604 child = node->firstChild;
4605 while (child) {
4606 newChild = domCloneNode(child, deep);
4607
4608 /* append new (cloned)child to cloned node, its new parent.
4609 Don't use domAppendChild for this, because that would
4610 mess around with the namespaces */
4611 if (n->ownerDocument->fragments->nextSibling) {
4612 n->ownerDocument->fragments =
4613 n->ownerDocument->fragments->nextSibling;
4614 n->ownerDocument->fragments->previousSibling = NULL;
4615 newChild->nextSibling = NULL;
4616 } else {
4617 n->ownerDocument->fragments = NULL;
4618 }
4619 if (n->firstChild) {
4620 newChild->previousSibling = n->lastChild;
4621 n->lastChild->nextSibling = newChild;
4622 } else {
4623 n->firstChild = newChild;
4624 }
4625 n->lastChild = newChild;
4626 newChild->parentNode = n;
4627
4628 /* clone next child */
4629 child = child->nextSibling;
4630 }
4631 }
4632 return n;
4633 }
4634
4635 /*----------------------------------------------------------------------------
4636 | domCopyNS
4637 |
4638 \---------------------------------------------------------------------------*/
4639 void
domCopyNS(domNode * from,domNode * to)4640 domCopyNS (
4641 domNode *from,
4642 domNode *to
4643 )
4644 {
4645 domNode *n, *n1;
4646 domNS *ns, *ns1;
4647 domAttrNode *attr, *attr1;
4648 int skip;
4649
4650 n = from;
4651 while (n) {
4652 attr = n->firstAttr;
4653 while (attr && (attr->nodeFlags & IS_NS_NODE)) {
4654 ns = n->ownerDocument->namespaces[attr->namespace-1];
4655 skip = 0;
4656 n1 = from;
4657 while (n1 != n) {
4658 attr1 = n1->firstAttr;
4659 while (attr1 && (attr1->nodeFlags & IS_NS_NODE)) {
4660 ns1 = n1->ownerDocument->namespaces[attr1->namespace-1];
4661 if ((ns1->prefix == NULL && ns->prefix == NULL)
4662 || (strcmp (ns1->prefix, ns->prefix)==0)) {
4663 skip = 1;
4664 break;
4665 }
4666 attr1 = attr1->nextSibling;
4667 }
4668 if (skip) break;
4669 n1 = n1->parentNode;
4670 }
4671 if (!skip) {
4672 /* Add this prefix/uri combination only to the
4673 destination, if it isn't already in scope */
4674 ns1 = domLookupPrefix (to, ns->prefix);
4675 if (!ns1 || (strcmp (ns->uri, ns1->uri)!=0)) {
4676 domAddNSToNode (to, ns);
4677 }
4678 }
4679 attr = attr->nextSibling;
4680 }
4681 n = n->parentNode;
4682 }
4683 }
4684
4685
4686 /*---------------------------------------------------------------------------
4687 | domCopyTo
4688 |
4689 \--------------------------------------------------------------------------*/
4690 void
domCopyTo(domNode * node,domNode * parent,int copyNS)4691 domCopyTo (
4692 domNode *node,
4693 domNode *parent,
4694 int copyNS
4695 )
4696 {
4697 domAttrNode *attr, *nattr;
4698 domNode *n, *child;
4699 domNS *ns, *ns1;
4700
4701 /*------------------------------------------------------------------
4702 | create new node
4703 \-----------------------------------------------------------------*/
4704 if (node->nodeType == PROCESSING_INSTRUCTION_NODE) {
4705 domProcessingInstructionNode *pinode = (domProcessingInstructionNode*)node;
4706 n = (domNode*) domNewProcessingInstructionNode(
4707 parent->ownerDocument,
4708 pinode->targetValue,
4709 pinode->targetLength,
4710 pinode->dataValue,
4711 pinode->dataLength);
4712 domAppendChild (parent, n);
4713 return;
4714 }
4715 if (node->nodeType != ELEMENT_NODE) {
4716 domTextNode *tnode = (domTextNode*)node;
4717 n = (domNode*) domNewTextNode(parent->ownerDocument,
4718 tnode->nodeValue, tnode->valueLength,
4719 tnode->nodeType);
4720 domAppendChild (parent, n);
4721 return;
4722 }
4723
4724 n = domAppendLiteralNode (parent, node);
4725 if (copyNS) {
4726 domCopyNS (node, n);
4727 }
4728
4729 /*------------------------------------------------------------------
4730 | copy attributes (if any)
4731 \-----------------------------------------------------------------*/
4732 attr = node->firstAttr;
4733 while (attr != NULL) {
4734 if (attr->nodeFlags & IS_NS_NODE) {
4735 if (copyNS) {
4736 /* If copyNS is true, then all namespaces in scope
4737 * (including the one declared with the node to copy)
4738 * are already copied over. */
4739 attr = attr->nextSibling;
4740 continue;
4741
4742 }
4743 ns = node->ownerDocument->namespaces[attr->namespace-1];
4744 ns1 = domLookupPrefix (n, ns->prefix);
4745 if (ns1 && strcmp (ns->uri, ns1->uri)==0) {
4746 /* This namespace is already in scope, so we
4747 don't copy the namespace attribute over */
4748 attr = attr->nextSibling;
4749 continue;
4750 }
4751 nattr = domSetAttribute (n, attr->nodeName, attr->nodeValue );
4752 nattr->nodeFlags = attr->nodeFlags;
4753 ns1 = domNewNamespace (n->ownerDocument, ns->prefix, ns->uri);
4754 nattr->namespace = ns1->index;
4755 } else {
4756 nattr = domSetAttribute (n, attr->nodeName, attr->nodeValue );
4757 nattr->nodeFlags = attr->nodeFlags;
4758 if (attr->namespace) {
4759 ns = node->ownerDocument->namespaces[attr->namespace-1];
4760 ns1 = domLookupPrefix (n, ns->prefix);
4761 if (ns1) {
4762 nattr->namespace = ns1->index;
4763 }
4764 }
4765 }
4766 attr = attr->nextSibling;
4767 }
4768
4769 /* We have to set the node namespace index after copying the
4770 attribute nodes over, because the node may be in a namespace,
4771 which is declared just at the node. */
4772 if (node->namespace) {
4773 ns = node->ownerDocument->namespaces[node->namespace-1];
4774 ns1 = domLookupPrefix (n, ns->prefix);
4775 n->namespace = ns1->index;
4776 }
4777
4778 child = node->firstChild;
4779 while (child) {
4780 domCopyTo(child, n, 0);
4781 child = child->nextSibling;
4782 }
4783 }
4784
4785
4786 /*---------------------------------------------------------------------------
4787 | domXPointerChild
4788 |
4789 \--------------------------------------------------------------------------*/
4790 int
domXPointerChild(domNode * node,int all,int instance,domNodeType type,char * element,char * attrName,char * attrValue,int attrLen,domAddCallback addCallback,void * clientData)4791 domXPointerChild (
4792 domNode * node,
4793 int all,
4794 int instance,
4795 domNodeType type,
4796 char * element,
4797 char * attrName,
4798 char * attrValue,
4799 int attrLen,
4800 domAddCallback addCallback,
4801 void * clientData
4802 )
4803 {
4804 domNode *child;
4805 domAttrNode *attr;
4806 int i=0, result;
4807
4808
4809 if (node->nodeType != ELEMENT_NODE) {
4810 return 0;
4811 }
4812
4813 if (instance<0) {
4814 child = node->lastChild;
4815 } else {
4816 child = node->firstChild;
4817 }
4818 while (child) {
4819 if ((type == ALL_NODES) || (child->nodeType == type)) {
4820 if ((element == NULL) ||
4821 ((child->nodeType == ELEMENT_NODE) && (strcmp(child->nodeName,element)==0))
4822 ) {
4823 if (attrName == NULL) {
4824 i = (instance<0) ? i-1 : i+1;
4825 if (all || (i == instance)) {
4826 result = addCallback (child, clientData);
4827 if (result) {
4828 return result;
4829 }
4830 }
4831 } else {
4832 attr = child->firstAttr;
4833 while (attr) {
4834 if ((strcmp(attr->nodeName,attrName)==0) &&
4835 ( (strcmp(attrValue,"*")==0) ||
4836 ( (attr->valueLength == attrLen) &&
4837 (strcmp(attr->nodeValue,attrValue)==0)
4838 )
4839 )
4840 ) {
4841 i = (instance<0) ? i-1 : i+1;
4842 if (all || (i == instance)) {
4843 result = addCallback (child, clientData);
4844 if (result) {
4845 return result;
4846 }
4847 }
4848 }
4849 attr = attr->nextSibling;
4850 }
4851 }
4852 }
4853 }
4854 if (instance<0) {
4855 child = child->previousSibling;
4856 } else {
4857 child = child->nextSibling;
4858 }
4859 }
4860 return 0;
4861 }
4862
4863
4864 /*---------------------------------------------------------------------------
4865 | domXPointerXSibling
4866 |
4867 \--------------------------------------------------------------------------*/
4868 int
domXPointerXSibling(domNode * node,int forward_mode,int all,int instance,domNodeType type,char * element,char * attrName,char * attrValue,int attrLen,domAddCallback addCallback,void * clientData)4869 domXPointerXSibling (
4870 domNode * node,
4871 int forward_mode,
4872 int all,
4873 int instance,
4874 domNodeType type,
4875 char * element,
4876 char * attrName,
4877 char * attrValue,
4878 int attrLen,
4879 domAddCallback addCallback,
4880 void * clientData
4881 )
4882 {
4883 domNode *sibling, *endSibling;
4884 domAttrNode *attr;
4885 int i=0, result;
4886
4887
4888 if (forward_mode) {
4889 if (instance<0) {
4890 endSibling = node;
4891 sibling = node;
4892 if (node->parentNode) {
4893 sibling = node->parentNode->lastChild;
4894 }
4895 } else {
4896 sibling = node->nextSibling;
4897 endSibling = NULL;
4898 }
4899 } else {
4900 if (instance<0) {
4901 endSibling = node;
4902 sibling = node;
4903 if (node->parentNode) {
4904 sibling = node->parentNode->firstChild;
4905 }
4906 } else {
4907 sibling = node->previousSibling;
4908 endSibling = NULL;
4909 }
4910 instance = -1 * instance;
4911 }
4912
4913 while (sibling != endSibling) {
4914 if ((type == ALL_NODES) || (sibling->nodeType == type)) {
4915 if ((element == NULL) ||
4916 ((sibling->nodeType == ELEMENT_NODE) && (strcmp(sibling->nodeName,element)==0))
4917 ) {
4918 if (attrName == NULL) {
4919 i = (instance<0) ? i-1 : i+1;
4920 if (all || (i == instance)) {
4921 result = addCallback (sibling, clientData);
4922 if (result) {
4923 return result;
4924 }
4925 }
4926 } else {
4927 attr = sibling->firstAttr;
4928 while (attr) {
4929 if ((strcmp(attr->nodeName,attrName)==0) &&
4930 ( (strcmp(attrValue,"*")==0) ||
4931 ( (attr->valueLength == attrLen) &&
4932 (strcmp(attr->nodeValue,attrValue)==0)
4933 )
4934 )
4935 ) {
4936 i = (instance<0) ? i-1 : i+1;
4937 if (all || (i == instance)) {
4938 result = addCallback (sibling, clientData);
4939 if (result) {
4940 return result;
4941 }
4942 }
4943 }
4944 attr = attr->nextSibling;
4945 }
4946 }
4947 }
4948 }
4949 if (instance<0) {
4950 sibling = sibling->previousSibling;
4951 } else {
4952 sibling = sibling->nextSibling;
4953 }
4954 }
4955 return 0;
4956 }
4957
4958
4959 /*---------------------------------------------------------------------------
4960 | domXPointerDescendant
4961 |
4962 \--------------------------------------------------------------------------*/
4963 int
domXPointerDescendant(domNode * node,int all,int instance,int * i,domNodeType type,char * element,char * attrName,char * attrValue,int attrLen,domAddCallback addCallback,void * clientData)4964 domXPointerDescendant (
4965 domNode * node,
4966 int all,
4967 int instance,
4968 int * i,
4969 domNodeType type,
4970 char * element,
4971 char * attrName,
4972 char * attrValue,
4973 int attrLen,
4974 domAddCallback addCallback,
4975 void * clientData
4976 )
4977 {
4978 domNode *child;
4979 domAttrNode *attr;
4980 int found=0, result;
4981
4982
4983 if (node->nodeType != ELEMENT_NODE) {
4984 return 0;
4985 }
4986
4987 if (instance<0) {
4988 child = node->lastChild;
4989 } else {
4990 child = node->firstChild;
4991 }
4992 while (child) {
4993 found = 0;
4994 if ((type == ALL_NODES) || (child->nodeType == type)) {
4995 if ((element == NULL) ||
4996 ((child->nodeType == ELEMENT_NODE) && (strcmp(child->nodeName,element)==0))
4997 ) {
4998 if (attrName == NULL) {
4999 *i = (instance<0) ? (*i)-1 : (*i)+1;
5000 if (all || (*i == instance)) {
5001 result = addCallback (child, clientData);
5002 if (result) {
5003 return result;
5004 }
5005 found = 1;
5006 }
5007 } else {
5008 attr = child->firstAttr;
5009 while (attr) {
5010 if ((strcmp(attr->nodeName,attrName)==0) &&
5011 ( (strcmp(attrValue,"*")==0) ||
5012 ( (attr->valueLength == attrLen) &&
5013 (strcmp(attr->nodeValue,attrValue)==0)
5014 )
5015 )
5016 ) {
5017 *i = (instance<0) ? (*i)-1 : (*i)+1;
5018 if (all || (*i == instance)) {
5019 result = addCallback (child, clientData);
5020 if (result) {
5021 return result;
5022 }
5023 found = 1;
5024 }
5025 }
5026 attr = attr->nextSibling;
5027 }
5028 }
5029 }
5030 }
5031 if (!found) {
5032 /* recurs into childs */
5033 result = domXPointerDescendant (child, all, instance, i,
5034 type, element, attrName,
5035 attrValue, attrLen,
5036 addCallback, clientData);
5037 if (result) {
5038 return result;
5039 }
5040 }
5041 if (instance<0) {
5042 child = child->previousSibling;
5043 } else {
5044 child = child->nextSibling;
5045 }
5046 }
5047 return 0;
5048 }
5049
5050
5051 /*---------------------------------------------------------------------------
5052 | domXPointerAncestor
5053 |
5054 \--------------------------------------------------------------------------*/
5055 int
domXPointerAncestor(domNode * node,int all,int instance,int * i,domNodeType type,char * element,char * attrName,char * attrValue,int attrLen,domAddCallback addCallback,void * clientData)5056 domXPointerAncestor (
5057 domNode * node,
5058 int all,
5059 int instance,
5060 int * i,
5061 domNodeType type,
5062 char * element,
5063 char * attrName,
5064 char * attrValue,
5065 int attrLen,
5066 domAddCallback addCallback,
5067 void * clientData
5068 )
5069 {
5070 domNode *ancestor;
5071 domAttrNode *attr;
5072 int result;
5073
5074
5075 ancestor = node->parentNode;
5076 if (ancestor) {
5077 if ((type == ALL_NODES) || (ancestor->nodeType == type)) {
5078 if ((element == NULL) ||
5079 ((ancestor->nodeType == ELEMENT_NODE) && (strcmp(ancestor->nodeName,element)==0))
5080 ) {
5081 if (attrName == NULL) {
5082 *i = (instance<0) ? (*i)-1 : (*i)+1;
5083 if (all || (*i == instance)) {
5084 result = addCallback (ancestor, clientData);
5085 if (result) {
5086 return result;
5087 }
5088 }
5089 } else {
5090 attr = ancestor->firstAttr;
5091 while (attr) {
5092 if ((strcmp(attr->nodeName,attrName)==0) &&
5093 ( (strcmp(attrValue,"*")==0) ||
5094 ( (attr->valueLength == attrLen) &&
5095 (strcmp(attr->nodeValue,attrValue)==0)
5096 )
5097 )
5098 ) {
5099 *i = (instance<0) ? (*i)-1 : (*i)+1;
5100 if (all || (*i == instance)) {
5101 result = addCallback (ancestor, clientData);
5102 if (result) {
5103 return result;
5104 }
5105 }
5106 }
5107 attr = attr->nextSibling;
5108 }
5109 }
5110 }
5111 }
5112
5113 /* go up */
5114 result = domXPointerAncestor (ancestor, all, instance, i,
5115 type, element, attrName,
5116 attrValue, attrLen,
5117 addCallback, clientData);
5118 if (result) {
5119 return result;
5120 }
5121 }
5122 return 0;
5123 }
5124
5125
5126
5127 /*---------------------------------------------------------------------------
5128 | type tdomCmdReadInfo
5129 |
5130 \--------------------------------------------------------------------------*/
5131 typedef struct _tdomCmdReadInfo {
5132
5133 XML_Parser parser;
5134 domDocument *document;
5135 domNode *currentNode;
5136 int depth;
5137 int ignoreWhiteSpaces;
5138 int cdataSection;
5139 Tcl_DString *cdata;
5140 int storeLineColumn;
5141 int ignorexmlns;
5142 int feedbackAfter;
5143 Tcl_Obj *feedbackCmd;
5144 int nextFeedbackPosition;
5145 Tcl_Interp *interp;
5146 int activeNSsize;
5147 int activeNSpos;
5148 domActiveNS *activeNS;
5149 int baseURIstackSize;
5150 int baseURIstackPos;
5151 domActiveBaseURI *baseURIstack;
5152 int insideDTD;
5153 /* Now the tdom cmd specific elements */
5154 int tdomStatus;
5155 Tcl_Obj *extResolver;
5156
5157 } tdomCmdReadInfo;
5158
5159 EXTERN int tcldom_returnDocumentObj (Tcl_Interp *interp,
5160 domDocument *document,
5161 int setVariable, Tcl_Obj *var_name,
5162 int trace, int forOwnerDocument);
5163
5164 void
tdom_freeProc(Tcl_Interp * interp,void * userData)5165 tdom_freeProc (
5166 Tcl_Interp *interp,
5167 void *userData
5168 )
5169 {
5170 tdomCmdReadInfo *info = (tdomCmdReadInfo *) userData;
5171
5172 if (info->document) {
5173 domFreeDocument (info->document, NULL, NULL);
5174 }
5175 if (info->activeNS) {
5176 FREE (info->activeNS);
5177 }
5178 if (info->baseURIstack) {
5179 FREE (info->baseURIstack);
5180 }
5181
5182 Tcl_DStringFree (info->cdata);
5183 FREE (info->cdata);
5184 if (info->extResolver) {
5185 Tcl_DecrRefCount (info->extResolver);
5186 }
5187 FREE (info);
5188 }
5189
5190 void
tdom_parserResetProc(XML_Parser parser,void * userData)5191 tdom_parserResetProc (
5192 XML_Parser parser,
5193 void *userData
5194 )
5195 {
5196 tdomCmdReadInfo *info = (tdomCmdReadInfo *) userData;
5197
5198 info->parser = parser;
5199 }
5200
5201 void
tdom_resetProc(Tcl_Interp * interp,void * userData)5202 tdom_resetProc (
5203 Tcl_Interp *interp,
5204 void *userData
5205 )
5206 {
5207 tdomCmdReadInfo *info = (tdomCmdReadInfo *) userData;
5208
5209 if (!info->tdomStatus) return;
5210
5211 if (info->document) {
5212 domFreeDocument (info->document, NULL, NULL);
5213 }
5214
5215 info->document = NULL;
5216 info->currentNode = NULL;
5217 info->depth = 0;
5218 info->feedbackAfter = 0;
5219 info->ignorexmlns = 0;
5220 Tcl_DStringSetLength (info->cdata, 0);
5221 info->nextFeedbackPosition = info->feedbackAfter;
5222 info->interp = interp;
5223 info->activeNSpos = -1;
5224 info->insideDTD = 0;
5225 info->baseURIstackPos = 0;
5226 info->tdomStatus = 0;
5227
5228 }
5229
5230 void
tdom_initParseProc(Tcl_Interp * interp,void * userData)5231 tdom_initParseProc (
5232 Tcl_Interp *interp,
5233 void *userData
5234 )
5235 {
5236 tdomCmdReadInfo *info = (tdomCmdReadInfo *) userData;
5237
5238 info->document = domCreateDoc(XML_GetBase (info->parser),
5239 info->storeLineColumn);
5240 if (info->extResolver) {
5241 info->document->extResolver =
5242 tdomstrdup (Tcl_GetString (info->extResolver));
5243 }
5244 info->baseURIstack[0].baseURI = XML_GetBase (info->parser);
5245 info->baseURIstack[0].depth = 0;
5246 info->tdomStatus = 2;
5247
5248 }
5249
5250 static void
tdom_charDataHandler(void * userData,const char * s,int len)5251 tdom_charDataHandler (
5252 void *userData,
5253 const char *s,
5254 int len
5255 )
5256 {
5257 domReadInfo *info = userData;
5258
5259 Tcl_DStringAppend (info->cdata, s, len);
5260 DispatchPCDATA (info);
5261 return;
5262 }
5263
5264 int
TclTdomObjCmd(dummy,interp,objc,objv)5265 TclTdomObjCmd (dummy, interp, objc, objv)
5266 ClientData dummy;
5267 Tcl_Interp *interp;
5268 int objc;
5269 Tcl_Obj *const objv[];
5270 {
5271 CHandlerSet *handlerSet;
5272 int methodIndex, result, bool;
5273 tdomCmdReadInfo *info;
5274 TclGenExpatInfo *expat;
5275 Tcl_Obj *newObjName = NULL;
5276
5277 static const char *tdomMethods[] = {
5278 "enable", "getdoc",
5279 "setStoreLineColumn",
5280 "setExternalEntityResolver", "keepEmpties",
5281 "remove", "ignorexmlns", "keepCDATA",
5282 NULL
5283 };
5284 enum tdomMethod {
5285 m_enable, m_getdoc,
5286 m_setStoreLineColumn,
5287 m_setExternalEntityResolver, m_keepEmpties,
5288 m_remove, m_ignorexmlns, m_keepCDATA
5289 };
5290
5291 if (objc < 3 || objc > 4) {
5292 Tcl_WrongNumArgs (interp, 1, objv, tdom_usage);
5293 return TCL_ERROR;
5294 }
5295
5296 if (!CheckExpatParserObj (interp, objv[1])) {
5297 Tcl_SetResult (interp, "First argument has to be a expat parser object", NULL);
5298 return TCL_ERROR;
5299 }
5300
5301 if (Tcl_GetIndexFromObj (interp, objv[2], tdomMethods, "method", 0,
5302 &methodIndex) != TCL_OK)
5303 {
5304 Tcl_SetResult (interp, tdom_usage, NULL);
5305 return TCL_ERROR;
5306 }
5307
5308 switch ((enum tdomMethod) methodIndex) {
5309
5310 default:
5311 Tcl_SetResult (interp, "unknown method", NULL);
5312 return TCL_ERROR;
5313
5314 case m_enable:
5315 expat = GetExpatInfo (interp, objv[1]);
5316 if (expat->parsingState != 0) {
5317 Tcl_SetResult (interp,
5318 "Parser is not in init or reset state.", NULL);
5319 return TCL_ERROR;
5320 }
5321
5322 handlerSet = CHandlerSetCreate ("tdom");
5323 handlerSet->ignoreWhiteCDATAs = 1;
5324 handlerSet->resetProc = tdom_resetProc;
5325 handlerSet->freeProc = tdom_freeProc;
5326 handlerSet->parserResetProc = tdom_parserResetProc;
5327 handlerSet->initParseProc = tdom_initParseProc;
5328 handlerSet->elementstartcommand = startElement;
5329 handlerSet->elementendcommand = endElement;
5330 handlerSet->datacommand = tdom_charDataHandler;
5331 /* handlerSet->datacommand = characterDataHandler; */
5332 handlerSet->commentCommand = commentHandler;
5333 handlerSet->picommand = processingInstructionHandler;
5334 handlerSet->entityDeclCommand = entityDeclHandler;
5335 handlerSet->startDoctypeDeclCommand = startDoctypeDeclHandler;
5336 handlerSet->endDoctypeDeclCommand = endDoctypeDeclHandler;
5337
5338 info = (tdomCmdReadInfo *) MALLOC (sizeof (tdomCmdReadInfo));
5339 info->parser = expat->parser;
5340 info->document = NULL;
5341 info->currentNode = NULL;
5342 info->depth = 0;
5343 info->ignoreWhiteSpaces = 1;
5344 info->cdataSection = 0;
5345 info->cdata = (Tcl_DString*) MALLOC (sizeof (Tcl_DString));
5346 Tcl_DStringInit (info->cdata);
5347 info->storeLineColumn = 0;
5348 info->ignorexmlns = 0;
5349 info->feedbackAfter = 0;
5350 info->feedbackCmd = NULL;
5351 info->nextFeedbackPosition = 0;
5352 info->interp = interp;
5353 info->activeNSpos = -1;
5354 info->activeNSsize = 8;
5355 info->activeNS =
5356 (domActiveNS*) MALLOC(sizeof(domActiveNS) * info->activeNSsize);
5357 info->baseURIstackPos = 0;
5358 info->baseURIstackSize = INITIAL_BASEURISTACK_SIZE;
5359 info->baseURIstack = (domActiveBaseURI*)
5360 MALLOC (sizeof(domActiveBaseURI) * info->baseURIstackSize);
5361 info->insideDTD = 0;
5362 info->tdomStatus = 0;
5363 info->extResolver = NULL;
5364
5365 handlerSet->userData = info;
5366
5367 CHandlerSetInstall (interp, objv[1], handlerSet);
5368 break;
5369
5370 case m_getdoc:
5371 info = CHandlerSetGetUserData (interp, objv[1], "tdom");
5372 if (!info) {
5373 Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL);
5374 return TCL_ERROR;
5375 }
5376 expat = GetExpatInfo (interp, objv[1]);
5377 if (info->tdomStatus != 2 || !expat->finished) {
5378 Tcl_SetResult (interp, "No DOM tree available.", NULL);
5379 return TCL_ERROR;
5380 }
5381 domSetDocumentElement (info->document);
5382 result = tcldom_returnDocumentObj (interp, info->document, 0,
5383 newObjName, 0, 0);
5384 info->document = NULL;
5385 return result;
5386
5387 case m_setStoreLineColumn:
5388 info = CHandlerSetGetUserData (interp, objv[1], "tdom");
5389 if (!info) {
5390 Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL);
5391 return TCL_ERROR;
5392 }
5393 Tcl_SetIntObj (Tcl_GetObjResult (interp), info->storeLineColumn);
5394 if (objc == 4) {
5395 if (Tcl_GetBooleanFromObj (interp, objv[3], &bool) != TCL_OK) {
5396 return TCL_ERROR;
5397 }
5398 info->storeLineColumn = bool;
5399 }
5400 info->tdomStatus = 1;
5401 break;
5402
5403 case m_remove:
5404 result = CHandlerSetRemove (interp, objv[1], "tdom");
5405 if (result == 2) {
5406 Tcl_SetResult (interp, "expat parser obj hasn't a C handler set named \"tdom\"", NULL);
5407 return TCL_ERROR;
5408 }
5409 break;
5410
5411 case m_setExternalEntityResolver:
5412 if (objc != 4) {
5413 Tcl_SetResult (interp, "You must name a Tcl command as external entity resolver for setExternalEntityResolver.", NULL);
5414 return TCL_ERROR;
5415 }
5416 info = CHandlerSetGetUserData (interp, objv[1], "tdom");
5417 if (!info) {
5418 Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL);
5419 return TCL_ERROR;
5420 }
5421 if (info->extResolver) {
5422 Tcl_DecrRefCount (info->extResolver);
5423 }
5424 if (strcmp (Tcl_GetString (objv[3]), "") == 0) {
5425 info->extResolver = NULL;
5426 } else {
5427 info->extResolver = objv[3];
5428 Tcl_IncrRefCount (info->extResolver);
5429 }
5430 info->tdomStatus = 1;
5431 break;
5432
5433 case m_keepEmpties:
5434 if (objc != 4) {
5435 Tcl_SetResult (interp, "wrong # of args for method keepEmpties.",
5436 NULL);
5437 return TCL_ERROR;
5438 }
5439 handlerSet = CHandlerSetGet (interp, objv[1], "tdom");
5440 if (!handlerSet) {
5441 Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL);
5442 return TCL_ERROR;
5443 }
5444 info = handlerSet->userData;
5445 if (!info) {
5446 Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL);
5447 return TCL_ERROR;
5448 }
5449 Tcl_SetIntObj (Tcl_GetObjResult (interp), info->ignoreWhiteSpaces);
5450 if (Tcl_GetBooleanFromObj (interp, objv[3], &bool) != TCL_OK) {
5451 return TCL_ERROR;
5452 }
5453 info->ignoreWhiteSpaces = !bool;
5454 handlerSet->ignoreWhiteCDATAs = !bool;
5455 info->tdomStatus = 1;
5456 break;
5457
5458 case m_keepCDATA:
5459 if (objc != 4) {
5460 Tcl_SetResult (interp, "wrong # of args for method keepCDATA.",
5461 NULL);
5462 return TCL_ERROR;
5463 }
5464 handlerSet = CHandlerSetGet (interp, objv[1], "tdom");
5465 if (!handlerSet) {
5466 Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL);
5467 return TCL_ERROR;
5468 }
5469 info = handlerSet->userData;
5470 if (!info) {
5471 Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL);
5472 return TCL_ERROR;
5473 }
5474 if (Tcl_GetBooleanFromObj (interp, objv[3], &bool) != TCL_OK) {
5475 return TCL_ERROR;
5476 }
5477 if (bool) {
5478 handlerSet->startCdataSectionCommand = startCDATA;
5479 handlerSet->endCdataSectionCommand = endCDATA;
5480 } else {
5481 handlerSet->startCdataSectionCommand = startCDATA;
5482 handlerSet->endCdataSectionCommand = endCDATA;
5483 }
5484 info->tdomStatus = 1;
5485 break;
5486
5487 case m_ignorexmlns:
5488 info = CHandlerSetGetUserData (interp, objv[1], "tdom");
5489 if (!info) {
5490 Tcl_SetResult (interp, "parser object isn't tdom enabled.", NULL);
5491 return TCL_ERROR;
5492 }
5493 Tcl_SetIntObj (Tcl_GetObjResult (interp), info->ignorexmlns);
5494 if (objc == 4) {
5495 if (Tcl_GetBooleanFromObj (interp, objv[3], &bool) != TCL_OK) {
5496 return TCL_ERROR;
5497 }
5498 info->ignorexmlns = bool;
5499 }
5500 info->tdomStatus = 1;
5501 break;
5502
5503
5504 }
5505
5506 return TCL_OK;
5507 }
5508