Mercurial > pidgin.yaz
comparison libpurple/xmlnode.c @ 15374:5fe8042783c1
Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author | Sean Egan <seanegan@gmail.com> |
---|---|
date | Sat, 20 Jan 2007 02:32:10 +0000 |
parents | |
children | c2d75b47198d |
comparison
equal
deleted
inserted
replaced
15373:f79e0f4df793 | 15374:5fe8042783c1 |
---|---|
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 } |