File Coverage

dom.c
Criterion Covered Total %
statement 430 603 71.3
branch 267 480 55.6
condition n/a
subroutine n/a
pod n/a
total 697 1083 64.3


line stmt bran cond sub pod time code
1             /* $Id$
2             *
3             * This is free software, you may use it and distribute it under the same terms as
4             * Perl itself.
5             *
6             * Copyright 2001-2003 AxKit.com Ltd., 2002-2006 Christian Glahn, 2006-2009 Petr Pajas
7             */
8              
9             #include "dom.h"
10             #include "perl-libxml-mm.h"
11              
12             /* #define warn(string) fprintf(stderr, string) */
13              
14             #ifdef XS_WARNINGS
15             #define xs_warn(string) warn("%s",string)
16             #else
17             #define xs_warn(string)
18             #endif
19              
20             void
21             domClearPSVIInList(xmlNodePtr list);
22              
23             void
24 72           domClearPSVI(xmlNodePtr tree) {
25             xmlAttrPtr prop;
26              
27 72 50         if (tree == NULL)
28 0           return;
29 72 100         if (tree->type == XML_ELEMENT_NODE) {
30 23           tree->psvi = NULL;
31 23           prop = tree->properties;
32 29 100         while (prop != NULL) {
33 6 50         if (tree->type == XML_ATTRIBUTE_NODE)
34 0           ((xmlAttrPtr) prop)->psvi = NULL;
35 6           domClearPSVIInList(prop->children);
36 6           prop = prop->next;
37             }
38 49 100         } else if (tree->type == XML_DOCUMENT_NODE) {
39 8           ((xmlDocPtr) tree)->psvi = NULL;
40             }
41 72 100         if (tree->children != NULL)
42 28           domClearPSVIInList(tree->children);
43             }
44              
45             void
46 34           domClearPSVIInList(xmlNodePtr list) {
47             xmlNodePtr cur;
48              
49 34 50         if (list == NULL)
50 0           return;
51 34           cur = list;
52 98 100         while (cur != NULL) {
53 64           domClearPSVI(cur);
54 64           cur = cur->next;
55             }
56             }
57              
58             /**
59             * Name: domReconcileNs
60             * Synopsis: void domReconcileNs( xmlNodePtr tree );
61             * @tree: the tree to reconcile
62             *
63             * Reconciles namespacing on a tree by removing declarations
64             * of element and attribute namespaces that are already
65             * declared in the scope of the corresponding node.
66             **/
67              
68             void
69 15           domAddNsDef(xmlNodePtr tree, xmlNsPtr ns)
70             {
71 15           xmlNsPtr i = tree->nsDef;
72 15 50         while(i != NULL && i != ns)
    0          
73 0           i = i->next;
74 15 50         if( i == NULL )
75             {
76 15           ns->next = tree->nsDef;
77 15           tree->nsDef = ns;
78             }
79 15           }
80              
81             char
82 22           domRemoveNsDef(xmlNodePtr tree, xmlNsPtr ns)
83             {
84 22           xmlNsPtr i = tree->nsDef;
85              
86 22 100         if( ns == tree->nsDef )
87             {
88 17           tree->nsDef = tree->nsDef->next;
89 17           ns->next = NULL;
90 17           return(1);
91             }
92 5 50         while( i != NULL )
93             {
94 0 0         if( i->next == ns )
95             {
96 0           i->next = ns->next;
97 0           ns->next = NULL;
98 0           return(1);
99             }
100 0           i = i->next;
101             }
102 5           return(0);
103             }
104              
105             /* ns->next must be NULL, or bad things could happen */
106             xmlNsPtr
107 7           _domAddNsChain(xmlNsPtr c, xmlNsPtr ns)
108             {
109 7 50         if( c == NULL )
110 7           return(ns);
111             else
112             {
113 0           xmlNsPtr i = c;
114 0 0         while(i != NULL && i != ns)
    0          
115 0           i = i->next;
116 0 0         if(i == NULL)
117             {
118 0           ns->next = c;
119 0           return(ns);
120             }
121             }
122 0           return(c);
123             }
124              
125             /* We need to be smarter with attributes, because the declaration is on the parent element */
126             void
127 11           _domReconcileNsAttr(xmlAttrPtr attr, xmlNsPtr * unused)
128             {
129 11           xmlNodePtr tree = attr->parent;
130 11 50         if (tree == NULL)
131 0           return;
132 11 100         if( attr->ns != NULL )
133             {
134             xmlNsPtr ns;
135 14           if ((attr->ns->prefix != NULL) &&
136 7           (xmlStrEqual(attr->ns->prefix, BAD_CAST "xml"))) {
137             /* prefix 'xml' has no visible declaration */
138 5           ns = xmlSearchNsByHref(tree->doc, tree, XML_XML_NAMESPACE);
139 5           attr->ns = ns;
140 5           return;
141             } else {
142 2           ns = xmlSearchNs( tree->doc, tree->parent, attr->ns->prefix );
143             }
144 3 100         if( ns != NULL && ns->href != NULL && attr->ns->href != NULL &&
    50          
145 1           xmlStrcmp(ns->href,attr->ns->href) == 0 )
146             {
147             /* Remove the declaration from the element */
148 1 50         if( domRemoveNsDef(tree, attr->ns) )
149             /* Queue up this namespace for freeing */
150 1           *unused = _domAddNsChain(*unused, attr->ns);
151              
152             /* Replace the namespace with the one found */
153 1           attr->ns = ns;
154             }
155             else
156             {
157             /* If the declaration is here, we don't need to do anything */
158 1 50         if( domRemoveNsDef(tree, attr->ns) )
159 0           domAddNsDef(tree, attr->ns);
160             else
161             {
162             /* Replace/Add the namespace declaration on the element */
163 1           attr->ns = xmlCopyNamespace(attr->ns);
164 1 50         if (attr->ns) {
165 1           domAddNsDef(tree, attr->ns);
166             }
167             }
168             }
169             }
170             }
171              
172             void
173 178           _domReconcileNs(xmlNodePtr tree, xmlNsPtr * unused)
174             {
175 178 100         if( tree->ns != NULL
176 47 100         && ((tree->type == XML_ELEMENT_NODE)
177 27 50         || (tree->type == XML_ATTRIBUTE_NODE)))
178             {
179 20           xmlNsPtr ns = xmlSearchNs( tree->doc, tree->parent, tree->ns->prefix );
180 27 100         if( ns != NULL && ns->href != NULL && tree->ns->href != NULL &&
    50          
181 7           xmlStrcmp(ns->href,tree->ns->href) == 0 )
182             {
183             /* Remove the declaration (if present) */
184 6 50         if( domRemoveNsDef(tree, tree->ns) )
185             /* Queue the namespace for freeing */
186 6           *unused = _domAddNsChain(*unused, tree->ns);
187              
188             /* Replace the namespace with the one found */
189 6           tree->ns = ns;
190             }
191             else
192             {
193             /* If the declaration is here, we don't need to do anything */
194 14 100         if( domRemoveNsDef(tree, tree->ns) ) {
195 10           domAddNsDef(tree, tree->ns);
196             }
197             else
198             {
199             /* Restart the namespace at this point */
200 4           tree->ns = xmlCopyNamespace(tree->ns);
201 4           domAddNsDef(tree, tree->ns);
202             }
203             }
204             }
205             /* Fix attribute namespacing */
206 178 100         if( tree->type == XML_ELEMENT_NODE )
207             {
208 97           xmlElementPtr ele = (xmlElementPtr) tree;
209             /* attributes is set to xmlAttributePtr,
210             but is an xmlAttrPtr??? */
211 97           xmlAttrPtr attr = (xmlAttrPtr) ele->attributes;
212 108 100         while( attr != NULL )
213             {
214 11           _domReconcileNsAttr(attr, unused);
215 11           attr = attr->next;
216             }
217             }
218             {
219             /* Recurse through all child nodes */
220 178           xmlNodePtr child = tree->children;
221 235 100         while( child != NULL )
222             {
223 57           _domReconcileNs(child, unused);
224 57           child = child->next;
225             }
226             }
227 178           }
228              
229             void
230 121           domReconcileNs(xmlNodePtr tree)
231             {
232 121           xmlNsPtr unused = NULL;
233 121           _domReconcileNs(tree, &unused);
234 121 100         if( unused != NULL )
235 7           xmlFreeNsList(unused);
236 121           }
237              
238             /**
239             * NAME domParseChar
240             * TYPE function
241             * SYNOPSIS
242             * int utf8char = domParseChar( curchar, &len );
243             *
244             * The current char value, if using UTF-8 this may actually span
245             * multiple bytes in the given string. This function parses an utf8
246             * character from a string into a UTF8 character (an integer). It uses
247             * a slightly modified version of libxml2's character parser. libxml2
248             * itself does not provide any function to parse characters dircetly
249             * from a string and test if they are valid utf8 characters.
250             *
251             * XML::LibXML uses this function rather than perls native UTF8
252             * support for two reasons:
253             * 1) perls UTF8 handling functions often lead to encoding errors,
254             * which partly comes, that they are badly documented.
255             * 2) not all perl versions XML::LibXML intends to run with have native
256             * UTF8 support.
257             *
258             * domParseChar() allows to use the very same code with all versions
259             * of perl :)
260             *
261             * Returns the current char value and its length
262             *
263             * NOTE: If the character passed to this function is not a UTF
264             * character, the return value will be 0 and the length of the
265             * character is -1!
266             */
267             int
268 892           domParseChar( xmlChar *cur, int *len )
269             {
270             unsigned char c;
271             unsigned int val;
272              
273             /*
274             * We are supposed to handle UTF8, check it's valid
275             * From rfc2044: encoding of the Unicode values on UTF-8:
276             *
277             * UCS-4 range (hex.) UTF-8 octet sequence (binary)
278             * 0000 0000-0000 007F 0xxxxxxx
279             * 0000 0080-0000 07FF 110xxxxx 10xxxxxx
280             * 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
281             *
282             * Check for the 0x110000 limit too
283             */
284              
285 892 50         if ( cur == NULL || *cur == 0 ) {
    50          
286 0           *len = 0;
287 0           return(0);
288             }
289              
290 892           c = *cur;
291 892 100         if ( c & 0x80 ) {
292 2 50         if ((c & 0xe0) == 0xe0) {
293 0 0         if ((c & 0xf0) == 0xf0) {
294             /* 4-byte code */
295 0           *len = 4;
296 0           val = (cur[0] & 0x7) << 18;
297 0           val |= (cur[1] & 0x3f) << 12;
298 0           val |= (cur[2] & 0x3f) << 6;
299 0           val |= cur[3] & 0x3f;
300             } else {
301             /* 3-byte code */
302 0           *len = 3;
303 0           val = (cur[0] & 0xf) << 12;
304 0           val |= (cur[1] & 0x3f) << 6;
305 0           val |= cur[2] & 0x3f;
306             }
307             } else {
308             /* 2-byte code */
309 2           *len = 2;
310 2           val = (cur[0] & 0x1f) << 6;
311 2           val |= cur[1] & 0x3f;
312             }
313 2 50         if ( !IS_CHAR(val) ) {
    50          
    50          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
314 0           *len = -1;
315 0           return(0);
316             }
317 2           return(val);
318             }
319             else {
320             /* 1-byte code */
321 890           *len = 1;
322 890           return((int)c);
323             }
324             }
325              
326             /**
327             * Name: domReadWellBalancedString
328             * Synopsis: xmlNodePtr domReadWellBalancedString( xmlDocPtr doc, xmlChar *string )
329             * @doc: the document, the string should belong to
330             * @string: the string to parse
331             *
332             * this function is pretty neat, since you can read in well balanced
333             * strings and get a list of nodes, which can be added to any other node.
334             * (sure - this should return a doucment_fragment, but still it doesn't)
335             *
336             * the code is pretty heavy i think, but deep in my heard i believe it's
337             * worth it :) (e.g. if you like to read a chunk of well-balanced code
338             * from a databasefield)
339             *
340             * in 99% the cases i believe it is faster than to create the dom by hand,
341             * and skip the parsing job which has to be done here.
342             *
343             * the repair flag will not be recognized with the current libxml2
344             **/
345             xmlNodePtr
346 43           domReadWellBalancedString( xmlDocPtr doc, xmlChar* block, int repair ) {
347 43           int retCode = -1;
348 43           xmlNodePtr nodes = NULL;
349              
350 43 50         if ( block ) {
351             /* read and encode the chunk */
352 43           retCode = xmlParseBalancedChunkMemory( doc,
353             NULL,
354             NULL,
355             0,
356             block,
357             &nodes );
358              
359             /* retCode = xmlParseBalancedChunkMemoryRecover( doc, */
360             /* NULL, */
361             /* NULL, */
362             /* 0, */
363             /* block, */
364             /* &nodes, */
365             /* repair ); */
366              
367             /* error handling */
368 43 100         if ( retCode != 0 && repair == 0 ) {
    50          
369             /* if the code was not well balanced, we will not return
370             * a bad node list, but we have to free the nodes */
371 8           xmlFreeNodeList( nodes );
372 8           nodes = NULL;
373             }
374             else {
375 35           xmlSetListDoc(nodes,doc);
376             }
377             }
378              
379 43           return nodes;
380             }
381              
382             /**
383             * internal helper: insert node to nodelist
384             * synopsis: xmlNodePtr insert_node_to_nodelist( leader, insertnode, followup );
385             * while leader and followup are already list nodes. both may be NULL
386             * if leader is null the parents children will be reset
387             * if followup is null the parent last will be reset.
388             * leader and followup has to be followups in the nodelist!!!
389             * the function returns the node inserted. if a fragment was inserted,
390             * the first node of the list will returned
391             *
392             * i ran into a misconception here. there should be a normalization function
393             * for the DOM, so sequences of text nodes can get replaced by a single
394             * text node. as i see DOM Level 1 does not allow text node sequences, while
395             * Level 2 and 3 do.
396             **/
397             int
398 45           domAddNodeToList(xmlNodePtr cur, xmlNodePtr leader, xmlNodePtr followup)
399             {
400 45           xmlNodePtr c1 = NULL, c2 = NULL, p = NULL;
401 45 50         if ( cur ) {
402 45           c1 = c2 = cur;
403 45 100         if( leader ) {
404 39           p = leader->parent;
405             }
406 6 50         else if( followup ) {
407 6           p = followup->parent;
408             }
409             else {
410 0           return 0; /* can't insert */
411             }
412              
413 45 100         if ( cur->type == XML_DOCUMENT_FRAG_NODE ) {
414 6           c1 = cur->children;
415 18 100         while ( c1 ){
416 12           c1->parent = p;
417 12           c1 = c1->next;
418             }
419 6           c1 = cur->children;
420 6           c2 = cur->last;
421 6           cur->last = cur->children = NULL;
422             }
423             else {
424 39           cur->parent = p;
425             }
426              
427 45 50         if (c1 && c2 && c1!=leader) {
    50          
    50          
428 45 100         if ( leader ) {
429 39           leader->next = c1;
430 39           c1->prev = leader;
431             }
432 6 50         else if ( p ) {
433 6           p->children = c1;
434             }
435              
436 45 100         if ( followup ) {
437 14           followup->prev = c2;
438 14           c2->next = followup;
439             }
440 31 50         else if ( p ) {
441 31           p->last = c2;
442             }
443             }
444 45           return 1;
445             }
446 0           return 0;
447             }
448              
449             /**
450             * domIsParent tests, if testnode is parent of the reference
451             * node. this test is very important to avoid circular constructs in
452             * trees. if the ref is a parent of the cur node the
453             * function returns 1 (TRUE), otherwise 0 (FALSE).
454             **/
455             int
456 89           domIsParent( xmlNodePtr cur, xmlNodePtr refNode ) {
457 89           xmlNodePtr helper = NULL;
458              
459 89 50         if ( cur == NULL || refNode == NULL) return 0;
    50          
460 89 100         if (refNode==cur) return 1;
461 88 100         if ( cur->doc != refNode->doc
462 71 100         || refNode->children == NULL
463 7 100         || cur->parent == (xmlNodePtr)cur->doc
464 1 50         || cur->parent == NULL ) {
465 87           return 0;
466             }
467              
468 1 50         if( refNode->type == XML_DOCUMENT_NODE ) {
469 0           return 1;
470             }
471              
472 1           helper= cur;
473 3 50         while ( helper && (xmlDocPtr) helper != cur->doc ) {
    100          
474 2 50         if( helper == refNode ) {
475 0           return 1;
476             }
477 2           helper = helper->parent;
478             }
479              
480 1           return 0;
481             }
482              
483             int
484 86           domTestHierarchy(xmlNodePtr cur, xmlNodePtr refNode)
485             {
486 86 50         if ( !refNode || !cur ) {
    50          
487 0           return 0;
488             }
489 86 100         if (cur->type == XML_ATTRIBUTE_NODE) {
490 2 50         switch ( refNode->type ){
491             case XML_TEXT_NODE:
492             case XML_ENTITY_REF_NODE:
493 2           return 1;
494             break;
495             default:
496 0           return 0;
497             break;
498             }
499             }
500              
501 84 50         switch ( refNode->type ){
502             case XML_ATTRIBUTE_NODE:
503             case XML_DOCUMENT_NODE:
504 0           return 0;
505             break;
506             default:
507 84           break;
508             }
509              
510 84 100         if ( domIsParent( cur, refNode ) ) {
511 1           return 0;
512             }
513              
514 83           return 1;
515             }
516              
517             int
518 85           domTestDocument(xmlNodePtr cur, xmlNodePtr refNode)
519             {
520 85 100         if ( cur->type == XML_DOCUMENT_NODE ) {
521 12 100         switch ( refNode->type ) {
522             case XML_ATTRIBUTE_NODE:
523             case XML_ELEMENT_NODE:
524             case XML_ENTITY_NODE:
525             case XML_ENTITY_REF_NODE:
526             case XML_TEXT_NODE:
527             case XML_CDATA_SECTION_NODE:
528             case XML_NAMESPACE_DECL:
529 1           return 0;
530             break;
531             default:
532 11           break;
533             }
534             }
535 84           return 1;
536             }
537              
538             void
539 109           domUnlinkNode( xmlNodePtr node ) {
540 109 50         if ( node == NULL
541 109 100         || ( node->prev == NULL
542 98 100         && node->next == NULL
543 84 100         && node->parent == NULL ) ) {
544 11           return;
545             }
546              
547 98 100         if (node->type == XML_DTD_NODE) {
548             /* This clears the doc->intSubset pointer. */
549 11           xmlUnlinkNode(node);
550 11           return;
551             }
552              
553 87 100         if ( node->prev != NULL ) {
554 11           node->prev->next = node->next;
555             }
556              
557 87 100         if ( node->next != NULL ) {
558 12           node->next->prev = node->prev;
559             }
560              
561 87 50         if ( node->parent != NULL ) {
562 87 100         if ( node == node->parent->last ) {
563 75           node->parent->last = node->prev;
564             }
565              
566 87 100         if ( node == node->parent->children ) {
567 76           node->parent->children = node->next;
568             }
569             }
570              
571 87           node->prev = NULL;
572 87           node->next = NULL;
573 87           node->parent = NULL;
574             }
575              
576             xmlNodePtr
577 27           domImportNode( xmlDocPtr doc, xmlNodePtr node, int move, int reconcileNS ) {
578 27           xmlNodePtr return_node = node;
579              
580 27 100         if ( move ) {
581 23           return_node = node;
582 23           domUnlinkNode( node );
583             }
584             else {
585 4 50         if ( node->type == XML_DTD_NODE ) {
586 0           return_node = (xmlNodePtr) xmlCopyDtd((xmlDtdPtr) node);
587             }
588             else {
589 4           return_node = xmlDocCopyNode( node, doc, 1 );
590             }
591             }
592              
593              
594             /* tell all children about the new boss */
595 27 50         if ( node && node->doc != doc ) {
    50          
596             /* if the source document contained psvi, mark the current document as psvi tainted */
597 27 100         if (PmmIsPSVITainted(node->doc))
    50          
    50          
598 0 0         PmmInvalidatePSVI(doc);
    0          
599 27           xmlSetTreeDoc(return_node, doc);
600             }
601              
602 27 100         if ( reconcileNS && doc && return_node
    100          
    50          
603 18 50         && return_node->type != XML_ENTITY_REF_NODE ) {
604 18           domReconcileNs(return_node);
605             }
606              
607 27           return return_node;
608             }
609              
610             /**
611             * Name: domName
612             * Synopsis: string = domName( node );
613             *
614             * domName returns the full name for the current node.
615             * If the node belongs to a namespace it returns the prefix and
616             * the local name. otherwise only the local name is returned.
617             **/
618             xmlChar*
619 253           domName(xmlNodePtr node) {
620 253           const xmlChar *prefix = NULL;
621 253           const xmlChar *name = NULL;
622 253           xmlChar *qname = NULL;
623              
624 253 50         if ( node == NULL ) {
625 0           return NULL;
626             }
627              
628 253           switch ( node->type ) {
629             case XML_XINCLUDE_START :
630             case XML_XINCLUDE_END :
631             case XML_ENTITY_REF_NODE :
632             case XML_ENTITY_NODE :
633             case XML_DTD_NODE :
634             case XML_ENTITY_DECL :
635             case XML_DOCUMENT_TYPE_NODE :
636             case XML_PI_NODE :
637             case XML_NOTATION_NODE :
638             case XML_NAMESPACE_DECL :
639 8           name = node->name;
640 8           break;
641              
642             case XML_COMMENT_NODE :
643 14           name = (const xmlChar *) "#comment";
644 14           break;
645              
646             case XML_CDATA_SECTION_NODE :
647 16           name = (const xmlChar *) "#cdata-section";
648 16           break;
649              
650             case XML_TEXT_NODE :
651 55           name = (const xmlChar *) "#text";
652 55           break;
653              
654              
655             case XML_DOCUMENT_NODE :
656             case XML_HTML_DOCUMENT_NODE :
657             case XML_DOCB_DOCUMENT_NODE :
658 1           name = (const xmlChar *) "#document";
659 1           break;
660              
661             case XML_DOCUMENT_FRAG_NODE :
662 1           name = (const xmlChar *) "#document-fragment";
663 1           break;
664              
665             case XML_ELEMENT_NODE :
666             case XML_ATTRIBUTE_NODE :
667 158 100         if ( node->ns != NULL ) {
668 33           prefix = node->ns->prefix;
669             }
670 158           name = node->name;
671 158           break;
672              
673             case XML_ELEMENT_DECL :
674 0           prefix = ((xmlElementPtr) node)->prefix;
675 0           name = node->name;
676 0           break;
677              
678             case XML_ATTRIBUTE_DECL :
679 0           prefix = ((xmlAttributePtr) node)->prefix;
680 0           name = node->name;
681 0           break;
682             }
683              
684 253 100         if ( prefix != NULL ) {
685 21           qname = xmlStrdup( prefix );
686 21           qname = xmlStrcat( qname , (const xmlChar *) ":" );
687 21           qname = xmlStrcat( qname , name );
688             }
689             else {
690 232           qname = xmlStrdup( name );
691             }
692              
693 253           return qname;
694             }
695              
696             /**
697             * Name: domAppendChild
698             * Synopsis: xmlNodePtr domAppendChild( xmlNodePtr par, xmlNodePtr newCld );
699             * @par: the node to append to
700             * @newCld: the node to append
701             *
702             * Returns newCld on success otherwise NULL
703             * The function will unbind newCld first if nesseccary. As well the
704             * function will fail, if par or newCld is a Attribute Node OR if newCld
705             * is a parent of par.
706             *
707             * If newCld belongs to a different DOM the node will be imported
708             * implicit before it gets appended.
709             **/
710             xmlNodePtr
711 64           domAppendChild( xmlNodePtr self,
712             xmlNodePtr newChild ){
713 64           xmlNodePtr fragment = NULL;
714 64 50         if ( self == NULL ) {
715 0           return newChild;
716             }
717              
718 127           if ( !(domTestHierarchy(self, newChild)
719 63           && domTestDocument(self, newChild))){
720 1           croak("appendChild: HIERARCHY_REQUEST_ERR\n");
721             return NULL;
722             }
723              
724 63 100         if ( newChild->doc == self->doc ){
725 59           domUnlinkNode( newChild );
726             }
727             else {
728             xs_warn("WRONG_DOCUMENT_ERR - non conform implementation\n");
729             /* xmlGenericError(xmlGenericErrorContext,"WRONG_DOCUMENT_ERR\n"); */
730 4           newChild = domImportNode( self->doc, newChild, 1, 0 );
731             }
732              
733 63 100         if ( self->children != NULL ) {
734 25 100         if (newChild->type == XML_DOCUMENT_FRAG_NODE )
735 2           fragment = newChild->children;
736 25           domAddNodeToList( newChild, self->last, NULL );
737             }
738 38 100         else if (newChild->type == XML_DOCUMENT_FRAG_NODE ) {
739 2           xmlNodePtr c1 = NULL;
740 2           self->children = newChild->children;
741 2           fragment = newChild->children;
742 2           c1 = fragment;
743 3 100         while ( c1 ){
744 1           c1->parent = self;
745 1           c1 = c1->next;
746             }
747 2           self->last = newChild->last;
748 2           newChild->last = newChild->children = NULL;
749             }
750             else {
751 36           self->children = newChild;
752 36           self->last = newChild;
753 36           newChild->parent= self;
754             }
755              
756 63 100         if ( fragment ) {
757             /* we must reconcile all nodes in the fragment */
758 3           newChild = fragment; /* return the first node in the fragment */
759 8 100         while ( fragment ) {
760 5           domReconcileNs(fragment);
761 5           fragment = fragment->next;
762             }
763             }
764 60 100         else if ( newChild->type != XML_ENTITY_REF_NODE ) {
765 59           domReconcileNs(newChild);
766             }
767              
768 63           return newChild;
769             }
770              
771             xmlNodePtr
772 17           domRemoveChild( xmlNodePtr self, xmlNodePtr old ) {
773 17 50         if ( self == NULL || old == NULL ) {
    50          
774 0           return NULL;
775             }
776 17 50         if ( old->type == XML_ATTRIBUTE_NODE
777 17 50         || old->type == XML_NAMESPACE_DECL ) {
778 0           return NULL;
779             }
780 17 100         if ( self != old->parent ) {
781             /* not a child! */
782 1           return NULL;
783             }
784              
785 16           domUnlinkNode( old );
786 16 100         if ( old->type == XML_ELEMENT_NODE ) {
787 15           domReconcileNs( old );
788             }
789              
790 16           return old ;
791             }
792              
793             xmlNodePtr
794 9           domReplaceChild( xmlNodePtr self, xmlNodePtr new, xmlNodePtr old ) {
795 9           xmlNodePtr fragment = NULL;
796 9           xmlNodePtr fragment_next = NULL;
797 9 50         if ( self== NULL )
798 0           return NULL;
799              
800 9 50         if ( new == old )
801 0           return NULL;
802              
803 9 50         if ( new == NULL ) {
804             /* level2 sais nothing about this case :( */
805 0           return domRemoveChild( self, old );
806             }
807              
808 9 50         if ( old == NULL ) {
809 0           domAppendChild( self, new );
810 0           return old;
811             }
812              
813 18           if ( !(domTestHierarchy(self, new)
814 9           && domTestDocument(self, new))){
815 0           croak("replaceChild: HIERARCHY_REQUEST_ERR\n");
816             return NULL;
817             }
818              
819 9 100         if ( new->doc == self->doc ) {
820 3           domUnlinkNode( new );
821             }
822             else {
823             /* WRONG_DOCUMENT_ERR - non conform implementation */
824 6           new = domImportNode( self->doc, new, 1, 1 );
825             }
826              
827 9 100         if( old == self->children && old == self->last ) {
    100          
828 2           domRemoveChild( self, old );
829 2           domAppendChild( self, new );
830             }
831 7 100         else if ( new->type == XML_DOCUMENT_FRAG_NODE
832 1 50         && new->children == NULL ) {
833             /* want to replace with an empty fragment, then remove ... */
834 0           fragment = new->children;
835 0           fragment_next = old->next;
836 0           domRemoveChild( self, old );
837             }
838             else {
839 7           domAddNodeToList(new, old->prev, old->next );
840 7           old->parent = old->next = old->prev = NULL;
841             }
842 9 50         if ( fragment ) {
843 0 0         while ( fragment && fragment != fragment_next ) {
    0          
844 0           domReconcileNs(fragment);
845 0           fragment = fragment->next;
846             }
847 9 50         } else if ( new->type != XML_ENTITY_REF_NODE ) {
848 9           domReconcileNs(new);
849             }
850              
851 9           return old;
852             }
853              
854              
855             xmlNodePtr
856 15           domInsertBefore( xmlNodePtr self,
857             xmlNodePtr newChild,
858             xmlNodePtr refChild ){
859 15           xmlNodePtr fragment = NULL;
860 15 50         if ( refChild == newChild ) {
861 0           return newChild;
862             }
863              
864 15 50         if ( self == NULL || newChild == NULL ) {
    50          
865 0           return NULL;
866             }
867              
868 15 100         if ( refChild != NULL ) {
869 10 50         if ( refChild->parent != self
870 10 100         || ( newChild->type == XML_DOCUMENT_FRAG_NODE
871 3 50         && newChild->children == NULL ) ) {
872             /* NOT_FOUND_ERR */
873 0           xmlGenericError(xmlGenericErrorContext,"NOT_FOUND_ERR\n");
874 0           return NULL;
875             }
876             }
877              
878 15 100         if ( self->children == NULL ) {
879 2           return domAppendChild( self, newChild );
880             }
881              
882 26           if ( !(domTestHierarchy( self, newChild )
883 13           && domTestDocument( self, newChild ))) {
884 1           croak("insertBefore/insertAfter: HIERARCHY_REQUEST_ERR\n");
885             return NULL;
886             }
887              
888 12 100         if ( self->doc == newChild->doc ){
889 8           domUnlinkNode( newChild );
890             }
891             else {
892 4           newChild = domImportNode( self->doc, newChild, 1, 0 );
893             }
894              
895 12 100         if ( newChild->type == XML_DOCUMENT_FRAG_NODE ) {
896 3           fragment = newChild->children;
897             }
898 12 100         if ( refChild == NULL ) {
899 3           domAddNodeToList(newChild, self->last, NULL);
900             }
901             else {
902 9           domAddNodeToList(newChild, refChild->prev, refChild);
903             }
904              
905 12 100         if ( fragment ) {
906 3           newChild = fragment; /* return the first node in the fragment */
907 9 50         while ( fragment && fragment != refChild ) {
    100          
908 6           domReconcileNs(fragment);
909 6           fragment = fragment->next;
910             }
911 9 50         } else if ( newChild->type != XML_ENTITY_REF_NODE ) {
912 9           domReconcileNs(newChild);
913             }
914              
915 12           return newChild;
916             }
917              
918             /*
919             * this function does not exist in the spec although it's useful
920             */
921             xmlNodePtr
922 6           domInsertAfter( xmlNodePtr self,
923             xmlNodePtr newChild,
924             xmlNodePtr refChild ){
925 6 100         if ( refChild == NULL ) {
926 2           return domInsertBefore( self, newChild, NULL );
927             }
928 4           return domInsertBefore( self, newChild, refChild->next );
929             }
930              
931             xmlNodePtr
932 0           domReplaceNode( xmlNodePtr oldNode, xmlNodePtr newNode ) {
933 0           xmlNodePtr prev = NULL, next = NULL, par = NULL, fragment = NULL;
934              
935 0 0         if ( oldNode == NULL
936 0 0         || newNode == NULL ) {
937             /* NOT_FOUND_ERROR */
938 0           return NULL;
939             }
940              
941 0 0         if ( oldNode->type == XML_ATTRIBUTE_NODE
942 0 0         || newNode->type == XML_ATTRIBUTE_NODE
943 0 0         || newNode->type == XML_DOCUMENT_NODE
944 0 0         || domIsParent( newNode, oldNode ) ) {
945             /* HIERARCHY_REQUEST_ERR
946             * wrong node type
947             * new node is parent of itself
948             */
949 0           croak("replaceNode: HIERARCHY_REQUEST_ERR\n");
950             return NULL;
951             }
952              
953 0           par = oldNode->parent;
954 0           prev = oldNode->prev;
955 0           next = oldNode->next;
956              
957 0 0         if ( oldNode->_private == NULL ) {
958 0           xmlUnlinkNode( oldNode );
959             }
960             else {
961 0           domUnlinkNode( oldNode );
962             }
963              
964 0 0         if ( newNode->type == XML_DOCUMENT_FRAG_NODE ) {
965 0           fragment = newNode->children;
966             }
967 0 0         if( prev == NULL && next == NULL ) {
    0          
968             /* oldNode was the only child */
969 0           domAppendChild( par , newNode );
970             }
971             else {
972 0           domAddNodeToList( newNode, prev, next );
973             }
974              
975 0 0         if ( fragment ) {
976 0 0         while ( fragment && fragment != next ) {
    0          
977 0           domReconcileNs(fragment);
978 0           fragment = fragment->next;
979             }
980 0 0         } else if ( newNode->type != XML_ENTITY_REF_NODE ) {
981 0           domReconcileNs(newNode);
982             }
983              
984 0           return oldNode;
985             }
986              
987             xmlChar*
988 192           domGetNodeValue( xmlNodePtr n ) {
989 192           xmlChar * retval = NULL;
990 192 50         if( n != NULL ) {
991 192 100         switch ( n->type ) {
992             case XML_ATTRIBUTE_NODE:
993             case XML_ENTITY_DECL:
994             case XML_TEXT_NODE:
995             case XML_COMMENT_NODE:
996             case XML_CDATA_SECTION_NODE:
997             case XML_PI_NODE:
998             case XML_ENTITY_REF_NODE:
999 190           break;
1000             default:
1001 2           return retval;
1002             break;
1003             }
1004 190 100         if ( n->type != XML_ENTITY_DECL ) {
1005 189           retval = xmlXPathCastNodeToString(n);
1006             }
1007             else {
1008 1 50         if ( n->content != NULL ) {
1009             xs_warn(" dublicate content\n" );
1010 1           retval = xmlStrdup(n->content);
1011             }
1012 0 0         else if ( n->children != NULL ) {
1013 0           xmlNodePtr cnode = n->children;
1014             xs_warn(" use child content\n" );
1015             /* ok then toString in this case ... */
1016 0 0         while (cnode) {
1017 0           xmlBufferPtr buffer = xmlBufferCreate();
1018             /* buffer = xmlBufferCreate(); */
1019 0           xmlNodeDump( buffer, n->doc, cnode, 0, 0 );
1020 0 0         if ( buffer->content != NULL ) {
1021             xs_warn( "add item" );
1022 0 0         if ( retval != NULL ) {
1023 0           retval = xmlStrcat( retval, buffer->content );
1024             }
1025             else {
1026 0           retval = xmlStrdup( buffer->content );
1027             }
1028             }
1029 0           xmlBufferFree( buffer );
1030 0           cnode = cnode->next;
1031             }
1032             }
1033             }
1034             }
1035              
1036 190           return retval;
1037             }
1038              
1039             void
1040 60           domSetNodeValue( xmlNodePtr n , xmlChar* val ){
1041 60 50         if ( n == NULL )
1042 0           return;
1043 60 50         if ( val == NULL ){
1044 0           val = (xmlChar *) "";
1045             }
1046              
1047 60 100         if( n->type == XML_ATTRIBUTE_NODE ){
1048             /* can't use xmlNodeSetContent - for Attrs it parses entities */
1049 1 50         if ( n->children != NULL ) {
1050 1           n->last = NULL;
1051 1           xmlFreeNodeList( n->children );
1052             }
1053 1           n->children = xmlNewText( val );
1054 1           n->children->parent = n;
1055 1           n->children->doc = n->doc;
1056 1           n->last = n->children;
1057             }
1058             else {
1059 59           xmlNodeSetContent( n, val );
1060             }
1061             }
1062              
1063              
1064             xmlNodeSetPtr
1065 0           domGetElementsByTagName( xmlNodePtr n, xmlChar* name ){
1066 0           xmlNodeSetPtr rv = NULL;
1067 0           xmlNodePtr cld = NULL;
1068              
1069 0 0         if ( n != NULL && name != NULL ) {
    0          
1070 0           cld = n->children;
1071 0 0         while ( cld != NULL ) {
1072 0 0         if ( xmlStrcmp( name, cld->name ) == 0 ){
1073 0 0         if ( rv == NULL ) {
1074 0           rv = xmlXPathNodeSetCreate( cld ) ;
1075             }
1076             else {
1077 0           xmlXPathNodeSetAdd( rv, cld );
1078             }
1079             }
1080 0           cld = cld->next;
1081             }
1082             }
1083              
1084 0           return rv;
1085             }
1086              
1087              
1088             xmlNodeSetPtr
1089 0           domGetElementsByTagNameNS( xmlNodePtr n, xmlChar* nsURI, xmlChar* name ){
1090 0           xmlNodeSetPtr rv = NULL;
1091              
1092 0 0         if ( nsURI == NULL ) {
1093 0           return domGetElementsByTagName( n, name );
1094             }
1095              
1096 0 0         if ( n != NULL && name != NULL ) {
    0          
1097 0           xmlNodePtr cld = n->children;
1098 0 0         while ( cld != NULL ) {
1099 0 0         if ( xmlStrcmp( name, cld->name ) == 0
1100 0 0         && cld->ns != NULL
1101 0 0         && xmlStrcmp( nsURI, cld->ns->href ) == 0 ){
1102 0 0         if ( rv == NULL ) {
1103 0           rv = xmlXPathNodeSetCreate( cld ) ;
1104             }
1105             else {
1106 0           xmlXPathNodeSetAdd( rv, cld );
1107             }
1108             }
1109 0           cld = cld->next;
1110             }
1111             }
1112              
1113 0           return rv;
1114             }
1115              
1116             xmlNsPtr
1117 0           domNewNs ( xmlNodePtr elem , xmlChar *prefix, xmlChar *href ) {
1118 0           xmlNsPtr ns = NULL;
1119              
1120 0 0         if (elem != NULL) {
1121 0           ns = xmlSearchNs( elem->doc, elem, prefix );
1122             }
1123             /* prefix is not in use */
1124 0 0         if (ns == NULL) {
1125 0           ns = xmlNewNs( elem , href , prefix );
1126             } else {
1127             /* prefix is in use; if it has same URI, let it go, otherwise it's
1128             an error */
1129 0 0         if (!xmlStrEqual(href, ns->href)) {
1130 0           ns = NULL;
1131             }
1132             }
1133 0           return ns;
1134             }
1135              
1136             xmlAttrPtr
1137 66           domGetAttrNode(xmlNodePtr node, const xmlChar *qname) {
1138 66           xmlChar * prefix = NULL;
1139 66           xmlChar * localname = NULL;
1140 66           xmlAttrPtr ret = NULL;
1141 66           xmlNsPtr ns = NULL;
1142              
1143 66 50         if ( qname == NULL || node == NULL )
    50          
1144 0           return NULL;
1145              
1146             /* first try qname without namespace */
1147 66           ret = xmlHasNsProp(node, qname, NULL);
1148 66 100         if ( ret == NULL ) {
1149 30           localname = xmlSplitQName2(qname, &prefix);
1150 30 100         if ( localname != NULL ) {
1151 16           ns = xmlSearchNs( node->doc, node, prefix );
1152 16 100         if ( ns != NULL ) {
1153             /* then try localname with the namespace bound to prefix */
1154 15           ret = xmlHasNsProp( node, localname, ns->href );
1155             }
1156 16 50         if ( prefix != NULL) {
1157 16           xmlFree( prefix );
1158             }
1159 16           xmlFree( localname );
1160             }
1161             }
1162 66 100         if (ret && ret->type != XML_ATTRIBUTE_NODE) {
    100          
1163 8           return NULL; /* we don't want fixed attribute decls */
1164             }
1165             else {
1166 66           return ret;
1167             }
1168             }
1169              
1170             xmlAttrPtr
1171 0           domSetAttributeNode( xmlNodePtr node, xmlAttrPtr attr ) {
1172 0 0         if ( node == NULL || attr == NULL ) {
    0          
1173 0           return attr;
1174             }
1175 0 0         if ( attr != NULL && attr->type != XML_ATTRIBUTE_NODE )
    0          
1176 0           return NULL;
1177 0 0         if ( node == attr->parent ) {
1178 0           return attr; /* attribute is already part of the node */
1179             }
1180 0 0         if ( attr->doc != node->doc ){
1181 0           attr = (xmlAttrPtr) domImportNode( node->doc, (xmlNodePtr) attr, 1, 1 );
1182             }
1183             else {
1184 0           xmlUnlinkNode( (xmlNodePtr) attr );
1185             }
1186              
1187             /* stolen from libxml2 */
1188 0 0         if ( attr != NULL ) {
1189 0 0         if (node->properties == NULL) {
1190 0           node->properties = attr;
1191             } else {
1192 0           xmlAttrPtr prev = node->properties;
1193              
1194 0 0         while (prev->next != NULL) prev = prev->next;
1195 0           prev->next = attr;
1196 0           attr->prev = prev;
1197             }
1198             }
1199              
1200 0           return attr;
1201             }
1202              
1203             void
1204 14           domAttrSerializeContent(xmlBufferPtr buffer, xmlAttrPtr attr)
1205             {
1206             xmlNodePtr children;
1207              
1208 14           children = attr->children;
1209 38 100         while (children != NULL) {
1210 24           switch (children->type) {
1211             case XML_TEXT_NODE:
1212 19           xmlAttrSerializeTxtContent(buffer, attr->doc,
1213 19           attr, children->content);
1214 19           break;
1215             case XML_ENTITY_REF_NODE:
1216 5           xmlBufferAdd(buffer, BAD_CAST "&", 1);
1217 5           xmlBufferAdd(buffer, children->name,
1218             xmlStrlen(children->name));
1219 5           xmlBufferAdd(buffer, BAD_CAST ";", 1);
1220 5           break;
1221             default:
1222             /* should not happen unless we have a badly built tree */
1223 0           break;
1224             }
1225 24           children = children->next;
1226             }
1227 14           }
1228              
1229              
1230             int
1231             domNodeNormalize( xmlNodePtr node );
1232              
1233             int
1234 16           domNodeNormalizeList( xmlNodePtr nodelist )
1235             {
1236 25 100         while ( nodelist ){
1237 9 50         if ( domNodeNormalize( nodelist ) == 0 )
1238 0           return(0);
1239 9           nodelist = nodelist->next;
1240             }
1241 16           return(1);
1242             }
1243              
1244             int
1245 12           domNodeNormalize( xmlNodePtr node )
1246             {
1247 12           xmlNodePtr next = NULL;
1248              
1249 12 50         if ( node == NULL )
1250 0           return(0);
1251              
1252 12           switch ( node->type ) {
1253             case XML_TEXT_NODE:
1254 8 100         while ( node->next
1255 5 50         && node->next->type == XML_TEXT_NODE ) {
1256 5           next = node->next;
1257 5           xmlNodeAddContent(node, next->content);
1258 5           xmlUnlinkNode( next );
1259              
1260             /**
1261             * keep only nodes that are referred by perl (or GDOME)
1262             */
1263 5 50         if ( !next->_private )
1264 0           xmlFreeNode( next );
1265             }
1266 3           break;
1267             case XML_ELEMENT_NODE:
1268 7           domNodeNormalizeList( (xmlNodePtr) node->properties );
1269             case XML_ATTRIBUTE_NODE:
1270             case XML_DOCUMENT_NODE:
1271 9           return( domNodeNormalizeList( node->children ) );
1272             break;
1273             default:
1274 0           break;
1275             }
1276 3           return(1);
1277             }
1278              
1279             int
1280 3           domRemoveNsRefs(xmlNodePtr tree, xmlNsPtr ns) {
1281             xmlAttrPtr attr;
1282 3           xmlNodePtr node = tree;
1283              
1284 3 50         if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) return(0);
    50          
