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 }