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.
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.
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.
17 * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
22 /* Internal routines */
23 xmlnode
_xmlnode_new(pool p
, const char* name
, unsigned int type
)
25 xmlnode result
= NULL
;
26 if (type
> NTYPE_LAST
)
29 if (type
!= NTYPE_CDATA
&& name
== NULL
)
34 p
= pool_heap(1*1024);
37 /* Allocate & zero memory */
38 result
= (xmlnode
)pmalloc(p
, sizeof(_xmlnode
));
39 memset(result
, '\0', sizeof(_xmlnode
));
41 /* Initialize fields */
42 if (type
!= NTYPE_CDATA
)
43 result
->name
= pstrdup(p
,name
);
49 static xmlnode
_xmlnode_append_sibling(xmlnode lastsibling
, const char* name
, unsigned int type
)
53 result
= _xmlnode_new(xmlnode_pool(lastsibling
), name
, type
);
56 /* Setup sibling pointers */
57 result
->prev
= lastsibling
;
58 lastsibling
->next
= result
;
63 static xmlnode
_xmlnode_insert(xmlnode parent
, const char* name
, unsigned int type
)
67 if(parent
== NULL
|| name
== NULL
) return NULL
;
69 /* If parent->firstchild is NULL, simply create a new node for the first child */
70 if (parent
->firstchild
== NULL
)
72 result
= _xmlnode_new(parent
->p
, name
, type
);
73 parent
->firstchild
= result
;
75 /* Otherwise, append this to the lastchild */
78 result
= _xmlnode_append_sibling(parent
->lastchild
, name
, type
);
80 result
->parent
= parent
;
81 parent
->lastchild
= result
;
86 static xmlnode
_xmlnode_search(xmlnode firstsibling
, const char* name
, unsigned int type
)
90 /* Walk the sibling list, looking for a NTYPE_TAG xmlnode with
92 current
= firstsibling
;
93 while (current
!= NULL
)
95 if ((current
->type
== type
) && (j_strcmp(current
->name
, name
) == 0))
98 current
= current
->next
;
103 static char* _xmlnode_merge(pool p
, char* dest
, unsigned int destsize
, const char* src
, unsigned int srcsize
)
106 result
= (char*)pmalloc(p
, destsize
+ srcsize
+ 1);
107 memcpy(result
, dest
, destsize
);
108 memcpy(result
+destsize
, src
, srcsize
);
109 result
[destsize
+ srcsize
] = '\0';
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 */
117 static void _xmlnode_hide_sibling(xmlnode child
)
122 if(child
->prev
!= NULL
)
123 child
->prev
->next
= child
->next
;
124 if(child
->next
!= NULL
)
125 child
->next
->prev
= child
->prev
;
128 void _xmlnode_tag2str(spool s
, xmlnode node
, int flag
)
132 if(flag
==0 || flag
==1)
134 spooler(s
,"<",xmlnode_get_name(node
),s
);
135 tmp
= xmlnode_get_firstattrib(node
);
137 spooler(s
," ",xmlnode_get_name(tmp
),"='",strescape(xmlnode_pool(node
),xmlnode_get_data(tmp
)),"'",s
);
138 tmp
= xmlnode_get_nextsibling(tmp
);
147 spooler(s
,"</",xmlnode_get_name(node
),">",s
);
151 spool
_xmlnode2spool(xmlnode node
)
157 if(!node
|| xmlnode_get_type(node
)!=NTYPE_TAG
)
160 s
= spool_new(xmlnode_pool(node
));
167 if(xmlnode_get_type(node
) == NTYPE_TAG
)
169 if(xmlnode_has_children(node
))
171 _xmlnode_tag2str(s
,node
,1);
172 node
= xmlnode_get_firstchild(node
);
178 _xmlnode_tag2str(s
,node
,0);
183 spool_add(s
,strescape(xmlnode_pool(node
),xmlnode_get_data(node
)));
187 tmp
= xmlnode_get_nextsibling(node
);
190 node
= xmlnode_get_parent(node
);
192 if(level
>=0) _xmlnode_tag2str(s
,node
,2);
207 /* External routines */
211 * xmlnode_new_tag -- create a tag node
212 * Automatically creates a memory pool for the node.
215 * name -- name of the tag
218 * a pointer to the tag node
219 * or NULL if it was unsuccessfull
221 xmlnode
xmlnode_new_tag(const char* name
)
223 return _xmlnode_new(NULL
, name
, NTYPE_TAG
);
228 * xmlnode_new_tag_pool -- create a tag node within given pool
231 * p -- previously created memory pool
232 * name -- name of the tag
235 * a pointer to the tag node
236 * or NULL if it was unsuccessfull
238 xmlnode
xmlnode_new_tag_pool(pool p
, const char* name
)
240 return _xmlnode_new(p
, name
, NTYPE_TAG
);
245 * xmlnode_insert_tag -- append a child tag to a tag
248 * parent -- pointer to the parent tag
249 * name -- name of the child tag
252 * a pointer to the child tag node
253 * or NULL if it was unsuccessfull
255 xmlnode
xmlnode_insert_tag(xmlnode parent
, const char* name
)
257 return _xmlnode_insert(parent
, name
, NTYPE_TAG
);
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.
267 * parent -- parent tag
268 * CDATA -- character data
269 * size -- size of CDATA
270 * or -1 for null-terminated CDATA strings
273 * a pointer to the child CDATA node
274 * or NULL if it was unsuccessfull
276 xmlnode
xmlnode_insert_cdata(xmlnode parent
, const char* CDATA
, unsigned int size
)
280 if(CDATA
== NULL
|| parent
== NULL
)
284 size
= strlen(CDATA
);
286 if ((parent
->lastchild
!= NULL
) && (parent
->lastchild
->type
== NTYPE_CDATA
))
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
;
294 result
= _xmlnode_insert(parent
, "", NTYPE_CDATA
);
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
;
309 * xmlnode_get_tag -- find given tag in an xmlnode tree
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
320 * a pointer to the tag matching search criteria
321 * or NULL if search was unsuccessfull
323 xmlnode
xmlnode_get_tag(xmlnode parent
, const char* name
)
325 char *str
, *slash
, *qmark
, *equals
;
328 if(parent
== NULL
|| parent
->firstchild
== NULL
|| name
== NULL
|| name
== '\0') return NULL
;
330 if(strstr(name
, "/") == NULL
&& strstr(name
,"?") == NULL
)
331 return _xmlnode_search(parent
->firstchild
, name
, NTYPE_TAG
);
333 /* jer's note: why can't I modify the name directly, why do I have to strdup it? damn c grrr! */
335 slash
= strstr(str
, "/");
336 qmark
= strstr(str
, "?");
337 equals
= strstr(str
, "=");
339 if(qmark
!= NULL
&& (slash
== NULL
|| qmark
< slash
))
340 { /* of type ?attrib */
350 for(step
= parent
->firstchild
; step
!= NULL
; step
= xmlnode_get_nextsibling(step
))
352 if(xmlnode_get_type(step
) != NTYPE_TAG
)
356 if(j_strcmp(xmlnode_get_name(step
),str
) != 0)
359 if(xmlnode_get_attrib(step
,qmark
) == NULL
)
362 if(equals
!= NULL
&& j_strcmp(xmlnode_get_attrib(step
,qmark
),equals
) != 0)
376 for(step
= parent
->firstchild
; step
!= NULL
; step
= xmlnode_get_nextsibling(step
))
378 if(xmlnode_get_type(step
) != NTYPE_TAG
) continue;
380 if(j_strcmp(xmlnode_get_name(step
),str
) != 0)
383 ret
= xmlnode_get_tag(step
, slash
);
396 /* return the cdata from any tag */
397 char *xmlnode_get_tag_data(xmlnode parent
, const char *name
)
401 tag
= xmlnode_get_tag(parent
, name
);
402 if(tag
== NULL
) return NULL
;
404 return xmlnode_get_data(tag
);
408 void xmlnode_put_attrib(xmlnode owner
, const char* name
, const char* value
)
412 if(owner
== NULL
|| name
== NULL
|| value
== NULL
) return;
414 /* If there are no existing attributs, allocate a new one to start
416 if (owner
->firstattrib
== NULL
)
418 attrib
= _xmlnode_new(owner
->p
, name
, NTYPE_ATTRIB
);
419 owner
->firstattrib
= attrib
;
420 owner
->lastattrib
= attrib
;
424 attrib
= _xmlnode_search(owner
->firstattrib
, name
, NTYPE_ATTRIB
);
427 attrib
= _xmlnode_append_sibling(owner
->lastattrib
, name
, NTYPE_ATTRIB
);
428 owner
->lastattrib
= attrib
;
431 /* Update the value of the attribute */
432 attrib
->data_sz
= strlen(value
);
433 attrib
->data
= pstrdup(owner
->p
, value
);
437 char* xmlnode_get_attrib(xmlnode owner
, const char* name
)
441 if (owner
!= NULL
&& owner
->firstattrib
!= NULL
)
443 attrib
= _xmlnode_search(owner
->firstattrib
, name
, NTYPE_ATTRIB
);
445 return (char*)attrib
->data
;
450 void xmlnode_put_vattrib(xmlnode owner
, const char* name
, void *value
)
456 attrib
= _xmlnode_search(owner
->firstattrib
, name
, NTYPE_ATTRIB
);
459 xmlnode_put_attrib(owner
, name
, "");
460 attrib
= _xmlnode_search(owner
->firstattrib
, name
, NTYPE_ATTRIB
);
463 attrib
->firstchild
= (xmlnode
)value
;
467 void* xmlnode_get_vattrib(xmlnode owner
, const char* name
)
471 if (owner
!= NULL
&& owner
->firstattrib
!= NULL
)
473 attrib
= _xmlnode_search(owner
->firstattrib
, name
, NTYPE_ATTRIB
);
475 return (void*)attrib
->firstchild
;
480 xmlnode
xmlnode_get_firstattrib(xmlnode parent
)
483 return parent
->firstattrib
;
487 xmlnode
xmlnode_get_firstchild(xmlnode parent
)
490 return parent
->firstchild
;
494 xmlnode
xmlnode_get_lastchild(xmlnode parent
)
497 return parent
->lastchild
;
501 xmlnode
xmlnode_get_nextsibling(xmlnode sibling
)
504 return sibling
->next
;
508 xmlnode
xmlnode_get_prevsibling(xmlnode sibling
)
511 return sibling
->prev
;
515 xmlnode
xmlnode_get_parent(xmlnode node
)
522 char* xmlnode_get_name(xmlnode node
)
529 char* xmlnode_get_data(xmlnode node
)
533 if(node
== NULL
) return NULL
;
535 if(xmlnode_get_type(node
) == NTYPE_TAG
) /* loop till we find a CDATA */
537 for(cur
= xmlnode_get_firstchild(node
); cur
!= NULL
; cur
= xmlnode_get_nextsibling(cur
))
538 if(xmlnode_get_type(cur
) == NTYPE_CDATA
)
546 int xmlnode_get_datasz(xmlnode node
)
549 return node
->data_sz
;
553 int xmlnode_get_type(xmlnode node
)
560 int xmlnode_has_children(xmlnode node
)
562 if ((node
!= NULL
) && (node
->firstchild
!= NULL
))
567 int xmlnode_has_attribs(xmlnode node
)
569 if ((node
!= NULL
) && (node
->firstattrib
!= NULL
))
574 pool
xmlnode_pool(xmlnode node
)
581 void xmlnode_hide(xmlnode child
)
585 if(child
== NULL
|| child
->parent
== NULL
)
588 parent
= child
->parent
;
590 /* first fix up at the child level */
591 _xmlnode_hide_sibling(child
);
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
;
600 void xmlnode_hide_attrib(xmlnode parent
, const char *name
)
604 if(parent
== NULL
|| parent
->firstattrib
== NULL
|| name
== NULL
)
607 attrib
= _xmlnode_search(parent
->firstattrib
, name
, NTYPE_ATTRIB
);
611 /* first fix up at the child level */
612 _xmlnode_hide_sibling(attrib
);
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
;
624 * xmlnode2str -- convert given xmlnode tree into a string
627 * node -- pointer to the xmlnode structure
630 * a pointer to the created string
631 * or NULL if it was unsuccessfull
633 char *xmlnode2str(xmlnode node
)
635 return spool_print(_xmlnode2spool(node
));
639 * xmlnode2tstr -- convert given xmlnode tree into a newline terminated string
642 * node -- pointer to the xmlnode structure
645 * a pointer to the created string
646 * or NULL if it was unsuccessfull
648 char* xmlnode2tstr(xmlnode node
)
650 spool s
= _xmlnode2spool(node
);
653 return spool_print(s
);
657 /* loop through both a and b comparing everything, attribs, cdata, children, etc */
658 int xmlnode_cmp(xmlnode a
, xmlnode b
)
664 if(a
== NULL
&& b
== NULL
)
667 if(a
== NULL
|| b
== NULL
)
670 if(xmlnode_get_type(a
) != xmlnode_get_type(b
))
673 switch(xmlnode_get_type(a
))
676 ret
= j_strcmp(xmlnode_get_name(a
), xmlnode_get_name(b
));
679 ret
= j_strcmp(xmlnode_get_data(a
), xmlnode_get_data(b
));
684 ret
= j_strcmp(xmlnode_get_name(a
), xmlnode_get_name(b
));
687 ret
= xmlnode_cmp(xmlnode_get_firstattrib(a
), xmlnode_get_firstattrib(b
));
690 ret
= xmlnode_cmp(xmlnode_get_firstchild(a
), xmlnode_get_firstchild(b
));
695 ret
= j_strcmp(xmlnode_get_data(a
), xmlnode_get_data(b
));
699 a
= xmlnode_get_nextsibling(a
);
700 b
= xmlnode_get_nextsibling(b
);
705 xmlnode
xmlnode_insert_tag_node(xmlnode parent
, xmlnode node
)
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
));
718 /* places copy of node and node's siblings in parent */
719 void xmlnode_insert_node(xmlnode parent
, xmlnode node
)
721 if(node
== NULL
|| parent
== NULL
)
726 switch(xmlnode_get_type(node
))
729 xmlnode_put_attrib(parent
, xmlnode_get_name(node
), xmlnode_get_data(node
));
732 xmlnode_insert_tag_node(parent
, node
);
735 xmlnode_insert_cdata(parent
, xmlnode_get_data(node
), xmlnode_get_datasz(node
));
737 node
= xmlnode_get_nextsibling(node
);
742 /* produce full duplicate of x with a new pool, x must be a tag! */
743 xmlnode
xmlnode_dup(xmlnode x
)
750 x2
= xmlnode_new_tag(xmlnode_get_name(x
));
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
));
760 xmlnode
xmlnode_dup_pool(pool p
, xmlnode x
)
767 x2
= xmlnode_new_tag_pool(p
, xmlnode_get_name(x
));
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
));
777 xmlnode
xmlnode_wrap(xmlnode x
,const char *wrapper
)
780 if(x
==NULL
||wrapper
==NULL
) return NULL
;
781 wrap
=xmlnode_new_tag_pool(xmlnode_pool(x
),wrapper
);
782 if(wrap
==NULL
) return NULL
;
789 void xmlnode_free(xmlnode node
)