| 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 } |
|