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, "&amp;", 5);
3955             start = i+1;
3956         } else
3957         if (*pc == '<') {
3958             Tcl_DStringAppend (escapedData, &value[start], i - start);
3959             Tcl_DStringAppend (escapedData, "&lt;", 4);
3960             start = i+1;
3961         } else
3962         if (*pc == '>') {
3963             Tcl_DStringAppend (escapedData, &value[start], i - start);
3964             Tcl_DStringAppend (escapedData, "&gt;", 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