Mercurial > pidgin.yaz
comparison libgaim/xmlnode.c @ 14192:60b1bc8dbf37
[gaim-migrate @ 16863]
Renamed 'core' to 'libgaim'
committer: Tailor Script <tailor@pidgin.im>
author | Evan Schoenberg <evan.s@dreskin.net> |
---|---|
date | Sat, 19 Aug 2006 01:50:10 +0000 |
parents | |
children | c18bdf510325 |
comparison
equal
deleted
inserted
replaced
14191:009db0b357b5 | 14192:60b1bc8dbf37 |
---|---|
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 #ifdef HAVE_LIBXML | |
33 #include <libxml/parser.h> | |
34 #endif | |
35 #include <string.h> | |
36 #include <glib.h> | |
37 | |
38 #include "util.h" | |
39 #include "xmlnode.h" | |
40 | |
41 #ifdef _WIN32 | |
42 # define NEWLINE_S "\r\n" | |
43 #else | |
44 # define NEWLINE_S "\n" | |
45 #endif | |
46 | |
47 static xmlnode* | |
48 new_node(const char *name, XMLNodeType type) | |
49 { | |
50 xmlnode *node = g_new0(xmlnode, 1); | |
51 | |
52 node->name = g_strdup(name); | |
53 node->type = type; | |
54 | |
55 return node; | |
56 } | |
57 | |
58 xmlnode* | |
59 xmlnode_new(const char *name) | |
60 { | |
61 g_return_val_if_fail(name != NULL, NULL); | |
62 | |
63 return new_node(name, XMLNODE_TYPE_TAG); | |
64 } | |
65 | |
66 xmlnode * | |
67 xmlnode_new_child(xmlnode *parent, const char *name) | |
68 { | |
69 xmlnode *node; | |
70 | |
71 g_return_val_if_fail(parent != NULL, NULL); | |
72 g_return_val_if_fail(name != NULL, NULL); | |
73 | |
74 node = new_node(name, XMLNODE_TYPE_TAG); | |
75 | |
76 xmlnode_insert_child(parent, node); | |
77 | |
78 return node; | |
79 } | |
80 | |
81 void | |
82 xmlnode_insert_child(xmlnode *parent, xmlnode *child) | |
83 { | |
84 g_return_if_fail(parent != NULL); | |
85 g_return_if_fail(child != NULL); | |
86 | |
87 child->parent = parent; | |
88 | |
89 if(parent->lastchild) { | |
90 parent->lastchild->next = child; | |
91 } else { | |
92 parent->child = child; | |
93 } | |
94 | |
95 parent->lastchild = child; | |
96 } | |
97 | |
98 void | |
99 xmlnode_insert_data(xmlnode *node, const char *data, gssize size) | |
100 { | |
101 xmlnode *child; | |
102 gsize real_size; | |
103 | |
104 g_return_if_fail(node != NULL); | |
105 g_return_if_fail(data != NULL); | |
106 g_return_if_fail(size != 0); | |
107 | |
108 real_size = size == -1 ? strlen(data) : size; | |
109 | |
110 child = new_node(NULL, XMLNODE_TYPE_DATA); | |
111 | |
112 child->data = g_memdup(data, real_size); | |
113 child->data_sz = real_size; | |
114 | |
115 xmlnode_insert_child(node, child); | |
116 } | |
117 | |
118 void | |
119 xmlnode_remove_attrib(xmlnode *node, const char *attr) | |
120 { | |
121 xmlnode *attr_node, *sibling = NULL; | |
122 | |
123 g_return_if_fail(node != NULL); | |
124 g_return_if_fail(attr != NULL); | |
125 | |
126 for(attr_node = node->child; attr_node; attr_node = attr_node->next) | |
127 { | |
128 if(attr_node->type == XMLNODE_TYPE_ATTRIB && | |
129 !strcmp(attr_node->name, attr)) { | |
130 if(node->child == attr_node) { | |
131 node->child = attr_node->next; | |
132 } else if (node->lastchild == attr_node) { | |
133 node->lastchild = sibling; | |
134 } else { | |
135 sibling->next = attr_node->next; | |
136 } | |
137 xmlnode_free(attr_node); | |
138 return; | |
139 } | |
140 sibling = attr_node; | |
141 } | |
142 } | |
143 | |
144 void | |
145 xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value) | |
146 { | |
147 xmlnode *attrib_node; | |
148 | |
149 g_return_if_fail(node != NULL); | |
150 g_return_if_fail(attr != NULL); | |
151 g_return_if_fail(value != NULL); | |
152 | |
153 xmlnode_remove_attrib(node, attr); | |
154 | |
155 attrib_node = new_node(attr, XMLNODE_TYPE_ATTRIB); | |
156 | |
157 attrib_node->data = g_strdup(value); | |
158 | |
159 xmlnode_insert_child(node, attrib_node); | |
160 } | |
161 | |
162 const char * | |
163 xmlnode_get_attrib(xmlnode *node, const char *attr) | |
164 { | |
165 xmlnode *x; | |
166 | |
167 g_return_val_if_fail(node != NULL, NULL); | |
168 | |
169 for(x = node->child; x; x = x->next) { | |
170 if(x->type == XMLNODE_TYPE_ATTRIB && !strcmp(attr, x->name)) { | |
171 return x->data; | |
172 } | |
173 } | |
174 | |
175 return NULL; | |
176 } | |
177 | |
178 | |
179 void xmlnode_set_namespace(xmlnode *node, const char *xmlns) | |
180 { | |
181 #ifdef HAVE_LIBXML | |
182 g_return_if_fail(node != NULL); | |
183 | |
184 g_free(node->namespace); | |
185 node->namespace = g_strdup(xmlns); | |
186 #else | |
187 xmlnode_set_attrib(node, "xmlns", xmlns); | |
188 #endif | |
189 } | |
190 | |
191 const char *xmlnode_get_namespace(xmlnode *node) | |
192 { | |
193 #ifdef HAVE_LIBXML | |
194 g_return_val_if_fail(node != NULL, NULL); | |
195 | |
196 return node->namespace; | |
197 #else | |
198 return xmlnode_get_attrib(node, "xmlns"); | |
199 #endif | |
200 } | |
201 | |
202 void | |
203 xmlnode_free(xmlnode *node) | |
204 { | |
205 xmlnode *x, *y; | |
206 | |
207 g_return_if_fail(node != NULL); | |
208 | |
209 x = node->child; | |
210 while(x) { | |
211 y = x->next; | |
212 xmlnode_free(x); | |
213 x = y; | |
214 } | |
215 | |
216 g_free(node->name); | |
217 g_free(node->data); | |
218 #ifdef HAVE_LIBXML | |
219 g_free(node->namespace); | |
220 #endif | |
221 g_free(node); | |
222 } | |
223 | |
224 xmlnode* | |
225 xmlnode_get_child(const xmlnode *parent, const char *name) | |
226 { | |
227 return xmlnode_get_child_with_namespace(parent, name, NULL); | |
228 } | |
229 | |
230 xmlnode * | |
231 xmlnode_get_child_with_namespace(const xmlnode *parent, const char *name, const char *ns) | |
232 { | |
233 xmlnode *x, *ret = NULL; | |
234 char **names; | |
235 char *parent_name, *child_name; | |
236 | |
237 g_return_val_if_fail(parent != NULL, NULL); | |
238 g_return_val_if_fail(name != NULL, NULL); | |
239 | |
240 names = g_strsplit(name, "/", 2); | |
241 parent_name = names[0]; | |
242 child_name = names[1]; | |
243 | |
244 for(x = parent->child; x; x = x->next) { | |
245 const char *xmlns = NULL; | |
246 if(ns) | |
247 xmlns = xmlnode_get_namespace(x); | |
248 | |
249 if(x->type == XMLNODE_TYPE_TAG && name && !strcmp(parent_name, x->name) | |
250 && (!ns || (xmlns && !strcmp(ns, xmlns)))) { | |
251 ret = x; | |
252 break; | |
253 } | |
254 } | |
255 | |
256 if(child_name && ret) | |
257 ret = xmlnode_get_child(ret, child_name); | |
258 | |
259 g_strfreev(names); | |
260 return ret; | |
261 } | |
262 | |
263 char * | |
264 xmlnode_get_data(xmlnode *node) | |
265 { | |
266 GString *str = NULL; | |
267 xmlnode *c; | |
268 | |
269 g_return_val_if_fail(node != NULL, NULL); | |
270 | |
271 for(c = node->child; c; c = c->next) { | |
272 if(c->type == XMLNODE_TYPE_DATA) { | |
273 if(!str) | |
274 str = g_string_new(""); | |
275 str = g_string_append_len(str, c->data, c->data_sz); | |
276 } | |
277 } | |
278 | |
279 if (str == NULL) | |
280 return NULL; | |
281 | |
282 return g_string_free(str, FALSE); | |
283 } | |
284 | |
285 static char * | |
286 xmlnode_to_str_helper(xmlnode *node, int *len, gboolean formatting, int depth) | |
287 { | |
288 GString *text = g_string_new(""); | |
289 xmlnode *c; | |
290 char *node_name, *esc, *esc2, *tab = NULL; | |
291 gboolean need_end = FALSE, pretty = formatting; | |
292 | |
293 g_return_val_if_fail(node != NULL, NULL); | |
294 | |
295 if(pretty && depth) { | |
296 tab = g_strnfill(depth, '\t'); | |
297 text = g_string_append(text, tab); | |
298 } | |
299 | |
300 node_name = g_markup_escape_text(node->name, -1); | |
301 g_string_append_printf(text, "<%s", node_name); | |
302 | |
303 #ifdef HAVE_LIBXML | |
304 if (node->namespace) { | |
305 char *namespace = g_markup_escape_text(node->namespace, -1); | |
306 g_string_append_printf(text, " xmlns='%s'", namespace); | |
307 g_free(namespace); | |
308 } | |
309 #endif | |
310 for(c = node->child; c; c = c->next) | |
311 { | |
312 if(c->type == XMLNODE_TYPE_ATTRIB) { | |
313 esc = g_markup_escape_text(c->name, -1); | |
314 esc2 = g_markup_escape_text(c->data, -1); | |
315 g_string_append_printf(text, " %s='%s'", esc, esc2); | |
316 g_free(esc); | |
317 g_free(esc2); | |
318 } else if(c->type == XMLNODE_TYPE_TAG || c->type == XMLNODE_TYPE_DATA) { | |
319 if(c->type == XMLNODE_TYPE_DATA) | |
320 pretty = FALSE; | |
321 need_end = TRUE; | |
322 } | |
323 } | |
324 | |
325 if(need_end) { | |
326 g_string_append_printf(text, ">%s", pretty ? NEWLINE_S : ""); | |
327 | |
328 for(c = node->child; c; c = c->next) | |
329 { | |
330 if(c->type == XMLNODE_TYPE_TAG) { | |
331 int esc_len; | |
332 esc = xmlnode_to_str_helper(c, &esc_len, pretty, depth+1); | |
333 text = g_string_append_len(text, esc, esc_len); | |
334 g_free(esc); | |
335 } else if(c->type == XMLNODE_TYPE_DATA && c->data_sz > 0) { | |
336 esc = g_markup_escape_text(c->data, c->data_sz); | |
337 text = g_string_append(text, esc); | |
338 g_free(esc); | |
339 } | |
340 } | |
341 | |
342 if(tab && pretty) | |
343 text = g_string_append(text, tab); | |
344 g_string_append_printf(text, "</%s>%s", node_name, formatting ? NEWLINE_S : ""); | |
345 } else { | |
346 g_string_append_printf(text, "/>%s", formatting ? NEWLINE_S : ""); | |
347 } | |
348 | |
349 g_free(node_name); | |
350 | |
351 g_free(tab); | |
352 | |
353 if(len) | |
354 *len = text->len; | |
355 | |
356 return g_string_free(text, FALSE); | |
357 } | |
358 | |
359 char * | |
360 xmlnode_to_str(xmlnode *node, int *len) | |
361 { | |
362 return xmlnode_to_str_helper(node, len, FALSE, 0); | |
363 } | |
364 | |
365 char * | |
366 xmlnode_to_formatted_str(xmlnode *node, int *len) | |
367 { | |
368 char *xml, *xml_with_declaration; | |
369 | |
370 g_return_val_if_fail(node != NULL, NULL); | |
371 | |
372 xml = xmlnode_to_str_helper(node, len, TRUE, 0); | |
373 xml_with_declaration = | |
374 g_strdup_printf("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S "%s", xml); | |
375 g_free(xml); | |
376 | |
377 return xml_with_declaration; | |
378 } | |
379 | |
380 struct _xmlnode_parser_data { | |
381 xmlnode *current; | |
382 }; | |
383 | |
384 #ifdef HAVE_LIBXML | |
385 static void | |
386 xmlnode_parser_element_start_libxml(void *user_data, | |
387 const xmlChar *element_name, const xmlChar *prefix, const xmlChar *namespace, | |
388 int nb_namespaces, const xmlChar **namespaces, | |
389 int nb_attributes, int nb_defaulted, const xmlChar **attributes) | |
390 { | |
391 struct _xmlnode_parser_data *xpd = user_data; | |
392 xmlnode *node; | |
393 int i; | |
394 | |
395 if(!element_name) { | |
396 return; | |
397 } else { | |
398 if(xpd->current) | |
399 node = xmlnode_new_child(xpd->current, element_name); | |
400 else | |
401 node = xmlnode_new(element_name); | |
402 | |
403 xmlnode_set_namespace(node, namespace); | |
404 | |
405 for(i=0; i < nb_attributes * 5; i+=5) { | |
406 int attrib_len = attributes[i+4] - attributes[i+3]; | |
407 char *attrib = g_malloc(attrib_len + 1); | |
408 memcpy(attrib, attributes[i+3], attrib_len); | |
409 attrib[attrib_len] = '\0'; | |
410 #ifdef HAVE_LIBXML | |
411 char *txt = attrib; | |
412 attrib = gaim_unescape_html(txt); | |
413 g_free(txt); | |
414 #endif | |
415 xmlnode_set_attrib(node, attributes[i], attrib); | |
416 g_free(attrib); | |
417 } | |
418 | |
419 xpd->current = node; | |
420 } | |
421 } | |
422 | |
423 static void | |
424 xmlnode_parser_element_end_libxml(void *user_data, const xmlChar *element_name, | |
425 const xmlChar *prefix, const xmlChar *namespace) | |
426 { | |
427 struct _xmlnode_parser_data *xpd = user_data; | |
428 | |
429 if(!element_name || !xpd->current) | |
430 return; | |
431 | |
432 if(xpd->current->parent) { | |
433 if(!strcmp(xpd->current->name, element_name)) | |
434 xpd->current = xpd->current->parent; | |
435 } | |
436 } | |
437 | |
438 static void | |
439 xmlnode_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len) | |
440 { | |
441 struct _xmlnode_parser_data *xpd = user_data; | |
442 | |
443 if(!xpd->current) | |
444 return; | |
445 | |
446 if(!text || !text_len) | |
447 return; | |
448 | |
449 xmlnode_insert_data(xpd->current, text, text_len); | |
450 } | |
451 | |
452 #else | |
453 | |
454 static void | |
455 xmlnode_parser_element_start(GMarkupParseContext *context, | |
456 const char *element_name, const char **attrib_names, | |
457 const char **attrib_values, gpointer user_data, GError **error) | |
458 { | |
459 struct _xmlnode_parser_data *xpd = user_data; | |
460 xmlnode *node; | |
461 int i; | |
462 | |
463 if(!element_name) { | |
464 return; | |
465 } else { | |
466 if(xpd->current) | |
467 node = xmlnode_new_child(xpd->current, element_name); | |
468 else | |
469 node = xmlnode_new(element_name); | |
470 | |
471 for(i=0; attrib_names[i]; i++) | |
472 xmlnode_set_attrib(node, attrib_names[i], attrib_values[i]); | |
473 | |
474 xpd->current = node; | |
475 } | |
476 } | |
477 | |
478 static void | |
479 xmlnode_parser_element_end(GMarkupParseContext *context, | |
480 const char *element_name, gpointer user_data, GError **error) | |
481 { | |
482 struct _xmlnode_parser_data *xpd = user_data; | |
483 | |
484 if(!element_name || !xpd->current) | |
485 return; | |
486 | |
487 if(xpd->current->parent) { | |
488 if(!strcmp(xpd->current->name, element_name)) | |
489 xpd->current = xpd->current->parent; | |
490 } | |
491 } | |
492 | |
493 static void | |
494 xmlnode_parser_element_text(GMarkupParseContext *context, const char *text, | |
495 gsize text_len, gpointer user_data, GError **error) | |
496 { | |
497 struct _xmlnode_parser_data *xpd = user_data; | |
498 | |
499 if(!xpd->current) | |
500 return; | |
501 | |
502 if(!text || !text_len) | |
503 return; | |
504 | |
505 xmlnode_insert_data(xpd->current, text, text_len); | |
506 } | |
507 #endif | |
508 | |
509 #ifdef HAVE_LIBXML | |
510 static xmlSAXHandler xmlnode_parser_libxml = { | |
511 .internalSubset = NULL, | |
512 .isStandalone = NULL, | |
513 .hasInternalSubset = NULL, | |
514 .hasExternalSubset = NULL, | |
515 .resolveEntity = NULL, | |
516 .getEntity = NULL, | |
517 .entityDecl = NULL, | |
518 .notationDecl = NULL, | |
519 .attributeDecl = NULL, | |
520 .elementDecl = NULL, | |
521 .unparsedEntityDecl = NULL, | |
522 .setDocumentLocator = NULL, | |
523 .startDocument = NULL, | |
524 .endDocument = NULL, | |
525 .startElement = NULL, | |
526 .endElement = NULL, | |
527 .reference = NULL, | |
528 .characters = xmlnode_parser_element_text_libxml, | |
529 .ignorableWhitespace = NULL, | |
530 .processingInstruction = NULL, | |
531 .comment = NULL, | |
532 .warning = NULL, | |
533 .error = NULL, | |
534 .fatalError = NULL, | |
535 .getParameterEntity = NULL, | |
536 .cdataBlock = NULL, | |
537 .externalSubset = NULL, | |
538 .initialized = XML_SAX2_MAGIC, | |
539 ._private = NULL, | |
540 .startElementNs = xmlnode_parser_element_start_libxml, | |
541 .endElementNs = xmlnode_parser_element_end_libxml, | |
542 .serror = NULL | |
543 }; | |
544 #else | |
545 static GMarkupParser xmlnode_parser = { | |
546 xmlnode_parser_element_start, | |
547 xmlnode_parser_element_end, | |
548 xmlnode_parser_element_text, | |
549 NULL, | |
550 NULL | |
551 }; | |
552 #endif | |
553 | |
554 xmlnode * | |
555 xmlnode_from_str(const char *str, gssize size) | |
556 { | |
557 struct _xmlnode_parser_data *xpd; | |
558 xmlnode *ret; | |
559 #ifndef HAVE_LIBXML | |
560 GMarkupParseContext *context; | |
561 #endif | |
562 gsize real_size; | |
563 | |
564 g_return_val_if_fail(str != NULL, NULL); | |
565 | |
566 real_size = size < 0 ? strlen(str) : size; | |
567 xpd = g_new0(struct _xmlnode_parser_data, 1); | |
568 | |
569 #ifdef HAVE_LIBXML | |
570 if (xmlSAXUserParseMemory(&xmlnode_parser_libxml, xpd, str, size) < 0) { | |
571 while(xpd->current && xpd->current->parent) | |
572 xpd->current = xpd->current->parent; | |
573 if(xpd->current) | |
574 xmlnode_free(xpd->current); | |
575 xpd->current = NULL; | |
576 } | |
577 #else | |
578 context = g_markup_parse_context_new(&xmlnode_parser, 0, xpd, NULL); | |
579 | |
580 if(!g_markup_parse_context_parse(context, str, real_size, NULL)) { | |
581 while(xpd->current && xpd->current->parent) | |
582 xpd->current = xpd->current->parent; | |
583 if(xpd->current) | |
584 xmlnode_free(xpd->current); | |
585 xpd->current = NULL; | |
586 } | |
587 g_markup_parse_context_free(context); | |
588 #endif | |
589 ret = xpd->current; | |
590 g_free(xpd); | |
591 return ret; | |
592 } | |
593 | |
594 xmlnode * | |
595 xmlnode_copy(xmlnode *src) | |
596 { | |
597 xmlnode *ret; | |
598 xmlnode *child; | |
599 xmlnode *sibling = NULL; | |
600 | |
601 g_return_val_if_fail(src != NULL, NULL); | |
602 | |
603 ret = new_node(src->name, src->type); | |
604 if(src->data) { | |
605 if(src->data_sz) { | |
606 ret->data = g_memdup(src->data, src->data_sz); | |
607 ret->data_sz = src->data_sz; | |
608 } else { | |
609 ret->data = g_strdup(src->data); | |
610 } | |
611 } | |
612 | |
613 for(child = src->child; child; child = child->next) { | |
614 if(sibling) { | |
615 sibling->next = xmlnode_copy(child); | |
616 sibling = sibling->next; | |
617 } else { | |
618 ret->child = xmlnode_copy(child); | |
619 sibling = ret->child; | |
620 } | |
621 sibling->parent = ret; | |
622 } | |
623 | |
624 ret->lastchild = sibling; | |
625 | |
626 return ret; | |
627 } | |
628 | |
629 xmlnode * | |
630 xmlnode_get_next_twin(xmlnode *node) | |
631 { | |
632 xmlnode *sibling; | |
633 const char *ns = xmlnode_get_namespace(node); | |
634 | |
635 g_return_val_if_fail(node != NULL, NULL); | |
636 g_return_val_if_fail(node->type == XMLNODE_TYPE_TAG, NULL); | |
637 | |
638 for(sibling = node->next; sibling; sibling = sibling->next) { | |
639 const char *xmlns = NULL; | |
640 if(ns) | |
641 xmlns = xmlnode_get_namespace(sibling); | |
642 | |
643 if(sibling->type == XMLNODE_TYPE_TAG && !strcmp(node->name, sibling->name) && | |
644 (!ns || (xmlns && !strcmp(ns, xmlns)))) | |
645 return sibling; | |
646 } | |
647 | |
648 return NULL; | |
649 } |