Mercurial > pidgin.yaz
comparison src/protocols/jabber/xmlnode.c @ 7014:67c4e9d39242
[gaim-migrate @ 7577]
Here it is, the bulk of the new Jabber prpl.
Left to do:
- Implement registration
- Implement password changing
- Keep track of conversation threads (since I apparently have to)
- Fix the bugs that always magically appear in code after I commit
committer: Tailor Script <tailor@pidgin.im>
author | Nathan Walp <nwalp@pidgin.im> |
---|---|
date | Mon, 29 Sep 2003 15:23:19 +0000 |
parents | 4e7cefc55971 |
children | 4e5654931401 |
comparison
equal
deleted
inserted
replaced
7013:859cafb6433f | 7014:67c4e9d39242 |
---|---|
1 /* -------------------------------------------------------------------------- | 1 /** |
2 * | 2 * @file xmlnode.c XML DOM functions |
3 * License | 3 * |
4 * | 4 * gaim |
5 * The contents of this file are subject to the Jabber Open Source License | 5 * |
6 * Version 1.0 (the "JOSL"). You may not copy or use this file, in either | 6 * Copyright (C) 2003 Nathan Walp <faceprint@faceprint.com> |
7 * source code or executable form, except in compliance with the JOSL. You | 7 * |
8 * may obtain a copy of the JOSL at http://www.jabber.org/ or at | 8 * This program is free software; you can redistribute it and/or modify |
9 * http://www.opensource.org/. | 9 * it under the terms of the GNU General Public License as published by |
10 * | 10 * the Free Software Foundation; either version 2 of the License, or |
11 * Software distributed under the JOSL is distributed on an "AS IS" basis, | 11 * (at your option) any later version. |
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL | 12 * |
13 * for the specific language governing rights and limitations under the | 13 * This program is distributed in the hope that it will be useful, |
14 * JOSL. | 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 * | 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 * Copyrights | 16 * GNU General Public License for more details. |
17 * | 17 * |
18 * Portions created by or assigned to Jabber.com, Inc. are | 18 * You should have received a copy of the GNU General Public License |
19 * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact | 19 * along with this program; if not, write to the Free Software |
20 * information for Jabber.com, Inc. is available at http://www.jabber.com/. | 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
21 * | |
22 * Portions Copyright (c) 1998-1999 Jeremie Miller. | |
23 * | |
24 * Acknowledgements | |
25 * | |
26 * Special thanks to the Jabber Open Source Contributors for their | |
27 * suggestions and support of Jabber. | |
28 * | |
29 * Alternatively, the contents of this file may be used under the terms of the | |
30 * GNU General Public License Version 2 or later (the "GPL"), in which case | |
31 * the provisions of the GPL are applicable instead of those above. If you | |
32 * wish to allow use of your version of this file only under the terms of the | |
33 * GPL and not to allow others to use your version of this file under the JOSL, | |
34 * indicate your decision by deleting the provisions above and replace them | |
35 * with the notice and other provisions required by the GPL. If you do not | |
36 * delete the provisions above, a recipient may use your version of this file | |
37 * under either the JOSL or the GPL. | |
38 * | |
39 * | |
40 * --------------------------------------------------------------------------*/ | |
41 | |
42 #include "lib.h" | |
43 | |
44 /* Internal routines */ | |
45 xmlnode _xmlnode_new(pool p, const char* name, unsigned int type) | |
46 { | |
47 xmlnode result = NULL; | |
48 if (type > NTYPE_LAST) | |
49 return NULL; | |
50 | |
51 if (type != NTYPE_CDATA && name == NULL) | |
52 return NULL; | |
53 | |
54 if (p == NULL) | |
55 { | |
56 p = pool_heap(1*1024); | |
57 } | |
58 | |
59 /* Allocate & zero memory */ | |
60 result = (xmlnode)pmalloco(p, sizeof(_xmlnode)); | |
61 | |
62 /* Initialize fields */ | |
63 if (type != NTYPE_CDATA) | |
64 result->name = pstrdup(p,name); | |
65 result->type = type; | |
66 result->p = p; | |
67 return result; | |
68 } | |
69 | |
70 static xmlnode _xmlnode_append_sibling(xmlnode lastsibling, const char* name, unsigned int type) | |
71 { | |
72 xmlnode result; | |
73 | |
74 result = _xmlnode_new(xmlnode_pool(lastsibling), name, type); | |
75 if (result != NULL) | |
76 { | |
77 /* Setup sibling pointers */ | |
78 result->prev = lastsibling; | |
79 lastsibling->next = result; | |
80 } | |
81 return result; | |
82 } | |
83 | |
84 static xmlnode _xmlnode_insert(xmlnode parent, const char* name, unsigned int type) | |
85 { | |
86 xmlnode result; | |
87 | |
88 if(parent == NULL || (type != NTYPE_CDATA && name == NULL)) return NULL; | |
89 | |
90 /* If parent->firstchild is NULL, simply create a new node for the first child */ | |
91 if (parent->firstchild == NULL) | |
92 { | |
93 result = _xmlnode_new(parent->p, name, type); | |
94 parent->firstchild = result; | |
95 } | |
96 /* Otherwise, append this to the lastchild */ | |
97 else | |
98 { | |
99 result= _xmlnode_append_sibling(parent->lastchild, name, type); | |
100 } | |
101 result->parent = parent; | |
102 parent->lastchild = result; | |
103 return result; | |
104 | |
105 } | |
106 | |
107 static xmlnode _xmlnode_search(xmlnode firstsibling, const char* name, unsigned int type) | |
108 { | |
109 xmlnode current; | |
110 | |
111 /* Walk the sibling list, looking for a NTYPE_TAG xmlnode with | |
112 the specified name */ | |
113 current = firstsibling; | |
114 while (current != NULL) | |
115 { | |
116 if ((current->type == type) && (j_strcmp(current->name, name) == 0)) | |
117 return current; | |
118 else | |
119 current = current->next; | |
120 } | |
121 return NULL; | |
122 } | |
123 | |
124 void _xmlnode_merge(xmlnode data) | |
125 { | |
126 xmlnode cur; | |
127 char *merge, *scur; | |
128 int imerge; | |
129 | |
130 /* get total size of all merged cdata */ | |
131 imerge = 0; | |
132 for(cur = data; cur != NULL && cur->type == NTYPE_CDATA; cur = cur->next) | |
133 imerge += cur->data_sz; | |
134 | |
135 /* copy in current data and then spin through all of them and merge */ | |
136 scur = merge = pmalloc(data->p,imerge + 1); | |
137 for(cur = data; cur != NULL && cur->type == NTYPE_CDATA; cur = cur->next) | |
138 { | |
139 memcpy(scur,cur->data,cur->data_sz); | |
140 scur += cur->data_sz; | |
141 } | |
142 *scur = '\0'; | |
143 | |
144 /* this effectively hides all of the merged-in chunks */ | |
145 data->next = cur; | |
146 if(cur == NULL) | |
147 data->parent->lastchild = data; | |
148 else | |
149 cur->prev = data; | |
150 | |
151 /* reset data */ | |
152 data->data = merge; | |
153 data->data_sz = imerge; | |
154 | |
155 } | |
156 | |
157 static void _xmlnode_hide_sibling(xmlnode child) | |
158 { | |
159 if(child == NULL) | |
160 return; | |
161 | |
162 if(child->prev != NULL) | |
163 child->prev->next = child->next; | |
164 if(child->next != NULL) | |
165 child->next->prev = child->prev; | |
166 } | |
167 | |
168 void _xmlnode_tag2str(spool s, xmlnode node, int flag) | |
169 { | |
170 xmlnode tmp; | |
171 | |
172 if(flag==0 || flag==1) | |
173 { | |
174 spooler(s,"<",xmlnode_get_name(node),s); | |
175 tmp = xmlnode_get_firstattrib(node); | |
176 while(tmp) { | |
177 spooler(s," ",xmlnode_get_name(tmp),"='",strescape(xmlnode_pool(node),xmlnode_get_data(tmp)),"'",s); | |
178 tmp = xmlnode_get_nextsibling(tmp); | |
179 } | |
180 if(flag==0) | |
181 spool_add(s,"/>"); | |
182 else | |
183 spool_add(s,">"); | |
184 } | |
185 else | |
186 { | |
187 spooler(s,"</",xmlnode_get_name(node),">",s); | |
188 } | |
189 } | |
190 | |
191 spool _xmlnode2spool(xmlnode node) | |
192 { | |
193 spool s; | |
194 int level=0,dir=0; | |
195 xmlnode tmp; | |
196 | |
197 if(!node || xmlnode_get_type(node)!=NTYPE_TAG) | |
198 return NULL; | |
199 | |
200 s = spool_new(xmlnode_pool(node)); | |
201 if(!s) return(NULL); | |
202 | |
203 while(1) | |
204 { | |
205 if(dir==0) | |
206 { | |
207 if(xmlnode_get_type(node) == NTYPE_TAG) | |
208 { | |
209 if(xmlnode_has_children(node)) | |
210 { | |
211 _xmlnode_tag2str(s,node,1); | |
212 node = xmlnode_get_firstchild(node); | |
213 level++; | |
214 continue; | |
215 }else{ | |
216 _xmlnode_tag2str(s,node,0); | |
217 } | |
218 }else{ | |
219 spool_add(s,strescape(xmlnode_pool(node),xmlnode_get_data(node))); | |
220 } | |
221 } | |
222 | |
223 tmp = xmlnode_get_nextsibling(node); | |
224 if(!tmp) | |
225 { | |
226 node = xmlnode_get_parent(node); | |
227 level--; | |
228 if(level>=0) _xmlnode_tag2str(s,node,2); | |
229 if(level<1) break; | |
230 dir = 1; | |
231 }else{ | |
232 node = tmp; | |
233 dir = 0; | |
234 } | |
235 } | |
236 | |
237 return s; | |
238 } | |
239 | |
240 | |
241 /* External routines */ | |
242 | |
243 | |
244 /* | |
245 * xmlnode_new_tag -- create a tag node | |
246 * Automatically creates a memory pool for the node. | |
247 * | |
248 * parameters | |
249 * name -- name of the tag | |
250 * | |
251 * returns | |
252 * a pointer to the tag node | |
253 * or NULL if it was unsuccessfull | |
254 */ | 21 */ |
255 xmlnode xmlnode_new_tag(const char* name) | 22 |
256 { | 23 /* A lot of this code at least resembles the code in libxode, but since |
257 return _xmlnode_new(NULL, name, NTYPE_TAG); | 24 * libxode uses memory pools that we simply have no need for, I decided to |
258 } | 25 * write my own stuff. Also, re-writing this lets me be as lightweight |
259 | 26 * as I want to be. Thank you libxode for giving me a good starting point */ |
260 | 27 |
261 /* | 28 #include "internal.h" |
262 * xmlnode_new_tag_pool -- create a tag node within given pool | 29 |
263 * | 30 #include <string.h> |
264 * parameters | 31 #include <glib.h> |
265 * p -- previously created memory pool | 32 |
266 * name -- name of the tag | 33 #include "xmlnode.h" |
267 * | 34 |
268 * returns | 35 static xmlnode* |
269 * a pointer to the tag node | 36 new_node(const char *name, NodeType type) |
270 * or NULL if it was unsuccessfull | 37 { |
271 */ | 38 xmlnode *node = g_new0(xmlnode, 1); |
272 xmlnode xmlnode_new_tag_pool(pool p, const char* name) | 39 if(name) |
273 { | 40 node->name = g_strdup(name); |
274 return _xmlnode_new(p, name, NTYPE_TAG); | 41 node->type = type; |
275 } | 42 |
276 | 43 return node; |
277 | 44 } |
278 /* | 45 |
279 * xmlnode_insert_tag -- append a child tag to a tag | 46 xmlnode* |
280 * | 47 xmlnode_new(const char *name) |
281 * parameters | 48 { |
282 * parent -- pointer to the parent tag | 49 g_return_val_if_fail(name != NULL, NULL); |
283 * name -- name of the child tag | 50 |
284 * | 51 return new_node(name, NODE_TYPE_TAG); |
285 * returns | 52 } |
286 * a pointer to the child tag node | 53 |
287 * or NULL if it was unsuccessfull | 54 xmlnode *xmlnode_new_child(xmlnode *parent, const char *name) |
288 */ | 55 { |
289 xmlnode xmlnode_insert_tag(xmlnode parent, const char* name) | 56 xmlnode *node; |
290 { | 57 |
291 return _xmlnode_insert(parent, name, NTYPE_TAG); | 58 g_return_val_if_fail(parent != NULL, NULL); |
292 } | 59 g_return_val_if_fail(name != NULL, NULL); |
293 | 60 |
294 | 61 node = new_node(name, NODE_TYPE_TAG); |
295 /* | 62 |
296 * xmlnode_insert_cdata -- append character data to a tag | 63 xmlnode_insert_child(parent, node); |
297 * | 64 |
298 * parameters | 65 return node; |
299 * parent -- parent tag | 66 } |
300 * CDATA -- character data | 67 |
301 * size -- size of CDATA | 68 void |
302 * or -1 for null-terminated CDATA strings | 69 xmlnode_insert_child(xmlnode *parent, xmlnode *child) |
303 * | 70 { |
304 * returns | 71 g_return_if_fail(parent != NULL); |
305 * a pointer to the child CDATA node | 72 g_return_if_fail(child != NULL); |
306 * or NULL if it was unsuccessfull | 73 |
307 */ | 74 child->parent = parent; |
308 xmlnode xmlnode_insert_cdata(xmlnode parent, const char* CDATA, unsigned int size) | 75 |
309 { | 76 if(parent->child) { |
310 xmlnode result; | 77 xmlnode *x; |
311 | 78 for(x = parent->child; x->next; x = x->next); |
312 if(CDATA == NULL || parent == NULL) | 79 x->next = child; |
313 return NULL; | 80 } else { |
314 | 81 parent->child = child; |
315 if(size == -1) | 82 } |
316 size = strlen(CDATA); | 83 } |
317 | 84 |
318 result = _xmlnode_insert(parent, NULL, NTYPE_CDATA); | 85 void |
319 if (result != NULL) | 86 xmlnode_insert_data(xmlnode *parent, const char *data, size_t size) |
320 { | 87 { |
321 result->data = (char*)pmalloc(result->p, size + 1); | 88 xmlnode *node; |
322 memcpy(result->data, CDATA, size); | 89 size_t real_size; |
323 result->data[size] = '\0'; | 90 |
324 result->data_sz = size; | 91 g_return_if_fail(parent != NULL); |
325 } | 92 g_return_if_fail(data != NULL); |
326 | 93 g_return_if_fail(size != 0); |
327 return result; | 94 |
328 } | 95 real_size = size == -1 ? strlen(data) : size; |
329 | 96 |
330 | 97 node = new_node(NULL, NODE_TYPE_DATA); |
331 /* | 98 |
332 * xmlnode_get_tag -- find given tag in an xmlnode tree | 99 node->data = g_memdup(data, real_size); |
333 * | 100 node->data_sz = real_size; |
334 * parameters | 101 |
335 * parent -- pointer to the parent tag | 102 xmlnode_insert_child(parent, node); |
336 * name -- "name" for the child tag of that name | 103 } |
337 * "name/name" for a sub child (recurses) | 104 |
338 * "?attrib" to match the first tag with that attrib defined | 105 void |
339 * "?attrib=value" to match the first tag with that attrib and value | 106 xmlnode_remove_attrib(xmlnode *node, const char *attr) |
340 * "=cdata" to match the cdata contents of the child | 107 { |
341 * or any combination: "name/name/?attrib", "name=cdata", etc | 108 xmlnode *attr_node, *sibling = NULL; |
342 * | 109 |
343 * results | 110 g_return_if_fail(node != NULL); |
344 * a pointer to the tag matching search criteria | 111 g_return_if_fail(attr != NULL); |
345 * or NULL if search was unsuccessfull | 112 |
346 */ | 113 for(attr_node = node->child; attr_node; attr_node = attr_node->next) |
347 xmlnode xmlnode_get_tag(xmlnode parent, const char* name) | 114 { |
348 { | 115 if(attr_node->type == NODE_TYPE_ATTRIB && |
349 char *str, *slash, *qmark, *equals; | 116 !strcmp(attr_node->name, attr)) { |
350 xmlnode step, ret; | 117 if(node->child == attr_node) { |
351 | 118 node->child = attr_node->next; |
352 | 119 } else { |
353 if(parent == NULL || parent->firstchild == NULL || name == NULL || name == '\0') return NULL; | 120 sibling->next = attr_node->next; |
354 | 121 } |
355 if(strstr(name, "/") == NULL && strstr(name,"?") == NULL && strstr(name, "=") == NULL) | 122 xmlnode_free(attr_node); |
356 return _xmlnode_search(parent->firstchild, name, NTYPE_TAG); | 123 return; |
357 | 124 } |
358 str = strdup(name); | 125 sibling = attr_node; |
359 slash = strstr(str, "/"); | 126 } |
360 qmark = strstr(str, "?"); | 127 } |
361 equals = strstr(str, "="); | 128 |
362 | 129 void |
363 if(equals != NULL && (slash == NULL || equals < slash) && (qmark == NULL || equals < qmark)) | 130 xmlnode_set_attrib(xmlnode *node, const char *attr, const char *value) |
364 { /* of type =cdata */ | 131 { |
365 | 132 xmlnode *attrib_node; |
366 *equals = '\0'; | 133 |
367 equals++; | 134 g_return_if_fail(node != NULL); |
368 | 135 g_return_if_fail(attr != NULL); |
369 for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step)) | 136 g_return_if_fail(value != NULL); |
370 { | 137 |
371 if(xmlnode_get_type(step) != NTYPE_TAG) | 138 xmlnode_remove_attrib(node, attr); |
372 continue; | 139 |
373 | 140 attrib_node = new_node(attr, NODE_TYPE_ATTRIB); |
374 if(*str != '\0') | 141 |
375 if(j_strcmp(xmlnode_get_name(step),str) != 0) | 142 attrib_node->data = g_strdup(value); |
376 continue; | 143 |
377 | 144 xmlnode_insert_child(node, attrib_node); |
378 if(j_strcmp(xmlnode_get_data(step),equals) != 0) | 145 } |
379 continue; | 146 |
380 | 147 const char* |
381 break; | 148 xmlnode_get_attrib(xmlnode *node, const char *attr) |
382 } | 149 { |
383 | 150 xmlnode *x; |
384 free(str); | 151 |
385 return step; | 152 g_return_val_if_fail(node != NULL, NULL); |
386 } | 153 |
387 | 154 for(x = node->child; x; x = x->next) { |
388 | 155 if(x->type == NODE_TYPE_ATTRIB && !strcmp(attr, x->name)) { |
389 if(qmark != NULL && (slash == NULL || qmark < slash)) | 156 return x->data; |
390 { /* of type ?attrib */ | 157 } |
391 | 158 } |
392 *qmark = '\0'; | 159 |
393 qmark++; | 160 return NULL; |
394 if(equals != NULL) | 161 } |
395 { | 162 |
396 *equals = '\0'; | 163 void xmlnode_free(xmlnode *node) |
397 equals++; | 164 { |
398 } | 165 xmlnode *x, *y; |
399 | 166 |
400 for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step)) | 167 g_return_if_fail(node != NULL); |
401 { | 168 |
402 if(xmlnode_get_type(step) != NTYPE_TAG) | 169 x = node->child; |
403 continue; | 170 while(x) { |
404 | 171 y = x->next; |
405 if(*str != '\0') | 172 xmlnode_free(x); |
406 if(j_strcmp(xmlnode_get_name(step),str) != 0) | 173 x = y; |
407 continue; | 174 } |
408 | 175 |
409 if(xmlnode_get_attrib(step,qmark) == NULL) | 176 if(node->name) |
410 continue; | 177 g_free(node->name); |
411 | 178 if(node->data) |
412 if(equals != NULL && j_strcmp(xmlnode_get_attrib(step,qmark),equals) != 0) | 179 g_free(node->data); |
413 continue; | 180 g_free(node); |
414 | 181 } |
415 break; | 182 |
416 } | 183 xmlnode* |
417 | 184 xmlnode_get_child(xmlnode *parent, const char *name) |
418 free(str); | 185 { |
419 return step; | 186 xmlnode *x, *ret = NULL; |
420 } | 187 char **names; |
421 | 188 char *parent_name, *child_name; |
422 | 189 |
423 *slash = '\0'; | 190 g_return_val_if_fail(parent != NULL, NULL); |
424 ++slash; | 191 |
425 | 192 names = g_strsplit(name, "/", 2); |
426 for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step)) | 193 parent_name = names[0]; |
427 { | 194 child_name = names[1]; |
428 if(xmlnode_get_type(step) != NTYPE_TAG) continue; | 195 |
429 | 196 for(x = parent->child; x; x = x->next) { |
430 if(j_strcmp(xmlnode_get_name(step),str) != 0) | 197 if(x->type == NODE_TYPE_TAG && name && !strcmp(parent_name, x->name)) { |
431 continue; | 198 ret = x; |
432 | 199 break; |
433 ret = xmlnode_get_tag(step, slash); | 200 } |
434 if(ret != NULL) | 201 } |
435 { | 202 |
436 free(str); | 203 if(child_name && ret) |
437 return ret; | 204 ret = xmlnode_get_child(x, child_name); |
438 } | 205 |
439 } | 206 g_strfreev(names); |
440 | 207 return ret; |
441 free(str); | 208 } |
442 return NULL; | 209 |
443 } | 210 char * |
444 | 211 xmlnode_get_data(xmlnode *node) |
445 | 212 { |
446 /* return the cdata from any tag */ | 213 GString *str; |
447 char *xmlnode_get_tag_data(xmlnode parent, const char *name) | 214 char *ret; |
448 { | 215 xmlnode *c; |
449 xmlnode tag; | 216 |
450 | 217 g_return_val_if_fail(node != NULL, NULL); |
451 tag = xmlnode_get_tag(parent, name); | 218 |
452 if(tag == NULL) return NULL; | 219 str = g_string_new(""); |
453 | 220 |
454 return xmlnode_get_data(tag); | 221 for(c = node->child; c; c = c->next) { |
455 } | 222 if(c->type == NODE_TYPE_DATA) |
456 | 223 str = g_string_append_len(str, c->data, c->data_sz); |
457 | 224 } |
458 void xmlnode_put_attrib(xmlnode owner, const char* name, const char* value) | 225 |
459 { | 226 ret = str->str; |
460 xmlnode attrib; | 227 g_string_free(str, FALSE); |
461 | 228 |
462 if(owner == NULL || name == NULL || value == NULL) return; | 229 return ret; |
463 | 230 } |
464 /* If there are no existing attributs, allocate a new one to start | 231 |
465 the list */ | 232 char *xmlnode_to_str(xmlnode *node) |
466 if (owner->firstattrib == NULL) | 233 { |
467 { | 234 char *ret; |
468 attrib = _xmlnode_new(owner->p, name, NTYPE_ATTRIB); | 235 GString *text = g_string_new(""); |
469 owner->firstattrib = attrib; | 236 xmlnode *c; |
470 owner->lastattrib = attrib; | 237 char *node_name, *esc, *esc2; |
471 } | 238 gboolean need_end = FALSE; |
472 else | 239 |
473 { | 240 node_name = g_markup_escape_text(node->name, -1); |
474 attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB); | 241 g_string_append_printf(text, "<%s", node_name); |
475 if(attrib == NULL) | 242 |
476 { | 243 |
477 attrib = _xmlnode_append_sibling(owner->lastattrib, name, NTYPE_ATTRIB); | 244 for(c = node->child; c; c = c->next) |
478 owner->lastattrib = attrib; | 245 { |
479 } | 246 if(c->type == NODE_TYPE_ATTRIB) { |
480 } | 247 esc = g_markup_escape_text(c->name, -1); |
481 /* Update the value of the attribute */ | 248 esc2 = g_markup_escape_text(c->data, -1); |
482 attrib->data_sz = strlen(value); | 249 g_string_append_printf(text, " %s='%s'", esc, esc2); |
483 attrib->data = pstrdup(owner->p, value); | 250 g_free(esc); |
484 | 251 g_free(esc2); |
485 } | 252 } else if(c->type == NODE_TYPE_TAG || c->type == NODE_TYPE_DATA) { |
486 | 253 need_end = TRUE; |
487 char* xmlnode_get_attrib(xmlnode owner, const char* name) | 254 } |
488 { | 255 } |
489 xmlnode attrib; | 256 |
490 | 257 if(need_end) { |
491 if (owner != NULL && owner->firstattrib != NULL) | 258 text = g_string_append_c(text, '>'); |
492 { | 259 |
493 attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB); | 260 for(c = node->child; c; c = c->next) |
494 if (attrib != NULL) | 261 { |
495 return (char*)attrib->data; | 262 if(c->type == NODE_TYPE_TAG) { |
496 } | 263 esc = xmlnode_to_str(c); |
497 return NULL; | 264 g_string_append_printf(text, "%s", esc); |
498 } | 265 g_free(esc); |
499 | 266 } else if(c->type == NODE_TYPE_DATA) { |
500 void xmlnode_put_vattrib(xmlnode owner, const char* name, void *value) | 267 esc = g_markup_escape_text(c->data, c->data_sz); |
501 { | 268 g_string_append_printf(text, "%s", esc); |
502 xmlnode attrib; | 269 g_free(esc); |
503 | 270 } |
504 if (owner != NULL) | 271 } |
505 { | 272 |
506 attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB); | 273 g_string_append_printf(text, "</%s>", node_name); |
507 if (attrib == NULL) | 274 } else { |
508 { | 275 g_string_append_printf(text, "/>"); |
509 xmlnode_put_attrib(owner, name, ""); | 276 } |
510 attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB); | 277 |
511 } | 278 g_free(node_name); |
512 if (attrib != NULL) | 279 |
513 attrib->firstchild = (xmlnode)value; | 280 ret = text->str; |
514 } | 281 g_string_free(text, FALSE); |
515 } | 282 return ret; |
516 | 283 } |
517 void* xmlnode_get_vattrib(xmlnode owner, const char* name) | 284 |
518 { | 285 struct _xmlnode_parser_data { |
519 xmlnode attrib; | 286 xmlnode *current; |
520 | 287 }; |
521 if (owner != NULL && owner->firstattrib != NULL) | 288 |
522 { | 289 static void |
523 attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB); | 290 xmlnode_parser_element_start(GMarkupParseContext *context, |
524 if (attrib != NULL) | 291 const char *element_name, const char **attrib_names, |
525 return (void*)attrib->firstchild; | 292 const char **attrib_values, gpointer user_data, GError **error) |
526 } | 293 { |
527 return NULL; | 294 struct _xmlnode_parser_data *xpd = user_data; |
528 } | 295 xmlnode *node; |
529 | 296 int i; |
530 xmlnode xmlnode_get_firstattrib(xmlnode parent) | 297 |
531 { | 298 if(!element_name) { |
532 if (parent != NULL) | 299 return; |
533 return parent->firstattrib; | 300 } else { |
534 return NULL; | 301 if(xpd->current) |
535 } | 302 node = xmlnode_new_child(xpd->current, element_name); |
536 | 303 else |
537 xmlnode xmlnode_get_firstchild(xmlnode parent) | 304 node = xmlnode_new(element_name); |
538 { | 305 |
539 if (parent != NULL) | 306 for(i=0; attrib_names[i]; i++) |
540 return parent->firstchild; | 307 xmlnode_set_attrib(node, attrib_names[i], attrib_values[i]); |
541 return NULL; | 308 |
542 } | 309 xpd->current = node; |
543 | 310 } |
544 xmlnode xmlnode_get_lastchild(xmlnode parent) | 311 } |
545 { | 312 |
546 if (parent != NULL) | 313 static void |
547 return parent->lastchild; | 314 xmlnode_parser_element_end(GMarkupParseContext *context, |
548 return NULL; | 315 const char *element_name, gpointer user_data, GError **error) |
549 } | 316 { |
550 | 317 struct _xmlnode_parser_data *xpd = user_data; |
551 xmlnode xmlnode_get_nextsibling(xmlnode sibling) | 318 |
552 { | 319 if(!element_name || !xpd->current) |
553 if (sibling != NULL) | 320 return; |
554 return sibling->next; | 321 |
555 return NULL; | 322 if(xpd->current->parent) { |
556 } | 323 if(!strcmp(xpd->current->name, element_name)) |
557 | 324 xpd->current = xpd->current->parent; |
558 xmlnode xmlnode_get_prevsibling(xmlnode sibling) | 325 } |
559 { | 326 } |
560 if (sibling != NULL) | 327 |
561 return sibling->prev; | 328 static void |
562 return NULL; | 329 xmlnode_parser_element_text(GMarkupParseContext *context, const char *text, |
563 } | 330 gsize text_len, gpointer user_data, GError **error) |
564 | 331 { |
565 xmlnode xmlnode_get_parent(xmlnode node) | 332 struct _xmlnode_parser_data *xpd = user_data; |
566 { | 333 |
567 if (node != NULL) | 334 if(!xpd->current) |
568 return node->parent; | 335 return; |
569 return NULL; | 336 |
570 } | 337 if(!text || !text_len) |
571 | 338 return; |
572 char* xmlnode_get_name(xmlnode node) | 339 |
573 { | 340 xmlnode_insert_data(xpd->current, text, text_len); |
574 if (node != NULL) | 341 } |
575 return node->name; | 342 |
576 return NULL; | 343 static GMarkupParser xmlnode_parser = { |
577 } | 344 xmlnode_parser_element_start, |
578 | 345 xmlnode_parser_element_end, |
579 char* xmlnode_get_data(xmlnode node) | 346 xmlnode_parser_element_text, |
580 { | 347 NULL, |
581 if(xmlnode_get_type(node) == NTYPE_TAG) /* loop till we find a CDATA in the children */ | 348 NULL |
582 for(node = xmlnode_get_firstchild(node); node != NULL; node = xmlnode_get_nextsibling(node)) | 349 }; |
583 if(xmlnode_get_type(node) == NTYPE_CDATA) break; | 350 |
584 | 351 |
585 if(node == NULL) return NULL; | 352 xmlnode *xmlnode_from_str(const char *str, size_t size) |
586 | 353 { |
587 /* check for a dirty node w/ unassembled cdata chunks */ | 354 struct _xmlnode_parser_data *xpd = g_new0(struct _xmlnode_parser_data, 1); |
588 if(xmlnode_get_type(node->next) == NTYPE_CDATA) | 355 xmlnode *ret; |
589 _xmlnode_merge(node); | 356 GMarkupParseContext *context; |
590 | 357 size_t real_size = size == -1 ? strlen(str) : size; |
591 return node->data; | 358 |
592 } | 359 context = g_markup_parse_context_new(&xmlnode_parser, 0, xpd, NULL); |
593 | 360 |
594 int xmlnode_get_datasz(xmlnode node) | 361 if(!g_markup_parse_context_parse(context, str, real_size, NULL)) { |
595 { | 362 while(xpd->current && xpd->current->parent) |
596 if(xmlnode_get_type(node) != NTYPE_CDATA) return 0; | 363 xpd->current = xpd->current->parent; |
597 | 364 xmlnode_free(xpd->current); |
598 /* check for a dirty node w/ unassembled cdata chunks */ | 365 xpd->current = NULL; |
599 if(xmlnode_get_type(node->next) == NTYPE_CDATA) | 366 } |
600 _xmlnode_merge(node); | 367 g_markup_parse_context_free(context); |
601 return node->data_sz; | 368 |
602 } | 369 ret = xpd->current; |
603 | 370 g_free(xpd); |
604 int xmlnode_get_type(xmlnode node) | 371 return ret; |
605 { | 372 } |
606 if (node != NULL) | |
607 return node->type; | |
608 return NTYPE_UNDEF; | |
609 } | |
610 | |
611 int xmlnode_has_children(xmlnode node) | |
612 { | |
613 if ((node != NULL) && (node->firstchild != NULL)) | |
614 return 1; | |
615 return 0; | |
616 } | |
617 | |
618 int xmlnode_has_attribs(xmlnode node) | |
619 { | |
620 if ((node != NULL) && (node->firstattrib != NULL)) | |
621 return 1; | |
622 return 0; | |
623 } | |
624 | |
625 pool xmlnode_pool(xmlnode node) | |
626 { | |
627 if (node != NULL) | |
628 return node->p; | |
629 return (pool)NULL; | |
630 } | |
631 | |
632 void xmlnode_hide(xmlnode child) | |
633 { | |
634 xmlnode parent; | |
635 | |
636 if(child == NULL || child->parent == NULL) | |
637 return; | |
638 | |
639 parent = child->parent; | |
640 | |
641 /* first fix up at the child level */ | |
642 _xmlnode_hide_sibling(child); | |
643 | |
644 /* next fix up at the parent level */ | |
645 if(parent->firstchild == child) | |
646 parent->firstchild = child->next; | |
647 if(parent->lastchild == child) | |
648 parent->lastchild = child->prev; | |
649 } | |
650 | |
651 void xmlnode_hide_attrib(xmlnode parent, const char *name) | |
652 { | |
653 xmlnode attrib; | |
654 | |
655 if(parent == NULL || parent->firstattrib == NULL || name == NULL) | |
656 return; | |
657 | |
658 attrib = _xmlnode_search(parent->firstattrib, name, NTYPE_ATTRIB); | |
659 if(attrib == NULL) | |
660 return; | |
661 | |
662 /* first fix up at the child level */ | |
663 _xmlnode_hide_sibling(attrib); | |
664 | |
665 /* next fix up at the parent level */ | |
666 if(parent->firstattrib == attrib) | |
667 parent->firstattrib = attrib->next; | |
668 if(parent->lastattrib == attrib) | |
669 parent->lastattrib = attrib->prev; | |
670 } | |
671 | |
672 | |
673 | |
674 /* | |
675 * xmlnode2str -- convert given xmlnode tree into a string | |
676 * | |
677 * parameters | |
678 * node -- pointer to the xmlnode structure | |
679 * | |
680 * results | |
681 * a pointer to the created string | |
682 * or NULL if it was unsuccessfull | |
683 */ | |
684 char *xmlnode2str(xmlnode node) | |
685 { | |
686 return spool_print(_xmlnode2spool(node)); | |
687 } | |
688 | |
689 /* | |
690 * xmlnode2tstr -- convert given xmlnode tree into a newline terminated string | |
691 * | |
692 * parameters | |
693 * node -- pointer to the xmlnode structure | |
694 * | |
695 * results | |
696 * a pointer to the created string | |
697 * or NULL if it was unsuccessfull | |
698 */ | |
699 char* xmlnode2tstr(xmlnode node) | |
700 { | |
701 spool s = _xmlnode2spool(node); | |
702 if (s != NULL) | |
703 spool_add(s, "\n"); | |
704 return spool_print(s); | |
705 } | |
706 | |
707 | |
708 /* loop through both a and b comparing everything, attribs, cdata, children, etc */ | |
709 int xmlnode_cmp(xmlnode a, xmlnode b) | |
710 { | |
711 int ret = 0; | |
712 | |
713 while(1) | |
714 { | |
715 if(a == NULL && b == NULL) | |
716 return 0; | |
717 | |
718 if(a == NULL || b == NULL) | |
719 return -1; | |
720 | |
721 if(xmlnode_get_type(a) != xmlnode_get_type(b)) | |
722 return -1; | |
723 | |
724 switch(xmlnode_get_type(a)) | |
725 { | |
726 case NTYPE_ATTRIB: | |
727 ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b)); | |
728 if(ret != 0) | |
729 return -1; | |
730 ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b)); | |
731 if(ret != 0) | |
732 return -1; | |
733 break; | |
734 case NTYPE_TAG: | |
735 ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b)); | |
736 if(ret != 0) | |
737 return -1; | |
738 ret = xmlnode_cmp(xmlnode_get_firstattrib(a), xmlnode_get_firstattrib(b)); | |
739 if(ret != 0) | |
740 return -1; | |
741 ret = xmlnode_cmp(xmlnode_get_firstchild(a), xmlnode_get_firstchild(b)); | |
742 if(ret != 0) | |
743 return -1; | |
744 break; | |
745 case NTYPE_CDATA: | |
746 ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b)); | |
747 if(ret != 0) | |
748 return -1; | |
749 } | |
750 a = xmlnode_get_nextsibling(a); | |
751 b = xmlnode_get_nextsibling(b); | |
752 } | |
753 } | |
754 | |
755 | |
756 xmlnode xmlnode_insert_tag_node(xmlnode parent, xmlnode node) | |
757 { | |
758 xmlnode child; | |
759 | |
760 child = xmlnode_insert_tag(parent, xmlnode_get_name(node)); | |
761 if (xmlnode_has_attribs(node)) | |
762 xmlnode_insert_node(child, xmlnode_get_firstattrib(node)); | |
763 if (xmlnode_has_children(node)) | |
764 xmlnode_insert_node(child, xmlnode_get_firstchild(node)); | |
765 | |
766 return child; | |
767 } | |
768 | |
769 /* places copy of node and node's siblings in parent */ | |
770 void xmlnode_insert_node(xmlnode parent, xmlnode node) | |
771 { | |
772 if(node == NULL || parent == NULL) | |
773 return; | |
774 | |
775 while(node != NULL) | |
776 { | |
777 switch(xmlnode_get_type(node)) | |
778 { | |
779 case NTYPE_ATTRIB: | |
780 xmlnode_put_attrib(parent, xmlnode_get_name(node), xmlnode_get_data(node)); | |
781 break; | |
782 case NTYPE_TAG: | |
783 xmlnode_insert_tag_node(parent, node); | |
784 break; | |
785 case NTYPE_CDATA: | |
786 xmlnode_insert_cdata(parent, xmlnode_get_data(node), xmlnode_get_datasz(node)); | |
787 } | |
788 node = xmlnode_get_nextsibling(node); | |
789 } | |
790 } | |
791 | |
792 | |
793 /* produce full duplicate of x with a new pool, x must be a tag! */ | |
794 xmlnode xmlnode_dup(xmlnode x) | |
795 { | |
796 xmlnode x2; | |
797 | |
798 if(x == NULL) | |
799 return NULL; | |
800 | |
801 x2 = xmlnode_new_tag(xmlnode_get_name(x)); | |
802 | |
803 if (xmlnode_has_attribs(x)) | |
804 xmlnode_insert_node(x2, xmlnode_get_firstattrib(x)); | |
805 if (xmlnode_has_children(x)) | |
806 xmlnode_insert_node(x2, xmlnode_get_firstchild(x)); | |
807 | |
808 return x2; | |
809 } | |
810 | |
811 xmlnode xmlnode_dup_pool(pool p, xmlnode x) | |
812 { | |
813 xmlnode x2; | |
814 | |
815 if(x == NULL) | |
816 return NULL; | |
817 | |
818 x2 = xmlnode_new_tag_pool(p, xmlnode_get_name(x)); | |
819 | |
820 if (xmlnode_has_attribs(x)) | |
821 xmlnode_insert_node(x2, xmlnode_get_firstattrib(x)); | |
822 if (xmlnode_has_children(x)) | |
823 xmlnode_insert_node(x2, xmlnode_get_firstchild(x)); | |
824 | |
825 return x2; | |
826 } | |
827 | |
828 xmlnode xmlnode_wrap(xmlnode x,const char *wrapper) | |
829 { | |
830 xmlnode wrap; | |
831 if(x==NULL||wrapper==NULL) return NULL; | |
832 wrap=xmlnode_new_tag_pool(xmlnode_pool(x),wrapper); | |
833 if(wrap==NULL) return NULL; | |
834 wrap->firstchild=x; | |
835 wrap->lastchild=x; | |
836 x->parent=wrap; | |
837 return wrap; | |
838 } | |
839 | |
840 void xmlnode_free(xmlnode node) | |
841 { | |
842 if(node == NULL) | |
843 return; | |
844 | |
845 pool_free(node->p); | |
846 } |