1285 5 100         while (node != NULL) {
1286 4 100         if (node->ns == ns)
1287 2           node->ns = NULL; /* remove namespace reference */
1288 4           attr = node->properties;
1289 5 100         while (attr != NULL) {
1290 1 50         if (attr->ns == ns)
1291 1           attr->ns = NULL; /* remove namespace reference */
1292 1           attr = attr->next;
1293             }
1294             /*
1295             * Browse the full subtree, deep first
1296             */
1297 4 100         if (node->children != NULL && node->type != XML_ENTITY_REF_NODE) {
    50          
1298             /* deep first */
1299 1           node = node->children;
1300 3 100         } else if ((node != tree) && (node->next != NULL)) {
    50          
1301             /* then siblings */
1302 0           node = node->next;
1303 3 100         } else if (node != tree) {
1304             /* go up to parents->next if needed */
1305 2 100         while (node != tree) {
1306 1 50         if (node->parent != NULL)
1307 1           node = node->parent;
1308 1 50         if ((node != tree) && (node->next != NULL)) {
    0          
1309 0           node = node->next;
1310 0           break;
1311             }
1312 1 50         if (node->parent == NULL) {
1313 0           node = NULL;
1314 0           break;
1315             }
1316             }
1317             /* exit condition */
1318 1 50         if (node == tree)
1319 1           node = NULL;
1320             } else
1321 2           break;
1322             }
1323 3           return(1);
1324             }
1325