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 }