1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or
5  *  (at your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *  GNU General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15  *
16  *  Jabber
17  *  Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
18  */
19 
20 #include "libxode.h"
21 
22 /* Internal routines */
_xmlnode_new(pool p,const char * name,unsigned int type)23 xmlnode _xmlnode_new(pool p, const char* name, unsigned int type)
24 {
25     xmlnode result = NULL;
26     if (type > NTYPE_LAST)
27         return NULL;
28 
29     if (type != NTYPE_CDATA && name == NULL)
30         return NULL;
31 
32     if (p == NULL)
33     {
34         p = pool_heap(1*1024);
35     }
36 
37     /* Allocate & zero memory */
38     result = (xmlnode)pmalloc(p, sizeof(_xmlnode));
39     memset(result, '\0', sizeof(_xmlnode));
40 
41     /* Initialize fields */
42     if (type != NTYPE_CDATA)
43         result->name = pstrdup(p,name);
44     result->type = type;
45     result->p = p;
46     return result;
47 }
48 
_xmlnode_append_sibling(xmlnode lastsibling,const char * name,unsigned int type)49 static xmlnode _xmlnode_append_sibling(xmlnode lastsibling, const char* name, unsigned int type)
50 {
51     xmlnode result;
52 
53     result = _xmlnode_new(xmlnode_pool(lastsibling), name, type);
54     if (result != NULL)
55     {
56         /* Setup sibling pointers */
57         result->prev = lastsibling;
58         lastsibling->next = result;
59     }
60     return result;
61 }
62 
_xmlnode_insert(xmlnode parent,const char * name,unsigned int type)63 static xmlnode _xmlnode_insert(xmlnode parent, const char* name, unsigned int type)
64 {
65     xmlnode result;
66 
67     if(parent == NULL || name == NULL) return NULL;
68 
69     /* If parent->firstchild is NULL, simply create a new node for the first child */
70     if (parent->firstchild == NULL)
71     {
72         result = _xmlnode_new(parent->p, name, type);
73         parent->firstchild = result;
74     }
75     /* Otherwise, append this to the lastchild */
76     else
77     {
78         result= _xmlnode_append_sibling(parent->lastchild, name, type);
79     }
80     result->parent = parent;
81     parent->lastchild = result;
82     return result;
83 
84 }
85 
_xmlnode_search(xmlnode firstsibling,const char * name,unsigned int type)86 static xmlnode _xmlnode_search(xmlnode firstsibling, const char* name, unsigned int type)
87 {
88     xmlnode current;
89 
90     /* Walk the sibling list, looking for a NTYPE_TAG xmlnode with
91     the specified name */
92     current = firstsibling;
93     while (current != NULL)
94     {
95         if ((current->type == type) && (j_strcmp(current->name, name) == 0))
96             return current;
97         else
98             current = current->next;
99     }
100     return NULL;
101 }
102 
_xmlnode_merge(pool p,char * dest,unsigned int destsize,const char * src,unsigned int srcsize)103 static char* _xmlnode_merge(pool p, char* dest, unsigned int destsize, const char* src, unsigned int srcsize)
104 {
105     char* result;
106     result = (char*)pmalloc(p, destsize + srcsize + 1);
107     memcpy(result, dest, destsize);
108     memcpy(result+destsize, src, srcsize);
109     result[destsize + srcsize] = '\0';
110 
111     /* WARNING: major ugly hack: since we're throwing the old data away, let's jump in the pool and subtract it from the size, this is for xmlstream's big-node checking */
112     p->size -= destsize;
113 
114     return result;
115 }
116 
_xmlnode_hide_sibling(xmlnode child)117 static void _xmlnode_hide_sibling(xmlnode child)
118 {
119     if(child == NULL)
120         return;
121 
122     if(child->prev != NULL)
123         child->prev->next = child->next;
124     if(child->next != NULL)
125         child->next->prev = child->prev;
126 }
127 
_xmlnode_tag2str(spool s,xmlnode node,int flag)128 void _xmlnode_tag2str(spool s, xmlnode node, int flag)
129 {
130     xmlnode tmp;
131 
132     if(flag==0 || flag==1)
133     {
134 	    spooler(s,"<",xmlnode_get_name(node),s);
135 	    tmp = xmlnode_get_firstattrib(node);
136 	    while(tmp) {
137 	        spooler(s," ",xmlnode_get_name(tmp),"='",strescape(xmlnode_pool(node),xmlnode_get_data(tmp)),"'",s);
138 	        tmp = xmlnode_get_nextsibling(tmp);
139 	    }
140 	    if(flag==0)
141 	        spool_add(s,"/>");
142 	    else
143 	        spool_add(s,">");
144     }
145     else
146     {
147 	    spooler(s,"</",xmlnode_get_name(node),">",s);
148     }
149 }
150 
_xmlnode2spool(xmlnode node)151 spool _xmlnode2spool(xmlnode node)
152 {
153     spool s;
154     int level=0,dir=0;
155     xmlnode tmp;
156 
157     if(!node || xmlnode_get_type(node)!=NTYPE_TAG)
158 	return NULL;
159 
160     s = spool_new(xmlnode_pool(node));
161     if(!s) return(NULL);
162 
163     while(1)
164     {
165         if(dir==0)
166         {
167     	    if(xmlnode_get_type(node) == NTYPE_TAG)
168             {
169 		        if(xmlnode_has_children(node))
170                 {
171 		            _xmlnode_tag2str(s,node,1);
172         		    node = xmlnode_get_firstchild(node);
173 		            level++;
174 		            continue;
175         		}
176                 else
177                 {
178 		            _xmlnode_tag2str(s,node,0);
179 		        }
180 	        }
181             else
182             {
183 		        spool_add(s,strescape(xmlnode_pool(node),xmlnode_get_data(node)));
184 	        }
185 	    }
186 
187     	tmp = xmlnode_get_nextsibling(node);
188 	    if(!tmp)
189         {
190 	        node = xmlnode_get_parent(node);
191 	        level--;
192 	        if(level>=0) _xmlnode_tag2str(s,node,2);
193 	        if(level<1) break;
194 	        dir = 1;
195 	    }
196         else
197         {
198 	        node = tmp;
199 	        dir = 0;
200 	    }
201     }
202 
203     return s;
204 }
205 
206 
207 /* External routines */
208 
209 
210 /*
211  *  xmlnode_new_tag -- create a tag node
212  *  Automatically creates a memory pool for the node.
213  *
214  *  parameters
215  *      name -- name of the tag
216  *
217  *  returns
218  *      a pointer to the tag node
219  *      or NULL if it was unsuccessfull
220  */
xmlnode_new_tag(const char * name)221 xmlnode xmlnode_new_tag(const char* name)
222 {
223     return _xmlnode_new(NULL, name, NTYPE_TAG);
224 }
225 
226 
227 /*
228  *  xmlnode_new_tag_pool -- create a tag node within given pool
229  *
230  *  parameters
231  *      p -- previously created memory pool
232  *      name -- name of the tag
233  *
234  *  returns
235  *      a pointer to the tag node
236  *      or NULL if it was unsuccessfull
237  */
xmlnode_new_tag_pool(pool p,const char * name)238 xmlnode xmlnode_new_tag_pool(pool p, const char* name)
239 {
240     return _xmlnode_new(p, name, NTYPE_TAG);
241 }
242 
243 
244 /*
245  *  xmlnode_insert_tag -- append a child tag to a tag
246  *
247  *  parameters
248  *      parent -- pointer to the parent tag
249  *      name -- name of the child tag
250  *
251  *  returns
252  *      a pointer to the child tag node
253  *      or NULL if it was unsuccessfull
254  */
xmlnode_insert_tag(xmlnode parent,const char * name)255 xmlnode xmlnode_insert_tag(xmlnode parent, const char* name)
256 {
257     return _xmlnode_insert(parent, name, NTYPE_TAG);
258 }
259 
260 
261 /*
262  *  xmlnode_insert_cdata -- append character data to a tag
263  *  If last child of the parent is CDATA, merges CDATA nodes. Otherwise
264  *  creates a CDATA node, and appends it to the parent's child list.
265  *
266  *  parameters
267  *      parent -- parent tag
268  *      CDATA -- character data
269  *      size -- size of CDATA
270  *              or -1 for null-terminated CDATA strings
271  *
272  *  returns
273  *      a pointer to the child CDATA node
274  *      or NULL if it was unsuccessfull
275  */
xmlnode_insert_cdata(xmlnode parent,const char * CDATA,unsigned int size)276 xmlnode xmlnode_insert_cdata(xmlnode parent, const char* CDATA, unsigned int size)
277 {
278     xmlnode result;
279 
280     if(CDATA == NULL || parent == NULL)
281         return NULL;
282 
283     if(size == -1)
284         size = strlen(CDATA);
285 
286     if ((parent->lastchild != NULL) && (parent->lastchild->type == NTYPE_CDATA))
287     {
288         result = parent->lastchild;
289         result->data = _xmlnode_merge(result->p, result->data, result->data_sz, CDATA, size);
290         result->data_sz = result->data_sz + size;
291     }
292     else
293     {
294         result = _xmlnode_insert(parent, "", NTYPE_CDATA);
295         if (result != NULL)
296         {
297             result->data = (char*)pmalloc(result->p, size + 1);
298             memcpy(result->data, CDATA, size);
299             result->data[size] = '\0';
300             result->data_sz = size;
301         }
302     }
303 
304     return result;
305 }
306 
307 
308 /*
309  *  xmlnode_get_tag -- find given tag in an xmlnode tree
310  *
311  *  parameters
312  *      parent -- pointer to the parent tag
313  *      name -- "name" for the child tag of that name
314  *              "name/name" for a sub child (recurses)
315  *              "?attrib" to match the first tag with that attrib defined
316  *              "?attrib=value" to match the first tag with that attrib and value
317  *              or any combination: "name/name/?attrib", etc
318  *
319  *  results
320  *      a pointer to the tag matching search criteria
321  *      or NULL if search was unsuccessfull
322  */
xmlnode_get_tag(xmlnode parent,const char * name)323 xmlnode xmlnode_get_tag(xmlnode parent, const char* name)
324 {
325     char *str, *slash, *qmark, *equals;
326     xmlnode step, ret;
327 
328     if(parent == NULL || parent->firstchild == NULL || name == NULL || name == '\0') return NULL;
329 
330     if(strstr(name, "/") == NULL && strstr(name,"?") == NULL)
331         return _xmlnode_search(parent->firstchild, name, NTYPE_TAG);
332 
333     /* jer's note: why can't I modify the name directly, why do I have to strdup it?  damn c grrr! */
334     str = strdup(name);
335     slash = strstr(str, "/");
336     qmark = strstr(str, "?");
337     equals = strstr(str, "=");
338 
339     if(qmark != NULL && (slash == NULL || qmark < slash))
340     { /* of type ?attrib */
341 
342         *qmark = '\0';
343         qmark++;
344         if(equals != NULL)
345         {
346             *equals = '\0';
347             equals++;
348         }
349 
350         for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step))
351         {
352             if(xmlnode_get_type(step) != NTYPE_TAG)
353                 continue;
354 
355             if(*str != '\0')
356                 if(j_strcmp(xmlnode_get_name(step),str) != 0)
357                     continue;
358 
359             if(xmlnode_get_attrib(step,qmark) == NULL)
360                 continue;
361 
362             if(equals != NULL && j_strcmp(xmlnode_get_attrib(step,qmark),equals) != 0)
363                 continue;
364 
365             break;
366         }
367 
368         free(str);
369         return step;
370     }
371 
372 
373     *slash = '\0';
374     ++slash;
375 
376     for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step))
377     {
378         if(xmlnode_get_type(step) != NTYPE_TAG) continue;
379 
380         if(j_strcmp(xmlnode_get_name(step),str) != 0)
381             continue;
382 
383         ret = xmlnode_get_tag(step, slash);
384         if(ret != NULL)
385         {
386             free(str);
387             return ret;
388         }
389     }
390 
391     free(str);
392     return NULL;
393 }
394 
395 
396 /* return the cdata from any tag */
xmlnode_get_tag_data(xmlnode parent,const char * name)397 char *xmlnode_get_tag_data(xmlnode parent, const char *name)
398 {
399     xmlnode tag;
400 
401     tag = xmlnode_get_tag(parent, name);
402     if(tag == NULL) return NULL;
403 
404     return xmlnode_get_data(tag);
405 }
406 
407 
xmlnode_put_attrib(xmlnode owner,const char * name,const char * value)408 void xmlnode_put_attrib(xmlnode owner, const char* name, const char* value)
409 {
410     xmlnode attrib;
411 
412     if(owner == NULL || name == NULL || value == NULL) return;
413 
414     /* If there are no existing attributs, allocate a new one to start
415     the list */
416     if (owner->firstattrib == NULL)
417     {
418         attrib = _xmlnode_new(owner->p, name, NTYPE_ATTRIB);
419         owner->firstattrib = attrib;
420         owner->lastattrib  = attrib;
421     }
422     else
423     {
424         attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
425         if(attrib == NULL)
426         {
427             attrib = _xmlnode_append_sibling(owner->lastattrib, name, NTYPE_ATTRIB);
428             owner->lastattrib = attrib;
429         }
430     }
431     /* Update the value of the attribute */
432     attrib->data_sz = strlen(value);
433     attrib->data    = pstrdup(owner->p, value);
434 
435 }
436 
xmlnode_get_attrib(xmlnode owner,const char * name)437 char* xmlnode_get_attrib(xmlnode owner, const char* name)
438 {
439     xmlnode attrib;
440 
441     if (owner != NULL && owner->firstattrib != NULL)
442     {
443         attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
444         if (attrib != NULL)
445             return (char*)attrib->data;
446     }
447     return NULL;
448 }
449 
xmlnode_put_vattrib(xmlnode owner,const char * name,void * value)450 void xmlnode_put_vattrib(xmlnode owner, const char* name, void *value)
451 {
452     xmlnode attrib;
453 
454     if (owner != NULL)
455     {
456         attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
457         if (attrib == NULL)
458         {
459             xmlnode_put_attrib(owner, name, "");
460             attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
461         }
462         if (attrib != NULL)
463             attrib->firstchild = (xmlnode)value;
464     }
465 }
466 
xmlnode_get_vattrib(xmlnode owner,const char * name)467 void* xmlnode_get_vattrib(xmlnode owner, const char* name)
468 {
469     xmlnode attrib;
470 
471     if (owner != NULL && owner->firstattrib != NULL)
472     {
473         attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
474         if (attrib != NULL)
475             return (void*)attrib->firstchild;
476     }
477     return NULL;
478 }
479 
xmlnode_get_firstattrib(xmlnode parent)480 xmlnode xmlnode_get_firstattrib(xmlnode parent)
481 {
482     if (parent != NULL)
483         return parent->firstattrib;
484     return NULL;
485 }
486 
xmlnode_get_firstchild(xmlnode parent)487 xmlnode xmlnode_get_firstchild(xmlnode parent)
488 {
489     if (parent != NULL)
490         return parent->firstchild;
491     return NULL;
492 }
493 
xmlnode_get_lastchild(xmlnode parent)494 xmlnode xmlnode_get_lastchild(xmlnode parent)
495 {
496     if (parent != NULL)
497         return parent->lastchild;
498     return NULL;
499 }
500 
xmlnode_get_nextsibling(xmlnode sibling)501 xmlnode xmlnode_get_nextsibling(xmlnode sibling)
502 {
503     if (sibling != NULL)
504         return sibling->next;
505     return NULL;
506 }
507 
xmlnode_get_prevsibling(xmlnode sibling)508 xmlnode xmlnode_get_prevsibling(xmlnode sibling)
509 {
510     if (sibling != NULL)
511         return sibling->prev;
512     return NULL;
513 }
514 
xmlnode_get_parent(xmlnode node)515 xmlnode xmlnode_get_parent(xmlnode node)
516 {
517     if (node != NULL)
518         return node->parent;
519     return NULL;
520 }
521 
xmlnode_get_name(xmlnode node)522 char* xmlnode_get_name(xmlnode node)
523 {
524     if (node != NULL)
525         return node->name;
526     return NULL;
527 }
528 
xmlnode_get_data(xmlnode node)529 char* xmlnode_get_data(xmlnode node)
530 {
531     xmlnode cur;
532 
533     if(node == NULL) return NULL;
534 
535     if(xmlnode_get_type(node) == NTYPE_TAG) /* loop till we find a CDATA */
536     {
537         for(cur = xmlnode_get_firstchild(node); cur != NULL; cur = xmlnode_get_nextsibling(cur))
538             if(xmlnode_get_type(cur) == NTYPE_CDATA)
539                 return cur->data;
540     }else{
541         return node->data;
542     }
543     return NULL;
544 }
545 
xmlnode_get_datasz(xmlnode node)546 int xmlnode_get_datasz(xmlnode node)
547 {
548     if (node != NULL)
549         return node->data_sz;
550     return (int)NULL;
551 }
552 
xmlnode_get_type(xmlnode node)553 int xmlnode_get_type(xmlnode node)
554 {
555     if (node != NULL)
556         return node->type;
557     return (int)NULL;
558 }
559 
xmlnode_has_children(xmlnode node)560 int xmlnode_has_children(xmlnode node)
561 {
562     if ((node != NULL) && (node->firstchild != NULL))
563         return 1;
564     return 0;
565 }
566 
xmlnode_has_attribs(xmlnode node)567 int xmlnode_has_attribs(xmlnode node)
568 {
569     if ((node != NULL) && (node->firstattrib != NULL))
570         return 1;
571     return 0;
572 }
573 
xmlnode_pool(xmlnode node)574 pool xmlnode_pool(xmlnode node)
575 {
576     if (node != NULL)
577         return node->p;
578     return (pool)NULL;
579 }
580 
xmlnode_hide(xmlnode child)581 void xmlnode_hide(xmlnode child)
582 {
583     xmlnode parent;
584 
585     if(child == NULL || child->parent == NULL)
586         return;
587 
588     parent = child->parent;
589 
590     /* first fix up at the child level */
591     _xmlnode_hide_sibling(child);
592 
593     /* next fix up at the parent level */
594     if(parent->firstchild == child)
595         parent->firstchild = child->next;
596     if(parent->lastchild == child)
597         parent->lastchild = child->prev;
598 }
599 
xmlnode_hide_attrib(xmlnode parent,const char * name)600 void xmlnode_hide_attrib(xmlnode parent, const char *name)
601 {
602     xmlnode attrib;
603 
604     if(parent == NULL || parent->firstattrib == NULL || name == NULL)
605         return;
606 
607     attrib = _xmlnode_search(parent->firstattrib, name, NTYPE_ATTRIB);
608     if(attrib == NULL)
609         return;
610 
611     /* first fix up at the child level */
612     _xmlnode_hide_sibling(attrib);
613 
614     /* next fix up at the parent level */
615     if(parent->firstattrib == attrib)
616         parent->firstattrib = attrib->next;
617     if(parent->lastattrib == attrib)
618         parent->lastattrib = attrib->prev;
619 }
620 
621 
622 
623 /*
624  *  xmlnode2str -- convert given xmlnode tree into a string
625  *
626  *  parameters
627  *      node -- pointer to the xmlnode structure
628  *
629  *  results
630  *      a pointer to the created string
631  *      or NULL if it was unsuccessfull
632  */
xmlnode2str(xmlnode node)633 char *xmlnode2str(xmlnode node)
634 {
635      return spool_print(_xmlnode2spool(node));
636 }
637 
638 /*
639  *  xmlnode2tstr -- convert given xmlnode tree into a newline terminated string
640  *
641  *  parameters
642  *      node -- pointer to the xmlnode structure
643  *
644  *  results
645  *      a pointer to the created string
646  *      or NULL if it was unsuccessfull
647  */
xmlnode2tstr(xmlnode node)648 char*    xmlnode2tstr(xmlnode node)
649 {
650      spool s = _xmlnode2spool(node);
651      if (s != NULL)
652 	  spool_add(s, "\n");
653     return spool_print(s);
654 }
655 
656 
657 /* loop through both a and b comparing everything, attribs, cdata, children, etc */
xmlnode_cmp(xmlnode a,xmlnode b)658 int xmlnode_cmp(xmlnode a, xmlnode b)
659 {
660     int ret = 0;
661 
662     while(1)
663     {
664         if(a == NULL && b == NULL)
665             return 0;
666 
667         if(a == NULL || b == NULL)
668             return -1;
669 
670         if(xmlnode_get_type(a) != xmlnode_get_type(b))
671             return -1;
672 
673         switch(xmlnode_get_type(a))
674         {
675         case NTYPE_ATTRIB:
676             ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b));
677             if(ret != 0)
678                 return -1;
679             ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b));
680             if(ret != 0)
681                 return -1;
682             break;
683         case NTYPE_TAG:
684             ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b));
685             if(ret != 0)
686                 return -1;
687             ret = xmlnode_cmp(xmlnode_get_firstattrib(a), xmlnode_get_firstattrib(b));
688             if(ret != 0)
689                 return -1;
690             ret = xmlnode_cmp(xmlnode_get_firstchild(a), xmlnode_get_firstchild(b));
691             if(ret != 0)
692                 return -1;
693             break;
694         case NTYPE_CDATA:
695             ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b));
696             if(ret != 0)
697                 return -1;
698         }
699         a = xmlnode_get_nextsibling(a);
700         b = xmlnode_get_nextsibling(b);
701     }
702 }
703 
704 
xmlnode_insert_tag_node(xmlnode parent,xmlnode node)705 xmlnode xmlnode_insert_tag_node(xmlnode parent, xmlnode node)
706 {
707     xmlnode child;
708 
709     child = xmlnode_insert_tag(parent, xmlnode_get_name(node));
710     if (xmlnode_has_attribs(node))
711         xmlnode_insert_node(child, xmlnode_get_firstattrib(node));
712     if (xmlnode_has_children(node))
713         xmlnode_insert_node(child, xmlnode_get_firstchild(node));
714 
715     return child;
716 }
717 
718 /* places copy of node and node's siblings in parent */
xmlnode_insert_node(xmlnode parent,xmlnode node)719 void xmlnode_insert_node(xmlnode parent, xmlnode node)
720 {
721     if(node == NULL || parent == NULL)
722         return;
723 
724     while(node != NULL)
725     {
726         switch(xmlnode_get_type(node))
727         {
728         case NTYPE_ATTRIB:
729             xmlnode_put_attrib(parent, xmlnode_get_name(node), xmlnode_get_data(node));
730             break;
731         case NTYPE_TAG:
732             xmlnode_insert_tag_node(parent, node);
733             break;
734         case NTYPE_CDATA:
735             xmlnode_insert_cdata(parent, xmlnode_get_data(node), xmlnode_get_datasz(node));
736         }
737         node = xmlnode_get_nextsibling(node);
738     }
739 }
740 
741 
742 /* produce full duplicate of x with a new pool, x must be a tag! */
xmlnode_dup(xmlnode x)743 xmlnode xmlnode_dup(xmlnode x)
744 {
745     xmlnode x2;
746 
747     if(x == NULL)
748         return NULL;
749 
750     x2 = xmlnode_new_tag(xmlnode_get_name(x));
751 
752     if (xmlnode_has_attribs(x))
753         xmlnode_insert_node(x2, xmlnode_get_firstattrib(x));
754     if (xmlnode_has_children(x))
755         xmlnode_insert_node(x2, xmlnode_get_firstchild(x));
756 
757     return x2;
758 }
759 
xmlnode_dup_pool(pool p,xmlnode x)760 xmlnode xmlnode_dup_pool(pool p, xmlnode x)
761 {
762     xmlnode x2;
763 
764     if(x == NULL)
765         return NULL;
766 
767     x2 = xmlnode_new_tag_pool(p, xmlnode_get_name(x));
768 
769     if (xmlnode_has_attribs(x))
770         xmlnode_insert_node(x2, xmlnode_get_firstattrib(x));
771     if (xmlnode_has_children(x))
772         xmlnode_insert_node(x2, xmlnode_get_firstchild(x));
773 
774     return x2;
775 }
776 
xmlnode_wrap(xmlnode x,const char * wrapper)777 xmlnode xmlnode_wrap(xmlnode x,const char *wrapper)
778 {
779     xmlnode wrap;
780     if(x==NULL||wrapper==NULL) return NULL;
781     wrap=xmlnode_new_tag_pool(xmlnode_pool(x),wrapper);
782     if(wrap==NULL) return NULL;
783     wrap->firstchild=x;
784     wrap->lastchild=x;
785     x->parent=wrap;
786     return wrap;
787 }
788 
xmlnode_free(xmlnode node)789 void xmlnode_free(xmlnode node)
790 {
791     if(node == NULL)
792         return;
793 
794     pool_free(node->p);
795 }
796