Mercurial > pidgin.yaz
annotate src/xmlnode.c @ 11735:8d7c99f20e4c
[gaim-migrate @ 14026]
Switch from using our old GaimGtkDisclosure widget to using the official
GtkExpander widget. It works better and should make us look more uniform
with other gtk apps.
To maintain compatability with gtk < 2.4, I grabbed the gtkexpander.c
and gtkexpander.h files from gtk+ CVS. I haven't tested compiling on
a computer with gtk < 2.4 yet, so it's possible we'll need to get more
stuff from gtk+ CVS.
For future reference, gtkexpander.h was not modified, and gtkexpander.c
was only modified to check if version is > 2.4 then do nothing. I also
changed the #includes to use <> instead of ""
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Sun, 23 Oct 2005 03:24:53 +0000 |
parents | 0906a3e9626c |
children | da44f68fb4d2 |
rev | line source |
---|---|
7131 | 1 /** |
2 * @file xmlnode.c XML DOM functions | |
3 * | |
4 * gaim | |
5 * | |
8046 | 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. | |
7131 | 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 <string.h> | |
33 #include <glib.h> | |
34 | |
35 #include "xmlnode.h" | |
36 | |
37 static xmlnode* | |
8135 | 38 new_node(const char *name, XMLNodeType type) |
7131 | 39 { |
40 xmlnode *node = g_new0(xmlnode, 1); | |
10423 | 41 |
7131 | 42 if(name) |
43 node->name = g_strdup(name); | |
44 node->type = type; | |
45 | |
46 return node; | |
47 } | |
48 | |
49 xmlnode* | |
50 xmlnode_new(const char *name) | |
51 { | |
52 g_return_val_if_fail(name != NULL, NULL); | |
53 | |
8135 | 54 return new_node(name, XMLNODE_TYPE_TAG); |
7131 | 55 } |
56 | |
10423 | 57 xmlnode * |
58 xmlnode_new_child(xmlnode *parent, const char *name) | |
7131 | 59 { |
60 xmlnode *node; | |
61 | |
62 g_return_val_if_fail(parent != NULL, NULL); | |
63 g_return_val_if_fail(name != NULL, NULL); | |
64 | |
8135 | 65 node = new_node(name, XMLNODE_TYPE_TAG); |
7131 | 66 |
67 xmlnode_insert_child(parent, node); | |
68 | |
69 return node; | |
70 } | |
71 | |
72 void | |
73 xmlnode_insert_child(xmlnode *parent, xmlnode *child) | |
74 { | |
75 g_return_if_fail(parent != NULL); | |
76 g_return_if_fail(child != NULL); | |
77 | |
78 child->parent = parent; | |
79 | |
80 if(parent->child) { | |
81 xmlnode *x; | |
82 for(x = parent->child; x->next; x = x->next); | |
83 x->next = child; | |
84 } else { | |
85 parent->child = child; | |
86 } | |
87 } | |
88 | |
89 void | |
10848 | 90 xmlnode_insert_data(xmlnode *node, const char *data, gssize size) |
7131 | 91 { |
10415 | 92 xmlnode *child; |
10848 | 93 gsize real_size; |
7131 | 94 |
10415 | 95 g_return_if_fail(node != NULL); |
7131 | 96 g_return_if_fail(data != NULL); |
97 g_return_if_fail(size != 0); | |
98 | |
99 real_size = size == -1 ? strlen(data) : size; | |
100 | |
10415 | 101 child = new_node(NULL, XMLNODE_TYPE_DATA); |
7131 | 102 |
10415 | 103 child->data = g_memdup(data, real_size); |
104 child->data_sz = real_size; | |
7131 | 105 |
10415 | 106 xmlnode_insert_child(node, child); |
7131 | 107 } |
108 | |
109 void | |
110 xmlnode_remove_attrib(xmlnode *node, const char *attr) | |
111 { | |
112 xmlnode *attr_node, *sibling = NULL; | |
113 | |
114 g_return_if_fail(node != NULL); | |
115 g_return_if_fail(attr != NULL); | |
116 | |
117 for(attr_node = node->child; attr_node; attr_node = attr_node->next) | |
118 { | |
8135 | 119 if(attr_node->type == XMLNODE_TYPE_ATTRIB && |
7131 | 120 !strcmp(attr_node->name, attr)) { |
121 if(node->child == attr_node) { | |
122 node->child = attr_node->next; | |
123 } else { | |
124 sibling->next = attr_node->next; | |
125 } | |
126 xmlnode_free(attr_node); | |
127 return; | |
128 } | |
129 sibling = attr_node; | |
130 } | |
131 } | |
132 | |
133 void | |
134 xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value) | |
135 { | |
136 xmlnode *attrib_node; | |
137 | |
138 g_return_if_fail(node != NULL); | |
139 g_return_if_fail(attr != NULL); | |
140 g_return_if_fail(value != NULL); | |
141 | |
142 xmlnode_remove_attrib(node, attr); | |
143 | |
8135 | 144 attrib_node = new_node(attr, XMLNODE_TYPE_ATTRIB); |
7131 | 145 |
146 attrib_node->data = g_strdup(value); | |
147 | |
148 xmlnode_insert_child(node, attrib_node); | |
149 } | |
150 | |
10425 | 151 const char * |
7131 | 152 xmlnode_get_attrib(xmlnode *node, const char *attr) |
153 { | |
154 xmlnode *x; | |
155 | |
156 g_return_val_if_fail(node != NULL, NULL); | |
157 | |
158 for(x = node->child; x; x = x->next) { | |
8135 | 159 if(x->type == XMLNODE_TYPE_ATTRIB && !strcmp(attr, x->name)) { |
7131 | 160 return x->data; |
161 } | |
162 } | |
163 | |
164 return NULL; | |
165 } | |
166 | |
10423 | 167 void |
168 xmlnode_free(xmlnode *node) | |
7131 | 169 { |
170 xmlnode *x, *y; | |
171 | |
172 g_return_if_fail(node != NULL); | |
173 | |
174 x = node->child; | |
175 while(x) { | |
176 y = x->next; | |
177 xmlnode_free(x); | |
178 x = y; | |
179 } | |
180 | |
181 if(node->name) | |
182 g_free(node->name); | |
183 if(node->data) | |
184 g_free(node->data); | |
185 g_free(node); | |
186 } | |
187 | |
188 xmlnode* | |
10736 | 189 xmlnode_get_child(const xmlnode *parent, const char *name) |
190 { | |
191 return xmlnode_get_child_with_namespace(parent, name, NULL); | |
192 } | |
193 | |
194 xmlnode * | |
195 xmlnode_get_child_with_namespace(const xmlnode *parent, const char *name, const char *ns) | |
7131 | 196 { |
197 xmlnode *x, *ret = NULL; | |
198 char **names; | |
199 char *parent_name, *child_name; | |
200 | |
201 g_return_val_if_fail(parent != NULL, NULL); | |
202 | |
203 names = g_strsplit(name, "/", 2); | |
204 parent_name = names[0]; | |
205 child_name = names[1]; | |
206 | |
207 for(x = parent->child; x; x = x->next) { | |
8262 | 208 const char *xmlns = NULL; |
209 if(ns) | |
210 xmlns = xmlnode_get_attrib(x, "xmlns"); | |
211 | |
212 if(x->type == XMLNODE_TYPE_TAG && name && !strcmp(parent_name, x->name) | |
213 && (!ns || (xmlns && !strcmp(ns, xmlns)))) { | |
7131 | 214 ret = x; |
215 break; | |
216 } | |
217 } | |
218 | |
219 if(child_name && ret) | |
8262 | 220 ret = xmlnode_get_child(ret, child_name); |
7131 | 221 |
222 g_strfreev(names); | |
223 return ret; | |
224 } | |
225 | |
226 char * | |
227 xmlnode_get_data(xmlnode *node) | |
228 { | |
229 GString *str = NULL; | |
230 xmlnode *c; | |
231 | |
232 g_return_val_if_fail(node != NULL, NULL); | |
233 | |
234 for(c = node->child; c; c = c->next) { | |
8135 | 235 if(c->type == XMLNODE_TYPE_DATA) { |
7131 | 236 if(!str) |
237 str = g_string_new(""); | |
238 str = g_string_append_len(str, c->data, c->data_sz); | |
239 } | |
240 } | |
241 | |
10331 | 242 if (str == NULL) |
243 return NULL; | |
7131 | 244 |
10331 | 245 return g_string_free(str, FALSE); |
7131 | 246 } |
247 | |
10425 | 248 static char * |
10423 | 249 xmlnode_to_str_helper(xmlnode *node, int *len, gboolean formatting, int depth) |
7131 | 250 { |
251 GString *text = g_string_new(""); | |
252 xmlnode *c; | |
9837 | 253 char *node_name, *esc, *esc2, *tab = NULL; |
9838 | 254 gboolean need_end = FALSE, pretty = formatting; |
9837 | 255 #ifdef _WIN32 |
256 static const char *newline = "\r\n"; | |
257 #else | |
258 static const char *newline = "\n"; | |
259 #endif | |
260 | |
261 if(pretty && depth) { | |
262 tab = g_strnfill(depth, '\t'); | |
263 text = g_string_append(text, tab); | |
264 } | |
7131 | 265 |
266 node_name = g_markup_escape_text(node->name, -1); | |
267 g_string_append_printf(text, "<%s", node_name); | |
268 | |
269 for(c = node->child; c; c = c->next) | |
270 { | |
8135 | 271 if(c->type == XMLNODE_TYPE_ATTRIB) { |
7131 | 272 esc = g_markup_escape_text(c->name, -1); |
273 esc2 = g_markup_escape_text(c->data, -1); | |
274 g_string_append_printf(text, " %s='%s'", esc, esc2); | |
275 g_free(esc); | |
276 g_free(esc2); | |
8135 | 277 } else if(c->type == XMLNODE_TYPE_TAG || c->type == XMLNODE_TYPE_DATA) { |
9837 | 278 if(c->type == XMLNODE_TYPE_DATA) |
9838 | 279 pretty = FALSE; |
7131 | 280 need_end = TRUE; |
281 } | |
282 } | |
283 | |
284 if(need_end) { | |
9838 | 285 g_string_append_printf(text, ">%s", pretty ? newline : ""); |
7131 | 286 |
287 for(c = node->child; c; c = c->next) | |
288 { | |
8135 | 289 if(c->type == XMLNODE_TYPE_TAG) { |
7642 | 290 int esc_len; |
9838 | 291 esc = xmlnode_to_str_helper(c, &esc_len, pretty, depth+1); |
7642 | 292 text = g_string_append_len(text, esc, esc_len); |
7131 | 293 g_free(esc); |
8135 | 294 } else if(c->type == XMLNODE_TYPE_DATA) { |
7131 | 295 esc = g_markup_escape_text(c->data, c->data_sz); |
7642 | 296 text = g_string_append(text, esc); |
7131 | 297 g_free(esc); |
298 } | |
299 } | |
300 | |
9838 | 301 if(tab && pretty) |
9837 | 302 text = g_string_append(text, tab); |
10415 | 303 g_string_append_printf(text, "</%s>%s", node_name, formatting ? newline : ""); |
7131 | 304 } else { |
10415 | 305 g_string_append_printf(text, "/>%s", formatting ? newline : ""); |
7131 | 306 } |
307 | |
308 g_free(node_name); | |
309 | |
9837 | 310 if(tab) |
311 g_free(tab); | |
312 | |
7642 | 313 if(len) |
314 *len = text->len; | |
10331 | 315 |
316 return g_string_free(text, FALSE); | |
7131 | 317 } |
318 | |
10425 | 319 char * |
10423 | 320 xmlnode_to_str(xmlnode *node, int *len) |
321 { | |
9837 | 322 return xmlnode_to_str_helper(node, len, FALSE, 0); |
323 } | |
324 | |
10425 | 325 char * |
10423 | 326 xmlnode_to_formatted_str(xmlnode *node, int *len) |
327 { | |
10425 | 328 char *xml, *xml_with_declaration; |
10415 | 329 |
330 xml = xmlnode_to_str_helper(node, len, TRUE, 0); | |
331 xml_with_declaration = | |
332 g_strdup_printf("<?xml version='1.0' encoding='UTF-8' ?>\n\n%s", xml); | |
333 g_free(xml); | |
334 | |
335 return xml_with_declaration; | |
9837 | 336 } |
337 | |
7131 | 338 struct _xmlnode_parser_data { |
339 xmlnode *current; | |
340 }; | |
341 | |
342 static void | |
343 xmlnode_parser_element_start(GMarkupParseContext *context, | |
344 const char *element_name, const char **attrib_names, | |
345 const char **attrib_values, gpointer user_data, GError **error) | |
346 { | |
347 struct _xmlnode_parser_data *xpd = user_data; | |
348 xmlnode *node; | |
349 int i; | |
350 | |
351 if(!element_name) { | |
352 return; | |
353 } else { | |
354 if(xpd->current) | |
355 node = xmlnode_new_child(xpd->current, element_name); | |
356 else | |
357 node = xmlnode_new(element_name); | |
358 | |
359 for(i=0; attrib_names[i]; i++) | |
360 xmlnode_set_attrib(node, attrib_names[i], attrib_values[i]); | |
361 | |
362 xpd->current = node; | |
363 } | |
364 } | |
365 | |
366 static void | |
367 xmlnode_parser_element_end(GMarkupParseContext *context, | |
368 const char *element_name, gpointer user_data, GError **error) | |
369 { | |
370 struct _xmlnode_parser_data *xpd = user_data; | |
371 | |
372 if(!element_name || !xpd->current) | |
373 return; | |
374 | |
375 if(xpd->current->parent) { | |
376 if(!strcmp(xpd->current->name, element_name)) | |
377 xpd->current = xpd->current->parent; | |
378 } | |
379 } | |
380 | |
381 static void | |
382 xmlnode_parser_element_text(GMarkupParseContext *context, const char *text, | |
383 gsize text_len, gpointer user_data, GError **error) | |
384 { | |
385 struct _xmlnode_parser_data *xpd = user_data; | |
386 | |
387 if(!xpd->current) | |
388 return; | |
389 | |
390 if(!text || !text_len) | |
391 return; | |
392 | |
393 xmlnode_insert_data(xpd->current, text, text_len); | |
394 } | |
395 | |
396 static GMarkupParser xmlnode_parser = { | |
397 xmlnode_parser_element_start, | |
398 xmlnode_parser_element_end, | |
399 xmlnode_parser_element_text, | |
400 NULL, | |
401 NULL | |
402 }; | |
403 | |
404 | |
10423 | 405 xmlnode * |
10848 | 406 xmlnode_from_str(const char *str, gssize size) |
7131 | 407 { |
11390 | 408 struct _xmlnode_parser_data *xpd; |
7131 | 409 xmlnode *ret; |
410 GMarkupParseContext *context; | |
11390 | 411 gsize real_size; |
7131 | 412 |
11390 | 413 g_return_val_if_fail(str != NULL, NULL); |
414 | |
11705
0906a3e9626c
[gaim-migrate @ 13996]
Richard Laager <rlaager@wiktel.com>
parents:
11390
diff
changeset
|
415 real_size = size < 0 ? strlen(str) : size; |
11390 | 416 xpd = g_new0(struct _xmlnode_parser_data, 1); |
7131 | 417 context = g_markup_parse_context_new(&xmlnode_parser, 0, xpd, NULL); |
418 | |
419 if(!g_markup_parse_context_parse(context, str, real_size, NULL)) { | |
420 while(xpd->current && xpd->current->parent) | |
421 xpd->current = xpd->current->parent; | |
422 if(xpd->current) | |
423 xmlnode_free(xpd->current); | |
424 xpd->current = NULL; | |
425 } | |
426 g_markup_parse_context_free(context); | |
427 | |
428 ret = xpd->current; | |
429 g_free(xpd); | |
430 return ret; | |
431 } | |
8135 | 432 |
10423 | 433 xmlnode * |
434 xmlnode_copy(xmlnode *src) | |
8135 | 435 { |
436 xmlnode *ret; | |
437 xmlnode *child; | |
438 xmlnode *sibling = NULL; | |
439 | |
440 if(!src) | |
441 return NULL; | |
442 | |
443 ret = new_node(src->name, src->type); | |
444 if(src->data) { | |
8167 | 445 if(src->data_sz) { |
446 ret->data = g_memdup(src->data, src->data_sz); | |
447 ret->data_sz = src->data_sz; | |
448 } else { | |
449 ret->data = g_strdup(src->data); | |
450 } | |
8135 | 451 } |
452 | |
453 for(child = src->child; child; child = child->next) { | |
454 if(sibling) { | |
455 sibling->next = xmlnode_copy(child); | |
456 sibling = sibling->next; | |
457 } else { | |
458 ret->child = xmlnode_copy(child); | |
459 sibling = ret->child; | |
460 } | |
461 sibling->parent = ret; | |
462 } | |
463 | |
464 return ret; | |
465 } | |
466 | |
10423 | 467 xmlnode * |
468 xmlnode_get_next_twin(xmlnode *node) | |
469 { | |
8135 | 470 xmlnode *sibling; |
8262 | 471 const char *ns = xmlnode_get_attrib(node, "xmlns"); |
8135 | 472 |
473 g_return_val_if_fail(node != NULL, NULL); | |
474 g_return_val_if_fail(node->type == XMLNODE_TYPE_TAG, NULL); | |
475 | |
476 for(sibling = node->next; sibling; sibling = sibling->next) { | |
8283 | 477 const char *xmlns = NULL; |
8262 | 478 if(ns) |
479 xmlns = xmlnode_get_attrib(sibling, "xmlns"); | |
480 | |
481 if(sibling->type == XMLNODE_TYPE_TAG && !strcmp(node->name, sibling->name) && | |
482 (!ns || (xmlns && !strcmp(ns, xmlns)))) | |
8135 | 483 return sibling; |
484 } | |
485 | |
486 return NULL; | |
487 } |