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