Mercurial > pidgin
annotate libgaim/xmlnode.c @ 14827:02b70dc43044
[gaim-migrate @ 17594]
Fix the bindings for scrolling through the histories in the entrybox.
committer: Tailor Script <tailor@pidgin.im>
author | Sadrul Habib Chowdhury <imadil@gmail.com> |
---|---|
date | Sat, 28 Oct 2006 04:59:07 +0000 |
parents | 58202142e9ad |
children | c6978ee9ac4d |
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 && | |
130 !strcmp(attr_node->name, attr)) { | |
131 if(node->child == attr_node) { | |
132 node->child = attr_node->next; | |
133 } else if (node->lastchild == attr_node) { | |
134 node->lastchild = sibling; | |
135 } else { | |
136 sibling->next = attr_node->next; | |
137 } | |
138 xmlnode_free(attr_node); | |
139 return; | |
140 } | |
141 sibling = attr_node; | |
142 } | |
143 } | |
144 | |
145 void | |
146 xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value) | |
147 { | |
148 xmlnode *attrib_node; | |
149 | |
150 g_return_if_fail(node != NULL); | |
151 g_return_if_fail(attr != NULL); | |
152 g_return_if_fail(value != NULL); | |
153 | |
154 xmlnode_remove_attrib(node, attr); | |
155 | |
156 attrib_node = new_node(attr, XMLNODE_TYPE_ATTRIB); | |
157 | |
158 attrib_node->data = g_strdup(value); | |
159 | |
160 xmlnode_insert_child(node, attrib_node); | |
161 } | |
162 | |
163 const char * | |
164 xmlnode_get_attrib(xmlnode *node, const char *attr) | |
165 { | |
166 xmlnode *x; | |
167 | |
168 g_return_val_if_fail(node != NULL, NULL); | |
169 | |
170 for(x = node->child; x; x = x->next) { | |
171 if(x->type == XMLNODE_TYPE_ATTRIB && !strcmp(attr, x->name)) { | |
172 return x->data; | |
173 } | |
174 } | |
175 | |
176 return NULL; | |
177 } | |
178 | |
179 | |
180 void xmlnode_set_namespace(xmlnode *node, const char *xmlns) | |
181 { | |
182 g_return_if_fail(node != NULL); | |
183 | |
184 g_free(node->namespace); | |
185 node->namespace = g_strdup(xmlns); | |
186 } | |
187 | |
188 const char *xmlnode_get_namespace(xmlnode *node) | |
189 { | |
190 g_return_val_if_fail(node != NULL, NULL); | |
191 | |
192 return node->namespace; | |
193 } | |
194 | |
195 void | |
196 xmlnode_free(xmlnode *node) | |
197 { | |
198 xmlnode *x, *y; | |
199 | |
200 g_return_if_fail(node != NULL); | |
201 | |
202 x = node->child; | |
203 while(x) { | |
204 y = x->next; | |
205 xmlnode_free(x); | |
206 x = y; | |
207 } | |
208 | |
209 g_free(node->name); | |
210 g_free(node->data); | |
211 g_free(node->namespace); | |
14324 | 212 |
213 GAIM_DBUS_UNREGISTER_POINTER(node); | |
14192 | 214 g_free(node); |
215 } | |
216 | |
217 xmlnode* | |
218 xmlnode_get_child(const xmlnode *parent, const char *name) | |
219 { | |
220 return xmlnode_get_child_with_namespace(parent, name, NULL); | |
221 } | |
222 | |
223 xmlnode * | |
224 xmlnode_get_child_with_namespace(const xmlnode *parent, const char *name, const char *ns) | |
225 { | |
226 xmlnode *x, *ret = NULL; | |
227 char **names; | |
228 char *parent_name, *child_name; | |
229 | |
230 g_return_val_if_fail(parent != NULL, NULL); | |
231 g_return_val_if_fail(name != NULL, NULL); | |
232 | |
233 names = g_strsplit(name, "/", 2); | |
234 parent_name = names[0]; | |
235 child_name = names[1]; | |
236 | |
237 for(x = parent->child; x; x = x->next) { | |
238 const char *xmlns = NULL; | |
239 if(ns) | |
240 xmlns = xmlnode_get_namespace(x); | |
241 | |
242 if(x->type == XMLNODE_TYPE_TAG && name && !strcmp(parent_name, x->name) | |
243 && (!ns || (xmlns && !strcmp(ns, xmlns)))) { | |
244 ret = x; | |
245 break; | |
246 } | |
247 } | |
248 | |
249 if(child_name && ret) | |
250 ret = xmlnode_get_child(ret, child_name); | |
251 | |
252 g_strfreev(names); | |
253 return ret; | |
254 } | |
255 | |
256 char * | |
257 xmlnode_get_data(xmlnode *node) | |
258 { | |
259 GString *str = NULL; | |
260 xmlnode *c; | |
261 | |
262 g_return_val_if_fail(node != NULL, NULL); | |
263 | |
264 for(c = node->child; c; c = c->next) { | |
265 if(c->type == XMLNODE_TYPE_DATA) { | |
266 if(!str) | |
267 str = g_string_new(""); | |
268 str = g_string_append_len(str, c->data, c->data_sz); | |
269 } | |
270 } | |
271 | |
272 if (str == NULL) | |
273 return NULL; | |
274 | |
275 return g_string_free(str, FALSE); | |
276 } | |
277 | |
278 static char * | |
279 xmlnode_to_str_helper(xmlnode *node, int *len, gboolean formatting, int depth) | |
280 { | |
281 GString *text = g_string_new(""); | |
282 xmlnode *c; | |
283 char *node_name, *esc, *esc2, *tab = NULL; | |
284 gboolean need_end = FALSE, pretty = formatting; | |
285 | |
286 g_return_val_if_fail(node != NULL, NULL); | |
287 | |
288 if(pretty && depth) { | |
289 tab = g_strnfill(depth, '\t'); | |
290 text = g_string_append(text, tab); | |
291 } | |
292 | |
293 node_name = g_markup_escape_text(node->name, -1); | |
294 g_string_append_printf(text, "<%s", node_name); | |
295 | |
296 if (node->namespace) { | |
297 char *namespace = g_markup_escape_text(node->namespace, -1); | |
298 g_string_append_printf(text, " xmlns='%s'", namespace); | |
299 g_free(namespace); | |
300 } | |
301 for(c = node->child; c; c = c->next) | |
302 { | |
303 if(c->type == XMLNODE_TYPE_ATTRIB) { | |
304 esc = g_markup_escape_text(c->name, -1); | |
305 esc2 = g_markup_escape_text(c->data, -1); | |
306 g_string_append_printf(text, " %s='%s'", esc, esc2); | |
307 g_free(esc); | |
308 g_free(esc2); | |
309 } else if(c->type == XMLNODE_TYPE_TAG || c->type == XMLNODE_TYPE_DATA) { | |
310 if(c->type == XMLNODE_TYPE_DATA) | |
311 pretty = FALSE; | |
312 need_end = TRUE; | |
313 } | |
314 } | |
315 | |
316 if(need_end) { | |
317 g_string_append_printf(text, ">%s", pretty ? NEWLINE_S : ""); | |
318 | |
319 for(c = node->child; c; c = c->next) | |
320 { | |
321 if(c->type == XMLNODE_TYPE_TAG) { | |
322 int esc_len; | |
323 esc = xmlnode_to_str_helper(c, &esc_len, pretty, depth+1); | |
324 text = g_string_append_len(text, esc, esc_len); | |
325 g_free(esc); | |
326 } else if(c->type == XMLNODE_TYPE_DATA && c->data_sz > 0) { | |
327 esc = g_markup_escape_text(c->data, c->data_sz); | |
328 text = g_string_append(text, esc); | |
329 g_free(esc); | |
330 } | |
331 } | |
332 | |
333 if(tab && pretty) | |
334 text = g_string_append(text, tab); | |
335 g_string_append_printf(text, "</%s>%s", node_name, formatting ? NEWLINE_S : ""); | |
336 } else { | |
337 g_string_append_printf(text, "/>%s", formatting ? NEWLINE_S : ""); | |
338 } | |
339 | |
340 g_free(node_name); | |
341 | |
342 g_free(tab); | |
343 | |
344 if(len) | |
345 *len = text->len; | |
346 | |
347 return g_string_free(text, FALSE); | |
348 } | |
349 | |
350 char * | |
351 xmlnode_to_str(xmlnode *node, int *len) | |
352 { | |
353 return xmlnode_to_str_helper(node, len, FALSE, 0); | |
354 } | |
355 | |
356 char * | |
357 xmlnode_to_formatted_str(xmlnode *node, int *len) | |
358 { | |
359 char *xml, *xml_with_declaration; | |
360 | |
361 g_return_val_if_fail(node != NULL, NULL); | |
362 | |
363 xml = xmlnode_to_str_helper(node, len, TRUE, 0); | |
364 xml_with_declaration = | |
365 g_strdup_printf("<?xml version='1.0' encoding='UTF-8' ?>" NEWLINE_S NEWLINE_S "%s", xml); | |
366 g_free(xml); | |
367 | |
368 return xml_with_declaration; | |
369 } | |
370 | |
371 struct _xmlnode_parser_data { | |
372 xmlnode *current; | |
373 }; | |
374 | |
375 static void | |
376 xmlnode_parser_element_start_libxml(void *user_data, | |
377 const xmlChar *element_name, const xmlChar *prefix, const xmlChar *namespace, | |
378 int nb_namespaces, const xmlChar **namespaces, | |
379 int nb_attributes, int nb_defaulted, const xmlChar **attributes) | |
380 { | |
381 struct _xmlnode_parser_data *xpd = user_data; | |
382 xmlnode *node; | |
383 int i; | |
384 | |
385 if(!element_name) { | |
386 return; | |
387 } else { | |
388 if(xpd->current) | |
14628
58202142e9ad
[gaim-migrate @ 17369]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14436
diff
changeset
|
389 node = xmlnode_new_child(xpd->current, (const char*) element_name); |
14192 | 390 else |
14628
58202142e9ad
[gaim-migrate @ 17369]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14436
diff
changeset
|
391 node = xmlnode_new((const char *) element_name); |
14192 | 392 |
14628
58202142e9ad
[gaim-migrate @ 17369]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14436
diff
changeset
|
393 xmlnode_set_namespace(node, (const char *) namespace); |
14192 | 394 |
395 for(i=0; i < nb_attributes * 5; i+=5) { | |
14228 | 396 char *txt; |
14192 | 397 int attrib_len = attributes[i+4] - attributes[i+3]; |
398 char *attrib = g_malloc(attrib_len + 1); | |
399 memcpy(attrib, attributes[i+3], attrib_len); | |
400 attrib[attrib_len] = '\0'; | |
14228 | 401 txt = attrib; |
14192 | 402 attrib = gaim_unescape_html(txt); |
403 g_free(txt); | |
14628
58202142e9ad
[gaim-migrate @ 17369]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14436
diff
changeset
|
404 xmlnode_set_attrib(node, (const char*) attributes[i], attrib); |
14192 | 405 g_free(attrib); |
406 } | |
407 | |
408 xpd->current = node; | |
409 } | |
410 } | |
411 | |
412 static void | |
413 xmlnode_parser_element_end_libxml(void *user_data, const xmlChar *element_name, | |
414 const xmlChar *prefix, const xmlChar *namespace) | |
415 { | |
416 struct _xmlnode_parser_data *xpd = user_data; | |
417 | |
418 if(!element_name || !xpd->current) | |
419 return; | |
420 | |
421 if(xpd->current->parent) { | |
14628
58202142e9ad
[gaim-migrate @ 17369]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14436
diff
changeset
|
422 if(!xmlStrcmp((xmlChar*) xpd->current->name, element_name)) |
14192 | 423 xpd->current = xpd->current->parent; |
424 } | |
425 } | |
426 | |
427 static void | |
428 xmlnode_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len) | |
429 { | |
430 struct _xmlnode_parser_data *xpd = user_data; | |
431 | |
432 if(!xpd->current) | |
433 return; | |
434 | |
435 if(!text || !text_len) | |
436 return; | |
437 | |
14628
58202142e9ad
[gaim-migrate @ 17369]
Daniel Atallah <daniel.atallah@gmail.com>
parents:
14436
diff
changeset
|
438 xmlnode_insert_data(xpd->current, (const char*) text, text_len); |
14192 | 439 } |
440 | |
441 static xmlSAXHandler xmlnode_parser_libxml = { | |
442 .internalSubset = NULL, | |
443 .isStandalone = NULL, | |
444 .hasInternalSubset = NULL, | |
445 .hasExternalSubset = NULL, | |
446 .resolveEntity = NULL, | |
447 .getEntity = NULL, | |
448 .entityDecl = NULL, | |
449 .notationDecl = NULL, | |
450 .attributeDecl = NULL, | |
451 .elementDecl = NULL, | |
452 .unparsedEntityDecl = NULL, | |
453 .setDocumentLocator = NULL, | |
454 .startDocument = NULL, | |
455 .endDocument = NULL, | |
456 .startElement = NULL, | |
457 .endElement = NULL, | |
458 .reference = NULL, | |
459 .characters = xmlnode_parser_element_text_libxml, | |
460 .ignorableWhitespace = NULL, | |
461 .processingInstruction = NULL, | |
462 .comment = NULL, | |
463 .warning = NULL, | |
464 .error = NULL, | |
465 .fatalError = NULL, | |
466 .getParameterEntity = NULL, | |
467 .cdataBlock = NULL, | |
468 .externalSubset = NULL, | |
469 .initialized = XML_SAX2_MAGIC, | |
470 ._private = NULL, | |
471 .startElementNs = xmlnode_parser_element_start_libxml, | |
472 .endElementNs = xmlnode_parser_element_end_libxml, | |
473 .serror = NULL | |
474 }; | |
475 | |
476 xmlnode * | |
477 xmlnode_from_str(const char *str, gssize size) | |
478 { | |
479 struct _xmlnode_parser_data *xpd; | |
480 xmlnode *ret; | |
481 gsize real_size; | |
482 | |
483 g_return_val_if_fail(str != NULL, NULL); | |
484 | |
485 real_size = size < 0 ? strlen(str) : size; | |
486 xpd = g_new0(struct _xmlnode_parser_data, 1); | |
487 | |
14322 | 488 if (xmlSAXUserParseMemory(&xmlnode_parser_libxml, xpd, str, real_size) < 0) { |
14192 | 489 while(xpd->current && xpd->current->parent) |
490 xpd->current = xpd->current->parent; | |
491 if(xpd->current) | |
492 xmlnode_free(xpd->current); | |
493 xpd->current = NULL; | |
494 } | |
495 ret = xpd->current; | |
496 g_free(xpd); | |
497 return ret; | |
498 } | |
499 | |
500 xmlnode * | |
501 xmlnode_copy(xmlnode *src) | |
502 { | |
503 xmlnode *ret; | |
504 xmlnode *child; | |
505 xmlnode *sibling = NULL; | |
506 | |
507 g_return_val_if_fail(src != NULL, NULL); | |
508 | |
509 ret = new_node(src->name, src->type); | |
510 if(src->data) { | |
511 if(src->data_sz) { | |
512 ret->data = g_memdup(src->data, src->data_sz); | |
513 ret->data_sz = src->data_sz; | |
514 } else { | |
515 ret->data = g_strdup(src->data); | |
516 } | |
517 } | |
518 | |
519 for(child = src->child; child; child = child->next) { | |
520 if(sibling) { | |
521 sibling->next = xmlnode_copy(child); | |
522 sibling = sibling->next; | |
523 } else { | |
524 ret->child = xmlnode_copy(child); | |
525 sibling = ret->child; | |
526 } | |
527 sibling->parent = ret; | |
528 } | |
529 | |
530 ret->lastchild = sibling; | |
531 | |
532 return ret; | |
533 } | |
534 | |
535 xmlnode * | |
536 xmlnode_get_next_twin(xmlnode *node) | |
537 { | |
538 xmlnode *sibling; | |
539 const char *ns = xmlnode_get_namespace(node); | |
540 | |
541 g_return_val_if_fail(node != NULL, NULL); | |
542 g_return_val_if_fail(node->type == XMLNODE_TYPE_TAG, NULL); | |
543 | |
544 for(sibling = node->next; sibling; sibling = sibling->next) { | |
545 const char *xmlns = NULL; | |
546 if(ns) | |
547 xmlns = xmlnode_get_namespace(sibling); | |
548 | |
549 if(sibling->type == XMLNODE_TYPE_TAG && !strcmp(node->name, sibling->name) && | |
550 (!ns || (xmlns && !strcmp(ns, xmlns)))) | |
551 return sibling; | |
552 } | |
553 | |
554 return NULL; | |
555 } |