src/xmlnode.c

changeset 7131
bde45b2e6bf3
child 7642
4e1735a499f5
equal deleted inserted replaced
7130:63b28524ba0b 7131:bde45b2e6bf3
1 /**
2 * @file xmlnode.c XML DOM functions
3 *
4 * gaim
5 *
6 * Copyright (C) 2003 Nathan Walp <faceprint@faceprint.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23 /* A lot of this code at least resembles the code in libxode, but since
24 * libxode uses memory pools that we simply have no need for, I decided to
25 * write my own stuff. Also, re-writing this lets me be as lightweight
26 * as I want to be. Thank you libxode for giving me a good starting point */
27
28 #include "internal.h"
29
30 #include <string.h>
31 #include <glib.h>
32
33 #include "xmlnode.h"
34
35 static xmlnode*
36 new_node(const char *name, NodeType type)
37 {
38 xmlnode *node = g_new0(xmlnode, 1);
39 if(name)
40 node->name = g_strdup(name);
41 node->type = type;
42
43 return node;
44 }
45
46 xmlnode*
47 xmlnode_new(const char *name)
48 {
49 g_return_val_if_fail(name != NULL, NULL);
50
51 return new_node(name, NODE_TYPE_TAG);
52 }
53
54 xmlnode *xmlnode_new_child(xmlnode *parent, const char *name)
55 {
56 xmlnode *node;
57
58 g_return_val_if_fail(parent != NULL, NULL);
59 g_return_val_if_fail(name != NULL, NULL);
60
61 node = new_node(name, NODE_TYPE_TAG);
62
63 xmlnode_insert_child(parent, node);
64
65 return node;
66 }
67
68 void
69 xmlnode_insert_child(xmlnode *parent, xmlnode *child)
70 {
71 g_return_if_fail(parent != NULL);
72 g_return_if_fail(child != NULL);
73
74 child->parent = parent;
75
76 if(parent->child) {
77 xmlnode *x;
78 for(x = parent->child; x->next; x = x->next);
79 x->next = child;
80 } else {
81 parent->child = child;
82 }
83 }
84
85 void
86 xmlnode_insert_data(xmlnode *parent, const char *data, size_t size)
87 {
88 xmlnode *node;
89 size_t real_size;
90
91 g_return_if_fail(parent != NULL);
92 g_return_if_fail(data != NULL);
93 g_return_if_fail(size != 0);
94
95 real_size = size == -1 ? strlen(data) : size;
96
97 node = new_node(NULL, NODE_TYPE_DATA);
98
99 node->data = g_memdup(data, real_size);
100 node->data_sz = real_size;
101
102 xmlnode_insert_child(parent, node);
103 }
104
105 void
106 xmlnode_remove_attrib(xmlnode *node, const char *attr)
107 {
108 xmlnode *attr_node, *sibling = NULL;
109
110 g_return_if_fail(node != NULL);
111 g_return_if_fail(attr != NULL);
112
113 for(attr_node = node->child; attr_node; attr_node = attr_node->next)
114 {
115 if(attr_node->type == NODE_TYPE_ATTRIB &&
116 !strcmp(attr_node->name, attr)) {
117 if(node->child == attr_node) {
118 node->child = attr_node->next;
119 } else {
120 sibling->next = attr_node->next;
121 }
122 xmlnode_free(attr_node);
123 return;
124 }
125 sibling = attr_node;
126 }
127 }
128
129 void
130 xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value)
131 {
132 xmlnode *attrib_node;
133
134 g_return_if_fail(node != NULL);
135 g_return_if_fail(attr != NULL);
136 g_return_if_fail(value != NULL);
137
138 xmlnode_remove_attrib(node, attr);
139
140 attrib_node = new_node(attr, NODE_TYPE_ATTRIB);
141
142 attrib_node->data = g_strdup(value);
143
144 xmlnode_insert_child(node, attrib_node);
145 }
146
147 const char*
148 xmlnode_get_attrib(xmlnode *node, const char *attr)
149 {
150 xmlnode *x;
151
152 g_return_val_if_fail(node != NULL, NULL);
153
154 for(x = node->child; x; x = x->next) {
155 if(x->type == NODE_TYPE_ATTRIB && !strcmp(attr, x->name)) {
156 return x->data;
157 }
158 }
159
160 return NULL;
161 }
162
163 void xmlnode_free(xmlnode *node)
164 {
165 xmlnode *x, *y;
166
167 g_return_if_fail(node != NULL);
168
169 x = node->child;
170 while(x) {
171 y = x->next;
172 xmlnode_free(x);
173 x = y;
174 }
175
176 if(node->name)
177 g_free(node->name);
178 if(node->data)
179 g_free(node->data);
180 g_free(node);
181 }
182
183 xmlnode*
184 xmlnode_get_child(xmlnode *parent, const char *name)
185 {
186 xmlnode *x, *ret = NULL;
187 char **names;
188 char *parent_name, *child_name;
189
190 g_return_val_if_fail(parent != NULL, NULL);
191
192 names = g_strsplit(name, "/", 2);
193 parent_name = names[0];
194 child_name = names[1];
195
196 for(x = parent->child; x; x = x->next) {
197 if(x->type == NODE_TYPE_TAG && name && !strcmp(parent_name, x->name)) {
198 ret = x;
199 break;
200 }
201 }
202
203 if(child_name && ret)
204 ret = xmlnode_get_child(x, child_name);
205
206 g_strfreev(names);
207 return ret;
208 }
209
210 char *
211 xmlnode_get_data(xmlnode *node)
212 {
213 GString *str = NULL;
214 char *ret = NULL;
215 xmlnode *c;
216
217 g_return_val_if_fail(node != NULL, NULL);
218
219
220 for(c = node->child; c; c = c->next) {
221 if(c->type == NODE_TYPE_DATA) {
222 if(!str)
223 str = g_string_new("");
224 str = g_string_append_len(str, c->data, c->data_sz);
225 }
226 }
227
228 if(str) {
229 ret = str->str;
230 g_string_free(str, FALSE);
231 }
232
233 return ret;
234 }
235
236 char *xmlnode_to_str(xmlnode *node)
237 {
238 char *ret;
239 GString *text = g_string_new("");
240 xmlnode *c;
241 char *node_name, *esc, *esc2;
242 gboolean need_end = FALSE;
243
244 node_name = g_markup_escape_text(node->name, -1);
245 g_string_append_printf(text, "<%s", node_name);
246
247
248 for(c = node->child; c; c = c->next)
249 {
250 if(c->type == NODE_TYPE_ATTRIB) {
251 esc = g_markup_escape_text(c->name, -1);
252 esc2 = g_markup_escape_text(c->data, -1);
253 g_string_append_printf(text, " %s='%s'", esc, esc2);
254 g_free(esc);
255 g_free(esc2);
256 } else if(c->type == NODE_TYPE_TAG || c->type == NODE_TYPE_DATA) {
257 need_end = TRUE;
258 }
259 }
260
261 if(need_end) {
262 text = g_string_append_c(text, '>');
263
264 for(c = node->child; c; c = c->next)
265 {
266 if(c->type == NODE_TYPE_TAG) {
267 esc = xmlnode_to_str(c);
268 g_string_append_printf(text, "%s", esc);
269 g_free(esc);
270 } else if(c->type == NODE_TYPE_DATA) {
271 esc = g_markup_escape_text(c->data, c->data_sz);
272 g_string_append_printf(text, "%s", esc);
273 g_free(esc);
274 }
275 }
276
277 g_string_append_printf(text, "</%s>", node_name);
278 } else {
279 g_string_append_printf(text, "/>");
280 }
281
282 g_free(node_name);
283
284 ret = text->str;
285 g_string_free(text, FALSE);
286 return ret;
287 }
288
289 struct _xmlnode_parser_data {
290 xmlnode *current;
291 };
292
293 static void
294 xmlnode_parser_element_start(GMarkupParseContext *context,
295 const char *element_name, const char **attrib_names,
296 const char **attrib_values, gpointer user_data, GError **error)
297 {
298 struct _xmlnode_parser_data *xpd = user_data;
299 xmlnode *node;
300 int i;
301
302 if(!element_name) {
303 return;
304 } else {
305 if(xpd->current)
306 node = xmlnode_new_child(xpd->current, element_name);
307 else
308 node = xmlnode_new(element_name);
309
310 for(i=0; attrib_names[i]; i++)
311 xmlnode_set_attrib(node, attrib_names[i], attrib_values[i]);
312
313 xpd->current = node;
314 }
315 }
316
317 static void
318 xmlnode_parser_element_end(GMarkupParseContext *context,
319 const char *element_name, gpointer user_data, GError **error)
320 {
321 struct _xmlnode_parser_data *xpd = user_data;
322
323 if(!element_name || !xpd->current)
324 return;
325
326 if(xpd->current->parent) {
327 if(!strcmp(xpd->current->name, element_name))
328 xpd->current = xpd->current->parent;
329 }
330 }
331
332 static void
333 xmlnode_parser_element_text(GMarkupParseContext *context, const char *text,
334 gsize text_len, gpointer user_data, GError **error)
335 {
336 struct _xmlnode_parser_data *xpd = user_data;
337
338 if(!xpd->current)
339 return;
340
341 if(!text || !text_len)
342 return;
343
344 xmlnode_insert_data(xpd->current, text, text_len);
345 }
346
347 static GMarkupParser xmlnode_parser = {
348 xmlnode_parser_element_start,
349 xmlnode_parser_element_end,
350 xmlnode_parser_element_text,
351 NULL,
352 NULL
353 };
354
355
356 xmlnode *xmlnode_from_str(const char *str, size_t size)
357 {
358 struct _xmlnode_parser_data *xpd = g_new0(struct _xmlnode_parser_data, 1);
359 xmlnode *ret;
360 GMarkupParseContext *context;
361 size_t real_size = size == -1 ? strlen(str) : size;
362
363 context = g_markup_parse_context_new(&xmlnode_parser, 0, xpd, NULL);
364
365 if(!g_markup_parse_context_parse(context, str, real_size, NULL)) {
366 while(xpd->current && xpd->current->parent)
367 xpd->current = xpd->current->parent;
368 if(xpd->current)
369 xmlnode_free(xpd->current);
370 xpd->current = NULL;
371 }
372 g_markup_parse_context_free(context);
373
374 ret = xpd->current;
375 g_free(xpd);
376 return ret;
377 }

mercurial