| |
1 /** |
| |
2 * @file xmlnode.c XML DOM functions |
| |
3 * |
| |
4 * gaim |
| |
5 * |
| |
6 * Gaim is the legal property of its developers, whose names are too numerous |
| |
7 * to list here. Please refer to the COPYRIGHT file distributed with this |
| |
8 * source distribution. |
| |
9 * |
| |
10 * This program is free software; you can redistribute it and/or modify |
| |
11 * it under the terms of the GNU General Public License as published by |
| |
12 * the Free Software Foundation; either version 2 of the License, or |
| |
13 * (at your option) any later version. |
| |
14 * |
| |
15 * This program is distributed in the hope that it will be useful, |
| |
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
18 * GNU General Public License for more details. |
| |
19 * |
| |
20 * You should have received a copy of the GNU General Public License |
| |
21 * along with this program; if not, write to the Free Software |
| |
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
23 */ |
| |
24 |
| |
25 /* A lot of this code at least resembles the code in libxode, but since |
| |
26 * libxode uses memory pools that we simply have no need for, I decided to |
| |
27 * write my own stuff. Also, re-writing this lets me be as lightweight |
| |
28 * as I want to be. Thank you libxode for giving me a good starting point */ |
| |
29 |
| |
30 #include "internal.h" |
| |
31 |
| |
32 #include <libxml/parser.h> |
| |
33 #include <string.h> |
| |
34 #include <glib.h> |
| |
35 |
| |
36 #include "dbus-maybe.h" |
| |
37 #include "util.h" |
| |
38 #include "xmlnode.h" |
| |
39 |
| |
40 #ifdef _WIN32 |
| |
41 # define NEWLINE_S "\r\n" |
| |
42 #else |
| |
43 # define NEWLINE_S "\n" |
| |
44 #endif |
| |
45 |
| |
46 static xmlnode* |
| |
47 new_node(const char *name, XMLNodeType type) |
| |
48 { |
| |
49 xmlnode *node = g_new0(xmlnode, 1); |
| |
50 |
| |
51 node->name = g_strdup(name); |
| |
52 node->type = type; |
| |
53 |
| |
54 GAIM_DBUS_REGISTER_POINTER(node, xmlnode); |
| |
55 |
| |
56 return node; |
| |
57 } |
| |
58 |
| |
59 xmlnode* |
| |
60 xmlnode_new(const char *name) |
| |
61 { |
| |
62 g_return_val_if_fail(name != NULL, NULL); |
| |
63 |
| |
64 return new_node(name, XMLNODE_TYPE_TAG); |
| |
65 } |
| |
66 |
| |
67 xmlnode * |
| |
68 xmlnode_new_child(xmlnode *parent, const char *name) |
| |
69 { |
| |
70 xmlnode *node; |
| |
71 |
| |
72 g_return_val_if_fail(parent != NULL, NULL); |
| |
73 g_return_val_if_fail(name != NULL, NULL); |
| |
74 |
| |
75 node = new_node(name, XMLNODE_TYPE_TAG); |
| |
76 |
| |
77 xmlnode_insert_child(parent, node); |
| |
78 |
| |
79 return node; |
| |
80 } |
| |
81 |
| |
82 void |
| |
83 xmlnode_insert_child(xmlnode *parent, xmlnode *child) |
| |
84 { |
| |
85 g_return_if_fail(parent != NULL); |
| |
86 g_return_if_fail(child != NULL); |
| |
87 |
| |
88 child->parent = parent; |
| |
89 |
| |
90 if(parent->lastchild) { |
| |
91 parent->lastchild->next = child; |
| |
92 } else { |
| |
93 parent->child = child; |
| |
94 } |
| |
95 |
| |
96 parent->lastchild = child; |
| |
97 } |
| |
98 |
| |
99 void |
| |
100 xmlnode_insert_data(xmlnode *node, const char *data, gssize size) |
| |
101 { |
| |
102 xmlnode *child; |
| |
103 gsize real_size; |
| |
104 |
| |
105 g_return_if_fail(node != NULL); |
| |
106 g_return_if_fail(data != NULL); |
| |
107 g_return_if_fail(size != 0); |
| |
108 |
| |
109 real_size = size == -1 ? strlen(data) : size; |
| |
110 |
| |
111 child = new_node(NULL, XMLNODE_TYPE_DATA); |
| |
112 |
| |
113 child->data = g_memdup(data, real_size); |
| |
114 child->data_sz = real_size; |
| |
115 |
| |
116 xmlnode_insert_child(node, child); |
| |
117 } |
| |
118 |
| |
119 void |
| |
120 xmlnode_remove_attrib(xmlnode *node, const char *attr) |
| |
121 { |
| |
122 xmlnode *attr_node, *sibling = NULL; |
| |
123 |
| |
124 g_return_if_fail(node != NULL); |
| |
125 g_return_if_fail(attr != NULL); |
| |
126 |
| |
127 for(attr_node = node->child; attr_node; attr_node = attr_node->next) |
| |
128 { |
| |
129 if(attr_node->type == XMLNODE_TYPE_ATTRIB && |
| |
130 !strcmp(attr_node->name, attr)) |
| |
131 { |
| |
132 if(node->child == attr_node) { |
| |
133 node->child = attr_node->next; |
| |
134 } else { |
| |
135 sibling->next = attr_node->next; |
| |
136 } |
| |
137 if (node->lastchild == attr_node) { |
| |
138 node->lastchild = sibling; |
| |
139 } |
| |
140 xmlnode_free(attr_node); |
| |
141 return; |
| |
142 } |
| |
143 sibling = attr_node; |
| |
144 } |
| |
145 } |
| |
146 |
| |
147 |
| |
148 void |
| |
149 xmlnode_remove_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns) |
| |
150 { |
| |
151 xmlnode *attr_node, *sibling = NULL; |
| |
152 |
| |
153 g_return_if_fail(node != NULL); |
| |
154 g_return_if_fail(attr != NULL); |
| |
155 |
| |
156 for(attr_node = node->child; attr_node; attr_node = attr_node->next) |
| |
157 { |
| |
158 if(attr_node->type == XMLNODE_TYPE_ATTRIB && |
| |
159 !strcmp(attr_node->name, attr) && |
| |
160 !strcmp(attr_node->xmlns, xmlns)) |
| |
161 { |
| |
162 if(node->child == attr_node) { |
| |
163 node->child = attr_node->next; |
| |
164 } else { |
| |
165 sibling->next = attr_node->next; |
| |
166 } |
| |
167 if (node->lastchild == attr_node) { |
| |
168 node->lastchild = sibling; |
| |
169 } |
| |
170 xmlnode_free(attr_node); |
| |
171 return; |
| |
172 } |
| |
173 sibling = attr_node; |
| |
174 } |
| |
175 } |
| |
176 |
| |
177 void |
| |
178 xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value) |
| |
179 { |
| |
180 xmlnode *attrib_node; |
| |
181 |
| |
182 g_return_if_fail(node != NULL); |
| |
183 g_return_if_fail(attr != NULL); |
| |
184 g_return_if_fail(value != NULL); |
| |
185 |
| |
186 xmlnode_remove_attrib(node, attr); |
| |
187 |
| |
188 attrib_node = new_node(attr, XMLNODE_TYPE_ATTRIB); |
| |
189 |
| |
190 attrib_node->data = g_strdup(value); |
| |
191 |
| |
192 xmlnode_insert_child(node, attrib_node); |
| |
193 } |
| |
194 |
| |
195 void |
| |
196 xmlnode_set_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns, const char *value) |
| |
197 { |
| |
198 xmlnode *attrib_node; |
| |
199 |
| |
200 g_return_if_fail(node != NULL); |
| |
201 g_return_if_fail(attr != NULL); |
| |
202 g_return_if_fail(value != NULL); |
| |
203 |
| |
204 xmlnode_remove_attrib_with_namespace(node, attr, xmlns); |
| |
205 |
| |
206 attrib_node = new_node(attr, XMLNODE_TYPE_ATTRIB); |
| |
207 |
| |
208 attrib_node->data = g_strdup(value); |
| |
209 attrib_node->xmlns = g_strdup(xmlns); |
| |
210 |
| |
211 xmlnode_insert_child(node, attrib_node); |
| |
212 } |
| |
213 |
| |
214 const char * |
| |
215 xmlnode_get_attrib(xmlnode *node, const char *attr) |
| |
216 { |
| |
217 xmlnode *x; |
| |
218 |
| |
219 g_return_val_if_fail(node != NULL, NULL); |
| |
220 |
| |
221 for(x = node->child; x; x = x->next) { |
| |
222 if(x->type == XMLNODE_TYPE_ATTRIB && !strcmp(attr, x->name)) { |
| |
223 return x->data; |
| |
224 } |
| |
225 } |
| |
226 |
| |
227 return NULL; |
| |
228 } |
| |
229 |
| |
230 const char * |
| |
231 xmlnode_get_attrib_with_namespace(xmlnode *node, const char *attr, const char *xmlns) |
| |
232 { |
| |
233 xmlnode *x; |
| |
234 |
| |
235 g_return_val_if_fail(node != NULL, NULL); |
| |
236 |
| |
237 for(x = node->child; x; x = x->next) { |
| |
238 if(x->type == XMLNODE_TYPE_ATTRIB && |
| |
239 !strcmp(attr, x->name) && !strcmp(x->xmlns, xmlns)) { |
| |
240 return x->data; |
| |
241 } |
| |
242 } |
| |
243 |
| |
244 return NULL; |
| |
245 } |
| |
246 |
| |
247 |
| |
248 void xmlnode_set_namespace(xmlnode *node, const char *xmlns) |
| |
249 { |
| |
250 g_return_if_fail(node != NULL); |
| |
251 |
| |
252 g_free(node->xmlns); |
| |
253 node->xmlns = g_strdup(xmlns); |
| |
254 } |
| |
255 |
| |
256 const char *xmlnode_get_namespace(xmlnode *node) |
| |
257 { |
| |
258 g_return_val_if_fail(node != NULL, NULL); |
| |
259 |
| |
260 return node->xmlns; |
| |
261 } |
| |
262 |
| |
263 void |
| |
264 xmlnode_free(xmlnode *node) |
| |
265 { |
| |
266 xmlnode *x, *y; |
| |
267 |
| |
268 g_return_if_fail(node != NULL); |
| |
269 |
| |
270 x = node->child; |
| |
271 while(x) { |
| |
272 y = x->next; |
| |
273 xmlnode_free(x); |
| |
274 x = y; |
| |
275 } |
| |
276 |
| |
277 g_free(node->name); |
| |
278 g_free(node->data); |
| |
279 g_free(node->xmlns); |
| |
280 |
| |
281 GAIM_DBUS_UNREGISTER_POINTER(node); |
| |
282 g_free(node); |
| |
283 } |
| |
284 |
| |
285 xmlnode* |
| |
286 xmlnode_get_child(const xmlnode *parent, const char *name) |
| |
287 { |
| |
288 return xmlnode_get_child_with_namespace(parent, name, NULL); |
| |
289 } |
| |
290 |
| |
291 xmlnode * |
| |
292 xmlnode_get_child_with_namespace(const xmlnode *parent, const char *name, const char *ns) |
| |
293 { |
| |
294 xmlnode *x, *ret = NULL; |
| |
295 char **names; |
| |
296 char *parent_name, *child_name; |
| |
297 |
| |
298 g_return_val_if_fail(parent != NULL, NULL); |
| |
299 g_return_val_if_fail(name != NULL, NULL); |
| |
300 |
| |
301 names = g_strsplit(name, "/", 2); |
| |
302 parent_name = names[0]; |
| |
303 child_name = names[1]; |
| |
304 |
| |
305 for(x = parent->child; x; x = x->next) { |
| |
306 const char *xmlns = NULL; |
| |
307 if(ns) |
| |
308 xmlns = xmlnode_get_namespace(x); |
| |
309 |
| |
310 if(x->type == XMLNODE_TYPE_TAG && name && !strcmp(parent_name, x->name) |
| |
311 && (!ns || (xmlns && !strcmp(ns, xmlns)))) { |
| |
312 ret = x; |
| |
313 break; |
| |
314 } |
| |
315 } |
| |
316 |
| |
317 if(child_name && ret) |
| |
318 ret = xmlnode_get_child(ret, child_name); |
| |
319 |
| |
320 g_strfreev(names); |
| |
321 return ret; |
| |
322 } |
| |
323 |
| |
324 char * |
| |
325 xmlnode_get_data(xmlnode *node) |
| |
326 { |
| |
327 GString *str = NULL; |
| |
328 xmlnode *c; |
| |
329 |
| |
330 g_return_val_if_fail(node != NULL, NULL); |
| |
331 |
| |
332 for(c = node->child; c; c = c->next) { |
| |
333 if(c->type == XMLNODE_TYPE_DATA) { |
| |
334 if(!str) |
| |
335 str = g_string_new(""); |
| |
336 str = g_string_append_len(str, c->data, c->data_sz); |
| |
337 } |
| |
338 } |
| |
339 |
| |
340 if (str == NULL) |
| |
341 return NULL; |
| |
342 |
| |
343 return g_string_free(str, FALSE); |
| |
344 } |
| |
345 |
| |
346 static char * |
| |
347 xmlnode_to_str_helper(xmlnode *node, int *len, gboolean formatting, int depth) |
| |
348 { |
| |
349 GString *text = g_string_new(""); |
| |
350 xmlnode *c; |
| |
351 char *node_name, *esc, *esc2, *tab = NULL; |
| |
352 gboolean need_end = FALSE, pretty = formatting; |
| |
353 |
| |
354 g_return_val_if_fail(node != NULL, NULL); |
| |
355 |
| |
356 if(pretty && depth) { |
| |
357 tab = g_strnfill(depth, '\t'); |
| |
358 text = g_string_append(text, tab); |
| |
359 } |
| |
360 |
| |
361 node_name = g_markup_escape_text(node->name, -1); |
| |
362 g_string_append_printf(text, "<%s", node_name); |
| |
363 |
| |
364 if (node->xmlns) { |
| |
365 if(!node->parent || !node->parent->xmlns || strcmp(node->xmlns, node->parent->xmlns)) |
| |
366 { |
| |
367 char *xmlns = g_markup_escape_text(node->xmlns, -1); |
| |
368 g_string_append_printf(text, " xmlns='%s'", xmlns); |
| |
369 g_free(xmlns); |
| |
370 } |
| |
371 } |
| |
372 for(c = node->child; c; c = c->next) |
| |
373 { |
| |
374 if(c->type == XMLNODE_TYPE_ATTRIB) { |
| |
375 esc = g_markup_escape_text(c->name, -1); |
| |
376 esc2 = g_markup_escape_text(c->data, -1); |
| |
377 g_string_append_printf(text, " %s='%s'", esc, esc2); |
| |
378 g_free(esc); |
| |
379 g_free(esc2); |
| |
380 } else if(c->type == XMLNODE_TYPE_TAG || c->type == XMLNODE_TYPE_DATA) { |
| |
381 if(c->type == XMLNODE_TYPE_DATA) |
| |
382 pretty = FALSE; |
| |
383 need_end = TRUE; |
| |
384 } |
| |
385 } |
| |
386 |
| |
387 if(need_end) { |
| |
388 g_string_append_printf(text, ">%s", pretty ? NEWLINE_S : ""); |
| |
389 |
| |
390 for(c = node->child; c; c = c->next) |
| |
391 { |
| |
392 if(c->type == XMLNODE_TYPE_TAG) { |
| |
393 int esc_len; |
| |
394 esc = xmlnode_to_str_helper(c, &esc_len, pretty, depth+1); |
| |
395 text = g_string_append_len(text, esc, esc_len); |
| |
396 g_free(esc); |
| |
397 } else if(c->type == XMLNODE_TYPE_DATA && c->data_sz > 0) { |
| |
398 esc = g_markup_escape_text(c->data, c->data_sz); |
| |
399 text = g_string_append(text, esc); |
| |
400 g_free(esc); |
| |
401 } |
| |
402 } |
| |
403 |
| |
404 if(tab && pretty) |
| |
405 text = g_string_append(text, tab); |
| |
406 g_string_append_printf(text, "</%s>%s", node_name, formatting ? NEWLINE_S : ""); |
| |
407 } else { |
| |
408 g_string_append_printf(text, "/>%s", formatting ? NEWLINE_S : ""); |
| |
409 } |
| |
410 |
| |
411 g_free(node_name); |
| |
412 |
| |
413 g_free(tab); |
| |
414 |
| |
415 if(len) |
| |
416 *len = text->len; |
| |
417 |
| |
418 return g_string_free(text, FALSE); |
| |
419 } |
| |
420 |
| |
421 char * |
| |
422 xmlnode_to_str(xmlnode *node, int *len) |
| |
423 { |
| |
424 return xmlnode_to_str_helper(node, len, FALSE, 0); |
| |
425 } |
| |
426 |
| |
427 char * |
| |
428 xmlnode_to_formatted_str(xmlnode *node, int *len) |
| |
429 { |
| |
430 char *xml, *xml_with_declaration; |
| |
431 |
| |
432 g_return_val_if_fail(node != NULL, NULL); |
| |
433 |
| |
434 xml = xmlnode_to_str_helper(node, len, TRUE, 0); |
| |
435 xml_with_declaration = |
| |
436 g_strdup_printf("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S "%s", xml); |
| |
437 g_free(xml); |
| |
438 |
| |
439 return xml_with_declaration; |
| |
440 } |
| |
441 |
| |
442 struct _xmlnode_parser_data { |
| |
443 xmlnode *current; |
| |
444 gboolean error; |
| |
445 }; |
| |
446 |
| |
447 static void |
| |
448 xmlnode_parser_element_start_libxml(void *user_data, |
| |
449 const xmlChar *element_name, const xmlChar *prefix, const xmlChar *xmlns, |
| |
450 int nb_namespaces, const xmlChar **namespaces, |
| |
451 int nb_attributes, int nb_defaulted, const xmlChar **attributes) |
| |
452 { |
| |
453 struct _xmlnode_parser_data *xpd = user_data; |
| |
454 xmlnode *node; |
| |
455 int i; |
| |
456 |
| |
457 if(!element_name || xpd->error) { |
| |
458 return; |
| |
459 } else { |
| |
460 if(xpd->current) |
| |
461 node = xmlnode_new_child(xpd->current, (const char*) element_name); |
| |
462 else |
| |
463 node = xmlnode_new((const char *) element_name); |
| |
464 |
| |
465 xmlnode_set_namespace(node, (const char *) xmlns); |
| |
466 |
| |
467 for(i=0; i < nb_attributes * 5; i+=5) { |
| |
468 char *txt; |
| |
469 int attrib_len = attributes[i+4] - attributes[i+3]; |
| |
470 char *attrib = g_malloc(attrib_len + 1); |
| |
471 memcpy(attrib, attributes[i+3], attrib_len); |
| |
472 attrib[attrib_len] = '\0'; |
| |
473 txt = attrib; |
| |
474 attrib = gaim_unescape_html(txt); |
| |
475 g_free(txt); |
| |
476 xmlnode_set_attrib(node, (const char*) attributes[i], attrib); |
| |
477 g_free(attrib); |
| |
478 } |
| |
479 |
| |
480 xpd->current = node; |
| |
481 } |
| |
482 } |
| |
483 |
| |
484 static void |
| |
485 xmlnode_parser_element_end_libxml(void *user_data, const xmlChar *element_name, |
| |
486 const xmlChar *prefix, const xmlChar *xmlns) |
| |
487 { |
| |
488 struct _xmlnode_parser_data *xpd = user_data; |
| |
489 |
| |
490 if(!element_name || !xpd->current || xpd->error) |
| |
491 return; |
| |
492 |
| |
493 if(xpd->current->parent) { |
| |
494 if(!xmlStrcmp((xmlChar*) xpd->current->name, element_name)) |
| |
495 xpd->current = xpd->current->parent; |
| |
496 } |
| |
497 } |
| |
498 |
| |
499 static void |
| |
500 xmlnode_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len) |
| |
501 { |
| |
502 struct _xmlnode_parser_data *xpd = user_data; |
| |
503 |
| |
504 if(!xpd->current || xpd->error) |
| |
505 return; |
| |
506 |
| |
507 if(!text || !text_len) |
| |
508 return; |
| |
509 |
| |
510 xmlnode_insert_data(xpd->current, (const char*) text, text_len); |
| |
511 } |
| |
512 |
| |
513 static void |
| |
514 xmlnode_parser_error_libxml(void *user_data, const char *msg, ...) |
| |
515 { |
| |
516 struct _xmlnode_parser_data *xpd = user_data; |
| |
517 xpd->error = TRUE; |
| |
518 } |
| |
519 |
| |
520 static xmlSAXHandler xmlnode_parser_libxml = { |
| |
521 .internalSubset = NULL, |
| |
522 .isStandalone = NULL, |
| |
523 .hasInternalSubset = NULL, |
| |
524 .hasExternalSubset = NULL, |
| |
525 .resolveEntity = NULL, |
| |
526 .getEntity = NULL, |
| |
527 .entityDecl = NULL, |
| |
528 .notationDecl = NULL, |
| |
529 .attributeDecl = NULL, |
| |
530 .elementDecl = NULL, |
| |
531 .unparsedEntityDecl = NULL, |
| |
532 .setDocumentLocator = NULL, |
| |
533 .startDocument = NULL, |
| |
534 .endDocument = NULL, |
| |
535 .startElement = NULL, |
| |
536 .endElement = NULL, |
| |
537 .reference = NULL, |
| |
538 .characters = xmlnode_parser_element_text_libxml, |
| |
539 .ignorableWhitespace = NULL, |
| |
540 .processingInstruction = NULL, |
| |
541 .comment = NULL, |
| |
542 .warning = NULL, |
| |
543 .error = xmlnode_parser_error_libxml, |
| |
544 .fatalError = NULL, |
| |
545 .getParameterEntity = NULL, |
| |
546 .cdataBlock = NULL, |
| |
547 .externalSubset = NULL, |
| |
548 .initialized = XML_SAX2_MAGIC, |
| |
549 ._private = NULL, |
| |
550 .startElementNs = xmlnode_parser_element_start_libxml, |
| |
551 .endElementNs = xmlnode_parser_element_end_libxml, |
| |
552 .serror = NULL |
| |
553 }; |
| |
554 |
| |
555 xmlnode * |
| |
556 xmlnode_from_str(const char *str, gssize size) |
| |
557 { |
| |
558 struct _xmlnode_parser_data *xpd; |
| |
559 xmlnode *ret; |
| |
560 gsize real_size; |
| |
561 |
| |
562 g_return_val_if_fail(str != NULL, NULL); |
| |
563 |
| |
564 real_size = size < 0 ? strlen(str) : size; |
| |
565 xpd = g_new0(struct _xmlnode_parser_data, 1); |
| |
566 |
| |
567 if (xmlSAXUserParseMemory(&xmlnode_parser_libxml, xpd, str, real_size) < 0) { |
| |
568 while(xpd->current && xpd->current->parent) |
| |
569 xpd->current = xpd->current->parent; |
| |
570 if(xpd->current) |
| |
571 xmlnode_free(xpd->current); |
| |
572 xpd->current = NULL; |
| |
573 } |
| |
574 ret = xpd->current; |
| |
575 if (xpd->error) { |
| |
576 ret = NULL; |
| |
577 if (xpd->current) |
| |
578 xmlnode_free(xpd->current); |
| |
579 } |
| |
580 |
| |
581 g_free(xpd); |
| |
582 return ret; |
| |
583 } |
| |
584 |
| |
585 xmlnode * |
| |
586 xmlnode_copy(xmlnode *src) |
| |
587 { |
| |
588 xmlnode *ret; |
| |
589 xmlnode *child; |
| |
590 xmlnode *sibling = NULL; |
| |
591 |
| |
592 g_return_val_if_fail(src != NULL, NULL); |
| |
593 |
| |
594 ret = new_node(src->name, src->type); |
| |
595 if(src->data) { |
| |
596 if(src->data_sz) { |
| |
597 ret->data = g_memdup(src->data, src->data_sz); |
| |
598 ret->data_sz = src->data_sz; |
| |
599 } else { |
| |
600 ret->data = g_strdup(src->data); |
| |
601 } |
| |
602 } |
| |
603 |
| |
604 for(child = src->child; child; child = child->next) { |
| |
605 if(sibling) { |
| |
606 sibling->next = xmlnode_copy(child); |
| |
607 sibling = sibling->next; |
| |
608 } else { |
| |
609 ret->child = xmlnode_copy(child); |
| |
610 sibling = ret->child; |
| |
611 } |
| |
612 sibling->parent = ret; |
| |
613 } |
| |
614 |
| |
615 ret->lastchild = sibling; |
| |
616 |
| |
617 return ret; |
| |
618 } |
| |
619 |
| |
620 xmlnode * |
| |
621 xmlnode_get_next_twin(xmlnode *node) |
| |
622 { |
| |
623 xmlnode *sibling; |
| |
624 const char *ns = xmlnode_get_namespace(node); |
| |
625 |
| |
626 g_return_val_if_fail(node != NULL, NULL); |
| |
627 g_return_val_if_fail(node->type == XMLNODE_TYPE_TAG, NULL); |
| |
628 |
| |
629 for(sibling = node->next; sibling; sibling = sibling->next) { |
| |
630 const char *xmlns = NULL; |
| |
631 if(ns) |
| |
632 xmlns = xmlnode_get_namespace(sibling); |
| |
633 |
| |
634 if(sibling->type == XMLNODE_TYPE_TAG && !strcmp(node->name, sibling->name) && |
| |
635 (!ns || (xmlns && !strcmp(ns, xmlns)))) |
| |
636 return sibling; |
| |
637 } |
| |
638 |
| |
639 return NULL; |
| |
640 } |