Mercurial > pidgin.yaz
annotate libgaim/xmlnode.c @ 15223:26357b7f117e
[gaim-migrate @ 18012]
* Get rid of an assertion failure that I think was happening when
you added a new account without setting a custom icon for it, and
you still had the Accounts window open
* A little code-reuse in some buddy icon scaling code
* An minor memleak that could happen when unable open a new file in
$HOME/.gaim/icons/
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Sun, 17 Dec 2006 05:04:23 +0000 |
parents | c65def04fb44 |
children | 682908b965cf |
rev | line source |
---|---|
14192 | 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 | |
14324 | 36 #include "dbus-maybe.h" |
14192 | 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 | |
14324 | 54 GAIM_DBUS_REGISTER_POINTER(node, xmlnode); |
55 | |
14192 | 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 && | |
15216 | 130 !strcmp(attr_node->name, attr)) |
131 { | |
14192 | 132 if(node->child == attr_node) { |
133 node->child = attr_node->next; | |
134 } else { | |
135 sibling->next = attr_node->next; | |
136 } | |
15216 | 137 if (node->lastchild == attr_node) { |
138 node->lastchild = sibling; | |
139 } | |
14192 | 140 xmlnode_free(attr_node); |
141 return; | |
142 } | |
143 sibling = attr_node; | |
144 } | |
145 } | |
146 | |
15204 | 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) && | |
15216 | 160 !strcmp(attr_node->xmlns, xmlns)) |
161 { | |
15204 | 162 if(node->child == attr_node) { |
163 node->child = attr_node->next; | |
164 } else { | |
165 sibling->next = attr_node->next; | |
166 } | |
15216 | 167 if (node->lastchild == attr_node) { |
168 node->lastchild = sibling; | |
169 } | |
15204 | 170 xmlnode_free(attr_node); |
171 return; | |
172 } | |
173 sibling = attr_node; | |
174 } | |
175 } | |
176 | |
14192 | 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 | |
15204 | 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 | |
15216 | 211 xmlnode_insert_child(node, attrib_node); |
15204 | 212 } |
213 | |
14192 | 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 | |
15204 | 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) { | |
15216 | 238 if(x->type == XMLNODE_TYPE_ATTRIB && |
15204 | 239 !strcmp(attr, x->name) && !strcmp(x->xmlns, xmlns)) { |
240 return x->data; | |
241 } | |
242 } | |
243 | |
15216 | 244 return NULL; |
15204 | 245 } |
246 | |
14192 | 247 |
248 void xmlnode_set_namespace(xmlnode *node, const char *xmlns) | |
249 { | |
250 g_return_if_fail(node != NULL); | |
251 | |
15177 | 252 g_free(node->xmlns); |
253 node->xmlns = g_strdup(xmlns); | |
14192 | 254 } |
255 | |
256 const char *xmlnode_get_namespace(xmlnode *node) | |
257 { | |
258 g_return_val_if_fail(node != NULL, NULL); | |
259 | |
15177 | 260 return node->xmlns; |
14192 | 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); | |
15177 | 279 g_free(node->xmlns); |
14324 | 280 |
281 GAIM_DBUS_UNREGISTER_POINTER(node); | |
14192 | 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 | |
15177 | 364 if (node->xmlns) { |
365 if(!node->parent || !node->parent->xmlns || strcmp(node->xmlns, node->parent->xmlns)) | |
15123 | 366 { |
15177 | 367 char *xmlns = g_markup_escape_text(node->xmlns, -1); |
368 g_string_append_printf(text, " xmlns='%s'", xmlns); | |
369 g_free(xmlns); | |
15123 | 370 } |
14192 | 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 }; | |
445 | |
446 static void | |
447 xmlnode_parser_element_start_libxml(void *user_data, | |
15177 | 448 const xmlChar *element_name, const xmlChar *prefix, const xmlChar *xmlns, |
14192 | 449 int nb_namespaces, const xmlChar **namespaces, |
450 int nb_attributes, int nb_defaulted, const xmlChar **attributes) | |
451 { | |
452 struct _xmlnode_parser_data *xpd = user_data; | |
453 xmlnode *node; | |
454 int i; | |
455 | |
456 if(!element_name) { | |
457 return; | |
458 } else { | |
459 if(xpd->current) | |
14628
58202142e9ad
[gaim-migrate @ 17369]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14436
diff
changeset
|
460 node = xmlnode_new_child(xpd->current, (const char*) element_name); |
14192 | 461 else |
14628
58202142e9ad
[gaim-migrate @ 17369]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14436
diff
changeset
|
462 node = xmlnode_new((const char *) element_name); |
14192 | 463 |
15177 | 464 xmlnode_set_namespace(node, (const char *) xmlns); |
14192 | 465 |
466 for(i=0; i < nb_attributes * 5; i+=5) { | |
14228 | 467 char *txt; |
14192 | 468 int attrib_len = attributes[i+4] - attributes[i+3]; |
469 char *attrib = g_malloc(attrib_len + 1); | |
470 memcpy(attrib, attributes[i+3], attrib_len); | |
471 attrib[attrib_len] = '\0'; | |
14228 | 472 txt = attrib; |
14192 | 473 attrib = gaim_unescape_html(txt); |
474 g_free(txt); | |
14628
58202142e9ad
[gaim-migrate @ 17369]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14436
diff
changeset
|
475 xmlnode_set_attrib(node, (const char*) attributes[i], attrib); |
14192 | 476 g_free(attrib); |
477 } | |
478 | |
479 xpd->current = node; | |
480 } | |
481 } | |
482 | |
483 static void | |
484 xmlnode_parser_element_end_libxml(void *user_data, const xmlChar *element_name, | |
15177 | 485 const xmlChar *prefix, const xmlChar *xmlns) |
14192 | 486 { |
487 struct _xmlnode_parser_data *xpd = user_data; | |
488 | |
489 if(!element_name || !xpd->current) | |
490 return; | |
491 | |
492 if(xpd->current->parent) { | |
14628
58202142e9ad
[gaim-migrate @ 17369]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14436
diff
changeset
|
493 if(!xmlStrcmp((xmlChar*) xpd->current->name, element_name)) |
14192 | 494 xpd->current = xpd->current->parent; |
495 } | |
496 } | |
497 | |
498 static void | |
499 xmlnode_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len) | |
500 { | |
501 struct _xmlnode_parser_data *xpd = user_data; | |
502 | |
503 if(!xpd->current) | |
504 return; | |
505 | |
506 if(!text || !text_len) | |
507 return; | |
508 | |
14628
58202142e9ad
[gaim-migrate @ 17369]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14436
diff
changeset
|
509 xmlnode_insert_data(xpd->current, (const char*) text, text_len); |
14192 | 510 } |
511 | |
512 static xmlSAXHandler xmlnode_parser_libxml = { | |
513 .internalSubset = NULL, | |
514 .isStandalone = NULL, | |
515 .hasInternalSubset = NULL, | |
516 .hasExternalSubset = NULL, | |
517 .resolveEntity = NULL, | |
518 .getEntity = NULL, | |
519 .entityDecl = NULL, | |
520 .notationDecl = NULL, | |
521 .attributeDecl = NULL, | |
522 .elementDecl = NULL, | |
523 .unparsedEntityDecl = NULL, | |
524 .setDocumentLocator = NULL, | |
525 .startDocument = NULL, | |
526 .endDocument = NULL, | |
527 .startElement = NULL, | |
528 .endElement = NULL, | |
529 .reference = NULL, | |
530 .characters = xmlnode_parser_element_text_libxml, | |
531 .ignorableWhitespace = NULL, | |
532 .processingInstruction = NULL, | |
533 .comment = NULL, | |
534 .warning = NULL, | |
535 .error = NULL, | |
536 .fatalError = NULL, | |
537 .getParameterEntity = NULL, | |
538 .cdataBlock = NULL, | |
539 .externalSubset = NULL, | |
540 .initialized = XML_SAX2_MAGIC, | |
541 ._private = NULL, | |
542 .startElementNs = xmlnode_parser_element_start_libxml, | |
543 .endElementNs = xmlnode_parser_element_end_libxml, | |
544 .serror = NULL | |
545 }; | |
546 | |
547 xmlnode * | |
548 xmlnode_from_str(const char *str, gssize size) | |
549 { | |
550 struct _xmlnode_parser_data *xpd; | |
551 xmlnode *ret; | |
552 gsize real_size; | |
553 | |
554 g_return_val_if_fail(str != NULL, NULL); | |
555 | |
556 real_size = size < 0 ? strlen(str) : size; | |
557 xpd = g_new0(struct _xmlnode_parser_data, 1); | |
558 | |
14322 | 559 if (xmlSAXUserParseMemory(&xmlnode_parser_libxml, xpd, str, real_size) < 0) { |
14192 | 560 while(xpd->current && xpd->current->parent) |
561 xpd->current = xpd->current->parent; | |
562 if(xpd->current) | |
563 xmlnode_free(xpd->current); | |
564 xpd->current = NULL; | |
565 } | |
566 ret = xpd->current; | |
567 g_free(xpd); | |
568 return ret; | |
569 } | |
570 | |
571 xmlnode * | |
572 xmlnode_copy(xmlnode *src) | |
573 { | |
574 xmlnode *ret; | |
575 xmlnode *child; | |
576 xmlnode *sibling = NULL; | |
577 | |
578 g_return_val_if_fail(src != NULL, NULL); | |
579 | |
580 ret = new_node(src->name, src->type); | |
581 if(src->data) { | |
582 if(src->data_sz) { | |
583 ret->data = g_memdup(src->data, src->data_sz); | |
584 ret->data_sz = src->data_sz; | |
585 } else { | |
586 ret->data = g_strdup(src->data); | |
587 } | |
588 } | |
589 | |
590 for(child = src->child; child; child = child->next) { | |
591 if(sibling) { | |
592 sibling->next = xmlnode_copy(child); | |
593 sibling = sibling->next; | |
594 } else { | |
595 ret->child = xmlnode_copy(child); | |
596 sibling = ret->child; | |
597 } | |
598 sibling->parent = ret; | |
599 } | |
600 | |
601 ret->lastchild = sibling; | |
602 | |
603 return ret; | |
604 } | |
605 | |
606 xmlnode * | |
607 xmlnode_get_next_twin(xmlnode *node) | |
608 { | |
609 xmlnode *sibling; | |
610 const char *ns = xmlnode_get_namespace(node); | |
611 | |
612 g_return_val_if_fail(node != NULL, NULL); | |
613 g_return_val_if_fail(node->type == XMLNODE_TYPE_TAG, NULL); | |
614 | |
615 for(sibling = node->next; sibling; sibling = sibling->next) { | |
616 const char *xmlns = NULL; | |
617 if(ns) | |
618 xmlns = xmlnode_get_namespace(sibling); | |
619 | |
620 if(sibling->type == XMLNODE_TYPE_TAG && !strcmp(node->name, sibling->name) && | |
621 (!ns || (xmlns && !strcmp(ns, xmlns)))) | |
622 return sibling; | |
623 } | |
624 | |
625 return NULL; | |
626 } |